From owner-freebsd-stable Wed Apr 5 22:26:43 2000 Delivered-To: freebsd-stable@freebsd.org Received: from fgwmail6.fujitsu.co.jp (fgwmail6.fujitsu.co.jp [192.51.44.36]) by hub.freebsd.org (Postfix) with ESMTP id E2F7C37B707 for ; Wed, 5 Apr 2000 22:26:30 -0700 (PDT) (envelope-from shin@nd.net.fujitsu.co.jp) Received: from m2.gw.fujitsu.co.jp by fgwmail6.fujitsu.co.jp (8.9.3/3.7W-MX0002-Fujitsu Gateway) id OAA14995 for ; Thu, 6 Apr 2000 14:26:30 +0900 (JST) (envelope-from shin@nd.net.fujitsu.co.jp) Received: from chisato.nd.net.fujitsu.co.jp by m2.gw.fujitsu.co.jp (8.9.3/3.7W-0003-Fujitsu Domain Master) id OAA03231; Thu, 6 Apr 2000 14:26:29 +0900 (JST) Received: from localhost (dhcp7173.nd.net.fujitsu.co.jp [10.18.7.173]) by chisato.nd.net.fujitsu.co.jp (8.8.5+2.7Wbeta5/3.3W8chisato-970826) with ESMTP id OAA25606 for ; Thu, 6 Apr 2000 14:26:28 +0900 (JST) To: stable@FreeBSD.ORG Subject: Re: 75 second delay using telnet/ssh (ipv6 related) In-Reply-To: <20000406125344R.shin@nd.net.fujitsu.co.jp> References: <20000401193005.A353@norn.ca.eu.org> <20000402124742P.shin@nd.net.fujitsu.co.jp> <20000406125344R.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(Thu_Apr__6_14:27:28_2000_41)--" Content-Transfer-Encoding: 7bit Message-Id: <20000406142730Q.shin@nd.net.fujitsu.co.jp> Date: Thu, 06 Apr 2000 14:27:30 +0900 From: Yoshinobu Inoue X-Dispatcher: imput version 990905(IM130) Lines: 616 Sender: owner-freebsd-stable@FreeBSD.ORG Precedence: bulk X-Loop: FreeBSD.ORG ----Next_Part(Thu_Apr__6_14:27:28_2000_41)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit There were problem reports that sometime ssh and/or telnet (and also other appss which use getaddrinfo for nameresolving) has 75 seconds delay in name resolution on FreeBSD4.0 and/or 5.0. I created patches to libc to fix the problem and once sent it to freebsd-current, and it seems to be working. So I also send the patches to this list. Please try the patches anyone who has same kind of problems. Thanks, Yoshinobu Inoue ----Next_Part(Thu_Apr__6_14:27:28_2000_41)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="resolver.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/06 03:50:46 @@ -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/06 03:50:47 @@ -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,23 @@ * (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 @@ 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 @@ } 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 @@ 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 @@ } 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 @@ { 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 @@ #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 @@ 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); @@ -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(Thu_Apr__6_14:27:28_2000_41)---- To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-stable" in the body of the message