From 187bcc3bf40bf187c5d76d206b04028fa8ca403b Mon Sep 17 00:00:00 2001 From: Rich Felker Date: Sat, 14 Jul 2018 20:20:19 -0400 Subject: implement getaddrinfo's AI_ADDRCONFIG flag this flag is notoriously under-/mis-specified, and in the past it was implemented as a nop, essentially considering the absence of a loopback interface with 127.0.0.1 and ::1 addresses an unsupported configuration. however, common real-world container environments omit IPv6 support (even for the network-namespaced loopback interface), and some kernels omit IPv6 support entirely. future systems on the other hand might omit IPv4 entirely. treat these as supported configurations and suppress results of the unconfigured/unsupported address families when AI_ADDRCONFIG is requested. use routability of the loopback address to make the determination; unlike other implementations, we do not exclude loopback from the "an address is configured" condition, since there is no basis in the specification for such exclusion. obtaining a result with AI_ADDRCONFIG does not imply routability of the result, and applications must still be able to cope with unroutable results even if they pass AI_ADDRCONFIG. --- src/network/getaddrinfo.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/network/getaddrinfo.c b/src/network/getaddrinfo.c index b9439f77..ba26847a 100644 --- a/src/network/getaddrinfo.c +++ b/src/network/getaddrinfo.c @@ -3,6 +3,10 @@ #include #include #include +#include +#include +#include +#include #include "lookup.h" int getaddrinfo(const char *restrict host, const char *restrict serv, const struct addrinfo *restrict hint, struct addrinfo **restrict res) @@ -43,6 +47,41 @@ int getaddrinfo(const char *restrict host, const char *restrict serv, const stru } } + if (flags & AI_ADDRCONFIG) { + /* Define the "an address is configured" condition for address + * families via ability to create a socket for the family plus + * routability of the loopback address for the family. */ + static const struct sockaddr_in lo4 = { + .sin_family = AF_INET, .sin_port = 65535, + .sin_addr.s_addr = __BYTE_ORDER == __BIG_ENDIAN + ? 0x7f000001 : 0x0100007f + }; + static const struct sockaddr_in6 lo6 = { + .sin6_family = AF_INET6, .sin6_port = 65535, + .sin6_addr = IN6ADDR_LOOPBACK_INIT + }; + int tf[2] = { AF_INET, AF_INET6 }; + const void *ta[2] = { &lo4, &lo6 }; + socklen_t tl[2] = { sizeof lo4, sizeof lo6 }; + for (i=0; i<2; i++) { + if (family==tf[1-i]) continue; + int s = socket(tf[i], SOCK_CLOEXEC|SOCK_DGRAM, + IPPROTO_UDP); + if (s>=0) { + int cs; + pthread_setcancelstate( + PTHREAD_CANCEL_DISABLE, &cs); + int r = connect(s, ta[i], tl[i]); + pthread_setcancelstate(cs, 0); + close(s); + if (!r) continue; + } + if (errno != EAFNOSUPPORT) return EAI_SYSTEM; + if (family == tf[i]) return EAI_NONAME; + family = tf[1-i]; + } + } + nservs = __lookup_serv(ports, serv, proto, socktype, flags); if (nservs < 0) return nservs; -- cgit v1.2.1