Date: Wed, 31 May 2000 17:15:15 +0900 From: Hajimu UMEMOTO (=?ISO-2022-JP?B?GyRCR19LXBsoQiA=?= =?ISO-2022-JP?B?GyRCSCUbKEI=?=) <ume@bisd.hitachi.co.jp> To: itojun@iijlab.net Cc: ume@mahoroba.org Subject: Re: ftp(1) breakage w/ passive mode? Message-ID: <20000531171515O.ume@bisd.hitachi.co.jp> In-Reply-To: <4986.959735673@coconut.itojun.org> References: <200005301830.e4UIUOS49066@peace.mahoroba.org> <4986.959735673@coconut.itojun.org>
next in thread | previous in thread | raw e-mail | index | archive | help
----Next_Part(Wed_May_31_17:15:11_2000_809)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit >>>>> On Wed, 31 May 2000 10:14:33 +0900 >>>>> itojun@iijlab.net said: itojun> isn't there some issue in getipnodebyname(), instead of getaddrinfo()? itojun> (NOTE: I'm not really up-to-date with the current status of freebsd4 itojun> lib/libc/net) itojun> if you can tell me repeatable example of getaddrinfo() failure, itojun> that would be helpful... Yes, it's a FreeBSD specific issue. Current getaddrinfo() is using getipnodebyname_multi(). This was introduced by fixing search order. There is a limitation that struct hostent cannot hold two address families at once. So, the host has both A RR and AAAA RR, A RR is converted into mapped address and getaddrinfo() returns only AF_INET6. This problem was known at introducing getipnodebyname_multi(). But, priority of fixing search order was higher than this problem in that time. So, this problem is remained. I think previous my patch is best workaround to solve this problem as far as getaddrinfo() use getipnodeby_multi(). But, it is still workaround. There is one more problem. getipnodebyname() cannot handle scope-id. To solve above problems, we should consider that getaddrinfo() don't use getipnodebyname(). Actually, KAME, NetBSD-current, and OpenBSD-current don't use getipnodebyname() any more and use res_*N() calls. I ported it from NetBSD-current version of getaddrinfo.c and it is running on my boxes. And, it was committed to KAME/FreeBSD4 version of getaddrinfo.c. But, I don't use NIS and NIS code is not tested yet. I attach the patch to don't use getipnodebyname_multi(). BTW, there is search order problem in current getipnodebyname(), too. If getipnodebyname() is called with `AI_ALL|AI_V4MAPPED', this is occur. I attach the patch to solve this problem, too. It also use res_*N() calls. ----Next_Part(Wed_May_31_17:15:11_2000_809)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Description: getaddrinfo-scopeid.diff Content-Disposition: attachment; filename="getaddrinfo-scopeid.diff" Index: lib/libc/net/getaddrinfo.c diff -u lib/libc/net/getaddrinfo.c.orig lib/libc/net/getaddrinfo.c --- lib/libc/net/getaddrinfo.c.orig Thu Apr 20 12:31:08 2000 +++ lib/libc/net/getaddrinfo.c Fri May 5 22:02:17 2000 @@ -37,6 +37,9 @@ * - Return values. There are nonstandard return values defined and used * in the source code. This is because RFC2553 is silent about which error * code must be returned for which situation. + * - freeaddrinfo(NULL). RFC2553 is silent about it. XNET 5.2 says it is + * invalid. + * current code - SEGV on freeaddrinfo(NULL) * Note: * - We use getipnodebyname() just for thread-safeness. There's no intent * to let it do PF_UNSPEC (actually we never pass PF_UNSPEC to @@ -45,6 +48,41 @@ * when globbing NULL hostname (to loopback, or wildcard). Is it the right * thing to do? What is the relationship with post-RFC2553 AI_ADDRCONFIG * in ai_flags? + * - (post-2553) semantics of AI_ADDRCONFIG itself is too vague. + * (1) what should we do against numeric hostname (2) what should we do + * against NULL hostname (3) what is AI_ADDRCONFIG itself. AF not ready? + * non-loopback address configured? global address configured? + * - To avoid search order issue, we have a big amount of code duplicate + * from gethnamaddr.c and some other places. The issues that there's no + * lower layer function to lookup "IPv4 or IPv6" record. Calling + * gethostbyname2 from getaddrinfo will end up in wrong search order, as + * follows: + * - The code makes use of following calls when asked to resolver with + * ai_family = PF_UNSPEC: + * getipnodebyname(host, AF_INET6); + * getipnodebyname(host, AF_INET); + * This will result in the following queries if the node is configure to + * prefer /etc/hosts than DNS: + * lookup /etc/hosts for IPv6 address + * lookup DNS for IPv6 address + * lookup /etc/hosts for IPv4 address + * lookup DNS for IPv4 address + * which may not meet people's requirement. + * The right thing to happen is to have underlying layer which does + * PF_UNSPEC lookup (lookup both) and return chain of addrinfos. + * This would result in a bit of code duplicate with _dns_ghbyname() and + * friends. + */ +/* + * diffs with other KAME platforms: + * - other KAME platforms already nuked FAITH ($GAI), but as FreeBSD + * 4.0-RELEASE supplies it, we still have the code here. + * - EAI_RESNULL support + * - AI_ADDRCONFIG support is supplied + * - EDNS0 support is not available due to resolver differences + * - some of FreeBSD style (#define tabify and others) + * - AI_ADDRCONFIG is turned on by default. + * - classful IPv4 numeric (127.1) is allowed. */ #include <sys/types.h> @@ -62,6 +100,7 @@ #include <ctype.h> #include <unistd.h> #include <stdio.h> +#include <errno.h> #if defined(__KAME__) && defined(INET6) # define FAITH @@ -108,6 +147,7 @@ }; struct explore { + int e_af; int e_socktype; int e_protocol; const char *e_protostr; @@ -118,10 +158,21 @@ }; static const struct explore explore[] = { - { SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, - { SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, - { SOCK_RAW, ANY, NULL, 0x05 }, - { 0, 0, NULL, 0 }, +#if 0 + { PF_LOCAL, 0, ANY, ANY, NULL, 0x01 }, +#endif +#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 }, + { PF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, + { PF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, + { PF_UNSPEC, SOCK_RAW, ANY, NULL, 0x05 }, + { -1, 0, 0, NULL, 0 }, }; #ifdef INET6 @@ -130,12 +181,30 @@ #define PTON_MAX 4 #endif -extern struct hostent * _getipnodebyname_multi __P((const char *name, - int af, int flags, int *errp)); +#if PACKETSZ > 1024 +#define MAXPACKET PACKETSZ +#else +#define MAXPACKET 1024 +#endif + +typedef union { + HEADER hdr; + u_char buf[MAXPACKET]; +} querybuf; + +struct res_target { + struct res_target *next; + const char *name; /* domain name */ + int class, type; /* class and type of query */ + u_char *answer; /* buffer to put answer */ + int anslen; /* size of answer buffer */ + int n; /* result length */ +}; + static int str_isnumber __P((const char *)); static int explore_fqdn __P((const struct addrinfo *, const char *, const char *, struct addrinfo **)); -static int explore_null __P((const struct addrinfo *, const char *, +static int explore_null __P((const struct addrinfo *, const char *, struct addrinfo **)); static int explore_numeric __P((const struct addrinfo *, const char *, const char *, struct addrinfo **)); @@ -148,6 +217,29 @@ static int get_portmatch __P((const struct addrinfo *, const char *)); static int get_port __P((struct addrinfo *, const char *, int)); static const struct afd *find_afd __P((int)); +static int addrconfig __P((struct addrinfo *)); +#ifdef INET6 +static int ip6_str2scopeid __P((char *, struct sockaddr_in6 *)); +#endif + +static struct addrinfo *getanswer __P((const querybuf *, int, const char *, + int, const struct addrinfo *)); +static int _dns_getaddrinfo __P((const struct addrinfo *, const char *, + struct addrinfo **)); +static struct addrinfo *_gethtent __P((FILE *fp, const char *, + const struct addrinfo *)); +static int _files_getaddrinfo __P((const struct addrinfo *, const char *, + struct addrinfo **)); +#ifdef YP +static int _nis_getaddrinfo __P((const struct addrinfo *, const char *, + struct addrinfo **)); +#endif + +static int res_queryN __P((const char *, struct res_target *)); +static int res_searchN __P((const char *, struct res_target *)); +static int res_querydomainN __P((const char *, const char *, + struct res_target *)); + static char *ai_errlist[] = { "Success", @@ -168,6 +260,32 @@ "Unknown error", /* EAI_MAX */ }; +/* + * Select order host function. + */ +#define MAXHOSTCONF 4 + +#ifndef HOSTCONF +# define HOSTCONF "/etc/host.conf" +#endif /* !HOSTCONF */ + +struct _hostconf { + int (*byname)(const struct addrinfo *, const char *, + struct addrinfo **); +}; + +/* default order */ +static struct _hostconf _hostconf[MAXHOSTCONF] = { + _dns_getaddrinfo, + _files_getaddrinfo, +#ifdef ICMPNL + NULL, +#endif /* ICMPNL */ +}; + +static int _hostconf_init_done; +static void _hostconf_init(void); + /* XXX macros that make external reference is BAD. */ #define GET_AI(ai, afd, addr) \ @@ -178,7 +296,7 @@ error = EAI_MEMORY; \ goto free; \ } \ -} while (0) +} while (/*CONSTCOND*/0) #define GET_PORT(ai, serv) \ do { \ @@ -186,7 +304,7 @@ error = get_port((ai), (serv), 0); \ if (error != 0) \ goto free; \ -} while (0) +} while (/*CONSTCOND*/0) #define GET_CANONNAME(ai, str) \ do { \ @@ -194,19 +312,20 @@ error = get_canonname(pai, (ai), (str)); \ if (error != 0) \ goto free; \ -} while (0) +} while (/*CONSTCOND*/0) #define ERR(err) \ do { \ /* external reference: error, and label bad */ \ error = (err); \ goto bad; \ -} while (0) + /*NOTREACHED*/ \ +} while (/*CONSTCOND*/0) #define MATCH_FAMILY(x, y, w) \ - ((x) == (y) || ((w) && ((x) == PF_UNSPEC || (y) == PF_UNSPEC))) + ((x) == (y) || (/*CONSTCOND*/(w) && ((x) == PF_UNSPEC || (y) == PF_UNSPEC))) #define MATCH(x, y, w) \ - ((x) == (y) || ((w) && ((x) == ANY || (y) == ANY))) + ((x) == (y) || (/*CONSTCOND*/(w) && ((x) == ANY || (y) == ANY))) char * gai_strerror(ecode) @@ -229,16 +348,17 @@ free(ai->ai_canonname); /* no need to free(ai->ai_addr) */ free(ai); - } while ((ai = next) != NULL); + ai = next; + } while (ai); } static int str_isnumber(p) const char *p; { - char *q = (char *)p; + const char *q = (const char *)p; while (*q) { - if (! isdigit(*q)) + if (!isdigit(*q)) return NO; q++; } @@ -257,10 +377,9 @@ struct addrinfo ai; struct addrinfo ai0; struct addrinfo *pai; - const struct afd *afd; const struct explore *ex; - sentinel.ai_next = NULL; + memset(&sentinel, 0, sizeof(sentinel)); cur = &sentinel; pai = &ai; pai->ai_flags = 0; @@ -300,75 +419,99 @@ * are meaningful combination. */ if (pai->ai_socktype != ANY && pai->ai_protocol != ANY) { - int matched = 0; - - for (ex = explore; ex->e_socktype; ex++) { + for (ex = explore; ex->e_af >= 0; ex++) { + if (pai->ai_family != ex->e_af) + continue; if (ex->e_socktype == ANY) continue; if (ex->e_protocol == ANY) continue; if (pai->ai_socktype == ex->e_socktype - && pai->ai_protocol == ex->e_protocol) - matched = 1; - else - continue; - if (matched == 0) + && pai->ai_protocol != ex->e_protocol) { ERR(EAI_BADHINTS); + } } } } - /* backup original pai contents */ - ai0 = *pai; + /* + * post-2553: AI_ALL and AI_V4MAPPED are effective only against + * AF_INET6 query. They needs to be ignored if specified in other + * occassions. + */ + switch (pai->ai_flags & (AI_ALL | AI_V4MAPPED)) { + case AI_V4MAPPED: + case AI_ALL | AI_V4MAPPED: + if (pai->ai_family != AF_INET6) + pai->ai_flags &= ~(AI_ALL | AI_V4MAPPED); + break; + case AI_ALL: +#if 1 + /* illegal */ + ERR(EAI_BADFLAGS); +#else + pai->ai_flags &= ~(AI_ALL | AI_V4MAPPED); +#endif + break; + } /* - * special cases check for inet and inet6 sockets. - * (1) servname is disallowed for raw sockets. - * (2) numeric servname is disallowed if socktype/protocol is left - * unspecified. + * check for special cases. (1) numeric servname is disallowed if + * socktype/protocol are left unspecified. (2) servname is disallowed + * for raw and other inet{,6} sockets. */ if (MATCH_FAMILY(pai->ai_family, PF_INET, 1) -#ifdef INET6 +#ifdef PF_INET6 || MATCH_FAMILY(pai->ai_family, PF_INET6, 1) #endif ) { - *pai = ai0; + ai0 = *pai; /* backup *pai */ - if (pai->ai_family == PF_UNSPEC) -#ifdef INET6 + if (pai->ai_family == PF_UNSPEC) { +#ifdef PF_INET6 pai->ai_family = PF_INET6; #else pai->ai_family = PF_INET; #endif + } error = get_portmatch(pai, servname); if (error) ERR(error); + + *pai = ai0; } + ai0 = *pai; + /* NULL hostname, or numeric hostname */ - for (afd = afdl; afd->a_af; afd++) - { - for (ex = explore; ex->e_socktype; ex++) { + for (ex = explore; ex->e_af >= 0; ex++) { *pai = ai0; - if (!MATCH_FAMILY(pai->ai_family, afd->a_af, WILD_AF(ex))) + /* PF_UNSPEC entries are prepared for DNS queries only */ + if (ex->e_af == PF_UNSPEC) continue; - if (!MATCH(pai->ai_socktype, ex->e_socktype, WILD_SOCKTYPE(ex))) + + 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; - if (!MATCH(pai->ai_protocol, ex->e_protocol, WILD_PROTOCOL(ex))) + if (!MATCH(pai->ai_protocol, ex->e_protocol, + WILD_PROTOCOL(ex))) continue; if (pai->ai_family == PF_UNSPEC) - pai->ai_family = afd->a_af; + 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) pai->ai_protocol = ex->e_protocol; if (hostname == NULL) - error = explore_null(pai, hostname, servname, &cur->ai_next); + error = explore_null(pai, servname, &cur->ai_next); else - error = explore_numeric_scope(pai, hostname, servname, &cur->ai_next); + error = explore_numeric_scope(pai, hostname, servname, + &cur->ai_next); if (error) goto free; @@ -376,7 +519,6 @@ while (cur && cur->ai_next) cur = cur->ai_next; } - } /* * XXX @@ -391,50 +533,143 @@ if (hostname == NULL) ERR(EAI_NONAME); - /* hostname as alphabetical name. */ - { - for (ex = explore; ex->e_socktype; ex++) { - *pai = ai0; - - if (!MATCH(pai->ai_socktype, ex->e_socktype, - WILD_SOCKTYPE(ex))) { - continue; - } - if (!MATCH(pai->ai_protocol, ex->e_protocol, - WILD_PROTOCOL(ex))) { - continue; - } +#if 1 + /* XXX: temporarily, behave as if AI_ADDRCONFIG is specified */ + pai->ai_flags |= AI_ADDRCONFIG; +#endif + if ((pai->ai_flags & AI_ADDRCONFIG) != 0 && !addrconfig(&ai0)) + ERR(EAI_FAIL); - if (pai->ai_socktype == ANY && ex->e_socktype != ANY) - pai->ai_socktype = ex->e_socktype; - if (pai->ai_protocol == ANY && ex->e_protocol != ANY) - pai->ai_protocol = ex->e_protocol; + /* + * hostname as alphabetical name. + * we would like to prefer AF_INET6 than AF_INET, so we'll make a + * outer loop by AFs. + */ + for (ex = explore; ex->e_af >= 0; ex++) { + *pai = ai0; - error = explore_fqdn(pai, hostname, servname, - &cur->ai_next); + /* require exact match for family field */ + if (pai->ai_family != ex->e_af) + continue; - while (cur && cur->ai_next) - cur = cur->ai_next; + if (!MATCH(pai->ai_socktype, ex->e_socktype, + WILD_SOCKTYPE(ex))) { + continue; } + if (!MATCH(pai->ai_protocol, ex->e_protocol, + WILD_PROTOCOL(ex))) { + continue; + } + + if (pai->ai_socktype == ANY && ex->e_socktype != ANY) + pai->ai_socktype = ex->e_socktype; + if (pai->ai_protocol == ANY && ex->e_protocol != ANY) + pai->ai_protocol = ex->e_protocol; + + error = explore_fqdn(pai, hostname, servname, &cur->ai_next); + + while (cur && cur->ai_next) + cur = cur->ai_next; } - /* XXX: if any addrinfo found, SUCCESS return even if (error != 0) */ - if (sentinel.ai_next) { + /* XXX */ + if (sentinel.ai_next) + error = 0; + + if (error) + goto free; + if (error == 0) { + if (sentinel.ai_next) { good: - *res = sentinel.ai_next; - return SUCCESS; + *res = sentinel.ai_next; + return SUCCESS; + } else + error = EAI_FAIL; } - /* else, failed */ free: bad: - if (error == 0) - error = EAI_FAIL; if (sentinel.ai_next) freeaddrinfo(sentinel.ai_next); *res = NULL; return error; } +static char * +_hgetword(char **pp) +{ + char c, *p, *ret; + const char *sp; + static const char sep[] = "# \t\n"; + + ret = NULL; + for (p = *pp; (c = *p) != '\0'; p++) { + for (sp = sep; *sp != '\0'; sp++) { + if (c == *sp) + break; + } + if (c == '#') + p[1] = '\0'; /* ignore rest of line */ + if (ret == NULL) { + if (*sp == '\0') + ret = p; + } else { + if (*sp != '\0') { + *p++ = '\0'; + break; + } + } + } + *pp = p; + if (ret == NULL || *ret == '\0') + return NULL; + return ret; +} + +/* + * Initialize hostconf structure. + */ + +static void +_hostconf_init(void) +{ + FILE *fp; + int n; + char *p, *line; + char buf[BUFSIZ]; + + _hostconf_init_done = 1; + n = 0; + p = HOSTCONF; + if ((fp = fopen(p, "r")) == NULL) + return; + while (n < MAXHOSTCONF && fgets(buf, sizeof(buf), fp)) { + line = buf; + if ((p = _hgetword(&line)) == NULL) + continue; + do { + if (strcmp(p, "hosts") == 0 + || strcmp(p, "local") == 0 + || strcmp(p, "file") == 0 + || strcmp(p, "files") == 0) + _hostconf[n++].byname = _files_getaddrinfo; + else if (strcmp(p, "dns") == 0 + || strcmp(p, "bind") == 0) + _hostconf[n++].byname = _dns_getaddrinfo; +#ifdef YP + else if (strcmp(p, "nis") == 0) + _hostconf[n++].byname = _nis_getaddrinfo; +#endif + } while ((p = _hgetword(&line)) != NULL); + } + fclose(fp); + if (n < 0) { + /* no keyword found. do not change default configuration */ + return; + } + for (; n < MAXHOSTCONF; n++) + _hostconf[n].byname = NULL; +} + /* * FQDN hostname, DNS lookup */ @@ -445,18 +680,12 @@ const char *servname; struct addrinfo **res; { - struct hostent *hp; - int h_error; - int af; - char *ap; - struct addrinfo sentinel, *cur; - int i; - const struct afd *afd; - int error; + struct addrinfo *result; + struct addrinfo *cur; + int error = 0, i; + result = NULL; *res = NULL; - sentinel.ai_next = NULL; - cur = &sentinel; /* * if the servname does not match socktype/protocol, ignore it. @@ -464,67 +693,26 @@ if (get_portmatch(pai, servname) != 0) return 0; - hp = _getipnodebyname_multi(hostname, pai->ai_family, AI_ADDRCONFIG, - &h_error); - if (hp == NULL) { - switch (h_error) { - case HOST_NOT_FOUND: - case NO_DATA: - error = EAI_NODATA; - break; - case TRY_AGAIN: - error = EAI_AGAIN; - break; - case NO_RECOVERY: - case NETDB_INTERNAL: - default: - error = EAI_FAIL; - break; - } - } else if ((hp->h_name == NULL) || (hp->h_name[0] == 0) - || (hp->h_addr_list[0] == NULL)) { - freehostent(hp); - hp = NULL; - error = EAI_FAIL; - } - - if (hp == NULL) - goto free; - - for (i = 0; hp->h_addr_list[i] != NULL; i++) { - af = hp->h_addrtype; - ap = hp->h_addr_list[i]; + if (!_hostconf_init_done) + _hostconf_init(); - if (pai->ai_family != AF_UNSPEC && af != pai->ai_family) + for (i = 0; i < MAXHOSTCONF; i++) { + if (!_hostconf[i].byname) continue; - - afd = find_afd(af); - if (afd == NULL) + error = (*_hostconf[i].byname)(pai, hostname, &result); + if (error != 0) continue; - - GET_AI(cur->ai_next, afd, ap); - GET_PORT(cur->ai_next, servname); - if ((pai->ai_flags & AI_CANONNAME) != 0) { - /* - * RFC2553 says that ai_canonname will be set only for - * the first element. we do it for all the elements, - * just for convenience. - */ - GET_CANONNAME(cur->ai_next, hp->h_name); + for (cur = result; cur; cur = cur->ai_next) { + GET_PORT(cur, servname); + /* canonname should be filled already */ } - - while (cur && cur->ai_next) - cur = cur->ai_next; + *res = result; + return 0; } - *res = sentinel.ai_next; - return 0; - free: - if (hp) - freehostent(hp); - if (sentinel.ai_next) - freeaddrinfo(sentinel.ai_next); + if (result) + freeaddrinfo(result); return error; } @@ -534,9 +722,8 @@ * non-passive socket -> localhost (127.0.0.1 or ::1) */ static int -explore_null(pai, hostname, servname, res) +explore_null(pai, servname, res) const struct addrinfo *pai; - const char *hostname; const char *servname; struct addrinfo **res; { @@ -555,23 +742,36 @@ * XXX errno? */ s = socket(pai->ai_family, SOCK_DGRAM, 0); - if (s < 0) + if (s < 0) { + if (errno != EMFILE) + return 0; + } else + _close(s); + + /* + * if the servname does not match socktype/protocol, ignore it. + */ + if (get_portmatch(pai, servname) != 0) return 0; - _close(s); + afd = find_afd(pai->ai_family); if (afd == NULL) return 0; - GET_AI(cur->ai_next, afd, - (pai->ai_flags & AI_PASSIVE) ? afd->a_addrany : afd->a_loopback - ); - /* xxx meaningless? - * GET_CANONNAME(cur->ai_next, "anyaddr"); - * or - * GET_CANONNAME(cur->ai_next, "localhost"); - */ - /* if the servname does not match socktype/protocol, ignored */ - GET_PORT(cur->ai_next, servname); + if (pai->ai_flags & AI_PASSIVE) { + GET_AI(cur->ai_next, afd, afd->a_addrany); + /* xxx meaningless? + * GET_CANONNAME(cur->ai_next, "anyaddr"); + */ + GET_PORT(cur->ai_next, servname); + } else { + GET_AI(cur->ai_next, afd, afd->a_loopback); + /* xxx meaningless? + * GET_CANONNAME(cur->ai_next, "localhost"); + */ + GET_PORT(cur->ai_next, servname); + } + cur = cur->ai_next; *res = sentinel.ai_next; return 0; @@ -597,7 +797,6 @@ struct addrinfo sentinel; int error; char pton[PTON_MAX]; - int flags; *res = NULL; sentinel.ai_next = NULL; @@ -612,7 +811,6 @@ afd = find_afd(pai->ai_family); if (afd == NULL) return 0; - flags = pai->ai_flags; if ((afd->a_af == AF_INET ? inet_aton(hostname, (struct in_addr *)pton) @@ -647,17 +845,14 @@ const char *servname; struct addrinfo **res; { -#ifndef SCOPE_DELIMITER +#if !defined(SCOPE_DELIMITER) || !defined(INET6) return explore_numeric(pai, hostname, servname, res); #else const struct afd *afd; struct addrinfo *cur; int error; - char *cp, *hostname2 = NULL; - int scope; -#ifdef INET6 + char *cp, *hostname2 = NULL, *scope, *addr; struct sockaddr_in6 *sin6; -#endif /* * if the servname does not match socktype/protocol, ignore it. @@ -683,37 +878,25 @@ return EAI_MEMORY; /* terminate at the delimiter */ hostname2[cp - hostname] = '\0'; + addr = hostname2; + scope = cp + 1; - cp++; - switch (pai->ai_family) { -#ifdef INET6 - case AF_INET6: - scope = if_nametoindex(cp); - if (scope == 0) { - error = EAI_SYSTEM; - goto free; - } - break; -#endif - } - - error = explore_numeric(pai, hostname2, servname, res); + error = explore_numeric(pai, addr, servname, res); if (error == 0) { + int scopeid; + for (cur = *res; cur; cur = cur->ai_next) { -#ifdef INET6 if (cur->ai_family != AF_INET6) continue; - sin6 = (struct sockaddr_in6 *)cur->ai_addr; - if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) || - IN6_IS_ADDR_MC_LINKLOCAL(&sin6->sin6_addr)) - sin6->sin6_scope_id = scope; -#endif + sin6 = (struct sockaddr_in6 *)(void *)cur->ai_addr; + if ((scopeid = ip6_str2scopeid(scope, sin6)) == -1) { + free(hostname2); + return(EAI_NONAME); /* XXX: is return OK? */ + } + sin6->sin6_scope_id = scopeid; } } -#ifdef INET6 -free: -#endif free(hostname2); return error; @@ -791,8 +974,8 @@ return NULL; memcpy(ai, pai, sizeof(struct addrinfo)); - ai->ai_addr = (struct sockaddr *)(ai + 1); - memset(ai->ai_addr, 0, afd->a_socklen); + ai->ai_addr = (struct sockaddr *)(void *)(ai + 1); + memset(ai->ai_addr, 0, (size_t)afd->a_socklen); ai->ai_addr->sa_len = afd->a_socklen; ai->ai_addrlen = afd->a_socklen; ai->ai_addr->sa_family = ai->ai_family = afd->a_af; @@ -812,7 +995,9 @@ const struct addrinfo *ai; const char *servname; { + /* get_port does not touch first argument. when matchonly == 1. */ + /* LINTED const cast */ return get_port((struct addrinfo *)ai, servname, 1); } @@ -829,12 +1014,15 @@ if (servname == NULL) return 0; - if (ai->ai_family != AF_INET -#ifdef INET6 - && ai->ai_family != AF_INET6 + switch (ai->ai_family) { + case AF_INET: +#ifdef AF_INET6 + case AF_INET6: #endif - ) + break; + default: return 0; + } switch (ai->ai_socktype) { case SOCK_RAW: @@ -877,11 +1065,13 @@ if (!matchonly) { switch (ai->ai_family) { case AF_INET: - ((struct sockaddr_in *)ai->ai_addr)->sin_port = port; + ((struct sockaddr_in *)(void *) + ai->ai_addr)->sin_port = port; break; #ifdef INET6 case AF_INET6: - ((struct sockaddr_in6 *)ai->ai_addr)->sin6_port = port; + ((struct sockaddr_in6 *)(void *) + ai->ai_addr)->sin6_port = port; break; #endif } @@ -903,4 +1093,844 @@ return afd; } return NULL; +} + +/* + * post-2553: AI_ADDRCONFIG check. if we use getipnodeby* as backend, backend + * will take care of it. + * the semantics of AI_ADDRCONFIG is not defined well. we are not sure + * if the code is right or not. + * + * XXX PF_UNSPEC -> PF_INET6 + PF_INET mapping needs to be in sync with + * _dns_getaddrinfo. + */ +static int +addrconfig(pai) + struct addrinfo *pai; +{ + int s, af; + + /* + * TODO: + * Note that implementation dependent test for address + * configuration should be done everytime called + * (or apropriate interval), + * because addresses will be dynamically assigned or deleted. + */ + af = pai->ai_family; + 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 0; + close(s); + } + pai->ai_family = af; + return 1; +} + +#ifdef INET6 +/* convert a string to a scope identifier. XXX: IPv6 specific */ +static int +ip6_str2scopeid(scope, sin6) + char *scope; + struct sockaddr_in6 *sin6; +{ + int scopeid; + struct in6_addr *a6 = &sin6->sin6_addr; + char *ep; + + if (IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6)) { + /* + * We currently assume a one-to-one mapping between links + * and interfaces, so we simply use interface indices for + * like-local scopes. + */ + scopeid = if_nametoindex(scope); + if (scopeid == 0) + goto trynumeric; + return(scopeid); + } + + /* still unclear about literal, allow numeric only - placeholder */ + if (IN6_IS_ADDR_SITELOCAL(a6) || IN6_IS_ADDR_MC_SITELOCAL(a6)) + goto trynumeric; + if (IN6_IS_ADDR_MC_ORGLOCAL(a6)) + goto trynumeric; + else + goto trynumeric; /* global */ + + /* try to convert to a numeric id as a last resort */ + trynumeric: + scopeid = (int)strtoul(scope, &ep, 10); + if (*ep == '\0') + return scopeid; + else + return -1; +} +#endif + +#ifdef DEBUG +static const char AskedForGot[] = + "gethostby*.getanswer: asked for \"%s\", got \"%s\""; +#endif + +static struct addrinfo * +getanswer(answer, anslen, qname, qtype, pai) + const querybuf *answer; + int anslen; + const char *qname; + int qtype; + const struct addrinfo *pai; +{ + struct addrinfo sentinel, *cur; + struct addrinfo ai; + const struct afd *afd; + char *canonname; + const HEADER *hp; + const u_char *cp; + int n; + const u_char *eom; + char *bp; + int type, class, buflen, ancount, qdcount; + int haveanswer, had_error; + char tbuf[MAXDNAME]; + int (*name_ok) __P((const char *)); + char hostbuf[8*1024]; + + memset(&sentinel, 0, sizeof(sentinel)); + cur = &sentinel; + + canonname = NULL; + eom = answer->buf + anslen; + switch (qtype) { + case T_A: + case T_AAAA: + case T_ANY: /*use T_ANY only for T_A/T_AAAA lookup*/ + name_ok = res_hnok; + break; + default: + return (NULL); /* XXX should be abort(); */ + } + /* + * find first satisfactory answer + */ + hp = &answer->hdr; + ancount = ntohs(hp->ancount); + qdcount = ntohs(hp->qdcount); + bp = hostbuf; + buflen = sizeof hostbuf; + cp = answer->buf + HFIXEDSZ; + if (qdcount != 1) { + h_errno = NO_RECOVERY; + return (NULL); + } + n = dn_expand(answer->buf, eom, cp, bp, buflen); + if ((n < 0) || !(*name_ok)(bp)) { + h_errno = NO_RECOVERY; + return (NULL); + } + cp += n + QFIXEDSZ; + if (qtype == T_A || qtype == T_AAAA || qtype == T_ANY) { + /* res_send() has already verified that the query name is the + * same as the one we sent; this just gets the expanded name + * (i.e., with the succeeding search-domain tacked on). + */ + n = strlen(bp) + 1; /* for the \0 */ + if (n >= MAXHOSTNAMELEN) { + h_errno = NO_RECOVERY; + return (NULL); + } + canonname = bp; + bp += n; + buflen -= n; + /* The qname can be abbreviated, but h_name is now absolute. */ + qname = canonname; + } + haveanswer = 0; + had_error = 0; + while (ancount-- > 0 && cp < eom && !had_error) { + n = dn_expand(answer->buf, eom, cp, bp, buflen); + if ((n < 0) || !(*name_ok)(bp)) { + had_error++; + continue; + } + cp += n; /* name */ + type = _getshort(cp); + cp += INT16SZ; /* type */ + class = _getshort(cp); + cp += INT16SZ + INT32SZ; /* class, TTL */ + n = _getshort(cp); + cp += INT16SZ; /* len */ + if (class != C_IN) { + /* XXX - debug? syslog? */ + cp += n; + continue; /* XXX - had_error++ ? */ + } + if ((qtype == T_A || qtype == T_AAAA || qtype == T_ANY) && + type == T_CNAME) { + n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf); + if ((n < 0) || !(*name_ok)(tbuf)) { + had_error++; + continue; + } + cp += n; + /* Get canonical name. */ + n = strlen(tbuf) + 1; /* for the \0 */ + if (n > buflen || n >= MAXHOSTNAMELEN) { + had_error++; + continue; + } + strcpy(bp, tbuf); + canonname = bp; + bp += n; + buflen -= n; + continue; + } + if (qtype == T_ANY) { + if (!(type == T_A || type == T_AAAA)) { + cp += n; + continue; + } + } else if (type != qtype) { +#ifdef DEBUG + if (type != T_KEY && type != T_SIG) + syslog(LOG_NOTICE|LOG_AUTH, + "gethostby*.getanswer: asked for \"%s %s %s\", got type \"%s\"", + qname, p_class(C_IN), p_type(qtype), + p_type(type)); +#endif + cp += n; + continue; /* XXX - had_error++ ? */ + } + switch (type) { + case T_A: + case T_AAAA: + if (strcasecmp(canonname, bp) != 0) { +#ifdef DEBUG + syslog(LOG_NOTICE|LOG_AUTH, + AskedForGot, canonname, bp); +#endif + cp += n; + continue; /* XXX - had_error++ ? */ + } + if (type == T_A && n != INADDRSZ) { + cp += n; + continue; + } + if (type == T_AAAA && n != IN6ADDRSZ) { + cp += n; + continue; + } +#ifdef FILTER_V4MAPPED + if (type == T_AAAA) { + struct in6_addr in6; + memcpy(&in6, cp, sizeof(in6)); + if (IN6_IS_ADDR_V4MAPPED(&in6)) { + cp += n; + continue; + } + } +#endif + if (!haveanswer) { + int nn; + + canonname = bp; + nn = strlen(bp) + 1; /* for the \0 */ + bp += nn; + buflen -= nn; + } + + /* don't overwrite pai */ + ai = *pai; + ai.ai_family = (type == T_A) ? AF_INET : AF_INET6; + afd = find_afd(ai.ai_family); + if (afd == NULL) { + cp += n; + continue; + } + cur->ai_next = get_ai(&ai, afd, (const char *)cp); + if (cur->ai_next == NULL) + had_error++; + while (cur && cur->ai_next) + cur = cur->ai_next; + cp += n; + break; + default: + abort(); + } + if (!had_error) + haveanswer++; + } + if (haveanswer) { + if (!canonname) + (void)get_canonname(pai, sentinel.ai_next, qname); + else + (void)get_canonname(pai, sentinel.ai_next, canonname); + h_errno = NETDB_SUCCESS; + return sentinel.ai_next; + } + + h_errno = NO_RECOVERY; + return NULL; +} + +/*ARGSUSED*/ +static int +_dns_getaddrinfo(pai, hostname, res) + const struct addrinfo *pai; + const char *hostname; + struct addrinfo **res; +{ + struct addrinfo *ai; + querybuf buf, buf2; + const char *name; + struct addrinfo sentinel, *cur; + struct res_target q, q2; + + memset(&q, 0, sizeof(q2)); + memset(&q2, 0, sizeof(q2)); + memset(&sentinel, 0, sizeof(sentinel)); + cur = &sentinel; + + switch (pai->ai_family) { + case AF_UNSPEC: + /* prefer IPv6 */ + q.class = C_IN; + q.type = T_AAAA; + q.answer = buf.buf; + q.anslen = sizeof(buf); + q.next = &q2; + q2.class = C_IN; + q2.type = T_A; + q2.answer = buf2.buf; + q2.anslen = sizeof(buf2); + break; + case AF_INET: + q.class = C_IN; + q.type = T_A; + q.answer = buf.buf; + q.anslen = sizeof(buf); + break; + case AF_INET6: + q.class = C_IN; + q.type = T_AAAA; + q.answer = buf.buf; + q.anslen = sizeof(buf); + break; + default: + return EAI_FAIL; + } + if (res_searchN(hostname, &q) < 0) + return EAI_NODATA; + ai = getanswer(&buf, q.n, q.name, q.type, pai); + if (ai) { + cur->ai_next = ai; + while (cur && cur->ai_next) + cur = cur->ai_next; + } + if (q.next) { + ai = getanswer(&buf2, q2.n, q2.name, q2.type, pai); + if (ai) + cur->ai_next = ai; + } + if (sentinel.ai_next == NULL) + switch (h_errno) { + case HOST_NOT_FOUND: + return EAI_NODATA; + case TRY_AGAIN: + return EAI_AGAIN; + default: + return EAI_FAIL; + } + *res = sentinel.ai_next; + return 0; +} + +static struct addrinfo * +_gethtent(hostf, name, pai) + FILE *hostf; + const char *name; + const struct addrinfo *pai; +{ + char *p; + char *cp, *tname, *cname; + struct addrinfo hints, *res0, *res; + int error; + const char *addr; + char hostbuf[8*1024]; + + again: + if (!(p = fgets(hostbuf, sizeof hostbuf, hostf))) + return (NULL); + if (*p == '#') + goto again; + if (!(cp = strpbrk(p, "#\n"))) + goto again; + *cp = '\0'; + if (!(cp = strpbrk(p, " \t"))) + goto again; + *cp++ = '\0'; + addr = p; + cname = NULL; + /* if this is not something we're looking for, skip it. */ + while (cp && *cp) { + if (*cp == ' ' || *cp == '\t') { + cp++; + continue; + } + tname = cp; + if (cname == NULL) + cname = cp; + if ((cp = strpbrk(cp, " \t")) != NULL) + *cp++ = '\0'; + if (strcasecmp(name, tname) == 0) + goto found; + } + goto again; + +found: + hints = *pai; + hints.ai_flags = AI_NUMERICHOST; + error = getaddrinfo(addr, NULL, &hints, &res0); + if (error) + goto again; +#ifdef FILTER_V4MAPPED + /* XXX should check all items in the chain */ + if (res0->ai_family == AF_INET6 && + IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)res0->ai_addr)->sin6_addr)) { + freeaddrinfo(res0); + goto again; + } +#endif + for (res = res0; res; res = res->ai_next) { + /* cover it up */ + res->ai_flags = pai->ai_flags; + + if (pai->ai_flags & AI_CANONNAME) { + if (get_canonname(pai, res, cname) != 0) { + freeaddrinfo(res0); + goto again; + } + } + } + return res0; +} + +/*ARGSUSED*/ +static int +_files_getaddrinfo(pai, hostname, res) + const struct addrinfo *pai; + const char *hostname; + struct addrinfo **res; +{ + FILE *hostf; + struct addrinfo sentinel, *cur; + struct addrinfo *p; + + sentinel.ai_next = NULL; + cur = &sentinel; + + if ((hostf = fopen(_PATH_HOSTS, "r")) == NULL) + return EAI_FAIL; + while ((p = _gethtent(hostf, hostname, pai)) != NULL) { + cur->ai_next = p; + while (cur && cur->ai_next) + cur = cur->ai_next; + } + fclose(hostf); + + if (!sentinel.ai_next) + return EAI_NODATA; + + *res = sentinel.ai_next; + return 0; +} + +#ifdef YP +/*ARGSUSED*/ +static int +_nis_getaddrinfo(pai, hostname, res) + const struct addrinfo *pai; + const char *hostname; + struct addrinfo **res; +{ + struct hostent *hp; + int h_error; + int af; + char *ap; + struct addrinfo sentinel, *cur; + int i; + const struct afd *afd; + int error; + + sentinel.ai_next = NULL; + cur = &sentinel; + + af = (pai->ai_family == AF_UNSPEC) ? AF_INET : pai->ai_family; + if (af != AF_INET) + return (EAI_ADDRFAMILY); + + if ((hp = _gethostbynisname(hostname, af)) == NULL) { + switch (errno) { + /* XXX: should be filled in */ + default: + error = EAI_FAIL; + break; + } + } else if (hp->h_name == NULL || + hp->h_name[0] == 0 || hp->h_addr_list[0] == NULL) { + hp = NULL; + error = EAI_FAIL; + } + + if (hp == NULL) + return error; + + for (i = 0; hp->h_addr_list[i] != NULL; i++) { + af = hp->h_addrtype; + ap = hp->h_addr_list[i]; + + if (af != pai->ai_family) + continue; + + afd = find_afd(af); + if (afd == NULL) + continue; + + GET_AI(cur->ai_next, afd, ap); + if ((pai->ai_flags & AI_CANONNAME) != 0) { + /* + * RFC2553 says that ai_canonname will be set only for + * the first element. we do it for all the elements, + * just for convenience. + */ + GET_CANONNAME(cur->ai_next, hp->h_name); + } + + while (cur && cur->ai_next) + cur = cur->ai_next; + } + + *res = sentinel.ai_next; + return 0; + +free: + if (sentinel.ai_next) + freeaddrinfo(sentinel.ai_next); + return error; +} +#endif + +/* resolver logic */ + +extern const char *__hostalias __P((const char *)); +extern int h_errno; + +/* + * Formulate a normal query, send, and await answer. + * Returned answer is placed in supplied buffer "answer". + * Perform preliminary check of answer, returning success only + * if no error is indicated and the answer count is nonzero. + * Return the size of the response on success, -1 on error. + * Error number is left in h_errno. + * + * Caller must parse answer and determine whether it answers the question. + */ +static int +res_queryN(name, target) + const char *name; /* domain name */ + struct res_target *target; +{ + u_char buf[MAXPACKET]; + HEADER *hp; + int n; + struct res_target *t; + int rcode; + int ancount; + + rcode = NOERROR; + ancount = 0; + + if ((_res.options & RES_INIT) == 0 && res_init() == -1) { + h_errno = NETDB_INTERNAL; + return (-1); + } + + for (t = target; t; t = t->next) { + int class, type; + u_char *answer; + int anslen; + + hp = (HEADER *)(void *)t->answer; + hp->rcode = NOERROR; /* default */ + + /* make it easier... */ + class = t->class; + type = t->type; + answer = t->answer; + anslen = t->anslen; +#ifdef DEBUG + if (_res.options & RES_DEBUG) + printf(";; res_query(%s, %d, %d)\n", name, class, type); +#endif + + n = res_mkquery(QUERY, name, class, type, NULL, 0, NULL, + buf, sizeof(buf)); + if (n <= 0) { +#ifdef DEBUG + if (_res.options & RES_DEBUG) + printf(";; res_query: mkquery failed\n"); +#endif + h_errno = NO_RECOVERY; + return (n); + } + n = res_send(buf, n, answer, anslen); +#if 0 + if (n < 0) { +#ifdef DEBUG + if (_res.options & RES_DEBUG) + printf(";; res_query: send error\n"); +#endif + h_errno = TRY_AGAIN; + return (n); + } +#endif + + if (n < 0 || hp->rcode != NOERROR || ntohs(hp->ancount) == 0) { + rcode = hp->rcode; /* record most recent error */ +#ifdef DEBUG + if (_res.options & RES_DEBUG) + printf(";; rcode = %d, ancount=%d\n", hp->rcode, + ntohs(hp->ancount)); +#endif + continue; + } + + ancount += ntohs(hp->ancount); + + t->n = n; + } + + if (ancount == 0) { + switch (rcode) { + case NXDOMAIN: + h_errno = HOST_NOT_FOUND; + break; + case SERVFAIL: + h_errno = TRY_AGAIN; + break; + case NOERROR: + h_errno = NO_DATA; + break; + case FORMERR: + case NOTIMP: + case REFUSED: + default: + h_errno = NO_RECOVERY; + break; + } + return (-1); + } + return (ancount); +} + +/* + * Formulate a normal query, send, and retrieve answer in supplied buffer. + * Return the size of the response on success, -1 on error. + * If enabled, implement search rules until answer or unrecoverable failure + * is detected. Error code, if any, is left in h_errno. + */ +static int +res_searchN(name, target) + const char *name; /* domain name */ + struct res_target *target; +{ + const char *cp, * const *domain; + HEADER *hp = (HEADER *)(void *)target->answer; /*XXX*/ + u_int dots; + int trailing_dot, ret, saved_herrno; + int got_nodata = 0, got_servfail = 0, tried_as_is = 0; + + if ((_res.options & RES_INIT) == 0 && res_init() == -1) { + h_errno = NETDB_INTERNAL; + return (-1); + } + + errno = 0; + h_errno = HOST_NOT_FOUND; /* default, if we never query */ + 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) + return (res_queryN(cp, target)); + + /* + * 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) { + ret = res_querydomainN(name, NULL, target); + if (ret > 0) + return (ret); + saved_herrno = h_errno; + 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++) { + + ret = res_querydomainN(name, *domain, target); + if (ret > 0) + return (ret); + + /* + * 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) { + h_errno = TRY_AGAIN; + return (-1); + } + + switch (h_errno) { + case NO_DATA: + got_nodata++; + /* FALLTHROUGH */ + case HOST_NOT_FOUND: + /* keep trying */ + break; + case TRY_AGAIN: + if (hp->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. + */ + if (!tried_as_is && (dots || !(_res.options & RES_NOTLDQUERY))) { + ret = res_querydomainN(name, NULL, target); + if (ret > 0) + return (ret); + } + + /* + * 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) + h_errno = saved_herrno; + else if (got_nodata) + h_errno = NO_DATA; + else if (got_servfail) + h_errno = TRY_AGAIN; + return (-1); +} + +/* + * Perform a call on res_query on the concatenation of name and domain, + * removing a trailing dot from name if domain is NULL. + */ +static int +res_querydomainN(name, domain, target) + const char *name, *domain; + struct res_target *target; +{ + char nbuf[MAXDNAME]; + const char *longname = nbuf; + size_t n, d; + + if ((_res.options & RES_INIT) == 0 && res_init() == -1) { + h_errno = NETDB_INTERNAL; + return (-1); + } +#ifdef DEBUG + if (_res.options & RES_DEBUG) + printf(";; res_querydomain(%s, %s)\n", + name, domain?domain:"<Nil>"); +#endif + if (domain == NULL) { + /* + * Check for trailing '.'; + * copy without '.' if present. + */ + n = strlen(name); + if (n >= MAXDNAME) { + h_errno = NO_RECOVERY; + return (-1); + } + if (n > 0 && name[--n] == '.') { + strncpy(nbuf, name, n); + nbuf[n] = '\0'; + } else + longname = name; + } else { + n = strlen(name); + d = strlen(domain); + if (n + d + 1 >= MAXDNAME) { + h_errno = NO_RECOVERY; + return (-1); + } + sprintf(nbuf, "%s.%s", name, domain); + } + return (res_queryN(longname, target)); } ----Next_Part(Wed_May_31_17:15:11_2000_809)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Description: getipnodebyname-order.diff Content-Disposition: attachment; filename="getipnodebyname-order.diff" Index: lib/libc/net/name6.c diff -u lib/libc/net/name6.c.orig lib/libc/net/name6.c --- lib/libc/net/name6.c.orig Wed May 10 09:47:20 2000 +++ lib/libc/net/name6.c Sun May 14 03:39:41 2000 @@ -169,18 +169,16 @@ static int _mapped_addr_enabled(void); static FILE *_files_open(int *errp); -static struct hostent *_files_ghbyname(const char *name, int af, int *errp); +static struct hostent *_files_ghbyname(const char *name, int af, int flags, int *errp); static struct hostent *_files_ghbyaddr(const void *addr, int addrlen, int af, int *errp); static void _files_shent(int stayopen); static void _files_ehent(void); #ifdef YP -static struct hostent *_nis_ghbyname(const char *name, int af, int *errp); +static struct hostent *_nis_ghbyname(const char *name, int af, int flags, int *errp); static struct hostent *_nis_ghbyaddr(const void *addr, int addrlen, int af, int *errp); #endif -static struct hostent *_dns_ghbyname(const char *name, int af, int *errp); +static struct hostent *_dns_ghbyname(const char *name, int af, int flags, int *errp); static struct hostent *_dns_ghbyaddr(const void *addr, int addrlen, int af, int *errp); -static void _dns_shent(int stayopen); -static void _dns_ehent(void); #ifdef ICMPNL static struct hostent *_icmp_ghbyaddr(const void *addr, int addrlen, int af, int *errp); #endif /* ICMPNL */ @@ -195,7 +193,7 @@ #endif /* !HOSTCONF */ struct _hostconf { - struct hostent *(*byname)(const char *name, int af, int *errp); + struct hostent *(*byname)(const char *name, int af, int flags, int *errp); struct hostent *(*byaddr)(const void *addr, int addrlen, int af, int *errp); }; @@ -344,7 +342,7 @@ for (i = 0; i < MAXHOSTCONF; i++) { if (_hostconf[i].byname - && (hp = (*_hostconf[i].byname)(name, af, errp)) != NULL) + && (hp = (*_hostconf[i].byname)(name, af, flags, errp)) != NULL) return hp; } @@ -399,20 +397,8 @@ hp = _ghbyname(name, af, flags, errp); #ifdef INET6 - if (af == AF_INET6 - && ((flags & AI_ALL) || hp == NULL) - && (MAPADDRENABLED(flags))) { - struct hostent *hp2 = _ghbyname(name, AF_INET, flags, errp); - if (hp == NULL) - hp = _hpmapv6(hp2, errp); - else { - if (hp2 && strcmp(hp->h_name, hp2->h_name) != 0) { - freehostent(hp2); - hp2 = NULL; - } - hp = _hpmerge(hp, hp2, errp); - } - } + if (af == AF_INET6 && hp != NULL && hp->h_addrtype == AF_INET) + hp = _hpmapv6(hp, errp); #endif return _hpsort(hp); } @@ -829,20 +815,27 @@ } static struct hostent * -_files_ghbyname(const char *name, int af, int *errp) +_files_ghbyname(const char *name, int af, int flags, int *errp) { int match, nalias; char *p, *line, *addrstr, *cname; FILE *fp; - struct hostent *rethp, *hp, hpbuf; + struct hostent **rethp, *rethp1, *rethp2, *hp, hpbuf; char *aliases[MAXALIASES + 1], *addrs[2]; union inx_addr addrbuf; char buf[BUFSIZ]; int af0 = af; +#ifdef INET6 + int mapaddrenabled; +#endif if ((fp = _files_open(errp)) == NULL) return NULL; - rethp = hp = NULL; + rethp1 = rethp2 = hp = NULL; +#ifdef INET6 + if (af0 == AF_INET6) + mapaddrenabled = MAPADDRENABLED(flags); +#endif while (fgets(buf, sizeof(buf), fp)) { line = buf; @@ -859,6 +852,7 @@ } if (!match) continue; + rethp = &rethp1; switch (af0) { case AF_INET: if (inet_aton(addrstr, (struct in_addr *)&addrbuf) @@ -870,12 +864,20 @@ break; #ifdef INET6 case AF_INET6: - if (inet_pton(af, addrstr, &addrbuf) != 1) { - *errp = NO_DATA; /* name found */ - continue; + if (inet_pton(af, addrstr, &addrbuf) == 1) { + af = af0; + break; } - af = af0; - break; + if (mapaddrenabled && + inet_aton(addrstr, + (struct in_addr *)&addrbuf) == 1) { + af = AF_INET; + rethp = &rethp2; + break; + } + *errp = NO_DATA; /* name found */ + continue; + /* NOTREACHED */ #endif case AF_UNSPEC: if (inet_aton(addrstr, (struct in_addr *)&addrbuf) @@ -903,10 +905,21 @@ addrs[0] = (char *)&addrbuf; addrs[1] = NULL; hp = _hpcopy(hp, errp); - rethp = _hpmerge(rethp, hp, errp); + *rethp = _hpmerge(*rethp, hp, errp); } fclose(fp); - return rethp; + rethp = &rethp1; +#ifdef INET6 + if (af0 == AF_INET6) { + if ((flags & AI_ALL)) + *rethp = _hpmerge(rethp1, rethp2, errp); + else if (rethp1 == NULL) + rethp = &rethp2; + else if (rethp2 != NULL) + freehostent(rethp2); + } +#endif + return *rethp; } static struct hostent * @@ -960,7 +973,7 @@ * XXX actually a hack, these are INET4 specific. */ static struct hostent * -_nis_ghbyname(const char *name, int af, int *errp) +_nis_ghbyname(const char *name, int af, int flags, int *errp) { struct hostent *hp = NULL; @@ -989,11 +1002,6 @@ } #endif -struct __res_type_list { - SLIST_ENTRY(__res_type_list) rtl_entry; - int rtl_type; -}; - #if PACKETSZ > 1024 #define MAXPACKET PACKETSZ #else @@ -1005,8 +1013,21 @@ u_char buf[MAXPACKET]; } querybuf; +struct res_target { + struct res_target *next; + const char *name; /* domain name */ + int class, type; /* class and type of query */ + u_char *answer; /* buffer to put answer */ + int anslen; /* size of answer buffer */ + int n; /* result length */ +}; + static struct hostent *getanswer __P((const querybuf *, int, const char *, int, struct hostent *, int *)); +static int res_queryN __P((const char *, struct res_target *)); +static int res_searchN __P((const char *, struct res_target *)); +static int res_querydomainN __P((const char *, const char *, + struct res_target *)); /* * we don't need to take care about sorting, nor IPv4 mapped address here. @@ -1287,26 +1308,140 @@ #undef DNS_FATAL } -/* res_search() variant with multiple query support. */ -static struct hostent * -_res_search_multi(name, rtl, errp) +extern const char *__hostalias __P((const char *)); + +/* + * Formulate a normal query, send, and await answer. + * Returned answer is placed in supplied buffer "answer". + * Perform preliminary check of answer, returning success only + * if no error is indicated and the answer count is nonzero. + * Return the size of the response on success, -1 on error. + * Error number is left in h_errno. + * + * Caller must parse answer and determine whether it answers the question. + */ +static int +res_queryN(name, target) const char *name; /* domain name */ - struct __res_type_list *rtl; /* list of query types */ - int *errp; + struct res_target *target; +{ + u_char buf[MAXPACKET]; + HEADER *hp; + int n; + struct res_target *t; + int rcode; + int ancount; + + rcode = NOERROR; + ancount = 0; + + if ((_res.options & RES_INIT) == 0 && res_init() == -1) { + h_errno = NETDB_INTERNAL; + return (-1); + } + + for (t = target; t; t = t->next) { + int class, type; + u_char *answer; + int anslen; + + hp = (HEADER *)(void *)t->answer; + hp->rcode = NOERROR; /* default */ + + /* make it easier... */ + class = t->class; + type = t->type; + answer = t->answer; + anslen = t->anslen; +#ifdef DEBUG + if (_res.options & RES_DEBUG) + printf(";; res_query(%s, %d, %d)\n", name, class, type); +#endif + + n = res_mkquery(QUERY, name, class, type, NULL, 0, NULL, + buf, sizeof(buf)); + if (n <= 0) { +#ifdef DEBUG + if (_res.options & RES_DEBUG) + printf(";; res_query: mkquery failed\n"); +#endif + h_errno = NO_RECOVERY; + return (n); + } + n = res_send(buf, n, answer, anslen); +#if 0 + if (n < 0) { +#ifdef DEBUG + if (_res.options & RES_DEBUG) + printf(";; res_query: send error\n"); +#endif + h_errno = TRY_AGAIN; + return (n); + } +#endif + + if (n < 0 || hp->rcode != NOERROR || ntohs(hp->ancount) == 0) { + rcode = hp->rcode; /* record most recent error */ +#ifdef DEBUG + if (_res.options & RES_DEBUG) + printf(";; rcode = %d, ancount=%d\n", hp->rcode, + ntohs(hp->ancount)); +#endif + continue; + } + + ancount += ntohs(hp->ancount); + + t->n = n; + } + + if (ancount == 0) { + switch (rcode) { + case NXDOMAIN: + h_errno = HOST_NOT_FOUND; + break; + case SERVFAIL: + h_errno = TRY_AGAIN; + break; + case NOERROR: + h_errno = NO_DATA; + break; + case FORMERR: + case NOTIMP: + case REFUSED: + default: + h_errno = NO_RECOVERY; + break; + } + return (-1); + } + return (ancount); +} + +/* + * Formulate a normal query, send, and retrieve answer in supplied buffer. + * Return the size of the response on success, -1 on error. + * If enabled, implement search rules until answer or unrecoverable failure + * is detected. Error code, if any, is left in h_errno. + */ +static int +res_searchN(name, target) + const char *name; /* domain name */ + struct res_target *target; { const char *cp, * const *domain; - struct hostent *hp0 = NULL, *hp; - struct hostent hpbuf; + HEADER *hp = (HEADER *)(void *)target->answer; /*XXX*/ 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; - querybuf buf; if ((_res.options & RES_INIT) == 0 && res_init() == -1) { - *errp = NETDB_INTERNAL; - return (NULL); + h_errno = NETDB_INTERNAL; + return (-1); } + + errno = 0; + h_errno = HOST_NOT_FOUND; /* default, if we never query */ dots = 0; for (cp = name; *cp; cp++) dots += (*cp == '.'); @@ -1314,24 +1449,11 @@ 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, buf.buf, - sizeof(buf.buf)); - if (ret > 0) { - hpbuf.h_addrtype = (rtl->rtl_type == T_AAAA) - ? AF_INET6 : AF_INET; - hpbuf.h_length = ADDRLEN(hpbuf.h_addrtype); - hp = getanswer(&buf, ret, name, rtl->rtl_type, - &hpbuf, errp); - hp = _hpcopy(&hpbuf, errp); - hp0 = _hpmerge(hp0, hp, errp); - } - } - return (hp0); - } + /* + * if there aren't any dots, it could be a user-level alias + */ + if (!dots && (cp = __hostalias(name)) != NULL) + return (res_queryN(cp, target)); /* * If there are dots in the name already, let's just give it a try @@ -1339,23 +1461,10 @@ */ 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, - buf.buf, sizeof(buf.buf)); - if (ret > 0) { - hpbuf.h_addrtype = (rtl->rtl_type == T_AAAA) - ? AF_INET6 : AF_INET; - hpbuf.h_length = ADDRLEN(hpbuf.h_addrtype); - hp = getanswer(&buf, ret, name, rtl->rtl_type, - &hpbuf, errp); - hp = _hpcopy(&hpbuf, errp); - hp0 = _hpmerge(hp0, hp, errp); - } - } - if (hp0 != NULL) - return (hp0); - saved_herrno = *errp; + ret = res_querydomainN(name, NULL, target); + if (ret > 0) + return (ret); + saved_herrno = h_errno; tried_as_is++; } @@ -1370,26 +1479,12 @@ int done = 0; for (domain = (const char * const *)_res.dnsrch; - *domain && !done; - domain++) { + *domain && !done; + domain++) { - for(rtl = rtl0; rtl != NULL; - rtl = SLIST_NEXT(rtl, rtl_entry)) { - ret = res_querydomain(name, *domain, C_IN, - rtl->rtl_type, - buf.buf, sizeof(buf.buf)); - if (ret > 0) { - hpbuf.h_addrtype = (rtl->rtl_type == T_AAAA) - ? AF_INET6 : AF_INET; - hpbuf.h_length = ADDRLEN(hpbuf.h_addrtype); - hp = getanswer(&buf, ret, name, - rtl->rtl_type, &hpbuf, errp); - hp = _hpcopy(&hpbuf, errp); - hp0 = _hpmerge(hp0, hp, errp); - } - } - if (hp0 != NULL) - return (hp0); + ret = res_querydomainN(name, *domain, target); + if (ret > 0) + return (ret); /* * If no server present, give up. @@ -1405,11 +1500,11 @@ * fully-qualified. */ if (errno == ECONNREFUSED) { - *errp = TRY_AGAIN; - return (NULL); + h_errno = TRY_AGAIN; + return (-1); } - switch (*errp) { + switch (h_errno) { case NO_DATA: got_nodata++; /* FALLTHROUGH */ @@ -1417,7 +1512,7 @@ /* keep trying */ break; case TRY_AGAIN: - if (buf.hdr.rcode == SERVFAIL) { + if (hp->rcode == SERVFAIL) { /* try next search element, if any */ got_servfail++; break; @@ -1427,40 +1522,28 @@ /* anything else implies that we're done */ done++; } - - /* if we got here for some reason other than DNSRCH, + /* + * 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++; + done++; } } /* - * If we have not already tried the name "as is", do that now. + * 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. + * name or whether it ends with a dot. */ 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, - buf.buf, sizeof(buf.buf)); - if (ret > 0) { - hpbuf.h_addrtype = (rtl->rtl_type == T_AAAA) - ? AF_INET6 : AF_INET; - hpbuf.h_length = ADDRLEN(hpbuf.h_addrtype); - hp = getanswer(&buf, ret, name, rtl->rtl_type, - &hpbuf, errp); - hp = _hpcopy(&hpbuf, errp); - hp0 = _hpmerge(hp0, hp, errp); - } - } - if (hp0 != NULL) - return (hp0); + ret = res_querydomainN(name, NULL, target); + if (ret > 0) + return (ret); } - /* if we got here, we didn't satisfy the search. + /* + * 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. @@ -1468,43 +1551,139 @@ * the last DNSRCH we did. */ if (saved_herrno != -1) - *errp = saved_herrno; + h_errno = saved_herrno; else if (got_nodata) - *errp = NO_DATA; + h_errno = NO_DATA; else if (got_servfail) - *errp = TRY_AGAIN; - return (NULL); + h_errno = TRY_AGAIN; + return (-1); +} + +/* + * Perform a call on res_query on the concatenation of name and domain, + * removing a trailing dot from name if domain is NULL. + */ +static int +res_querydomainN(name, domain, target) + const char *name, *domain; + struct res_target *target; +{ + char nbuf[MAXDNAME]; + const char *longname = nbuf; + size_t n, d; + + if ((_res.options & RES_INIT) == 0 && res_init() == -1) { + h_errno = NETDB_INTERNAL; + return (-1); + } +#ifdef DEBUG + if (_res.options & RES_DEBUG) + printf(";; res_querydomain(%s, %s)\n", + name, domain?domain:"<Nil>"); +#endif + if (domain == NULL) { + /* + * Check for trailing '.'; + * copy without '.' if present. + */ + n = strlen(name); + if (n >= MAXDNAME) { + h_errno = NO_RECOVERY; + return (-1); + } + if (n > 0 && name[--n] == '.') { + strncpy(nbuf, name, n); + nbuf[n] = '\0'; + } else + longname = name; + } else { + n = strlen(name); + d = strlen(domain); + if (n + d + 1 >= MAXDNAME) { + h_errno = NO_RECOVERY; + return (-1); + } + sprintf(nbuf, "%s.%s", name, domain); + } + return (res_queryN(longname, target)); } static struct hostent * -_dns_ghbyname(const char *name, int af, int *errp) +_dns_ghbyname(const char *name, int af, int flags, int *errp) { - struct __res_type_list *rtl, rtl4; -#ifdef INET6 - struct __res_type_list rtl6; -#endif + struct hostent *hp, *hp2; + struct hostent hbuf; + querybuf buf, buf2; + struct res_target q, q2; -#ifdef INET6 + if ((_res.options & RES_INIT) == 0) { + if (res_init() < 0) { + *errp = h_errno; + return NULL; + } + } + + memset(&q, 0, sizeof(q2)); + memset(&q2, 0, sizeof(q2)); 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; +#ifdef INET6 + /* prefer IPv6 */ + q.class = C_IN; + q.type = T_AAAA; + q.answer = buf.buf; + q.anslen = sizeof(buf); + q.next = &q2; + q2.class = C_IN; + q2.type = T_A; + q2.answer = buf2.buf; + q2.anslen = sizeof(buf2); break; +#endif case AF_INET: - SLIST_NEXT(&rtl4, rtl_entry) = NULL; rtl4.rtl_type = T_A; - rtl = &rtl4; + q.class = C_IN; + q.type = T_A; + q.answer = buf.buf; + q.anslen = sizeof(buf); + break; +#ifdef INET6 + case AF_INET6: + q.class = C_IN; + q.type = T_AAAA; + q.answer = buf.buf; + q.anslen = sizeof(buf); + if (MAPADDRENABLED(flags)) { + q.next = &q2; + q2.class = C_IN; + q2.type = T_A; + q2.answer = buf2.buf; + q2.anslen = sizeof(buf2); + } break; - } -#else - SLIST_NEXT(&rtl4, rtl_entry) = NULL; rtl4.rtl_type = T_A; - rtl = &rtl4; #endif - return(_res_search_multi(name, rtl, errp)); + default: + *errp = NO_RECOVERY; + return NULL; + } + if (res_searchN(name, &q) < 0) { + *errp = h_errno; + return NULL; + } + memset(&hbuf, 0, sizeof(hbuf)); + hbuf.h_addrtype = (q.type == T_AAAA) ? AF_INET6 : AF_INET; + hbuf.h_length = ADDRLEN(hbuf.h_addrtype); + hp = getanswer(&buf, q.n, q.name, q.type, &hbuf, errp); + hp = _hpcopy(hp, errp); + if (q.next == NULL) + return hp; + if (hp != NULL && af == AF_INET6 && !(flags & AI_ALL)) + return hp; + memset(&hbuf, 0, sizeof(hbuf)); + hbuf.h_addrtype = (q2.type == T_AAAA) ? AF_INET6 : AF_INET; + hbuf.h_length = ADDRLEN(hbuf.h_addrtype); + hp2 = getanswer(&buf2, q2.n, q2.name, q2.type, &hbuf, errp); + hp2 = _hpcopy(hp2, errp); + return _hpmerge(hp, hp2, errp); } static struct hostent * @@ -1584,24 +1763,6 @@ hlist[0] = (char *)addr; hlist[1] = NULL; return _hpcopy(&hbuf, errp); -} - -static void -_dns_shent(int stayopen) -{ - if ((_res.options & RES_INIT) == 0) { - if (res_init() < 0) - return; - } - if (stayopen) - _res.options |= RES_STAYOPEN | RES_USEVC; -} - -static void -_dns_ehent(void) -{ - _res.options &= ~(RES_STAYOPEN | RES_USEVC); - res_close(); } #ifdef ICMPNL ----Next_Part(Wed_May_31_17:15:11_2000_809)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename=".signature-world" Hajimu UMEMOTO @ Internet Mutual Aid Society Yokohama, Japan ume@mahoroba.org ume@bisd.hitachi.co.jp ume@FreeBSD.org http://www.imasy.org/~ume/ ----Next_Part(Wed_May_31_17:15:11_2000_809)---- To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-stable" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20000531171515O.ume>