Add AI_V4MAPPED and AI_ALL support for getaddrinfo(3).

PR:		198092
This commit is contained in:
ume 2015-12-25 11:17:21 +00:00
parent 8feffa60ae
commit 55621219f5
3 changed files with 177 additions and 29 deletions

View File

@ -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 */

View File

@ -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

View File

@ -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"
@ -450,6 +451,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
@ -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;