diff options
author | Lorenzo Colitti <lorenzo@google.com> | 2011-01-14 12:26:05 -0800 |
---|---|---|
committer | Lorenzo Colitti <lorenzo@google.com> | 2011-01-15 19:02:58 -0800 |
commit | ba96e30fa08212e48ec1ff9c1d545b2d05e787b0 (patch) | |
tree | 51f64cc87bf9e2950bd7fc287f0bdf0dc8d4c92b | |
parent | 7bbd4580afe0347892a0680b5917ec7df38c003a (diff) | |
download | bionic-ba96e30fa08212e48ec1ff9c1d545b2d05e787b0.zip bionic-ba96e30fa08212e48ec1ff9c1d545b2d05e787b0.tar.gz bionic-ba96e30fa08212e48ec1ff9c1d545b2d05e787b0.tar.bz2 |
Only look up A records if the system has IPv4.
getaddrinfo only asks DNS for IPv6 addresses if the system
has IPv6 connectivity, but always asks for IPv4 addresses.
Don't ask for IPv4 addresses if there is no IPv4
connectivity.
Change-Id: Iefe9fcb006fabe60b4b11dd4653a7c4a406506f4
-rw-r--r-- | libc/netbsd/net/getaddrinfo.c | 88 |
1 files changed, 59 insertions, 29 deletions
diff --git a/libc/netbsd/net/getaddrinfo.c b/libc/netbsd/net/getaddrinfo.c index e7564c4..895662e 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 @@ -1667,17 +1687,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: |