diff options
author | ume <ume@FreeBSD.org> | 2015-12-25 11:17:21 +0000 |
---|---|---|
committer | ume <ume@FreeBSD.org> | 2015-12-25 11:17:21 +0000 |
commit | 55621219f54a49a3877e8da2ae39078ed442909f (patch) | |
tree | 4fae6957848e34a9978725bc4fd58583879faf43 | |
parent | 8feffa60ae68c48c771c3384447148f6102f0010 (diff) | |
download | FreeBSD-src-55621219f54a49a3877e8da2ae39078ed442909f.zip FreeBSD-src-55621219f54a49a3877e8da2ae39078ed442909f.tar.gz |
Add AI_V4MAPPED and AI_ALL support for getaddrinfo(3).
PR: 198092
-rw-r--r-- | include/netdb.h | 2 | ||||
-rw-r--r-- | lib/libc/net/getaddrinfo.3 | 46 | ||||
-rw-r--r-- | lib/libc/net/getaddrinfo.c | 158 |
3 files changed, 177 insertions, 29 deletions
diff --git a/include/netdb.h b/include/netdb.h index 9ed6e61..102567f 100644 --- a/include/netdb.h +++ b/include/netdb.h @@ -179,7 +179,7 @@ struct addrinfo { /* valid flags for addrinfo (not a standard def, apps should not use it) */ #define AI_MASK \ (AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST | AI_NUMERICSERV | \ - AI_ADDRCONFIG) + AI_ADDRCONFIG | AI_ALL | AI_V4MAPPED) #define AI_ALL 0x00000100 /* IPv6 and IPv4-mapped (with AI_V4MAPPED) */ #define AI_V4MAPPED_CFG 0x00000200 /* accept IPv4-mapped if kernel supports */ diff --git a/lib/libc/net/getaddrinfo.3 b/lib/libc/net/getaddrinfo.3 index 07fefc0..f87ec76 100644 --- a/lib/libc/net/getaddrinfo.3 +++ b/lib/libc/net/getaddrinfo.3 @@ -18,7 +18,7 @@ .\" .\" $FreeBSD$ .\" -.Dd October 5, 2015 +.Dd December 19, 2015 .Dt GETADDRINFO 3 .Os .Sh NAME @@ -126,11 +126,13 @@ field to which the parameter points shall be set to zero or be the bitwise-inclusive OR of one or more of the values .Dv AI_ADDRCONFIG , +.Dv AI_ALL , .Dv AI_CANONNAME , .Dv AI_NUMERICHOST , -.Dv AI_NUMERICSERV +.Dv AI_NUMERICSERV , +.Dv AI_PASSIVE and -.Dv AI_PASSIVE . +.Dv AI_V4MAPPED . .Bl -tag -width "AI_CANONNAMEXX" .It Dv AI_ADDRCONFIG If the @@ -139,6 +141,25 @@ bit is set, IPv4 addresses shall be returned only if an IPv4 address is configured on the local system, and IPv6 addresses shall be returned only if an IPv6 address is configured on the local system. +.It Dv AI_ALL +If the +.Dv AI_ALL +flag is used with the +.Dv AI_V4MAPPED +flag, then +.Fn getaddrinfo +shall return all matching IPv6 and IPv4 addresses. +.Pp +For example, when using the DNS, queries are made for both AAAA records and A records, and +.Fn getaddrinfo +returns the combined results of both queries. +Any IPv4 addresses found are returned as IPv4-mapped IPv6 addresses. +.Pp +The +.Dv AI_ALL +flag without the +.Dv AI_V4MAPPED +flag is ignored. .It Dv AI_CANONNAME If the .Dv AI_CANONNAME @@ -203,6 +224,25 @@ loopback address if is the null pointer and .Dv AI_PASSIVE is not set. +.It Dv AI_V4MAPPED +If the +.Dv AI_V4MAPPED +flag is specified along with an ai_family of +.Dv AF_INET6 , +then +.Fn getaddrinfo +shall return IPv4-mapped IPv6 addresses on finding no matching IPv6 addresses ( +.Fa ai_addrlen +shall be 16). +.Pp +For example, when using the DNS, if no AAAA records are found then a query is made for A records and any found are returned as IPv4-mapped IPv6 addresses. +.Pp +The +.Dv AI_V4MAPPED +flag shall be ignored unless +.Fa ai_family +equals +.Dv AF_INET6 . .El .El .Pp diff --git a/lib/libc/net/getaddrinfo.c b/lib/libc/net/getaddrinfo.c index 9bc2c9b..4d1972d 100644 --- a/lib/libc/net/getaddrinfo.c +++ b/lib/libc/net/getaddrinfo.c @@ -96,6 +96,7 @@ __FBSDID("$FreeBSD$"); #include <stdarg.h> #include <nsswitch.h> #include "un-namespace.h" +#include "netdb_private.h" #include "libc_private.h" #ifdef NS_CACHING #include "nscache.h" @@ -451,6 +452,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; + } + + /* * check for special cases. (1) numeric servname is disallowed if * socktype/protocol are left unspecified. (2) servname is disallowed * for raw and other inet{,6} sockets. @@ -842,6 +861,16 @@ 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 (_connect(s, ai.ai_addr, ai.ai_addrlen) < 0) goto cleanup; srclen = ai.ai_addrlen; @@ -1177,7 +1206,7 @@ explore_numeric(const struct addrinfo *pai, const char *hostname, const char *servname, struct addrinfo **res, const char *canonname) { const struct afd *afd; - struct addrinfo *ai; + struct addrinfo *ai, ai0; int error; char pton[PTON_MAX]; @@ -1201,8 +1230,17 @@ explore_numeric(const struct addrinfo *pai, const char *hostname, return 0; break; default: - if (inet_pton(afd->a_af, hostname, pton) != 1) - return 0; + 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; } @@ -1321,6 +1359,9 @@ get_ai(const struct addrinfo *pai, const struct afd *afd, const char *addr) char *fp_str; int translate = 0; #endif +#ifdef INET6 + struct in6_addr mapaddr; +#endif #ifdef FAITH /* @@ -1358,6 +1399,14 @@ get_ai(const struct addrinfo *pai, const struct afd *afd, const char *addr) } #endif +#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 = (struct addrinfo *)malloc(sizeof(struct addrinfo) + (afd->a_socklen)); if (ai == NULL) @@ -2191,7 +2240,7 @@ addr4sort(struct addrinfo *sentinel, res_state res) static int _dns_getaddrinfo(void *rv, void *cb_data, va_list ap) { - struct addrinfo *ai; + struct addrinfo *ai, ai0; querybuf *buf, *buf2; const char *hostname; const struct addrinfo *pai; @@ -2221,6 +2270,13 @@ _dns_getaddrinfo(void *rv, void *cb_data, va_list ap) return NS_NOTFOUND; } + if (pai->ai_family == AF_INET6 && + (pai->ai_flags & AI_V4MAPPED) == AI_V4MAPPED) { + ai0 = *pai; + ai0.ai_family = AF_UNSPEC; + pai = &ai0; + } + switch (pai->ai_family) { case AF_UNSPEC: q.name = hostname; @@ -2276,9 +2332,12 @@ _dns_getaddrinfo(void *rv, void *cb_data, va_list ap) cur = cur->ai_next; } } - ai = getanswer(buf, q.n, q.name, q.qtype, pai, res); - if (ai) - cur->ai_next = ai; + if (!ai || pai->ai_family != AF_UNSPEC || + (pai->ai_flags & (AI_ALL | AI_V4MAPPED)) != AI_V4MAPPED) { + ai = getanswer(buf, q.n, q.name, q.qtype, pai, res); + if (ai) + cur->ai_next = ai; + } free(buf); free(buf2); if (sentinel.ai_next == NULL) @@ -2360,6 +2419,9 @@ found: hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = 0; 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, "0", &hints, &res0); if (error) goto again; @@ -2387,6 +2449,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 +2470,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; FILE *hostf = NULL; name = va_arg(ap, char *); @@ -2404,11 +2479,19 @@ _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); *((struct addrinfo **)rv) = sentinel.ai_next; @@ -2468,6 +2551,9 @@ 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) { @@ -2515,15 +2601,46 @@ _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; @@ -2534,16 +2651,7 @@ _yp_getaddrinfo(void *rv, void *cb_data, va_list ap) } } - /* 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) { RES_SET_H_ERRNO(__res_state(), HOST_NOT_FOUND); return NS_NOTFOUND; |