Index | Thread | Search

From:
YASUOKA Masahiko <yasuoka@openbsd.org>
Subject:
family inet[46]" and NULL for getaddrinfo() (was Re: "family inet[46]" and numeric address for getaddrinfo())
To:
florian@openbsd.org, tech@openbsd.org
Date:
Sat, 13 Dec 2025 14:02:36 +0900

Download raw body.

Thread
Hello,

On Wed, 17 Sep 2025 12:00:09 +0900 (JST)
YASUOKA Masahiko <yasuoka@openbsd.org> wrote:
> On Tue, 16 Sep 2025 13:27:36 +0200
> Florian Obser <florian@openbsd.org> wrote:
>> Coming back to an old email.
>> 
>> On 2025-03-07 09:43 +09, YASUOKA Masahiko <yasuoka@openbsd.org> wrote:
>>> Hello,
>>>
>>> sshd's config has the following default values,
>>>
>>>   #AddressFamily any
>>>   #ListenAddress 0.0.0.0
>>>   #ListenAddress ::
>>>
>>> If we configure "family" in /etc/resolv.conf, sshd will stop listening
>>> 0.0.0.0 or :: following the "family" config.
>>>
>>>   - "family inet4" => stop listening on ::
>>>   - "family inet6" => stop listening on 0.0.0.0
>>>
>>> This is because getaddrinfo() doesn't convert a numeric address if the
>>> family is disabled and NI_NUMERIC_HOST is not specified.
>>>
>>> I don't think this is correct behavior.
>> 
>> I don't think this is correct behaviour either.

Other than this problem, getaddrinfo can receive NULL for hostname.
Then returning "::" and "0.0.0.0" is expected when AI_PASSIVE.  I
suppose that "family" in "resolv.conf" must not affect this behavior
in this case as well, but our resolver changes the result.  (Only
"0.0.0.0" is returned when "family inet4" is configured).

Also, because the default sshd_config doesn't have "ListenAddress"
line, the previous diff hasn't fixed the original problem of sshd.

The diff fixes the problem.  It prepares the constant variables for
IPv6 but not for IPv4 because I avoided to use htonl() in the
initializers.

We can reproduce the problem in regress/lib/libc/getaddrinfo/ by
modifying "/etc/resolv.conf".

ok?

Index: lib/libc/asr/getaddrinfo_async.c
===================================================================
RCS file: /cvs/src/lib/libc/asr/getaddrinfo_async.c,v
diff -u -p -r1.64 getaddrinfo_async.c
--- lib/libc/asr/getaddrinfo_async.c	17 Sep 2025 10:47:30 -0000	1.64
+++ lib/libc/asr/getaddrinfo_async.c	13 Dec 2025 04:06:05 -0000
@@ -42,7 +42,6 @@ struct match {
 
 static int getaddrinfo_async_run(struct asr_query *, struct asr_result *);
 static int get_port(const char *, const char *, int);
-static int iter_family(struct asr_query *, int);
 static int addrinfo_add(struct asr_query *, const struct sockaddr *, const char *);
 static int addrinfo_from_file(struct asr_query *, int,  FILE *);
 static int addrinfo_from_pkt(struct asr_query *, char *, size_t);
@@ -122,6 +121,16 @@ getaddrinfo_async_run(struct asr_query *
 		struct sockaddr_in	sain;
 		struct sockaddr_in6	sain6;
 	} sa;
+	const struct sockaddr_in6	sockaddr_in6_any = {
+		.sin6_family = AF_INET6,
+		.sin6_len = sizeof(struct sockaddr_in6),
+		.sin6_addr = IN6ADDR_ANY_INIT
+	};
+	const struct sockaddr_in6	sockaddr_in6_lo = {
+		.sin6_family = AF_INET6,
+		.sin6_len = sizeof(struct sockaddr_in6),
+		.sin6_addr = IN6ADDR_LOOPBACK_INIT
+	};
 
     next:
 	switch (as->as_state) {
@@ -230,6 +239,8 @@ getaddrinfo_async_run(struct asr_query *
 
 		if (!(ai->ai_flags & AI_NUMERICHOST))
 			is_localhost = _asr_is_localhost(as->as.ai.hostname);
+		if (as->as.ai.hostname == NULL)
+			is_localhost = (ai->ai_flags & AI_PASSIVE)? 0 : 1;
 		/*
 		 * If hostname is NULL, "localhost" or falls within the
 		 * ".localhost." domain, use local address.
@@ -242,27 +253,25 @@ getaddrinfo_async_run(struct asr_query *
 		 * DNS server(s).
 		 */
 		if (as->as.ai.hostname == NULL || is_localhost) {
-			for (family = iter_family(as, 1);
-			    family != -1;
-			    family = iter_family(as, 0)) {
-				/*
-				 * We could use statically built sockaddrs for
-				 * those, rather than parsing over and over.
-				 */
-				if (family == PF_INET)
-					str = (ai->ai_flags & AI_PASSIVE &&
-					    !is_localhost) ? "0.0.0.0" :
-					    "127.0.0.1";
-				else /* PF_INET6 */
-					str = (ai->ai_flags & AI_PASSIVE &&
-					    !is_localhost) ? "::" : "::1";
-				 /* This can't fail */
-				_asr_sockaddr_from_str(&sa.sa, family, str);
+			if (ai->ai_family == AF_UNSPEC ||
+			    ai->ai_family == AF_INET) {
+				memset(&sa, 0, sizeof(sa));
+				sa.sain.sin_family = AF_INET;
+				sa.sain.sin_len = sizeof(struct sockaddr_in);
+				sa.sain.sin_addr.s_addr = htonl((is_localhost)?
+				    INADDR_LOOPBACK : INADDR_ANY);
 				if ((r = addrinfo_add(as, &sa.sa,
-				    "localhost."))) {
+				    "localhost.")))
+					ar->ar_gai_errno = r;
+			}
+			if (ar->ar_gai_errno == 0 &&
+			    (ai->ai_family == AF_UNSPEC ||
+			    ai->ai_family == AF_INET6)) {
+				if ((r = addrinfo_add(as,
+				    (const struct sockaddr *)((is_localhost)?
+				    &sockaddr_in6_lo : &sockaddr_in6_any),
+				    "localhost.")))
 					ar->ar_gai_errno = r;
-					break;
-				}
 			}
 			if (ar->ar_gai_errno == 0 && as->as_count == 0) {
 				ar->ar_gai_errno = EAI_NODATA;
@@ -503,28 +512,6 @@ get_port(const char *servname, const cha
 	endservent_r(&sed);
 
 	return (port);
-}
-
-/*
- * Iterate over the address families that are to be queried. Use the
- * list on the async context, unless a specific family was given in hints.
- */
-static int
-iter_family(struct asr_query *as, int first)
-{
-	if (first) {
-		as->as_family_idx = 0;
-		if (as->as.ai.hints.ai_family != PF_UNSPEC)
-			return as->as.ai.hints.ai_family;
-		return AS_FAMILY(as);
-	}
-
-	if (as->as.ai.hints.ai_family != PF_UNSPEC)
-		return (-1);
-
-	as->as_family_idx++;
-
-	return AS_FAMILY(as);
 }
 
 /*