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, just 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
diff --git a/libc/inet/getaddrinfo.c b/libc/inet/getaddrinfo.c --- a/libc/inet/getaddrinfo.c +++ b/libc/inet/getaddrinfo.c @@ -62,6 +62,7 @@ #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 @@ #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 +923,7 @@ getaddrinfo(const char *name, const char *service, } else pservice = NULL;
+ __gai_conf_parse(); g = gaih; pg = NULL; p = NULL; --
Hi Leonid, Leonid Lisovskiy wrote,
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, just create /etc/gai.conf containing single line:
precedence ::ffff:0:0/96 100
Looks good to me. You have decided to make the keywords and syntax RFC and GNU libc compatible and just implement the simplest feature you need. If there are no complains I will add it soon.
best regards Waldemar
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 --- libc/inet/getaddrinfo.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 94 insertions(+), 3 deletions(-)
diff --git a/libc/inet/getaddrinfo.c b/libc/inet/getaddrinfo.c index 168adb1..090d7e9 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;
Hi Leonid, Leonid Lisovskiy wrote,
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
Applied and pushed, thx Waldemar