diff options
author | shin <shin@FreeBSD.org> | 2000-04-20 03:31:40 +0000 |
---|---|---|
committer | shin <shin@FreeBSD.org> | 2000-04-20 03:31:40 +0000 |
commit | 8b8912f3ffaeeb9de2bb400275ac39c44808c755 (patch) | |
tree | 4868e6813d6e3e66f097f7adac970359e047ab0b /lib | |
parent | 445a25dc8313cc702e504bf1261865668ca8c86a (diff) | |
download | FreeBSD-src-8b8912f3ffaeeb9de2bb400275ac39c44808c755.zip FreeBSD-src-8b8912f3ffaeeb9de2bb400275ac39c44808c755.tar.gz |
Change getaddrinfo() resolve order
from
all AAAA trial, then all A trial
to
try AAAA and A for each trial
TODO: more fix for the case where IPv4 mapped IPv6 addr is disabled
Reviewed by: ume
Diffstat (limited to 'lib')
-rw-r--r-- | lib/libc/net/getaddrinfo.c | 67 | ||||
-rw-r--r-- | lib/libc/net/name6.c | 325 |
2 files changed, 317 insertions, 75 deletions
diff --git a/lib/libc/net/getaddrinfo.c b/lib/libc/net/getaddrinfo.c index a1f2726..3fae16d 100644 --- a/lib/libc/net/getaddrinfo.c +++ b/lib/libc/net/getaddrinfo.c @@ -108,7 +108,6 @@ static const struct afd { }; struct explore { - int e_af; int e_socktype; int e_protocol; const char *e_protostr; @@ -119,15 +118,10 @@ struct explore { }; static const struct explore explore[] = { -#ifdef INET6 - { PF_INET6, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, - { PF_INET6, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, - { PF_INET6, SOCK_RAW, ANY, NULL, 0x05 }, -#endif - { PF_INET, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, - { PF_INET, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, - { PF_INET, SOCK_RAW, ANY, NULL, 0x05 }, - { -1, 0, 0, NULL, 0 }, + { SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, + { SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, + { SOCK_RAW, ANY, NULL, 0x05 }, + { 0, 0, NULL, 0 }, }; #ifdef INET6 @@ -136,7 +130,8 @@ static const struct explore explore[] = { #define PTON_MAX 4 #endif - +extern struct hostent * _getipnodebyname_multi __P((const char *name, + int af, int flags, int *errp)); static int str_isnumber __P((const char *)); static int explore_fqdn __P((const struct addrinfo *, const char *, const char *, struct addrinfo **)); @@ -307,9 +302,7 @@ getaddrinfo(hostname, servname, hints, res) if (pai->ai_socktype != ANY && pai->ai_protocol != ANY) { int matched = 0; - for (ex = explore; ex->e_af >= 0; ex++) { - if (pai->ai_family != ex->e_af) - continue; + for (ex = explore; ex->e_socktype; ex++) { if (ex->e_socktype == ANY) continue; if (ex->e_protocol == ANY) @@ -353,10 +346,12 @@ getaddrinfo(hostname, servname, hints, res) } /* NULL hostname, or numeric hostname */ - for (ex = explore; ex->e_af >= 0; ex++) { + for (afd = afdl; afd->a_af; afd++) + { + for (ex = explore; ex->e_socktype; ex++) { *pai = ai0; - if (!MATCH_FAMILY(pai->ai_family, ex->e_af, WILD_AF(ex))) + if (!MATCH_FAMILY(pai->ai_family, afd->a_af, WILD_AF(ex))) continue; if (!MATCH(pai->ai_socktype, ex->e_socktype, WILD_SOCKTYPE(ex))) continue; @@ -364,7 +359,7 @@ getaddrinfo(hostname, servname, hints, res) continue; if (pai->ai_family == PF_UNSPEC) - pai->ai_family = ex->e_af; + pai->ai_family = afd->a_af; if (pai->ai_socktype == ANY && ex->e_socktype != ANY) pai->ai_socktype = ex->e_socktype; if (pai->ai_protocol == ANY && ex->e_protocol != ANY) @@ -381,6 +376,7 @@ getaddrinfo(hostname, servname, hints, res) while (cur && cur->ai_next) cur = cur->ai_next; } + } /* * XXX @@ -395,26 +391,11 @@ getaddrinfo(hostname, servname, hints, res) if (hostname == NULL) ERR(EAI_NONAME); - /* - * hostname as alphabetical name. - * we would like to prefer AF_INET6 than AF_INET, so we'll make a - * outer loop by AFs. - */ - for (afd = afdl; afd->a_af; afd++) { - *pai = ai0; - - if (!MATCH_FAMILY(pai->ai_family, afd->a_af, 1)) - continue; - - for (ex = explore; ex->e_af >= 0; ex++) { + /* hostname as alphabetical name. */ + { + for (ex = explore; ex->e_socktype; ex++) { *pai = ai0; - if (pai->ai_family == PF_UNSPEC) - pai->ai_family = afd->a_af; - - if (!MATCH_FAMILY(pai->ai_family, ex->e_af, - WILD_AF(ex))) - continue; if (!MATCH(pai->ai_socktype, ex->e_socktype, WILD_SOCKTYPE(ex))) { continue; @@ -424,8 +405,6 @@ getaddrinfo(hostname, servname, hints, res) continue; } - if (pai->ai_family == PF_UNSPEC) - pai->ai_family = ex->e_af; if (pai->ai_socktype == ANY && ex->e_socktype != ANY) pai->ai_socktype = ex->e_socktype; if (pai->ai_protocol == ANY && ex->e_protocol != ANY) @@ -485,12 +464,8 @@ explore_fqdn(pai, hostname, servname, res) if (get_portmatch(pai, servname) != 0) return 0; - afd = find_afd(pai->ai_family); - if (afd == NULL) - return 0; - - hp = getipnodebyname(hostname, pai->ai_family, AI_ADDRCONFIG, - &h_error); + hp = _getipnodebyname_multi(hostname, pai->ai_family, AI_ADDRCONFIG, + &h_error); if (hp == NULL) { switch (h_error) { case HOST_NOT_FOUND: @@ -520,7 +495,11 @@ explore_fqdn(pai, hostname, servname, res) af = hp->h_addrtype; ap = hp->h_addr_list[i]; - if (af != pai->ai_family) + if (pai->ai_family != AF_UNSPEC && af != pai->ai_family) + continue; + + afd = find_afd(af); + if (afd == NULL) continue; GET_AI(cur->ai_next, afd, ap); diff --git a/lib/libc/net/name6.c b/lib/libc/net/name6.c index e87f40f..ae05352 100644 --- a/lib/libc/net/name6.c +++ b/lib/libc/net/name6.c @@ -42,11 +42,13 @@ #include <sys/param.h> #include <sys/socket.h> #include <sys/time.h> +#include <sys/queue.h> #include <netinet/in.h> #include <arpa/inet.h> #include <arpa/nameser.h> +#include <errno.h> #include <netdb.h> #include <resolv.h> #include <stdio.h> @@ -255,8 +257,6 @@ _ghbyname(const char *name, int af, int flags, int *errp) if (flags & AI_ADDRCONFIG) { int s; - if ((s = socket(af, SOCK_DGRAM, 0)) < 0) - return NULL; /* * TODO: * Note that implementation dependent test for address @@ -264,7 +264,23 @@ _ghbyname(const char *name, int af, int flags, int *errp) * (or apropriate interval), * because addresses will be dynamically assigned or deleted. */ - _close(s); + if (af == AF_UNSPEC) { + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) + af = AF_INET; + else { + _close(s); + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + af = AF_INET6; + else + _close(s); + } + + } + if (af != AF_UNSPEC) { + if ((s = socket(af, SOCK_DGRAM, 0)) < 0) + return NULL; + _close(s); + } } for (i = 0; i < MAXHOSTCONF; i++) { @@ -277,16 +293,19 @@ _ghbyname(const char *name, int af, int flags, int *errp) return NULL; } +/* getipnodebyname() internal routine for multiple query(PF_UNSPEC) support. */ struct hostent * -getipnodebyname(const char *name, int af, int flags, int *errp) +_getipnodebyname_multi(const char *name, int af, int flags, int *errp) { struct hostent *hp; union inx_addr addrbuf; + /* XXX: PF_UNSPEC is only supposed to be passed from getaddrinfo() */ if (af != AF_INET #ifdef INET6 && af != AF_INET6 #endif + && af != PF_UNSPEC ) { *errp = NO_RECOVERY; @@ -341,6 +360,21 @@ getipnodebyname(const char *name, int af, int flags, int *errp) } struct hostent * +getipnodebyname(const char *name, int af, int flags, int *errp) +{ + if (af != AF_INET +#ifdef INET6 + && af != AF_INET6 +#endif + ) + { + *errp = NO_RECOVERY; + return NULL; + } + return(_getipnodebyname_multi(name, af ,flags, errp)); +} + +struct hostent * getipnodebyaddr(const void *src, size_t len, int af, int *errp) { struct hostent *hp; @@ -746,6 +780,7 @@ _files_ghbyname(const char *name, int af, int *errp) char *aliases[MAXALIASES + 1], *addrs[2]; union inx_addr addrbuf; char buf[BUFSIZ]; + int af0 = af; if ((fp = _files_open(errp)) == NULL) return NULL; @@ -766,11 +801,39 @@ _files_ghbyname(const char *name, int af, int *errp) } if (!match) continue; - if ((af == AF_INET - ? inet_aton(addrstr, (struct in_addr *)&addrbuf) - : inet_pton(af, addrstr, &addrbuf)) != 1) { + switch (af0) { + case AF_INET: + if (inet_aton(addrstr, (struct in_addr *)&addrbuf) + != 1) { + *errp = NO_DATA; /* name found */ + continue; + } + af = af0; + break; +#ifdef INET6 + case AF_INET6: + if (inet_pton(af, addrstr, &addrbuf) != 1) { + *errp = NO_DATA; /* name found */ + continue; + } + af = af0; + break; +#endif + case AF_UNSPEC: + if (inet_aton(addrstr, (struct in_addr *)&addrbuf) + == 1) { + af = AF_INET; + break; + } +#ifdef INET6 + if (inet_pton(AF_INET6, addrstr, &addrbuf) == 1) { + af = AF_INET6; + break; + } +#endif *errp = NO_DATA; /* name found */ continue; + /* NOTREACHED */ } hp = &hpbuf; hp->h_name = cname; @@ -842,6 +905,8 @@ _nis_ghbyname(const char *name, int af, int *errp) { struct hostent *hp = NULL; + if (af == AF_UNSPEC) + af = AF_INET; if (af == AF_INET) { hp = _gethostbynisname(name, af); if (hp != NULL) @@ -870,15 +935,22 @@ _nis_ghbyaddr(const void *addr, int addrlen, int af, int *errp) #define DNS_ASSERT(X) if (!(X)) { goto badanswer; } #endif +struct __res_type_list { + SLIST_ENTRY(__res_type_list) rtl_entry; + int rtl_type; +}; + static struct hostent * -_dns_ghbyname(const char *name, int af, int *errp) +_gethpbyanswer(answer, anslen, qtype, errp) + const u_char *answer; + int anslen; + int qtype; + int *errp; { int n; - u_char answer[BUFSIZ]; char tbuf[MAXDNAME+1]; HEADER *hp; - u_char *cp, *eom; - int qtype; + const u_char *cp, *eom; int type, class, ancount, qdcount; u_long ttl; char hostbuf[BUFSIZ]; @@ -889,30 +961,17 @@ _dns_ghbyname(const char *name, int af, int *errp) int buflen; int na, nh; - if ((_res.options & RES_INIT) == 0) { - if (res_init() < 0) { - *errp = h_errno; - return NULL; - } - } hbuf.h_aliases = alist; - hbuf.h_addrtype = af; - hbuf.h_length = ADDRLEN(af); - hbuf.h_addr_list = hlist; - na = nh = 0; - + hbuf.h_addrtype = #ifdef INET6 - qtype = (af == AF_INET6 ? T_AAAA : T_A); -#else - qtype = T_A; + (qtype == T_AAAA) ? AF_INET6 : #endif - n = res_search(name, C_IN, qtype, answer, sizeof(answer)); - if (n < 0) { - *errp = h_errno; - return NULL; - } + AF_INET; + hbuf.h_length = ADDRLEN(hbuf.h_addrtype); + hbuf.h_addr_list = hlist; + na = nh = 0; hp = (HEADER *)answer; - eom = answer + n; + eom = answer + anslen; ancount = ntohs(hp->ancount); qdcount = ntohs(hp->qdcount); DNS_ASSERT(qdcount == 1); @@ -994,6 +1053,210 @@ _dns_ghbyname(const char *name, int af, int *errp) return _hpcopy(&hbuf, errp); } +/* res_search() variant with multiple query support. */ +static struct hostent * +_res_search_multi(name, rtl, errp) + const char *name; /* domain name */ + struct __res_type_list *rtl; /* list of query types */ + int *errp; +{ + u_char answer[BUFSIZ]; /* buffer to put answer */ + const char *cp, * const *domain; + struct hostent *hp0 = NULL, *hp; + u_int dots; + int trailing_dot, ret, saved_herrno; + int got_nodata = 0, got_servfail = 0, tried_as_is = 0; + struct __res_type_list *rtl0 = rtl; + + if ((_res.options & RES_INIT) == 0 && res_init() == -1) { + *errp = NETDB_INTERNAL; + return (NULL); + } + dots = 0; + for (cp = name; *cp; cp++) + dots += (*cp == '.'); + trailing_dot = 0; + if (cp > name && *--cp == '.') + trailing_dot++; + + /* If there aren't any dots, it could be a user-level alias */ + if (!dots && (cp = hostalias(name)) != NULL) { + for(rtl = rtl0; rtl != NULL; + rtl = SLIST_NEXT(rtl, rtl_entry)) { + ret = res_query(cp, C_IN, rtl->rtl_type, answer, + sizeof(answer)); + if (ret > 0) { + hp = _gethpbyanswer(answer, ret, rtl->rtl_type, + errp); + hp0 = _hpmerge(hp0, hp, errp); + } + } + return (hp0); + } + + /* + * If there are dots in the name already, let's just give it a try + * 'as is'. The threshold can be set with the "ndots" option. + */ + saved_herrno = -1; + if (dots >= _res.ndots) { + for(rtl = rtl0; rtl != NULL; + rtl = SLIST_NEXT(rtl, rtl_entry)) { + ret = res_querydomain(name, NULL, C_IN, rtl->rtl_type, + answer, sizeof(answer)); + if (ret > 0) { + hp = _gethpbyanswer(answer, ret, rtl->rtl_type, + errp); + hp0 = _hpmerge(hp0, hp, errp); + } + } + if (hp0 != NULL) + return (hp0); + saved_herrno = *errp; + tried_as_is++; + } + + /* + * We do at least one level of search if + * - there is no dot and RES_DEFNAME is set, or + * - there is at least one dot, there is no trailing dot, + * and RES_DNSRCH is set. + */ + if ((!dots && (_res.options & RES_DEFNAMES)) || + (dots && !trailing_dot && (_res.options & RES_DNSRCH))) { + int done = 0; + + for (domain = (const char * const *)_res.dnsrch; + *domain && !done; + domain++) { + + for(rtl = rtl0; rtl != NULL; + rtl = SLIST_NEXT(rtl, rtl_entry)) { + ret = res_querydomain(name, *domain, C_IN, + rtl->rtl_type, + answer, sizeof(answer)); + if (ret > 0) { + hp = _gethpbyanswer(answer, ret, + rtl->rtl_type, + errp); + hp0 = _hpmerge(hp0, hp, errp); + } + } + if (hp0 != NULL) + return (hp0); + + /* + * If no server present, give up. + * If name isn't found in this domain, + * keep trying higher domains in the search list + * (if that's enabled). + * On a NO_DATA error, keep trying, otherwise + * a wildcard entry of another type could keep us + * from finding this entry higher in the domain. + * If we get some other error (negative answer or + * server failure), then stop searching up, + * but try the input name below in case it's + * fully-qualified. + */ + if (errno == ECONNREFUSED) { + *errp = TRY_AGAIN; + return (NULL); + } + + switch (*errp) { + case NO_DATA: + got_nodata++; + /* FALLTHROUGH */ + case HOST_NOT_FOUND: + /* keep trying */ + break; + case TRY_AGAIN: + if (((HEADER *)answer)->rcode == SERVFAIL) { + /* try next search element, if any */ + got_servfail++; + break; + } + /* FALLTHROUGH */ + default: + /* anything else implies that we're done */ + done++; + } + + /* if we got here for some reason other than DNSRCH, + * we only wanted one iteration of the loop, so stop. + */ + if (!(_res.options & RES_DNSRCH)) + done++; + } + } + + /* + * If we have not already tried the name "as is", do that now. + * note that we do this regardless of how many dots were in the + * name or whether it ends with a dot unless NOTLDQUERY is set. + */ + if (!tried_as_is && (dots || !(_res.options & RES_NOTLDQUERY))) { + for(rtl = rtl0; rtl != NULL; + rtl = SLIST_NEXT(rtl, rtl_entry)) { + ret = res_querydomain(name, NULL, C_IN, rtl->rtl_type, + answer, sizeof(answer)); + if (ret > 0) { + hp = _gethpbyanswer(answer, ret, rtl->rtl_type, + errp); + hp0 = _hpmerge(hp0, hp, errp); + } + } + if (hp0 != NULL) + return (hp0); + } + + /* if we got here, we didn't satisfy the search. + * if we did an initial full query, return that query's h_errno + * (note that we wouldn't be here if that query had succeeded). + * else if we ever got a nodata, send that back as the reason. + * else send back meaningless h_errno, that being the one from + * the last DNSRCH we did. + */ + if (saved_herrno != -1) + *errp = saved_herrno; + else if (got_nodata) + *errp = NO_DATA; + else if (got_servfail) + *errp = TRY_AGAIN; + return (NULL); +} + +static struct hostent * +_dns_ghbyname(const char *name, int af, int *errp) +{ + struct __res_type_list *rtl, rtl4; +#ifdef INET6 + struct __res_type_list rtl6; +#endif + +#ifdef INET6 + switch (af) { + case AF_UNSPEC: + SLIST_NEXT(&rtl4, rtl_entry) = NULL; rtl4.rtl_type = T_A; + SLIST_NEXT(&rtl6, rtl_entry) = &rtl4; rtl6.rtl_type = T_AAAA; + rtl = &rtl6; + break; + case AF_INET6: + SLIST_NEXT(&rtl6, rtl_entry) = NULL; rtl6.rtl_type = T_AAAA; + rtl = &rtl6; + break; + case AF_INET: + SLIST_NEXT(&rtl4, rtl_entry) = NULL; rtl4.rtl_type = T_A; + rtl = &rtl4; + break; + } +#else + SLIST_NEXT(&rtl4, rtl_entry) = NULL; rtl4.rtl_type = T_A; + rtl = &rtl4; +#endif + return(_res_search_multi(name, rtl, errp)); +} + static struct hostent * _dns_ghbyaddr(const void *addr, int addrlen, int af, int *errp) { |