Add AI_V4MAPPED and AI_ALL support for getaddrinfo(3).
PR: 198092
This commit is contained in:
parent
8feffa60ae
commit
55621219f5
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user