diff -ru glibc-2.16.90-67cbf9a2.orig/sysdeps/posix/getaddrinfo.c glibc-2.16.90-67cbf9a2/sysdeps/posix/getaddrinfo.c --- glibc-2.16.90-67cbf9a2.orig/sysdeps/posix/getaddrinfo.c 2012-12-07 21:55:19.000000000 +0000 +++ glibc-2.16.90-67cbf9a2/sysdeps/posix/getaddrinfo.c 2012-12-09 23:24:47.615410906 +0000 @@ -829,14 +829,47 @@ } } + /* AI_ADDRCONFIG determines whether or not we should suppress any + * hostname lookups. If the local host has only IPv4 interfaces, + * then suppress lookups for IPv6 addresses, and vice versa; if + * the local host has only IPv6 interfaces, suppress any lookups + * for IPv4 addresses.. + * + * Link-local IPv6 addresses and loopback addresses of either + * family are ignored when determining whether or not the host has + * an interface of the given address family, cf. __check_pf(). + * + * This logic is only applied for AF_UNSPEC. If the caller + * explicitly requested an address family, give him what he asked + * for. + * + * If we didn't find any interfaces of either address family, + * we ignore AI_ADDRCONFIG and return all available resutlts. */ + int suppress_af = 0; + if (req->ai_family == AF_UNSPEC && req->ai_flags & AI_ADDRCONFIG) + { + struct in6addrinfo *in6ai = NULL; + size_t in6ailen = 0; + bool seen_ipv4 = false; + bool seen_ipv6 = false; + __check_pf (&seen_ipv4, &seen_ipv6, &in6ai, &in6ailen); + __free_in6ai (in6ai); + + if(seen_ipv4 && !seen_ipv6) + suppress_af = AF_INET6; + else if(seen_ipv6 && !seen_ipv4) + suppress_af = AF_INET; + } + while (!no_more) { no_data = 0; nss_gethostbyname4_r fct4 = NULL; /* gethostbyname4_r sends out parallel A and AAAA queries and - is thus only suitable for PF_UNSPEC. */ - if (req->ai_family == PF_UNSPEC) + is thus only suitable for PF_UNSPEC, and only if we're not + suppressing lookups for IPv4 or IPv6 addresses. */ + if (req->ai_family == PF_UNSPEC && !suppress_af) fct4 = __nss_lookup_function (nip, "gethostbyname4_r"); if (fct4 != NULL) @@ -941,20 +974,22 @@ if (fct != NULL) { - if (req->ai_family == AF_INET6 - || req->ai_family == AF_UNSPEC) + if ((req->ai_family == AF_INET6 + || req->ai_family == AF_UNSPEC) + && suppress_af != AF_INET6) { gethosts (AF_INET6, struct in6_addr); no_inet6_data = no_data; inet6_status = status; } - if (req->ai_family == AF_INET - || req->ai_family == AF_UNSPEC - || (req->ai_family == AF_INET6 - && (req->ai_flags & AI_V4MAPPED) - /* Avoid generating the mapped addresses if we - know we are not going to need them. */ - && ((req->ai_flags & AI_ALL) || !got_ipv6))) + if ((req->ai_family == AF_INET + || req->ai_family == AF_UNSPEC + || (req->ai_family == AF_INET6 + && (req->ai_flags & AI_V4MAPPED) + /* Avoid generating the mapped addresses if we + know we are not going to need them. */ + && ((req->ai_flags & AI_ALL) || !got_ipv6))) + && suppress_af != AF_INET) { gethosts (AF_INET, struct in_addr); @@ -2373,7 +2408,6 @@ int nresults = 0; struct addrinfo *p = NULL; struct gaih_service gaih_service, *pservice; - struct addrinfo local_hints; if (name != NULL && name[0] == '*' && name[1] == 0) name = NULL; @@ -2399,42 +2433,6 @@ if ((hints->ai_flags & AI_CANONNAME) && name == NULL) return EAI_BADFLAGS; - struct in6addrinfo *in6ai = NULL; - size_t in6ailen = 0; - bool seen_ipv4 = false; - bool seen_ipv6 = false; - bool check_pf_called = false; - - if (hints->ai_flags & AI_ADDRCONFIG) - { - /* We might need information about what interfaces are available. - Also determine whether we have IPv4 or IPv6 interfaces or both. We - cannot cache the results since new interfaces could be added at - any time. */ - __check_pf (&seen_ipv4, &seen_ipv6, &in6ai, &in6ailen); - check_pf_called = true; - - /* Now make a decision on what we return, if anything. */ - if (hints->ai_family == PF_UNSPEC && (seen_ipv4 || seen_ipv6)) - { - /* If we haven't seen both IPv4 and IPv6 interfaces we can - narrow down the search. */ - if ((! seen_ipv4 || ! seen_ipv6) && (seen_ipv4 || seen_ipv6)) - { - local_hints = *hints; - local_hints.ai_family = seen_ipv4 ? PF_INET : PF_INET6; - hints = &local_hints; - } - } - else if ((hints->ai_family == PF_INET && ! seen_ipv4) - || (hints->ai_family == PF_INET6 && ! seen_ipv6)) - { - /* We cannot possibly return a valid answer. */ - __free_in6ai (in6ai); - return EAI_NONAME; - } - } - if (service && service[0]) { char *c; @@ -2443,10 +2441,7 @@ if (*c != '\0') { if (hints->ai_flags & AI_NUMERICSERV) - { - __free_in6ai (in6ai); return EAI_NONAME; - } gaih_service.num = -1; } @@ -2466,7 +2461,6 @@ if (last_i != 0) { freeaddrinfo (p); - __free_in6ai (in6ai); return -(last_i & GAIH_EAI); } @@ -2477,10 +2471,7 @@ } } else - { - __free_in6ai (in6ai); return EAI_FAMILY; - } if (naddrs > 1) { @@ -2495,9 +2486,12 @@ struct addrinfo *last = NULL; char *canonname = NULL; - /* Now we definitely need the interface information. */ - if (! check_pf_called) - __check_pf (&seen_ipv4, &seen_ipv6, &in6ai, &in6ailen); + /* Now we need the interface information. */ + struct in6addrinfo *in6ai = NULL; + size_t in6ailen = 0; + bool seen_ipv4 = false; + bool seen_ipv6 = false; + __check_pf (&seen_ipv4, &seen_ipv6, &in6ai, &in6ailen); /* If we have information about deprecated and temporary addresses sort the array now. */ @@ -2664,9 +2658,9 @@ /* Fill in the canonical name into the new first entry. */ p->ai_canonname = canonname; - } - __free_in6ai (in6ai); + __free_in6ai (in6ai); + } if (p) {