diff --git a/headers/posix/netdb.h b/headers/posix/netdb.h index beac8170d9..975bea0f52 100644 --- a/headers/posix/netdb.h +++ b/headers/posix/netdb.h @@ -83,9 +83,9 @@ const char *hstrerror(int); #define AI_ADDRCONFIG 0x00000400 #define AI_V4MAPPED 0x00000800 -#define AI_MASK \ - (AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST | AI_NUMERICSERV | AI_ADDRCONFIG) - +#define AI_MASK \ + (AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST | AI_NUMERICSERV | AI_ADDRCONFIG | AI_ALL \ + | AI_V4MAPPED) /* getnameinfo */ #if defined(_DEFAULT_SOURCE) diff --git a/src/system/libnetwork/netresolv/Jamfile b/src/system/libnetwork/netresolv/Jamfile index d2095f66af..47605aab3a 100644 --- a/src/system/libnetwork/netresolv/Jamfile +++ b/src/system/libnetwork/netresolv/Jamfile @@ -64,6 +64,7 @@ for architectureObject in [ MultiArchSubDirSetup ] { getservent.c getservent_r.c hesiod.c + map_v4v6.c nsdispatch.c nslexer.l nsparser.y diff --git a/src/system/libnetwork/netresolv/net/getaddrinfo.c b/src/system/libnetwork/netresolv/net/getaddrinfo.c index 336a36af17..f0af65e722 100644 --- a/src/system/libnetwork/netresolv/net/getaddrinfo.c +++ b/src/system/libnetwork/netresolv/net/getaddrinfo.c @@ -88,9 +88,10 @@ __RCSID("$NetBSD: getaddrinfo.c,v 1.119.2.1 2020/11/29 11:25:31 martin Exp $"); #include -#include +#include "netdb_private.h" #include "nsswitch.h" #include "servent.h" +#include #ifndef RUMP_ACTION #ifdef __weak_alias @@ -136,17 +137,19 @@ static const struct afd { const char *a_addrany; const char *a_loopback; int a_scoped; -} afdl [] = { +} afdl[] = { #ifdef INET6 - {PF_INET6, sizeof(struct in6_addr), - sizeof(struct sockaddr_in6), - offsetof(struct sockaddr_in6, sin6_addr), - in6_addrany, in6_loopback, 1}, +#define N_INET6 0 + {PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6), + offsetof(struct sockaddr_in6, sin6_addr), in6_addrany, in6_loopback, 1}, +#define N_INET 1 +#define N_LOCAL 2 +#else +#define N_INET 0 +#define N_LOCAL 1 #endif - {PF_INET, sizeof(struct in_addr), - sizeof(struct sockaddr_in), - offsetof(struct sockaddr_in, sin_addr), - in_addrany, in_loopback, 0}, + {PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in), + offsetof(struct sockaddr_in, sin_addr), in_addrany, in_loopback, 0}, {0, 0, 0, 0, NULL, NULL, 0}, }; @@ -522,6 +525,24 @@ getaddrinfo(const char *hostname, const char *servname, } } + /* + * RFC 3493: AI_ALL and AI_V4MAPPED are effective only against + * AF_INET6 query. They need to be ignored if specified in other + * occassions. + */ + switch (pai->ai_flags & (AI_ALL | AI_V4MAPPED)) { + case AI_V4MAPPED: + case AI_ALL | AI_V4MAPPED: +#ifdef INET6 + if (pai->ai_family != AF_INET6) + pai->ai_flags &= ~(AI_ALL | AI_V4MAPPED); + break; +#endif + case AI_ALL: + pai->ai_flags &= ~(AI_ALL | AI_V4MAPPED); + break; + } + if ((pai->ai_flags & AI_ADDRCONFIG) != 0 && addrconfig(&mask) == -1) ERR(EAI_FAIL); @@ -925,6 +946,17 @@ set_source(struct ai_order *aio, struct policyhead *ph, if ((s = socket(ai.ai_family, ai.ai_socktype | SOCK_CLOEXEC, ai.ai_protocol)) < 0) return; /* give up */ + +#ifdef INET6 + if (ai.ai_family == AF_INET6) { + struct sockaddr_in6* sin6 = (struct sockaddr_in6*)ai.ai_addr; + int off = 0; + + if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) + (void)setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&off, sizeof(off)); + } +#endif + #if SOCK_CLOEXEC == 0 fcntl(s, F_SETFD, FD_CLOEXEC); #endif @@ -1308,7 +1340,7 @@ explore_numeric(const struct addrinfo *pai, const char *hostname, struct servent_data *svd) { const struct afd *afd; - struct addrinfo *cur; + struct addrinfo *cur, ai0; struct addrinfo sentinel; int error; char pton[PTON_MAX]; @@ -1369,26 +1401,35 @@ explore_numeric(const struct addrinfo *pai, const char *hostname, } break; default: - if (inet_pton(afd->a_af, hostname, pton) == 1) { - if (pai->ai_family == afd->a_af || - pai->ai_family == PF_UNSPEC /*?*/) { - GET_AI(cur->ai_next, afd, pton); - GET_PORT(cur->ai_next, servname, svd); - if ((pai->ai_flags & AI_CANONNAME)) { - /* - * Set the numeric address itself as - * the canonical name, based on a - * clarification in rfc2553bis-03. - */ - GET_CANONNAME(cur->ai_next, canonname); - } - while (cur->ai_next) - cur = cur->ai_next; - } else - ERR(EAI_FAMILY); /*xxx*/ + if (inet_pton(afd->a_af, hostname, pton) != 1) { + if (pai->ai_family != AF_INET6 || (pai->ai_flags & AI_V4MAPPED) != AI_V4MAPPED) + return 0; + if (inet_aton(hostname, (struct in_addr*)pton) != 1) + return 0; + afd = &afdl[N_INET]; + ai0 = *pai; + ai0.ai_family = AF_INET; + pai = &ai0; } break; } + if (pai->ai_family == afd->a_af || pai->ai_family == PF_UNSPEC /*?*/) { + GET_AI(cur->ai_next, afd, pton); + GET_PORT(cur->ai_next, servname, svd); + if (pai->ai_flags & AI_CANONNAME) { + /* + * Set the numeric address itself as + * the canonical name, based on a + * clarification in rfc2553bis-03. + */ + GET_CANONNAME(cur->ai_next, canonname); + } + while (cur->ai_next) + cur = cur->ai_next; + } else { + ERR(EAI_FAMILY); /*xxx*/ + } + *res = sentinel.ai_next; return 0; @@ -1503,6 +1544,7 @@ allocaddrinfo(socklen_t addrlen) return ai; } + static struct addrinfo * get_ai(const struct addrinfo *pai, const struct afd *afd, const char *addr) { @@ -1510,10 +1552,22 @@ get_ai(const struct addrinfo *pai, const struct afd *afd, const char *addr) struct addrinfo *ai; struct sockaddr *save; +#ifdef INET6 + struct in6_addr mapaddr; +#endif + _DIAGASSERT(pai != NULL); _DIAGASSERT(afd != NULL); _DIAGASSERT(addr != NULL); +#ifdef INET6 + if (afd->a_af == AF_INET && (pai->ai_flags & AI_V4MAPPED) != 0) { + afd = &afdl[N_INET6]; + _map_v4v6_address(addr, (char*)&mapaddr); + addr = (char*)&mapaddr; + } +#endif + ai = allocaddrinfo((socklen_t)afd->a_socklen); if (ai == NULL) return NULL; @@ -2097,10 +2151,13 @@ _dns_query(struct res_target *q, const struct addrinfo *pai, cur = cur->ai_next; } if (q2) { - ai = getanswer(res, buf2, q2->n, q2->name, q2->qtype, pai); - if (ai) - cur->ai_next = ai; - } + if (!ai || pai->ai_family != AF_UNSPEC + || (pai->ai_flags & (AI_ALL | AI_V4MAPPED)) != AI_V4MAPPED) { + ai = getanswer(res, buf2, q2->n, q2->name, q2->qtype, pai); + if (ai) + cur->ai_next = ai; + } + } free(buf); free(buf2); return sentinel.ai_next; @@ -2245,7 +2302,7 @@ _dns_host_lookup(const char *name, const struct addrinfo *pai) static int _dns_getaddrinfo(void *rv, void *cb_data, va_list ap) { - struct addrinfo *ai = NULL; + struct addrinfo ai0, *ai = NULL; const char *name, *servname; const struct addrinfo *pai; @@ -2253,6 +2310,12 @@ _dns_getaddrinfo(void *rv, void *cb_data, va_list ap) pai = va_arg(ap, const struct addrinfo *); servname = va_arg(ap, char *); + if (pai->ai_family == AF_INET6 && (pai->ai_flags & AI_V4MAPPED) == AI_V4MAPPED) { + ai0 = *pai; + ai0.ai_family = AF_UNSPEC; + pai = &ai0; + } + /* * Try doing SRV lookup on service first. */ @@ -2370,6 +2433,10 @@ _gethtent(FILE **hostf, const char *name, const struct addrinfo *pai) found: hints = *pai; hints.ai_flags = AI_NUMERICHOST; + + if (pai->ai_family == AF_INET6 && (pai->ai_flags & AI_V4MAPPED) == AI_V4MAPPED) + hints.ai_flags |= AI_V4MAPPED; + error = getaddrinfo(addr, NULL, &hints, &res0); if (error) goto again; @@ -2387,6 +2454,20 @@ found: return res0; } + +static struct addrinfo* +_getht(FILE** hostf, const char* name, const struct addrinfo* pai, struct addrinfo* cur) +{ + struct addrinfo* p; + + while ((p = _gethtent(hostf, name, pai)) != NULL) { + cur->ai_next = p; + while (cur && cur->ai_next) + cur = cur->ai_next; + } + return cur; +} + /*ARGSUSED*/ static int _files_getaddrinfo(void *rv, void *cb_data, va_list ap) @@ -2394,7 +2475,6 @@ _files_getaddrinfo(void *rv, void *cb_data, va_list ap) const char *name; const struct addrinfo *pai; struct addrinfo sentinel, *cur; - struct addrinfo *p; #ifndef _REENTRANT static #endif @@ -2407,10 +2487,18 @@ _files_getaddrinfo(void *rv, void *cb_data, va_list ap) cur = &sentinel; _sethtent(&hostf); - while ((p = _gethtent(&hostf, name, pai)) != NULL) { - cur->ai_next = p; - while (cur && cur->ai_next) - cur = cur->ai_next; + if (pai->ai_family == AF_INET6 && (pai->ai_flags & (AI_ALL | AI_V4MAPPED)) == AI_V4MAPPED) { + struct addrinfo ai0 = *pai; + + ai0.ai_flags &= ~AI_V4MAPPED; + cur = _getht(&hostf, name, &ai0, cur); + if (sentinel.ai_next == NULL) { + _sethtent(&hostf); + ai0.ai_flags |= AI_V4MAPPED; + cur = _getht(&hostf, name, &ai0, cur); + } + } else { + cur = _getht(&hostf, name, pai, cur); } _endhtent(&hostf); @@ -2475,6 +2563,8 @@ nextline: hints = *pai; hints.ai_flags = AI_NUMERICHOST; + if (pai->ai_family == AF_INET6 && (pai->ai_flags & AI_V4MAPPED) == AI_V4MAPPED) + hints.ai_flags |= AI_V4MAPPED; error = getaddrinfo(addr, NULL, &hints, &res0); if (error == 0) { for (res = res0; res; res = res->ai_next) { @@ -2522,15 +2612,44 @@ _yp_getaddrinfo(void *rv, void *cb_data, va_list ap) memset(&sentinel, 0, sizeof(sentinel)); cur = &sentinel; + /* ipnodes.byname can hold both IPv4/v6 */ + r = yp_match(ypdomain, "ipnodes.byname", name, (int)strlen(name), &ypbuf, &ypbuflen); + if (r == 0) { + ai = _yphostent(ypbuf, pai); + if (ai) { + cur->ai_next = ai; + while (cur && cur->ai_next) + cur = cur->ai_next; + } + free(ypbuf); + } + + if (ai != NULL) { + struct sockaddr_in6* sin6; + + switch (ai->ai_family) { + case AF_INET: + goto done; + case AF_INET6: + sin6 = (struct sockaddr_in6*)ai->ai_addr; + if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) + goto done; + break; + } + } + /* hosts.byname is only for IPv4 (Solaris8) */ - if (pai->ai_family == PF_UNSPEC || pai->ai_family == PF_INET) { + if (pai->ai_family == AF_UNSPEC || pai->ai_family == AF_INET + || ((pai->ai_family == AF_INET6 && (pai->ai_flags & AI_V4MAPPED) == AI_V4MAPPED) + && (ai == NULL || (pai->ai_flags & AI_ALL) == AI_ALL))) { r = yp_match(ypdomain, "hosts.byname", name, (int)strlen(name), &ypbuf, &ypbuflen); if (r == 0) { struct addrinfo ai4; ai4 = *pai; - ai4.ai_family = AF_INET; + if (pai->ai_family == AF_UNSPEC) + ai4.ai_family = AF_INET; ai = _yphostent(ypbuf, &ai4); if (ai) { cur->ai_next = ai; @@ -2541,16 +2660,7 @@ _yp_getaddrinfo(void *rv, void *cb_data, va_list ap) free(ypbuf); } - /* ipnodes.byname can hold both IPv4/v6 */ - r = yp_match(ypdomain, "ipnodes.byname", name, - (int)strlen(name), &ypbuf, &ypbuflen); - if (r == 0) { - ai = _yphostent(ypbuf, pai); - if (ai) - cur->ai_next = ai; - free(ypbuf); - } - +done: if (sentinel.ai_next == NULL) { h_errno = HOST_NOT_FOUND; return NS_NOTFOUND; diff --git a/src/system/libnetwork/netresolv/net/map_v4v6.c b/src/system/libnetwork/netresolv/net/map_v4v6.c new file mode 100644 index 0000000000..9ffdb387be --- /dev/null +++ b/src/system/libnetwork/netresolv/net/map_v4v6.c @@ -0,0 +1,80 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * ++Copyright++ 1985, 1988, 1993 + * - + * Copyright (c) 1985, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * - + * Portions Copyright (c) 1993 by Digital Equipment Corporation. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, and that + * the name of Digital Equipment Corporation not be used in advertising or + * publicity pertaining to distribution of the document or software without + * specific, written prior permission. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + * - + * --Copyright-- + */ + +#include +#include +#include +#include +#include + +#include "netdb_private.h" +#include +#include +#include +#include + +typedef union { + int32_t al; + char ac; +} align; + + +void +_map_v4v6_address(const char* src, char* dst) +{ + /* Our caller may update in place. */ + memmove(&dst[12], src, NS_INADDRSZ); + /* Mark this ipv6 addr as a mapped ipv4. */ + memset(&dst[10], 0xff, 2); + memset(&dst[0], 0, 10); +} diff --git a/src/system/libnetwork/netresolv/net/netdb_private.h b/src/system/libnetwork/netresolv/net/netdb_private.h new file mode 100644 index 0000000000..1949cc23f5 --- /dev/null +++ b/src/system/libnetwork/netresolv/net/netdb_private.h @@ -0,0 +1,27 @@ +/*- + * Copyright (C) 2005 The FreeBSD Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +void _map_v4v6_address(const char*, char*);