From: florian Date: Mon, 20 Nov 2023 12:15:16 +0000 (+0000) Subject: localhost is either 127.0.0.1 or ::1, nothing else. X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=373da8abb60949de81b32d3c8eaecdaabf17bdaa;p=openbsd localhost is either 127.0.0.1 or ::1, nothing else. RFC 6761, 6.3 Domain Name Reservation Considerations for "localhost.": 3. Name resolution APIs and libraries SHOULD recognize localhost names as special and SHOULD always return the IP loopback address for address queries and negative responses for all other query types. Name resolution APIs SHOULD NOT send queries for localhost names to their configured caching DNS server(s). This makes sure that the getaddrinfo(3) and gethostbyname(3) family of functions always return the loopback address and do not send queries to name servers. This includes "localhost", "localhost." and everything under ".localhost" and ".localhost.". For example, a host underneath the .com.ar zone will per default have a search list of "com.ar.". resolv.conf(5) has a default of "lookup bind file". Both combined will result in lookups for "localhost" to not return 127.0.0.1 because localhost.com.ar is registered in DNS. It has been known for decades that this is a problem, especially for localhost. Problem recently spotted by gonzalo@ and debugged by sthen@ Testing sthen, gonzalo Input & OK phessler, eric, millert OK sthen, kn, deraadt --- diff --git a/lib/libc/asr/asr_private.h b/lib/libc/asr/asr_private.h index 64f044f12d1..27f5421a946 100644 --- a/lib/libc/asr/asr_private.h +++ b/lib/libc/asr/asr_private.h @@ -1,4 +1,4 @@ -/* $OpenBSD: asr_private.h,v 1.48 2022/11/17 17:39:41 florian Exp $ */ +/* $OpenBSD: asr_private.h,v 1.49 2023/11/20 12:15:16 florian Exp $ */ /* * Copyright (c) 2012 Eric Faurot * @@ -299,6 +299,7 @@ int _asr_sockaddr_from_str(struct sockaddr *, int, const char *); ssize_t _asr_dname_from_fqdn(const char *, char *, size_t); ssize_t _asr_addr_as_fqdn(const char *, int, char *, size_t); int hnok_lenient(const char *); +int _asr_is_localhost(const char*); /* asr.c */ void _asr_resolver_done(void *); diff --git a/lib/libc/asr/asr_utils.c b/lib/libc/asr/asr_utils.c index 9ae9bfa863b..ab8603ab15a 100644 --- a/lib/libc/asr/asr_utils.c +++ b/lib/libc/asr/asr_utils.c @@ -1,4 +1,4 @@ -/* $OpenBSD: asr_utils.c,v 1.21 2023/03/15 22:12:00 millert Exp $ */ +/* $OpenBSD: asr_utils.c,v 1.22 2023/11/20 12:15:16 florian Exp $ */ /* * Copyright (c) 2009-2012 Eric Faurot * @@ -582,3 +582,35 @@ hnok_lenient(const char *dn) } return 1; } + +/* Check if the hostname is localhost or if it's in the localhost domain */ +int +_asr_is_localhost(const char *hn) +{ + size_t hnlen, localhostlen; + + if (hn == NULL) + return 0; + + if (strcasecmp(hn, "localhost") == 0 || + strcasecmp(hn, "localhost.") == 0) + return 1; + + hnlen = strlen(hn); + localhostlen = strlen(".localhost"); + + if (hnlen < localhostlen) + return 0; + + if (strcasecmp(hn + hnlen - localhostlen, ".localhost") == 0) + return 1; + + localhostlen++; + if (hnlen < localhostlen) + return 0; + + if (strcasecmp(hn + hnlen - localhostlen, ".localhost.") == 0) + return 1; + + return 0; +} diff --git a/lib/libc/asr/getaddrinfo_async.c b/lib/libc/asr/getaddrinfo_async.c index 5e0d186f315..d5a9561faf2 100644 --- a/lib/libc/asr/getaddrinfo_async.c +++ b/lib/libc/asr/getaddrinfo_async.c @@ -1,4 +1,4 @@ -/* $OpenBSD: getaddrinfo_async.c,v 1.59 2022/12/27 17:10:06 jmc Exp $ */ +/* $OpenBSD: getaddrinfo_async.c,v 1.60 2023/11/20 12:15:16 florian Exp $ */ /* * Copyright (c) 2012 Eric Faurot * @@ -115,7 +115,7 @@ getaddrinfo_async_run(struct asr_query *as, struct asr_result *ar) char fqdn[MAXDNAME]; const char *str; struct addrinfo *ai; - int i, family, r; + int i, family, r, is_localhost; FILE *f; union { struct sockaddr sa; @@ -228,8 +228,19 @@ getaddrinfo_async_run(struct asr_query *as, struct asr_result *ar) ar->ar_gai_errno = 0; - /* If hostname is NULL, use local address */ - if (as->as.ai.hostname == NULL) { + is_localhost = _asr_is_localhost(as->as.ai.hostname); + /* + * If hostname is NULL, "localhost" or falls within the + * ".localhost." domain, use local address. + * RFC 6761, 6.3: + * 3. Name resolution APIs and libraries SHOULD recognize + * localhost names as special and SHOULD always return the IP + * loopback address for address queries and negative responses + * for all other query types. Name resolution APIs SHOULD NOT + * send queries for localhost names to their configured caching + * DNS server(s). + */ + if (as->as.ai.hostname == NULL || is_localhost) { for (family = iter_family(as, 1); family != -1; family = iter_family(as, 0)) { @@ -238,11 +249,12 @@ getaddrinfo_async_run(struct asr_query *as, struct asr_result *ar) * those, rather than parsing over and over. */ if (family == PF_INET) - str = (ai->ai_flags & AI_PASSIVE) ? \ - "0.0.0.0" : "127.0.0.1"; + 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) ? \ - "::" : "::1"; + 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, NULL))) { diff --git a/lib/libc/asr/gethostnamadr_async.c b/lib/libc/asr/gethostnamadr_async.c index bdc00cbddfa..ed998cfde0c 100644 --- a/lib/libc/asr/gethostnamadr_async.c +++ b/lib/libc/asr/gethostnamadr_async.c @@ -1,4 +1,4 @@ -/* $OpenBSD: gethostnamadr_async.c,v 1.47 2023/11/14 08:27:33 florian Exp $ */ +/* $OpenBSD: gethostnamadr_async.c,v 1.48 2023/11/20 12:15:16 florian Exp $ */ /* * Copyright (c) 2012 Eric Faurot * @@ -209,6 +209,37 @@ gethostnamadr_async_run(struct asr_query *as, struct asr_result *ar) async_set_state(as, ASR_STATE_HALT); break; } + + /* + * If hostname is "localhost" or falls within the + * ".localhost." domain, use local address. + * RFC 6761, 6.3: + * 3. Name resolution APIs and libraries SHOULD + * recognize localhost names as special and SHOULD + * always return the IP loopback address for address + * queries and negative responses for all other query + * types. Name resolution APIs SHOULD NOT send queries + * for localhost names to their configured caching DNS + * server(s). + */ + + if (_asr_is_localhost(as->as.hostnamadr.name)) { + inet_pton(as->as.hostnamadr.family, + as->as.hostnamadr.family == AF_INET ? + "127.0.0.1" : "::1", addr); + h = hostent_from_addr(as->as.hostnamadr.family, + as->as.hostnamadr.name, addr); + if (h == NULL) { + ar->ar_errno = errno; + ar->ar_h_errno = NETDB_INTERNAL; + } + else { + ar->ar_hostent = &h->h; + ar->ar_h_errno = NETDB_SUCCESS; + } + async_set_state(as, ASR_STATE_HALT); + break; + } } async_set_state(as, ASR_STATE_NEXT_DB); break;