git: 1538284a5fdd - main - libc/resolv: Switch default to loopback address

From: Dag-Erling Smørgrav <des_at_FreeBSD.org>
Date: Sat, 31 Jan 2026 17:53:19 UTC
The branch main has been updated by des:

URL: https://cgit.FreeBSD.org/src/commit/?id=1538284a5fddfce546db678cb873b7edc6adb9ed

commit 1538284a5fddfce546db678cb873b7edc6adb9ed
Author:     Dag-Erling Smørgrav <des@FreeBSD.org>
AuthorDate: 2026-01-31 17:52:53 +0000
Commit:     Dag-Erling Smørgrav <des@FreeBSD.org>
CommitDate: 2026-01-31 17:52:53 +0000

    libc/resolv: Switch default to loopback address
    
    If no resolver configuration was found, we would fall back to INADDR_ANY
    and IN6ADDR_ANY.  This made sense when it was first written thirty or
    forty years ago but not today, especially since connecting to INADDR_ANY
    or IN6ADDR_ANY is no longer supported.  Switch to the loopback address
    and simplify the code.
    
    Note that (as the pre-existing comment in the code states) running
    without a resolver configuration is not really supported.  Still, if
    we're going to have a hardcoded fallback, it might as well work.
    
    PR:             291790
    MFC after:      1 week
    Reviewed by:    kevans
    Differential Revision:  https://reviews.freebsd.org/D55011
---
 lib/libc/resolv/res_init.c | 67 ++++++++++++++++++----------------------------
 1 file changed, 26 insertions(+), 41 deletions(-)

diff --git a/lib/libc/resolv/res_init.c b/lib/libc/resolv/res_init.c
index 5a2fce013c8c..b21a3fa1670f 100644
--- a/lib/libc/resolv/res_init.c
+++ b/lib/libc/resolv/res_init.c
@@ -118,21 +118,12 @@ static u_int32_t net_mask(struct in_addr);
 /*%
  * Set up default settings.  If the configuration file exist, the values
  * there will have precedence.  Otherwise, the server address is set to
- * INADDR_ANY and the default domain name comes from the gethostname().
- *
- * An interim version of this code (BIND 4.9, pre-4.4BSD) used 127.0.0.1
- * rather than INADDR_ANY ("0.0.0.0") as the default name server address
- * since it was noted that INADDR_ANY actually meant ``the first interface
- * you "ifconfig"'d at boot time'' and if this was a SLIP or PPP interface,
- * it had to be "up" in order for you to reach your own name server.  It
- * was later decided that since the recommended practice is to always 
- * install local static routes through 127.0.0.1 for all your network
- * interfaces, that we could solve this problem without a code change.
+ * the loopback address the default domain name comes from gethostname().
  *
  * The configuration file should always be used, since it is the only way
- * to specify a default domain.  If you are running a server on your local
- * machine, you should say "nameserver 0.0.0.0" or "nameserver 127.0.0.1"
- * in the configuration file.
+ * to specify options and a default domain.  If you are running a server
+ * on your local machine, you should say "nameserver 127.0.0.1" or
+ * "nameserver ::1" in the configuration file.
  *
  * Return 0 if completes successfully, -1 on error
  */
@@ -146,6 +137,26 @@ res_ninit(res_state statp) {
 /*% This function has to be reachable by res_data.c but not publicly. */
 int
 __res_vinit(res_state statp, int preinit) {
+	union res_sockaddr_union u[] = {
+		{ .sin = {
+			.sin_family = AF_INET,
+#ifdef HAVE_SA_LEN
+			.sin_len = sizeof(struct sockaddr_in),
+#endif
+			.sin_port = htons(NAMESERVER_PORT),
+			.sin_addr = { htonl(INADDR_LOOPBACK) },
+		} },
+#ifdef HAS_INET6_STRUCTS
+		{ .sin6 = {
+			.sin6_family = AF_INET6,
+#ifdef HAVE_SA_LEN
+			.sin6_len = sizeof(struct sockaddr_in6),
+#endif
+			.sin6_port = htons(NAMESERVER_PORT),
+			.sin6_addr = IN6ADDR_LOOPBACK_INIT,
+		} },
+#endif
+	};
 	FILE *fp;
 	char *cp, **pp;
 	int n;
@@ -158,7 +169,6 @@ __res_vinit(res_state statp, int preinit) {
 	char *net;
 #endif
 	int dots;
-	union res_sockaddr_union u[2];
 	int maxns = MAXNS;
 
 	RES_SET_H_ERRNO(statp, 0);
@@ -173,23 +183,6 @@ __res_vinit(res_state statp, int preinit) {
 
 	statp->id = res_nrandomid(statp);
 
-	memset(u, 0, sizeof(u));
-	u[nserv].sin.sin_addr.s_addr = INADDR_ANY;
-	u[nserv].sin.sin_family = AF_INET;
-	u[nserv].sin.sin_port = htons(NAMESERVER_PORT);
-#ifdef HAVE_SA_LEN
-	u[nserv].sin.sin_len = sizeof(struct sockaddr_in);
-#endif
-	nserv++;
-#ifdef HAS_INET6_STRUCTS
-	u[nserv].sin6.sin6_addr = in6addr_any;
-	u[nserv].sin6.sin6_family = AF_INET6;
-	u[nserv].sin6.sin6_port = htons(NAMESERVER_PORT);
-#ifdef HAVE_SA_LEN
-	u[nserv].sin6.sin6_len = sizeof(struct sockaddr_in6);
-#endif
-	nserv++;
-#endif
 	statp->nscount = 0;
 	statp->ndots = 1;
 	statp->pfcode = 0;
@@ -224,7 +217,7 @@ __res_vinit(res_state statp, int preinit) {
 #ifdef RESOLVSORT
 	statp->nsort = 0;
 #endif
-	res_setservers(statp, u, nserv);
+	res_setservers(statp, u, nitems(u));
 
 #ifdef	SOLARIS2
 	/*
@@ -288,7 +281,6 @@ __res_vinit(res_state statp, int preinit) {
 	(line[sizeof(name) - 1] == ' ' || \
 	 line[sizeof(name) - 1] == '\t'))
 
-	nserv = 0;
 	if ((fp = fopen(_PATH_RESCONF, "re")) != NULL) {
 	    struct stat sb;
 	    struct timespec now;
@@ -507,15 +499,8 @@ __res_vinit(res_state statp, int preinit) {
 #endif
 	    (void) fclose(fp);
 	}
-/*
- * Last chance to get a nameserver.  This should not normally
- * be necessary
- */
-#ifdef NO_RESOLV_CONF
-	if(nserv == 0)
-		nserv = get_nameservers(statp);
-#endif
 
+	/* guess default domain if not set */
 	if (statp->defdname[0] == 0 &&
 	    gethostname(buf, sizeof(statp->defdname) - 1) == 0 &&
 	    (cp = strchr(buf, '.')) != NULL)