summaryrefslogtreecommitdiffstats
path: root/contrib/bind/lib/irs/dns_ho.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/bind/lib/irs/dns_ho.c')
-rw-r--r--contrib/bind/lib/irs/dns_ho.c1083
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. */
OpenPOWER on IntegriCloud