diff options
Diffstat (limited to 'contrib/bind/lib/irs/dns_ho.c')
-rw-r--r-- | contrib/bind/lib/irs/dns_ho.c | 1083 |
1 files changed, 989 insertions, 94 deletions
diff --git a/contrib/bind/lib/irs/dns_ho.c b/contrib/bind/lib/irs/dns_ho.c index 8b2df77..e340f02 100644 --- a/contrib/bind/lib/irs/dns_ho.c +++ b/contrib/bind/lib/irs/dns_ho.c @@ -52,7 +52,7 @@ /* BIND Id: gethnamaddr.c,v 8.15 1996/05/22 04:56:30 vixie Exp $ */ #if defined(LIBC_SCCS) && !defined(lint) -static const char rcsid[] = "$Id: dns_ho.c,v 1.28 2000/04/20 07:47:54 vixie Exp $"; +static const char rcsid[] = "$Id: dns_ho.c,v 1.33 2001/10/05 04:30:21 marka Exp $"; #endif /* LIBC_SCCS and not lint */ /* Imports. */ @@ -94,11 +94,7 @@ static const char rcsid[] = "$Id: dns_ho.c,v 1.28 2000/04/20 07:47:54 vixie Exp #define MAXALIASES 35 #define MAXADDRS 35 -#if PACKETSZ > 1024 -#define MAXPACKET PACKETSZ -#else -#define MAXPACKET 1024 -#endif +#define MAXPACKET (1024*64) #define BOUNDS_CHECK(ptr, count) \ if ((ptr) + (count) > eom) { \ @@ -106,6 +102,26 @@ static const char rcsid[] = "$Id: dns_ho.c,v 1.28 2000/04/20 07:47:54 vixie Exp continue; \ } else (void)0 +typedef union { + HEADER hdr; + u_char buf[MAXPACKET]; +} querybuf; + +struct dns_res_target { + struct dns_res_target *next; + querybuf qbuf; /* query buffer */ + u_char *answer; /* buffer to put answer */ + int anslen; /* size of answer buffer */ + int qclass, qtype; /* class and type of query */ + int action; /* condition whether query is really issued */ + char qname[MAXDNAME +1]; /* domain name */ +#if 0 + int n; /* result length */ +#endif +}; +enum {RESTGT_DOALWAYS, RESTGT_AFTERFAILURE, RESTGT_IGNORE}; +enum {RESQRY_SUCCESS, RESQRY_FAIL}; + struct pvt { struct hostent host; char * h_addr_ptrs[MAXADDRS + 1]; @@ -141,6 +157,8 @@ static struct __res_state * ho_res_get(struct irs_ho *this); static void ho_res_set(struct irs_ho *this, struct __res_state *res, void (*free_res)(void *)); +static struct addrinfo * ho_addrinfo(struct irs_ho *this, const char *name, + const struct addrinfo *pai); static void map_v4v6_hostent(struct hostent *hp, char **bp, int *len); @@ -148,7 +166,20 @@ static void addrsort(res_state, char **, int); static struct hostent * gethostans(struct irs_ho *this, const u_char *ansbuf, int anslen, const char *qname, int qtype, - int af, int size); + int af, int size, + struct addrinfo **ret_aip, + const struct addrinfo *pai); +static int add_hostent(struct pvt *pvt, char *bp, char **hap, + struct addrinfo *ai); +static const u_char * ar_head(const u_char *, int, const u_char *, + const u_char *, struct pvt *, + int (*)(const char *)); +static struct addrinfo * a6_expand(const u_char *, const u_char *, int, + const u_char *, const u_char *, + const struct in6_addr *, int, + const struct addrinfo *, + struct pvt *, int (*)(const char *), int *); +static const char *dname_subst(const char *, const char *, const char *); static int init(struct irs_ho *this); /* Exports. */ @@ -158,6 +189,8 @@ irs_dns_ho(struct irs_acc *this) { struct irs_ho *ho; struct pvt *pvt; + UNUSED(this); + if (!(pvt = memget(sizeof *pvt))) { errno = ENOMEM; return (NULL); @@ -180,6 +213,7 @@ irs_dns_ho(struct irs_acc *this) { ho->minimize = ho_minimize; ho->res_get = ho_res_get; ho->res_set = ho_res_set; + ho->addrinfo = ho_addrinfo; return (ho); } @@ -214,24 +248,50 @@ ho_byname(struct irs_ho *this, const char *name) { } static struct hostent * -ho_byname2(struct irs_ho *this, const char *name, int af) { +ho_byname2(struct irs_ho *this, const char *name, int af) +{ struct pvt *pvt = (struct pvt *)this->private; - int n, size, type; - u_char buf[MAXPACKET]; + struct hostent *hp = NULL; + int n, size; char tmp[NS_MAXDNAME]; const char *cp; + struct addrinfo ai; + struct dns_res_target q, q2, *p; + int querystate = RESQRY_FAIL; if (init(this) == -1) return (NULL); + memset(&q, 0, sizeof(q2)); + memset(&q2, 0, sizeof(q2)); + switch (af) { case AF_INET: size = INADDRSZ; - type = T_A; + q.qclass = C_IN; + q.qtype = T_A; + q.answer = q.qbuf.buf; + q.anslen = sizeof(q.qbuf); + q.action = RESTGT_DOALWAYS; break; case AF_INET6: size = IN6ADDRSZ; - type = T_AAAA; + q.qclass = C_IN; + q.qtype = ns_t_a6; + q.answer = q.qbuf.buf; + q.anslen = sizeof(q.qbuf); + q.next = &q2; +#ifdef RES_USE_A6 + if ((pvt->res->options & RES_USE_A6) == 0) + q.action = RESTGT_IGNORE; + else +#endif + q.action = RESTGT_DOALWAYS; + q2.qclass = C_IN; + q2.qtype = T_AAAA; + q2.answer = q2.qbuf.buf; + q2.anslen = sizeof(q2.qbuf); + q2.action = RESTGT_AFTERFAILURE; break; default: RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL); @@ -248,30 +308,62 @@ ho_byname2(struct irs_ho *this, const char *name, int af) { tmp, sizeof tmp))) name = cp; - if ((n = res_nsearch(pvt->res, name, C_IN, type, - buf, sizeof buf)) < 0) - return (NULL); - return (gethostans(this, buf, n, name, type, af, size)); + for (p = &q; p; p = p->next) { + switch(p->action) { + case RESTGT_DOALWAYS: + break; + case RESTGT_AFTERFAILURE: + if (querystate == RESQRY_SUCCESS) + continue; + break; + case RESTGT_IGNORE: + continue; + } + + if ((n = res_nsearch(pvt->res, name, p->qclass, p->qtype, + p->answer, p->anslen)) < 0) { + querystate = RESQRY_FAIL; + continue; + } + + memset(&ai, 0, sizeof(ai)); + ai.ai_family = af; + if ((hp = gethostans(this, p->answer, n, name, p->qtype, + af, size, NULL, + (const struct addrinfo *)&ai)) != NULL) + return(hp); /* no more loop is necessary */ + + querystate = RESQRY_FAIL; + continue; + } + + return(hp); /* should be NULL */ } static struct hostent * -ho_byaddr(struct irs_ho *this, const void *addr, int len, int af) { +ho_byaddr(struct irs_ho *this, const void *addr, int len, int af) +{ struct pvt *pvt = (struct pvt *)this->private; const u_char *uaddr = addr; - char qbuf[MAXDNAME+1], *qp; - u_char buf[MAXPACKET]; + char *qp; struct hostent *hp; + struct addrinfo ai; + struct dns_res_target q, q2, *p; int n, size; + int querystate = RESQRY_FAIL; if (init(this) == -1) return (NULL); + memset(&q, 0, sizeof(q2)); + memset(&q2, 0, sizeof(q2)); + if (af == AF_INET6 && len == IN6ADDRSZ && (!memcmp(uaddr, mapped, sizeof mapped) || (!memcmp(uaddr, tunnelled, sizeof tunnelled) && memcmp(&uaddr[sizeof tunnelled], v6local, sizeof(v6local))))) { /* Unmap. */ - addr = (char *)addr + sizeof mapped; + addr = (const char *)addr + sizeof mapped; uaddr += sizeof mapped; af = AF_INET; len = INADDRSZ; @@ -279,9 +371,31 @@ ho_byaddr(struct irs_ho *this, const void *addr, int len, int af) { switch (af) { case AF_INET: size = INADDRSZ; + q.qclass = C_IN; + q.qtype = T_PTR; + q.answer = q.qbuf.buf; + q.anslen = sizeof(q.qbuf); + q.action = RESTGT_DOALWAYS; break; case AF_INET6: size = IN6ADDRSZ; + q.qclass = C_IN; + q.qtype = T_PTR; + q.answer = q.qbuf.buf; + q.anslen = sizeof(q.qbuf); + q.next = &q2; + if ((pvt->res->options & RES_NO_BITSTRING) != 0) + q.action = RESTGT_IGNORE; + else + q.action = RESTGT_DOALWAYS; + q2.qclass = C_IN; + q2.qtype = T_PTR; + q2.answer = q2.qbuf.buf; + q2.anslen = sizeof(q2.qbuf); + if ((pvt->res->options & RES_NO_NIBBLE) != 0) + q2.action = RESTGT_IGNORE; + else + q2.action = RESTGT_AFTERFAILURE; break; default: errno = EAFNOSUPPORT; @@ -295,49 +409,93 @@ ho_byaddr(struct irs_ho *this, const void *addr, int len, int af) { } switch (af) { case AF_INET: - (void) sprintf(qbuf, "%u.%u.%u.%u.in-addr.arpa", + qp = q.qname; + (void) sprintf(qp, "%u.%u.%u.%u.in-addr.arpa", (uaddr[3] & 0xff), (uaddr[2] & 0xff), (uaddr[1] & 0xff), (uaddr[0] & 0xff)); break; case AF_INET6: - qp = qbuf; - for (n = IN6ADDRSZ - 1; n >= 0; n--) { - qp += SPRINTF((qp, "%x.%x.", - uaddr[n] & 0xf, - (uaddr[n] >> 4) & 0xf)); + if (q.action != RESTGT_IGNORE) { + qp = q.qname; + qp += SPRINTF((qp, "\\[x")); + for (n = 0; n < IN6ADDRSZ; n++) + qp += SPRINTF((qp, "%02x", uaddr[n])); + SPRINTF((qp, "/128].%s", + res_get_bitstringsuffix(pvt->res))); + } + if (q2.action != RESTGT_IGNORE) { + qp = q2.qname; + for (n = IN6ADDRSZ - 1; n >= 0; n--) { + qp += SPRINTF((qp, "%x.%x.", + uaddr[n] & 0xf, + (uaddr[n] >> 4) & 0xf)); + } + strcpy(qp, res_get_nibblesuffix(pvt->res)); } - strcpy(qp, "ip6.int"); break; default: abort(); } - n = res_nquery(pvt->res, qbuf, C_IN, T_PTR, buf, sizeof buf); - if (n < 0) - return (NULL); - hp = gethostans(this, buf, n, qbuf, T_PTR, af, size); - if (!hp) - return (NULL); /* H_ERRNO was set by gethostans() */ - memcpy(pvt->host_addr, addr, len); - pvt->h_addr_ptrs[0] = (char *)pvt->host_addr; - pvt->h_addr_ptrs[1] = NULL; - if (af == AF_INET && (pvt->res->options & RES_USE_INET6)) { - map_v4v6_address((char*)pvt->host_addr, (char*)pvt->host_addr); - pvt->host.h_addrtype = AF_INET6; - pvt->host.h_length = IN6ADDRSZ; - } - RES_SET_H_ERRNO(pvt->res, NETDB_SUCCESS); - return (hp); + + for (p = &q; p; p = p->next) { + switch(p->action) { + case RESTGT_DOALWAYS: + break; + case RESTGT_AFTERFAILURE: + if (querystate == RESQRY_SUCCESS) + continue; + break; + case RESTGT_IGNORE: + continue; + } + + if ((n = res_nquery(pvt->res, p->qname, p->qclass, p->qtype, + p->answer, p->anslen)) < 0) { + querystate = RESQRY_FAIL; + continue; + } + + memset(&ai, 0, sizeof(ai)); + ai.ai_family = af; + hp = gethostans(this, p->answer, n, p->qname, T_PTR, af, size, + NULL, (const struct addrinfo *)&ai); + if (!hp) { + querystate = RESQRY_FAIL; + continue; + } + + memcpy(pvt->host_addr, addr, len); + pvt->h_addr_ptrs[0] = (char *)pvt->host_addr; + pvt->h_addr_ptrs[1] = NULL; + if (af == AF_INET && (pvt->res->options & RES_USE_INET6)) { + map_v4v6_address((char*)pvt->host_addr, + (char*)pvt->host_addr); + pvt->host.h_addrtype = AF_INET6; + pvt->host.h_length = IN6ADDRSZ; + } + + RES_SET_H_ERRNO(pvt->res, NETDB_SUCCESS); + return (hp); /* no more loop is necessary. */ + } + + return(NULL); /* H_ERRNO was set by subroutines */ } static struct hostent * ho_next(struct irs_ho *this) { + + UNUSED(this); + return (NULL); } static void ho_rewind(struct irs_ho *this) { + + UNUSED(this); + /* NOOP */ } @@ -367,6 +525,495 @@ ho_res_get(struct irs_ho *this) { return (pvt->res); } +/* XXX */ +extern struct addrinfo *addr2addrinfo __P((const struct addrinfo *, + const char *)); + +static struct addrinfo * +ho_addrinfo(struct irs_ho *this, const char *name, const struct addrinfo *pai) +{ + struct pvt *pvt = (struct pvt *)this->private; + int n; + char tmp[NS_MAXDNAME]; + const char *cp; + struct dns_res_target q, q2, q3, *p; + struct addrinfo sentinel, *cur; + int querystate = RESQRY_FAIL; + + if (init(this) == -1) + return (NULL); + + memset(&q, 0, sizeof(q2)); + memset(&q2, 0, sizeof(q2)); + memset(&q3, 0, sizeof(q3)); + memset(&sentinel, 0, sizeof(sentinel)); + cur = &sentinel; + + switch (pai->ai_family) { + case AF_UNSPEC: + /* prefer IPv6 */ + q.qclass = C_IN; + q.qtype = ns_t_a6; + q.answer = q.qbuf.buf; + q.anslen = sizeof(q.qbuf); + q.next = &q2; +#ifdef RES_USE_A6 + if ((pvt->res->options & RES_USE_A6) == 0) + q.action = RESTGT_IGNORE; + else +#endif + q.action = RESTGT_DOALWAYS; + q2.qclass = C_IN; + q2.qtype = T_AAAA; + q2.answer = q2.qbuf.buf; + q2.anslen = sizeof(q2.qbuf); + q2.next = &q3; + /* try AAAA only when A6 query fails */ + q2.action = RESTGT_AFTERFAILURE; + q3.qclass = C_IN; + q3.qtype = T_A; + q3.answer = q3.qbuf.buf; + q3.anslen = sizeof(q3.qbuf); + q3.action = RESTGT_DOALWAYS; + break; + case AF_INET: + q.qclass = C_IN; + q.qtype = T_A; + q.answer = q.qbuf.buf; + q.anslen = sizeof(q.qbuf); + q.action = RESTGT_DOALWAYS; + break; + case AF_INET6: + q.qclass = C_IN; + q.qtype = ns_t_a6; + q.answer = q.qbuf.buf; + q.anslen = sizeof(q.qbuf); + q.next = &q2; +#ifdef RES_USE_A6 + if ((pvt->res->options & RES_USE_A6) == 0) + q.action = RESTGT_IGNORE; + else +#endif + q.action = RESTGT_DOALWAYS; + q2.qclass = C_IN; + q2.qtype = T_AAAA; + q2.answer = q2.qbuf.buf; + q2.anslen = sizeof(q2.qbuf); + q2.action = RESTGT_AFTERFAILURE; + break; + default: + RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); /* better error? */ + return(NULL); + } + + /* + * if there aren't any dots, it could be a user-level alias. + * this is also done in res_nquery() since we are not the only + * function that looks up host names. + */ + if (!strchr(name, '.') && (cp = res_hostalias(pvt->res, name, + tmp, sizeof tmp))) + name = cp; + + for (p = &q; p; p = p->next) { + struct addrinfo *ai; + + switch(p->action) { + case RESTGT_DOALWAYS: + break; + case RESTGT_AFTERFAILURE: + if (querystate == RESQRY_SUCCESS) + continue; + break; + case RESTGT_IGNORE: + continue; + } + + if ((n = res_nsearch(pvt->res, name, p->qclass, p->qtype, + p->answer, p->anslen)) < 0) { + querystate = RESQRY_FAIL; + continue; + } + (void)gethostans(this, p->answer, n, name, p->qtype, + pai->ai_family, /* XXX: meaningless */ + 0, &ai, pai); + if (ai) { + querystate = RESQRY_SUCCESS; + cur->ai_next = ai; + while (cur && cur->ai_next) + cur = cur->ai_next; + } + else + querystate = RESQRY_FAIL; + } + + return(sentinel.ai_next); +} + +static const u_char * +ar_head(cp, count, msg, eom, pvt, name_ok) + const u_char *cp, *msg, *eom; + int count; + struct pvt *pvt; + int (*name_ok)(const char *); +{ + int n; + char buf[1024]; /* XXX */ + + while (count-- > 0 && cp < eom) { + n = dn_expand(msg, eom, cp, buf, sizeof(buf)); + if (n < 0 || !maybe_ok(pvt->res, buf, name_ok)) + goto end; + cp += n; /* name */ + if (cp + 3 * INT16SZ + INT32SZ >= eom) + goto end; + cp += INT16SZ; /* type */ + cp += INT16SZ + INT32SZ; /* class, TTL */ + n = ns_get16(cp); + cp += n + INT16SZ; /* len */ + } + return(cp); + + end: + return(eom); /* XXX */ +} + +/* XXX: too many arguments */ +static struct addrinfo * +a6_expand(const u_char *ansbuf, const u_char *a6p, + int a6len, const u_char *arp, const u_char *eom, + const struct in6_addr *in6, int plen, const struct addrinfo *pai, + struct pvt *pvt, int (*name_ok)(const char *), int *errorp) +{ + struct in6_addr a; + int n, pbyte, plen1, pbyte1, error = 0; + const u_char *cp; + struct addrinfo sentinel, *cur; + char pname[1024], buf[1024]; /* XXX */ + + *errorp = NETDB_SUCCESS; + memset(&sentinel, 0, sizeof(sentinel)); + cur = &sentinel; + + /* + * Validate A6 parameters. + */ + if (a6len == 0) { /* an A6 record must contain at least 1 byte. */ + error = NO_RECOVERY; + goto bad; + } + /* prefix length check. */ + if ((plen1 = *a6p) > 128) { + error = NO_RECOVERY; + goto bad; + } + if (plen1 > plen) { + /* + * New length must not be greater than old one. + * Ignore the record as specified in RFC 2874 + * Section 3.1.2. + */ + return(NULL); /* just ignore. */ + } + /* boundary check for new plen and prefix addr */ + pbyte1 = (plen1 & ~7) / 8; + if ((int)sizeof(struct in6_addr) - pbyte1 > a6len - 1) { + error = NO_RECOVERY; + goto bad; + } + + /* + * merge the new prefix portion. + * <--- plen(bits) ---> + * <--- pbyte ---><-b-> + * 000000000000000pppppxxxxxxxxxxx(= in6, 0: unknown, x: known, p: pad) + * PP++++++++(+ should be merged. P: padding, must be 0) + * <-- plen1--> + * <-pbyte1-> + * ^a6p+1 + * The result should be: + * 0000000000PP++++++++xxxxxxxxxxx(= a) + */ + pbyte = (plen & ~7) / 8; + a = *in6; + if (pbyte > pbyte1) { + /* N.B. the case of "pbyte1 == 128" is implicitly excluded. */ + int b = plen % 8; /* = the length of "pp..." above */ + u_char c_hi, c_lo; + + memcpy(&a.s6_addr[pbyte1], a6p + 1, pbyte - pbyte1); + if (b > 0) { + c_hi = a6p[pbyte - pbyte1 + 1]; + c_lo = in6->s6_addr[pbyte]; + a.s6_addr[pbyte] = + (c_hi & (0xff << (8 - b))) | + ((0x00ff >> b) & c_lo); + } + } + +#if 0 /* for debug */ + if ((pvt->res->options & RES_DEBUG) != 0) { + u_char ntopbuf[INET6_ADDRSTRLEN]; + + inet_ntop(AF_INET6, &a, ntopbuf, sizeof(ntopbuf)); + printf("a6_expand: %s\\%d\n", ntopbuf, plen1); + } +#endif + + if (plen1 == 0) { + /* Here is the end of A6 chain. make addrinfo, then return. */ + return(addr2addrinfo(pai, (const char *)&a)); + } + + /* + * Expand the new prefix name. Since the prefix name must not be + * compressed (RFC 2874 Section 3.1.1), we could use ns_name_ntop() + * here if it had a stricter boundary check. + */ + cp = a6p + 1 + (sizeof(*in6) - pbyte1); + n = dn_expand(ansbuf, eom, cp, pname, sizeof(pname)); + if (n < 0 || !maybe_ok(pvt->res, pname, name_ok)) { + error = NO_RECOVERY; + goto bad; + } + if (cp + n != a6p + a6len) { /* length mismatch */ + error = NO_RECOVERY; + goto bad; + } + + /* + * we need (more) additional section records, but no one is + * available, which possibly means a malformed answer. + */ + if (arp == NULL) { + error = NO_RECOVERY; /* we can't resolve the chain. */ + goto bad; + } + + /* + * Loop thru the rest of the buffer, searching for the next A6 record + * that has the same owner name as the prefix name. If found, then + * recursively call this function to expand the whole A6 chain. + */ + plen = plen1; + for (cp = arp; cp != NULL && cp < eom; cp += n) { + int class, type; + + n = dn_expand(ansbuf, eom, cp, buf, sizeof(buf)); + if (n < 0 || !maybe_ok(pvt->res, buf, name_ok)) { + error = NO_RECOVERY; + goto bad; + } + cp += n; /* name */ + if (cp + 3 * INT16SZ + INT32SZ > eom) { + error = NO_RECOVERY; + goto bad; + } + type = ns_get16(cp); + cp += INT16SZ; /* type */ + class = ns_get16(cp); + cp += INT16SZ + INT32SZ; /* class, TTL */ + n = ns_get16(cp); + cp += INT16SZ; /* len */ + if (cp + n > eom) { + error = NO_RECOVERY; + goto bad; + } + if (class != C_IN || type != ns_t_a6) { + /* we are only interested in A6 records. skip others */ + continue; + } + + if (ns_samename(buf, pname) != 1) { + continue; + } + + /* Proceed to the next record in the chain. */ + cur->ai_next = a6_expand(ansbuf, cp, n, cp + n, eom, + (const struct in6_addr *)&a, + plen, pai, pvt, name_ok, &error); + if (error != NETDB_SUCCESS) + goto bad; + while (cur && cur->ai_next) + cur = cur->ai_next; + } + + return(sentinel.ai_next); + + bad: + *errorp = error; + if (sentinel.ai_next) + freeaddrinfo(sentinel.ai_next); + return(NULL); +} + +static const char * +dname_subst(const char *qname0, const char *owner0, const char *target) { + char owner[MAXDNAME]; + static char qname[MAXDNAME]; + const char blabelhead[] = "\\[x"; /* we can assume hex strings */ + int qlen, olen; + int bufsiz = sizeof(qname); + + /* make local copies, which are canonicalized. */ + if (ns_makecanon(qname0, qname, sizeof(qname)) < 0 || + ns_makecanon(owner0, owner, sizeof(owner)) < 0) + return(NULL); + qlen = strlen(qname); + olen = strlen(owner); + /* from now on, do not refer to qname0 nor owner0. */ + + /* + * check if QNAME is a subdomain of OWNER. + * XXX: currently, we only handle the following two cases: + * (A) none of the labels are bitlabels, or + * (B) both of the head labels are bitlabels (and the following + * labels are NOT bitlabels). + * If we pass the check, then subtract the remaining part from QNAME. + * ex. (A) qname=www.foo.com,owner=foo.com => new qname=www. + * (B) qname=\[x3ffe0501/32].foo.com,owner=\[x3ffe/16].foo.com + * => new qname=\[x0501/16]. + */ + if (ns_samedomain(qname, owner)) { /* check (A) */ + /* at this point, qlen must not be smaller than olen */ + qname[qlen - olen] = 0; + bufsiz -= (qlen - olen); + } else { /* check (B) */ + char *parent0, *parent1; + /* the following 3 have enough size to store 1 bitlabel */ + u_char qlabel[64], olabel[64], newlabel[64]; + int qlabellen, olabellen; + + if (strncmp(qname, blabelhead, 3) != 0 || + strncmp(owner, blabelhead, 3) != 0) + return(NULL); + /* + * Both two begin with bitlabels. The succeeding parts + * must exact match. + */ + if ((parent0 = strchr(qname, '.')) == NULL || + (parent1 = strchr(owner, '.')) == NULL) + return(NULL); + + /* ns_samename allows names to begin with '.' */ + if (ns_samename(parent0, parent1) != 1) + return(NULL); + + /* cut the upper domain parts off. */ + *(parent0 + 1) = 0; + *(parent1 + 1) = 0; + /* convert the textual form into binary one. */ + if (ns_name_pton(qname, qlabel, sizeof(qlabel)) < 0 || + ns_name_pton(owner, olabel, sizeof(olabel)) < 0) + return(NULL); + if ((qlabellen = *(qlabel + 1)) == 0) + qlabellen = 256; + if ((olabellen = *(olabel + 1)) == 0) + olabellen = 256; + if (olabellen > qlabellen) + return(NULL); /* owner does not contain qname. */ + else { + int qplen = (qlabellen + 7) / 8; + int oplen = (olabellen + 7) / 8; + int sft = olabellen % 8; + int nllen, n; + u_char *qp, *op, *np; + + /* skip ELT and Count. */ + qp = qlabel + 2; + op = olabel + 2; + + /* check if olabel is a "subdomain" of qlabel. */ + if (memcmp(qp, op, oplen - 1) != 0) + return(NULL); + if (sft > 0) { + /* compare trailing bits (between 1 and 7) */ + if ((qp[qplen - 1] & (0xff << sft)) != + op[qplen - 1]) + return(NULL); + } + + /* OK, get remaining bits from qlabel. */ + np = newlabel; + if (olabellen == qlabellen) { + /* + * Two names (including bitlabels) are exactly + * same. Discard the whole names. + * XXX: ns_samename() above should exclude + * this case... + */ + qname[0] = 0; + goto maketarget; + } + *np++ = 0x41; /* XXX hardcoding */ + *np++ = nllen = (qlabellen - olabellen); + if (sft == 0) { + /* + * No alignment issue. can just use memcpy. + * Note that the "else" part below contains + * this case. We separate the two cases just + * for efficiency. + * We assume that ns_name_pton above ensures + * QP does not contain trailing garbages. + */ + memcpy(np, qp + oplen, qplen - oplen); + np += qplen - oplen; + *np = 0; + } else { + /* + * copy the lower (8-SFT) bits of QP to the + * upper (8-SFT) bits of NP, then copy the + * upper SFT bits of QP+1 to the lower SFT bits + * of NP, and so on... + * if QP is xxxyyyyy zzzwww..., then + * NP would be yyyyyzzz ... + * Again, we assume QP does not contain + * trailing garbages. + */ + qp += (oplen - 1); + while (nllen > 0) { + *np = (*qp << sft) & 0xff; + if ((nllen -= (8 - sft)) <= 0) + break; /* done */ + qp++; + *np |= ((*qp >> sft) & 0xff); + np++; + nllen -= sft; + } + *++np = 0; + } + + /* + * make a new bitlabel with the remaining bits. + * Note that there's no buffer boundary issue, since + * qlabel, olabel, and newlabel all have the same size. + * ns_name_ntop() must not return 0, since we have + * a non-empty bitlabel. + */ + if ((n = ns_name_ntop(newlabel, qname, sizeof(qname))) + <= 0) + return(NULL); + bufsiz -= n; + if (qname[n - 1] != '.') { /* XXX no trailing dot */ + qname[n - 1] = '.'; + qname[n] = 0; + bufsiz--; + } + + } + } + + maketarget: + /* + * Finally, append the remaining part (maybe empty) to the new target. + */ + if (bufsiz < (int)strlen(target)) /* bufsiz takes care of the \0. */ + return(NULL); + strcat(qname, target); + + return((const char *)qname); +} + static void ho_res_set(struct irs_ho *this, struct __res_state *res, void (*free_res)(void *)) { @@ -386,23 +1033,37 @@ ho_res_set(struct irs_ho *this, struct __res_state *res, static struct hostent * gethostans(struct irs_ho *this, const u_char *ansbuf, int anslen, const char *qname, int qtype, - int af, int size) + int af, int size, /* meaningless for addrinfo cases */ + struct addrinfo **ret_aip, const struct addrinfo *pai) { struct pvt *pvt = (struct pvt *)this->private; int type, class, buflen, ancount, qdcount, n, haveanswer, had_error; + int error = NETDB_SUCCESS, arcount; int (*name_ok)(const char *); const HEADER *hp; const u_char *eom; + const u_char *eor; const u_char *cp; - const char *tname, **tap; + const char *tname; + const char *hname; char *bp, **ap, **hap; char tbuf[MAXDNAME+1]; + struct addrinfo sentinel, *cur, ai; + const u_char *arp = NULL; + + if (pai == NULL) abort(); + if (ret_aip != NULL) + *ret_aip = NULL; + memset(&sentinel, 0, sizeof(sentinel)); + cur = &sentinel; tname = qname; eom = ansbuf + anslen; switch (qtype) { + case ns_t_a6: case T_A: case T_AAAA: + case T_ANY: /* use T_ANY only for T_A/T_AAAA lookup */ name_ok = res_hnok; break; case T_PTR: @@ -414,7 +1075,7 @@ gethostans(struct irs_ho *this, pvt->host.h_addrtype = af; pvt->host.h_length = size; - pvt->host.h_name = NULL; + hname = pvt->host.h_name = NULL; /* * Find first satisfactory answer. @@ -423,9 +1084,10 @@ gethostans(struct irs_ho *this, RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); return (NULL); } - hp = (HEADER *)ansbuf; + hp = (const HEADER *)ansbuf; ancount = ntohs(hp->ancount); qdcount = ntohs(hp->qdcount); + arcount = ntohs(hp->arcount); bp = pvt->hostbuf; buflen = sizeof pvt->hostbuf; cp = ansbuf + HFIXEDSZ; @@ -443,7 +1105,8 @@ gethostans(struct irs_ho *this, RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); return (NULL); } - if (qtype == T_A || qtype == T_AAAA) { + if (qtype == T_A || qtype == T_AAAA || + qtype == ns_t_a6 || qtype == T_ANY) { /* res_nsend() 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). @@ -454,9 +1117,10 @@ gethostans(struct irs_ho *this, return (NULL); } pvt->host.h_name = bp; + hname = bp; bp += n; buflen -= n; - /* The qname can be abbreviated, but h_name is now absolute. */ + /* The qname can be abbreviated, but hname is now absolute. */ qname = pvt->host.h_name; } ap = pvt->host_aliases; @@ -486,10 +1150,12 @@ gethostans(struct irs_ho *this, cp += n; continue; } - if ((qtype == T_A || qtype == T_AAAA) && type == T_CNAME) { + eor = cp + n; + if ((qtype == T_A || qtype == T_AAAA || qtype == ns_t_a6 || + qtype == T_ANY) && type == T_CNAME) { if (ap >= &pvt->host_aliases[MAXALIASES-1]) continue; - n = dn_expand(ansbuf, eom, cp, tbuf, sizeof tbuf); + n = dn_expand(ansbuf, eor, cp, tbuf, sizeof tbuf); if (n < 0 || !maybe_ok(pvt->res, tbuf, name_ok)) { had_error++; continue; @@ -508,17 +1174,78 @@ gethostans(struct irs_ho *this, } strcpy(bp, tbuf); pvt->host.h_name = bp; + hname = bp; bp += n; buflen -= n; continue; } + if (type == ns_t_dname) { + const char *t0, *t; + + /* + * just replace the query target; do not update the + * alias list. (Or should we?) + */ + t0 = (qtype == T_PTR) ? tname : hname; + + n = dn_expand(ansbuf, eor, cp, tbuf, sizeof(tbuf)); + if (n < 0 || !maybe_dnok(pvt->res, tbuf)) { + had_error++; + continue; + } +#ifdef RES_USE_DNAME + if ((pvt ->res->options & RES_USE_DNAME) == 0) { + cp += n; + continue; + } +#endif + if ((t = dname_subst(t0, bp, tbuf)) == NULL) { + cp += n; + continue; + } +#if 0 /* for debug */ + if ((pvt->res->options & RES_DEBUG) != 0) { + printf("DNAME owner=%s, target=%s, next=%s\n", + bp, tbuf, t); + } +#endif + cp += n; + + n = strlen(t) + 1; /* for the \0 */ + if (n > buflen) { + had_error++; + continue; + } + strcpy(bp, t); + if (qtype == T_PTR) + tname = bp; + else + hname = bp; + bp += n; + buflen -= n; + + continue; + } if (qtype == T_PTR && type == T_CNAME) { - n = dn_expand(ansbuf, eom, cp, tbuf, sizeof tbuf); + n = dn_expand(ansbuf, eor, cp, tbuf, sizeof tbuf); if (n < 0 || !maybe_dnok(pvt->res, tbuf)) { had_error++; continue; } cp += n; +#ifdef RES_USE_DNAME + if ((pvt->res->options & RES_USE_DNAME) != 0) +#endif + { + /* + * We may be able to check this regardless + * of the USE_DNAME bit, but we add the check + * for now since the DNAME support is + * experimental. + */ + if (ns_samename(tname, bp) != 1) + continue; + } /* Get canonical name. */ n = strlen(tbuf) + 1; /* for the \0 */ if (n > buflen) { @@ -531,25 +1258,38 @@ gethostans(struct irs_ho *this, buflen -= n; continue; } - if (type != qtype) { + if (qtype == T_ANY) { + if (!(type == T_A || type == T_AAAA || + type == ns_t_a6)) { + cp += n; + continue; + } + } else if (type != qtype) { cp += n; continue; } switch (type) { case T_PTR: + if (ret_aip != NULL) { + /* addrinfo never needs T_PTR */ + cp += n; + continue; + } if (ns_samename(tname, bp) != 1) { cp += n; continue; } - n = dn_expand(ansbuf, eom, cp, bp, buflen); + n = dn_expand(ansbuf, eor, cp, bp, buflen); if (n < 0 || !maybe_hnok(pvt->res, bp) || n >= MAXHOSTNAMELEN) { had_error++; break; } cp += n; - if (!haveanswer) + if (!haveanswer) { pvt->host.h_name = bp; + hname = bp; + } else if (ap < &pvt->host_aliases[MAXALIASES-1]) *ap++ = bp; else @@ -560,16 +1300,91 @@ gethostans(struct irs_ho *this, buflen -= n; } break; + case ns_t_a6: { + struct in6_addr in6; + struct addrinfo ai; + +#ifdef RES_USE_A6 + if ((pvt->res->options & RES_USE_A6) == 0) { + cp += n; + continue; + } +#endif + + if (ns_samename(hname, bp) != 1) { + cp += n; + continue; + } + + /* + * search for the top of the additional section. + * once found, keep it for the case where we have + * more than one A6 record. + * XXX: however, we may not need this part. + */ + if (arp == NULL && arcount > 0) { + int nscount = ntohs(hp->nscount); + + arp = ar_head(cp + n, nscount + ancount - 1, + ansbuf, eom, pvt, name_ok); + } + + /* recursively collect the whole A6 chain */ + ai = *pai; /* XXX: we can't override constant pai */ + ai.ai_family = AF_INET6; + memset(&in6, 0, sizeof(in6)); /* just for safety */ + cur->ai_next = a6_expand(ansbuf, cp, n, arp, eom, + &in6, 128, + (const struct addrinfo *)&ai, + pvt, name_ok, &error); + if (error != NETDB_SUCCESS) { +#ifdef DEBUG + /* in this case, cur->ai_next must be NULL. */ + if (cur->ai_next != NULL) + abort(); +#endif + had_error++; + continue; + } + + /* + * We don't bother even if cur->ai_next is NULL unless + * the expansion failed by a fatal error. The list + * can be NULL if the given A6 is incomplete, but we + * may have another complete A6 chain in this answer. + * See the last paragraph of RFC 2874 Section 3.1.4. + */ + if (cur->ai_next == NULL) { + cp += n; + continue; /* no error, no answer */ + } + goto convertinfo; + } /* FALLTHROUGH */ case T_A: case T_AAAA: - if (ns_samename(pvt->host.h_name, bp) != 1) { + if (ns_samename(hname, bp) != 1) { + cp += n; + continue; + } + if (type == T_A && n != INADDRSZ) { cp += n; continue; } - if (n != pvt->host.h_length) { + if (type == T_AAAA && n != IN6ADDRSZ) { cp += n; continue; } + + /* make addrinfo. don't overwrite constant PAI */ + ai = *pai; + ai.ai_family = (type == T_AAAA) ? AF_INET6 : AF_INET; + cur->ai_next = addr2addrinfo( + (const struct addrinfo *)&ai, + (const char *)cp); + if (cur->ai_next == NULL) + had_error++; + + convertinfo: /* convert addrinfo into hostent form */ if (!haveanswer) { int nn; @@ -580,34 +1395,44 @@ gethostans(struct irs_ho *this, continue; } pvt->host.h_name = bp; + hname = bp; bp += nn; buflen -= nn; } /* Ensure alignment. */ - bp += sizeof(align) - ((u_long)bp % sizeof(align)); + bp = (char *)(((u_long)bp + (sizeof(align) - 1)) & + ~(sizeof(align) - 1)); /* Avoid overflows. */ if (bp + n >= &pvt->hostbuf[sizeof pvt->hostbuf]) { had_error++; continue; } - if (hap >= &pvt->h_addr_ptrs[MAXADDRS-1]) { - cp += n; - continue; - } - /* Suppress duplicates. */ - for (tap = (const char **)pvt->h_addr_ptrs; - *tap != NULL; - tap++) - if (memcmp(*tap, cp, n) == 0) - break; - if (*tap != NULL) { - cp += n; - continue; + if (ret_aip) { /* need addrinfo. keep it. */ + while (cur && cur->ai_next) + cur = cur->ai_next; + } else if (cur->ai_next) { /* need hostent */ + struct addrinfo *aip = cur->ai_next; + + for (aip = cur->ai_next; aip; + aip = aip->ai_next) { + int m; + + m = add_hostent(pvt, bp, hap, aip); + if (m < 0) { + had_error++; + break; + } + if (m == 0) + continue; + if (hap < &pvt->h_addr_ptrs[MAXADDRS-1]) + hap++; + + bp += m; + } + + freeaddrinfo(cur->ai_next); + cur->ai_next = NULL; } - /* Store address. */ - memcpy(*hap++ = bp, cp, n); - *hap = NULL; - bp += n; cp += n; break; default: @@ -617,27 +1442,94 @@ gethostans(struct irs_ho *this, haveanswer++; } if (haveanswer) { - *ap = NULL; - - if (pvt->res->nsort && haveanswer > 1 && qtype == T_A) - addrsort(pvt->res, pvt->h_addr_ptrs, haveanswer); - if (!pvt->host.h_name) { - n = strlen(qname) + 1; /* for the \0 */ - if (n > buflen || n >= MAXHOSTNAMELEN) - goto no_recovery; - strcpy(bp, qname); - pvt->host.h_name = bp; - bp += n; - buflen -= n; + if (ret_aip == NULL) { + *ap = NULL; + *hap = NULL; + + if (pvt->res->nsort && haveanswer > 1 && qtype == T_A) + addrsort(pvt->res, pvt->h_addr_ptrs, + haveanswer); + if (pvt->host.h_name == NULL) { + n = strlen(qname) + 1; /* for the \0 */ + if (n > buflen || n >= MAXHOSTNAMELEN) + goto no_recovery; + strcpy(bp, qname); + pvt->host.h_name = bp; + bp += n; + buflen -= n; + } + if (pvt->res->options & RES_USE_INET6) + map_v4v6_hostent(&pvt->host, &bp, &buflen); + RES_SET_H_ERRNO(pvt->res, NETDB_SUCCESS); + return (&pvt->host); + } else { + if ((pai->ai_flags & AI_CANONNAME) != 0) { + if (pvt->host.h_name == NULL) { + sentinel.ai_next->ai_canonname = + strdup(qname); + } + else { + sentinel.ai_next->ai_canonname = + strdup(pvt->host.h_name); + } + } + *ret_aip = sentinel.ai_next; + return(NULL); } - if (pvt->res->options & RES_USE_INET6) - map_v4v6_hostent(&pvt->host, &bp, &buflen); - RES_SET_H_ERRNO(pvt->res, NETDB_SUCCESS); - return (&pvt->host); } no_recovery: - RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); - return (NULL); + if (sentinel.ai_next) { + /* this should be impossible, but check it for safety */ + freeaddrinfo(sentinel.ai_next); + } + if (error == NETDB_SUCCESS) + RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); + else + RES_SET_H_ERRNO(pvt->res, error); + return(NULL); +} + +static int +add_hostent(struct pvt *pvt, char *bp, char **hap, struct addrinfo *ai) +{ + int addrlen; + char *addrp; + const char **tap; + char *obp = bp; + + switch(ai->ai_addr->sa_family) { + case AF_INET6: + addrlen = IN6ADDRSZ; + addrp = (char *)&((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr; + break; + case AF_INET: + addrlen = INADDRSZ; + addrp = (char *)&((struct sockaddr_in *)ai->ai_addr)->sin_addr; + break; + default: + return(-1); /* abort? */ + } + + /* Ensure alignment. */ + bp = (char *)(((u_long)bp + (sizeof(align) - 1)) & + ~(sizeof(align) - 1)); + /* Avoid overflows. */ + if (bp + addrlen >= &pvt->hostbuf[sizeof pvt->hostbuf]) + return(-1); + if (hap >= &pvt->h_addr_ptrs[MAXADDRS-1]) + return(0); /* fail, but not treat it as an error. */ + + /* Suppress duplicates. */ + for (tap = (const char **)pvt->h_addr_ptrs; + *tap != NULL; + tap++) + if (memcmp(*tap, addrp, addrlen) == 0) + break; + if (*tap != NULL) + return (0); + + memcpy(*hap = bp, addrp, addrlen); + return((bp + addrlen) - obp); } static void @@ -649,7 +1541,10 @@ map_v4v6_hostent(struct hostent *hp, char **bpp, int *lenp) { hp->h_addrtype = AF_INET6; hp->h_length = IN6ADDRSZ; for (ap = hp->h_addr_list; *ap; ap++) { - int i = sizeof(align) - ((u_long)*bpp % sizeof(align)); + int i = (u_long)*bpp % sizeof(align); + + if (i != 0) + i = sizeof(align) - i; if (*lenp < (i + IN6ADDRSZ)) { /* Out of memory. Truncate address list here. */ |