From ba96e30fa08212e48ec1ff9c1d545b2d05e787b0 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Fri, 14 Jan 2011 12:26:05 -0800 Subject: [PATCH] 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 --- libc/netbsd/net/getaddrinfo.c | 88 +++++++++++++++++++++++------------ 1 file changed, 59 insertions(+), 29 deletions(-) diff --git a/libc/netbsd/net/getaddrinfo.c b/libc/netbsd/net/getaddrinfo.c index e7564c427..895662ed0 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: