Index | Thread | Search

From:
YASUOKA Masahiko <yasuoka@openbsd.org>
Subject:
Re: family inet[46]" and NULL for getaddrinfo()
To:
florian@openbsd.org
Cc:
tech@openbsd.org
Date:
Thu, 25 Jun 2026 06:56:35 +0900

Download raw body.

Thread
Hi,

On Tue, 23 Jun 2026 20:03:58 +0200
Florian Obser <florian@openbsd.org> wrote:
> On 2025-12-13 14:02 +09, YASUOKA Masahiko <yasuoka@openbsd.org> wrote:
>> 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.
>>
> 
> I think (and I've also checked with deraadt at g2k26) that using htonl()
> in the initializers is fine, it's a local variable.

Thanks for the review.  I updated the diff so it use const variables
for IPv4 as well.

> I'd prefer to have the v4 case symmetric to the v6 case.
> 
> I agree the change makes sense and I also like the simplification of the
> code.
> 
>> 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.67 getaddrinfo_async.c
--- lib/libc/asr/getaddrinfo_async.c	23 Jun 2026 11:36:36 -0000	1.67
+++ lib/libc/asr/getaddrinfo_async.c	24 Jun 2026 21:44:22 -0000
@@ -43,7 +43,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);
@@ -123,6 +122,26 @@ getaddrinfo_async_run(struct asr_query *
 		struct sockaddr_in	sain;
 		struct sockaddr_in6	sain6;
 	} sa;
+	const struct sockaddr_in	sockaddr_in_any = {
+		.sin_family = AF_INET,
+		.sin_len = sizeof(struct sockaddr_in),
+		.sin_addr = htonl(INADDR_ANY)
+	};
+	const struct sockaddr_in	sockaddr_in_lo = {
+		.sin_family = AF_INET,
+		.sin_len = sizeof(struct sockaddr_in),
+		.sin_addr = htonl(INADDR_LOOPBACK)
+	};
+	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) {
@@ -231,6 +250,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.
@@ -243,27 +264,26 @@ 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 ((r = addrinfo_add(as, &sa.sa,
-				    "localhost."))) {
+			/*
+			 * We could use statically built sockaddrs for
+			 * those, rather than parsing over and over.
+			 */
+			if (ai->ai_family == AF_UNSPEC ||
+			    ai->ai_family == AF_INET) {
+				if ((r = addrinfo_add(as,
+				    (const struct sockaddr *)((is_localhost)?
+				    &sockaddr_in_lo : &sockaddr_in_any),
+				    "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;
@@ -507,28 +527,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);
 }
 
 /*