diff options
author | Leonid Lisovskiy <lly.dev@gmail.com> | 2015-12-28 20:17:08 +0300 |
---|---|---|
committer | Waldemar Brodkorb <wbx@uclibc-ng.org> | 2016-01-02 16:57:03 +0100 |
commit | bad1263e3316825b5ef0037ad7a897ae62a30d7e (patch) | |
tree | 034cf7c3dd94f3ead8c9d9492400711aba0c66c8 /libc/inet | |
parent | 86387d4eb3e8e6f97ab8b7f0e2d6988ed0e62e48 (diff) |
inet/getaddrinfo: simplest /etc/gai.conf to control IPv6/IPv4 addresses sort order
Implement simplest variant of /etc/gai.conf to control getaddrinfo
IPv6/IPv4 addresses sorting. Keep the default sort order - IPv6 first,
IPv4 second. To invert it, create /etc/gai.conf containing single line:
precedence ::ffff:0:0/96 100
Example before:
$ nslookup security.debian.org 8.8.8.8
Server: 8.8.8.8
Address 1: 8.8.8.8 google-public-dns-a.google.com
Name: security.debian.org
Address 1: 2001:a78:5:0:216:35ff:fe7f:be4f villa.debian.org
Address 2: 2001:a78:5:1:216:35ff:fe7f:6ceb lobos.debian.org
Address 3: 195.20.242.89 wieck.debian.org
Address 4: 212.211.132.250 lobos.debian.org
Address 5: 212.211.132.32 villa.debian.org
After patch & precedence set in /etc/gai.conf:
$ nslookup security.debian.org 8.8.8.8
Server: 8.8.8.8
Address 1: 8.8.8.8 google-public-dns-a.google.com
Name: security.debian.org
Address 1: 195.20.242.89 wieck.debian.org
Address 2: 212.211.132.250 lobos.debian.org
Address 3: 212.211.132.32 villa.debian.org
Address 4: 2001:a78:5:0:216:35ff:fe7f:be4f villa.debian.org
Address 5: 2001:a78:5:1:216:35ff:fe7f:6ceb lobos.debian.org
bloat-o-meter report:
function old new delta
getaddrinfo 726 1138 +412
gaih_inet 2660 2692 +32
.rodata 16618 16643 +25
__gai_precedence - 1 +1
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 4/0 up/down: 882/0) Total: 470 bytes
Signed-off-by: Leonid Lisovskiy <lly.dev@gmail.com>
Diffstat (limited to 'libc/inet')
-rw-r--r-- | libc/inet/getaddrinfo.c | 97 |
1 files changed, 94 insertions, 3 deletions
diff --git a/libc/inet/getaddrinfo.c b/libc/inet/getaddrinfo.c index 168adb115..090d7e96e 100644 --- a/libc/inet/getaddrinfo.c +++ b/libc/inet/getaddrinfo.c @@ -62,6 +62,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <stdbool.h> #include <arpa/inet.h> #include <sys/socket.h> #include <netinet/in.h> @@ -70,6 +71,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <sys/utsname.h> #include <net/if.h> #include <ifaddrs.h> +#include "internal/parse_config.h" #define GAIH_OKIFUNSPEC 0x0100 #define GAIH_EAI ~(GAIH_OKIFUNSPEC) @@ -348,6 +350,11 @@ gaih_inet_serv(const char *servicename, const struct gaih_typeproto *tp, return 0; } +#if defined __UCLIBC_HAS_IPV6__ +static uint8_t __gai_precedence = 0; /* =1 - IPv6, IPv4 + =2 - IPv4, IPv6 */ +#endif + /* NB: also uses h,pat,rc,no_data variables */ #define gethosts(_family, _type) \ { \ @@ -552,21 +559,32 @@ gaih_inet(const char *name, const struct gaih_service *service, if (at->family == AF_UNSPEC && !(req->ai_flags & AI_NUMERICHOST)) { struct hostent *h; struct gaih_addrtuple **pat = &at; - int no_data = 0; - int no_inet6_data; + int no_data, no_inet6_data; +#if defined __UCLIBC_HAS_IPV6__ + bool first_try = true; +#endif /* * If we are looking for both IPv4 and IPv6 address we don't want * the lookup functions to automatically promote IPv4 addresses to * IPv6 addresses. */ + no_inet6_data = no_data = 0; #if defined __UCLIBC_HAS_IPV6__ + if (__gai_precedence == 2) + goto try_v4; + + try_v6: if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6) if (!(req->ai_flags & AI_ADDRCONFIG) || (seen & SEEN_IPV6)) gethosts(AF_INET6, struct in6_addr); -#endif no_inet6_data = no_data; + if (!first_try) + goto tried_all; + first_try = false; + try_v4: +#endif if (req->ai_family == AF_INET || (!v4mapped && req->ai_family == AF_UNSPEC) || (v4mapped && (no_inet6_data != 0 || (req->ai_flags & AI_ALL))) @@ -574,7 +592,14 @@ gaih_inet(const char *name, const struct gaih_service *service, if (!(req->ai_flags & AI_ADDRCONFIG) || (seen & SEEN_IPV4)) gethosts(AF_INET, struct in_addr); } +#if defined __UCLIBC_HAS_IPV6__ + if (first_try) { + first_try = false; + goto try_v6; + } + tried_all: +#endif if (no_data != 0 && no_inet6_data != 0) { /* If both requests timed out report this. */ if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN) @@ -775,6 +800,71 @@ static const struct gaih gaih[] = { { PF_UNSPEC, NULL } }; +#if defined __UCLIBC_HAS_IPV6__ + +/* + * A call to getaddrinfo might return multiple answers. To provide + * possibility to change the sorting we must use /etc/gai.conf file, + * like glibc. + * + * gai.conf format: + * + * label <netmask> <precedence> + * The value is added to the label table used in + * the RFC 3484 sorting. If any label definition + * is present in the configuration file is present, + * the default table is not used. All the label + * definitions of the default table which are to + * be maintained have to be duplicated. + * precedence <netmask> <precedence> + * This keyword is similar to label, but instead + * the value is added to the precedence table as + * specified in RFC 3484. Once again, the presence + * of a single precedence line in the configuration + * file causes the default table to not be used. + * + * The simplified uclibc's implementation allows to change the IPv4/IPv6 + * sorting order for a whole address space only, i.e + * "precedence ::ffff:0:0/96 100" is only supported. + */ +static void __gai_conf_parse(void) +{ + /* NO reread of /etc/gai.conf on change. */ + if (__gai_precedence != 0) + return; + + __gai_precedence = 1; /* default IPv6 */ + + parser_t *parser; + char **tok = NULL; + + parser = config_open("/etc/gai.conf"); + if (!parser) + return; + + while (config_read(parser, &tok, 3, 3, "# \t", PARSE_NORMAL)) { + if (strcmp(tok[0], "precedence") == 0) { + char *pfx; + struct in6_addr mask; + + pfx = strchr(tok[1], '/'); + if (!pfx) + continue; + *(pfx++) = 0; + if (inet_pton(AF_INET6, tok[1], &mask) <= 0) + continue; + if (IN6_IS_ADDR_V4MAPPED(&mask) + && mask.s6_addr32[3] == 0 + && atoi(pfx) == 96 && atoi(tok[2]) == 100) + __gai_precedence = 2; /* IPv4 first */ + } + } + config_close(parser); +} +#else +# define __gai_conf_parse(x) +#endif /* __UCLIBC_HAS_IPV6__ */ + void freeaddrinfo(struct addrinfo *ai) { @@ -834,6 +924,7 @@ getaddrinfo(const char *name, const char *service, } else pservice = NULL; + __gai_conf_parse(); g = gaih; pg = NULL; p = NULL; |