summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libc/include/netinet/in6.h4
-rw-r--r--libc/netbsd/net/getaddrinfo.c142
2 files changed, 100 insertions, 46 deletions
diff --git a/libc/include/netinet/in6.h b/libc/include/netinet/in6.h
index 2f5fee1..efd5c0a 100644
--- a/libc/include/netinet/in6.h
+++ b/libc/include/netinet/in6.h
@@ -60,6 +60,10 @@
#define IN6_IS_ADDR_SITELOCAL(a) \
(((a)->s6_addr[0] == 0xfe) && (((a)->s6_addr[1] & 0xc0) == 0xc0))
+/* RFC 4193. */
+#define IN6_IS_ADDR_ULA(a) \
+ (((a)->s6_addr[0] & 0xfe) == 0xfc)
+
#define IN6_IS_ADDR_MULTICAST(a) \
(((__const uint8_t *) (a))[0] == 0xff)
diff --git a/libc/netbsd/net/getaddrinfo.c b/libc/netbsd/net/getaddrinfo.c
index e7564c4..e4d8c56 100644
--- a/libc/netbsd/net/getaddrinfo.c
+++ b/libc/netbsd/net/getaddrinfo.c
@@ -340,35 +340,55 @@ str2number(const char *p)
return -1;
}
-/* Determine whether IPv6 connectivity is available. */
+/*
+ * Connect a UDP socket to a given unicast address. This will cause no network
+ * traffic, but will fail fast if the system has no or limited reachability to
+ * the destination (e.g., no IPv4 address, no IPv6 default route, ...).
+ */
static int
-_have_ipv6() {
- /*
- * Connect a UDP socket to an global unicast IPv6 address. This will
- * cause no network traffic, but will fail fast if the system has no or
- * limited IPv6 connectivity (e.g., only a link-local address).
- */
- static const struct sockaddr_in6 sin6_test = {
- /* family, port, flow label */
- AF_INET6, 0, 0,
- /* 2000:: */
- {{{ 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }}},
- /* scope ID */
- 0};
- sockaddr_union addr_test;
- addr_test.in6 = sin6_test;
- int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+_test_connect(int pf, struct sockaddr *addr, size_t addrlen) {
+ int s = socket(pf, SOCK_DGRAM, IPPROTO_UDP);
if (s < 0)
return 0;
int ret;
do {
- ret = connect(s, &addr_test.generic, sizeof(addr_test.in6));
+ ret = connect(s, addr, addrlen);
} while (ret < 0 && errno == EINTR);
- int have_ipv6 = (ret == 0);
+ int success = (ret == 0);
do {
ret = close(s);
} while (ret < 0 && errno == EINTR);
- return have_ipv6;
+ return success;
+}
+
+/*
+ * The following functions determine whether IPv4 or IPv6 connectivity is
+ * available in order to implement AI_ADDRCONFIG.
+ *
+ * Strictly speaking, AI_ADDRCONFIG should not look at whether connectivity is
+ * available, but whether addresses of the specified family are "configured
+ * on the local system". However, bionic doesn't currently support getifaddrs,
+ * so checking for connectivity is the next best thing.
+ */
+static int
+_have_ipv6() {
+ static const struct sockaddr_in6 sin6_test = {
+ .sin6_family = AF_INET6,
+ .sin6_addr.s6_addr = { // 2000::
+ 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+ };
+ sockaddr_union addr = { .in6 = sin6_test };
+ return _test_connect(PF_INET6, &addr.generic, sizeof(addr.in6));
+}
+
+static int
+_have_ipv4() {
+ static const struct sockaddr_in sin_test = {
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = __constant_htonl(0x08080808L) // 8.8.8.8
+ };
+ sockaddr_union addr = { .in = sin_test };
+ return _test_connect(PF_INET, &addr.generic, sizeof(addr.in));
}
int
@@ -1299,11 +1319,13 @@ _get_scope(const struct sockaddr *addr)
if (IN_LOOPBACK(na) || /* 127.0.0.0/8 */
(na & 0xffff0000) == 0xa9fe0000) { /* 169.254.0.0/16 */
return IPV6_ADDR_SCOPE_LINKLOCAL;
- } else if ((na & 0xff000000) == 0x0a000000 || /* 10.0.0.0/8 */
- (na & 0xfff00000) == 0xac100000 || /* 172.16.0.0/12 */
- (na & 0xffff0000) == 0xc0a80000) { /* 192.168.0.0/16 */
- return IPV6_ADDR_SCOPE_SITELOCAL;
} else {
+ /*
+ * According to draft-ietf-6man-rfc3484-revise-01 section 2.3,
+ * it is best not to treat the private IPv4 ranges
+ * (10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16) as being
+ * in a special scope, so we don't.
+ */
return IPV6_ADDR_SCOPE_GLOBAL;
}
} else {
@@ -1325,9 +1347,13 @@ _get_scope(const struct sockaddr *addr)
#define IN6_IS_ADDR_6TO4(a) \
(((a)->s6_addr[0] == 0x20) && ((a)->s6_addr[1] == 0x02))
+/* 6bone testing address area (3ffe::/16), deprecated in RFC 3701. */
+#define IN6_IS_ADDR_6BONE(a) \
+ (((a)->s6_addr[0] == 0x3f) && ((a)->s6_addr[1] == 0xfe))
+
/*
* Get the label for a given IPv4/IPv6 address.
- * RFC 3484, section 2.1, plus Teredo added in with label 5.
+ * RFC 3484, section 2.1, plus changes from draft-ietf-6man-rfc3484-revise-01.
*/
/*ARGSUSED*/
@@ -1335,19 +1361,27 @@ static int
_get_label(const struct sockaddr *addr)
{
if (addr->sa_family == AF_INET) {
- return 4;
+ return 3;
} else if (addr->sa_family == AF_INET6) {
const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr;
if (IN6_IS_ADDR_LOOPBACK(&addr6->sin6_addr)) {
return 0;
- } else if (IN6_IS_ADDR_V4COMPAT(&addr6->sin6_addr)) {
+ } else if (IN6_IS_ADDR_ULA(&addr6->sin6_addr)) {
+ return 1;
+ } else if (IN6_IS_ADDR_V4MAPPED(&addr6->sin6_addr)) {
return 3;
+ } else if (IN6_IS_ADDR_6TO4(&addr6->sin6_addr)) {
+ return 4;
} else if (IN6_IS_ADDR_TEREDO(&addr6->sin6_addr)) {
return 5;
- } else if (IN6_IS_ADDR_6TO4(&addr6->sin6_addr)) {
- return 2;
+ } else if (IN6_IS_ADDR_V4COMPAT(&addr6->sin6_addr)) {
+ return 10;
+ } else if (IN6_IS_ADDR_SITELOCAL(&addr6->sin6_addr)) {
+ return 11;
+ } else if (IN6_IS_ADDR_6BONE(&addr6->sin6_addr)) {
+ return 12;
} else {
- return 1;
+ return 2;
}
} else {
/*
@@ -1360,7 +1394,7 @@ _get_label(const struct sockaddr *addr)
/*
* Get the precedence for a given IPv4/IPv6 address.
- * RFC 3484, section 2.1, plus Teredo added in with precedence 25.
+ * RFC 3484, section 2.1, plus changes from draft-ietf-6man-rfc3484-revise-01.
*/
/*ARGSUSED*/
@@ -1368,22 +1402,28 @@ static int
_get_precedence(const struct sockaddr *addr)
{
if (addr->sa_family == AF_INET) {
- return 10;
+ return 30;
} else if (addr->sa_family == AF_INET6) {
const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr;
if (IN6_IS_ADDR_LOOPBACK(&addr6->sin6_addr)) {
+ return 60;
+ } else if (IN6_IS_ADDR_ULA(&addr6->sin6_addr)) {
return 50;
- } else if (IN6_IS_ADDR_V4COMPAT(&addr6->sin6_addr)) {
+ } else if (IN6_IS_ADDR_V4MAPPED(&addr6->sin6_addr)) {
+ return 30;
+ } else if (IN6_IS_ADDR_6TO4(&addr6->sin6_addr)) {
return 20;
} else if (IN6_IS_ADDR_TEREDO(&addr6->sin6_addr)) {
- return 25;
- } else if (IN6_IS_ADDR_6TO4(&addr6->sin6_addr)) {
- return 30;
+ return 10;
+ } else if (IN6_IS_ADDR_V4COMPAT(&addr6->sin6_addr) ||
+ IN6_IS_ADDR_SITELOCAL(&addr6->sin6_addr) ||
+ IN6_IS_ADDR_6BONE(&addr6->sin6_addr)) {
+ return 1;
} else {
return 40;
}
} else {
- return 5;
+ return 1;
}
}
@@ -1667,17 +1707,27 @@ _dns_getaddrinfo(void *rv, void *cb_data, va_list ap)
q.qclass = C_IN;
q.answer = buf->buf;
q.anslen = sizeof(buf->buf);
- /* If AI_ADDRCONFIG, lookup IPv6 only if we have connectivity */
- if (!(pai->ai_flags & AI_ADDRCONFIG) || _have_ipv6()) {
+ int query_ipv6 = 1, query_ipv4 = 1;
+ if (pai->ai_flags & AI_ADDRCONFIG) {
+ query_ipv6 = _have_ipv6();
+ query_ipv4 = _have_ipv4();
+ }
+ if (query_ipv6) {
q.qtype = T_AAAA;
- q.next = &q2;
- q2.name = name;
- q2.qclass = C_IN;
- q2.qtype = T_A;
- q2.answer = buf2->buf;
- q2.anslen = sizeof(buf2->buf);
- } else {
+ if (query_ipv4) {
+ q.next = &q2;
+ q2.name = name;
+ q2.qclass = C_IN;
+ q2.qtype = T_A;
+ q2.answer = buf2->buf;
+ q2.anslen = sizeof(buf2->buf);
+ }
+ } else if (query_ipv4) {
q.qtype = T_A;
+ } else {
+ free(buf);
+ free(buf2);
+ return NS_NOTFOUND;
}
break;
case AF_INET: