summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorume <ume@FreeBSD.org>2015-12-18 17:53:18 +0000
committerume <ume@FreeBSD.org>2015-12-18 17:53:18 +0000
commit360683f7540313cc6eba3fc15a17d1911810ad8b (patch)
tree2590eceac0a8fcf2d16c1cf1111e68b76f1c98c1
parent4a57c82f71d4dc58cefcceb5b41b611808ea0a65 (diff)
downloadFreeBSD-src-360683f7540313cc6eba3fc15a17d1911810ad8b.zip
FreeBSD-src-360683f7540313cc6eba3fc15a17d1911810ad8b.tar.gz
Add AI_V4MAPPED and AI_ALL support for getaddrinfo(3).
PR: 198092 MFC after: 1 week
-rw-r--r--lib/libc/net/getaddrinfo.346
-rw-r--r--lib/libc/net/getaddrinfo.c156
2 files changed, 174 insertions, 28 deletions
diff --git a/lib/libc/net/getaddrinfo.3 b/lib/libc/net/getaddrinfo.3
index 1d6cb56..69601e6 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
@@ -130,11 +130,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 .
For a UNIX-domain address,
.Fa ai_flags
is ignored.
@@ -146,6 +148,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
@@ -210,6 +231,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 ab6b65f..b37e4fa 100644
--- a/lib/libc/net/getaddrinfo.c
+++ b/lib/libc/net/getaddrinfo.c
@@ -93,6 +93,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"
@@ -470,6 +471,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.
@@ -861,6 +880,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;
@@ -1199,7 +1228,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], path[PATH_MAX], *p;
@@ -1236,8 +1265,17 @@ explore_numeric(const struct addrinfo *pai, const char *hostname,
p = pton;
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;
+ }
p = pton;
break;
}
@@ -1354,6 +1392,15 @@ get_ai(const struct addrinfo *pai, const struct afd *afd, const char *addr)
{
char *p;
struct addrinfo *ai;
+#ifdef INET6
+ struct in6_addr mapaddr;
+
+ 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));
@@ -2192,7 +2239,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;
@@ -2222,6 +2269,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;
@@ -2277,9 +2331,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)
@@ -2361,6 +2418,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;
@@ -2388,6 +2448,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)
@@ -2395,7 +2469,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 *);
@@ -2405,11 +2478,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;
@@ -2469,6 +2550,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) {
@@ -2516,15 +2600,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;
@@ -2535,16 +2650,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;
OpenPOWER on IntegriCloud