diff --git a/libc/netbsd/net/getaddrinfo.c b/libc/netbsd/net/getaddrinfo.c index 778f44c21..bb6645c85 100644 --- a/libc/netbsd/net/getaddrinfo.c +++ b/libc/netbsd/net/getaddrinfo.c @@ -347,35 +347,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)); } // Returns 0 on success, else returns non-zero on error (in which case @@ -1525,9 +1545,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*/ @@ -1535,19 +1559,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 { /* @@ -1560,7 +1592,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*/ @@ -1568,22 +1600,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; } } @@ -1867,17 +1905,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: