From owner-freebsd-current Sat Apr 1 14:55:26 2000 Delivered-To: freebsd-current@freebsd.org Received: from fgwmail7.fujitsu.co.jp (fgwmail7.fujitsu.co.jp [192.51.44.37]) by hub.freebsd.org (Postfix) with ESMTP id 7113D37B6F7 for ; Sat, 1 Apr 2000 14:55:13 -0800 (PST) (envelope-from shin@nd.net.fujitsu.co.jp) Received: from m1.gw.fujitsu.co.jp by fgwmail7.fujitsu.co.jp (8.9.3/3.7W-MX0002-Fujitsu Gateway) id HAA07291; Sun, 2 Apr 2000 07:55:05 +0900 (JST) (envelope-from shin@nd.net.fujitsu.co.jp) Received: from incapgw.fujitsu.co.jp by m1.gw.fujitsu.co.jp (8.9.3/3.7W-0003-Fujitsu Domain Master) id HAA23981; Sun, 2 Apr 2000 07:54:57 +0900 (JST) Received: from localhost ([192.168.245.183]) by incapgw.fujitsu.co.jp (8.9.3/3.7W-0002) id HAA15398; Sun, 2 Apr 2000 07:54:53 +0900 (JST) To: cpiazza@jaxon.net Cc: current@FreeBSD.ORG Subject: Re: 75 second delay using telnet/ssh (ipv6 related) In-Reply-To: <20000321105026G.shin@nd.net.fujitsu.co.jp> References: <20000319150009.A404@norn.ca.eu.org> <20000321105026G.shin@nd.net.fujitsu.co.jp> X-Mailer: Mew version 1.94 on Emacs 20.4 / Mule 4.0 (HANANOEN) X-Prom-Mew: Prom-Mew 1.93.4 (procmail reader for Mew) Mime-Version: 1.0 Content-Type: Multipart/Mixed; boundary="--Next_Part(Sun_Apr__2_07:55:48_2000_518)--" Content-Transfer-Encoding: 7bit Message-Id: <20000402075551N.shin@nd.net.fujitsu.co.jp> Date: Sun, 02 Apr 2000 07:55:51 +0900 From: Yoshinobu Inoue X-Dispatcher: imput version 990905(IM130) Lines: 647 Sender: owner-freebsd-current@FreeBSD.ORG Precedence: bulk X-Loop: FreeBSD.ORG ----Next_Part(Sun_Apr__2_07:55:48_2000_518)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit > Hi, > > > This is kind of weird, so I want to see if anyone else has noticed > > this or has a solution to it. > > > > If I use telnet or ssh (there might be more programs, > > but I have only noticed these two so far), and supply a hostname to it, > > my machine is constantly requesting AAAA records, and finally after > > 75 seconds it requests and receives an A record from the nameserver. > > Currently, using -4 option is a workaround for the problem, > but I think this should be fixed by a resolver change as > discussed on this list before. > > The change is from, > all AAAA trial, then all A trial, > to > try AAAA and A for each trial. > > Sorry for the inconvenience and I'll try the fix. Sorry to be late, but I tried resolver fix and it seems to work. This should remove such 75 seconds delay in apps which use getaddrinfo(). Please review and try this patches. Thanks, Yoshinobu Inoue ----Next_Part(Sun_Apr__2_07:55:48_2000_518)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="resolv.diff" Index: getaddrinfo.c =================================================================== RCS file: /home/ncvs/src/lib/libc/net/getaddrinfo.c,v retrieving revision 1.9 diff -u -r1.9 getaddrinfo.c --- getaddrinfo.c 2000/02/19 16:10:12 1.9 +++ getaddrinfo.c 2000/04/01 20:38:03 @@ -108,7 +108,6 @@ }; struct explore { - int e_af; int e_socktype; int e_protocol; const char *e_protostr; @@ -119,15 +118,10 @@ }; 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 @@ #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 @@ 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 @@ } /* 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 @@ 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 @@ while (cur && cur->ai_next) cur = cur->ai_next; } + } /* * XXX @@ -394,27 +390,12 @@ ERR(EAI_NONAME); 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 @@ 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 @@ 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: @@ -519,8 +494,12 @@ for (i = 0; hp->h_addr_list[i] != NULL; i++) { af = hp->h_addrtype; ap = hp->h_addr_list[i]; + + if (pai->ai_family != AF_UNSPEC && af != pai->ai_family) + continue; - if (af != pai->ai_family) + afd = find_afd(af); + if (afd == NULL) continue; GET_AI(cur->ai_next, afd, ap); Index: name6.c =================================================================== RCS file: /home/ncvs/src/lib/libc/net/name6.c,v retrieving revision 1.7 diff -u -r1.7 name6.c --- name6.c 2000/03/15 15:04:54 1.7 +++ name6.c 2000/04/01 20:38:04 @@ -42,11 +42,13 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -255,8 +257,6 @@ 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,25 @@ * (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); +#ifdef INET6 + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + af = AF_INET6; + else + _close(s); +#endif + } + + } + if (af != AF_UNSPEC) { + if ((s = socket(af, SOCK_DGRAM, 0)) < 0) + return NULL; + _close(s); + } } for (i = 0; i < MAXHOSTCONF; i++) { @@ -277,16 +295,19 @@ 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 +362,21 @@ } 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 +782,7 @@ 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 +803,39 @@ } 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 +907,8 @@ { struct hostent *hp = NULL; + if (af == AF_UNSPEC) + af = AF_INET; if (af == AF_INET) { hp = _gethostbynisname(name, af); if (hp != NULL) @@ -870,15 +937,22 @@ #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 +963,13 @@ 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_addrtype = AF_INET; + hbuf.h_length = ADDRLEN(AF_INET); hbuf.h_addr_list = hlist; na = nh = 0; - -#ifdef INET6 - qtype = (af == AF_INET6 ? T_AAAA : T_A); -#else - qtype = T_A; -#endif - n = res_search(name, C_IN, qtype, answer, sizeof(answer)); - if (n < 0) { - *errp = h_errno; - return NULL; - } hp = (HEADER *)answer; - eom = answer + n; + eom = answer + anslen; ancount = ntohs(hp->ancount); qdcount = ntohs(hp->qdcount); DNS_ASSERT(qdcount == 1); @@ -962,11 +1019,13 @@ bp += n; buflen -= n; break; - case T_A: #ifdef INET6 case T_AAAA: + hbuf.h_addrtype = AF_INET6; + hbuf.h_length = ADDRLEN(AF_INET6); #endif - DNS_ASSERT(type == qtype); + case T_A: + DNS_ASSERT(qtype == 0 || type == qtype); bp = (char *)ALIGN(bp); DNS_ASSERT(n == hbuf.h_length); DNS_ASSERT(n < buflen); @@ -992,6 +1051,210 @@ alist[na] = NULL; hlist[nh] = NULL; 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 * ----Next_Part(Sun_Apr__2_07:55:48_2000_518)---- To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-current" in the body of the message