+ ia= inet_ntoa(rr); assert(ia);
+ CSP_ADDSTR(ia);
+ return adns_s_ok;
+}
+
+/*
+ * _in6addr (pa,di,cs)
+ */
+
+static adns_status pa_in6addr(const parseinfo *pai, int cbyte,
+ int max, void *datap) {
+ struct in6_addr *storeto= datap;
+
+ if (max-cbyte != 16) return adns_s_invaliddata;
+ memcpy(storeto->s6_addr, pai->dgram + cbyte, 16);
+ return adns_s_ok;
+}
+
+static int di_in6addr(adns_state ads,
+ const void *datap_a, const void *datap_b) {
+ return dip_genaddr(ads,AF_INET6,datap_a,datap_b);
+}
+
+static adns_status cs_in6addr(vbuf *vb, const void *datap) {
+ char buf[INET6_ADDRSTRLEN];
+ const char *ia;
+
+ ia= inet_ntop(AF_INET6, datap, buf, sizeof(buf)); assert(ia);
+ CSP_ADDSTR(ia);
+ return adns_s_ok;
+}
+
+/*
+ * _addr (pap,pa,di,csp,cs,gsz,qs, +search_sortlist_sa, dip_sockaddr,
+ * addr_rrtypes)
+ */
+
+static const typeinfo tinfo_addrsub;
+
+#define ADDR_RRTYPES(_) _(a) _(aaaa)
+
+static const adns_rrtype addr_all_rrtypes[] = {
+#define RRTY_CODE(ty) adns_r_##ty,
+ ADDR_RRTYPES(RRTY_CODE)
+#undef RRTY_CODE
+};
+
+enum {
+#define RRTY_INDEX(ty) addr__ri_##ty,
+ ADDR_RRTYPES(RRTY_INDEX)
+#undef RRTY_INDEX
+ addr_nrrtypes,
+#define RRTY_FLAG(ty) addr_rf_##ty = 1 << addr__ri_##ty,
+ ADDR_RRTYPES(RRTY_FLAG)
+ addr__rrty_hunoz
+#undef RRTY_FLAG
+};
+
+static unsigned addr_rrtypeflag(adns_rrtype type)
+{
+ int i;
+
+ type &= adns_rrt_typemask;
+ for (i = 0; i < addr_nrrtypes && type != addr_all_rrtypes[i]; i++);
+ assert(i < addr_nrrtypes);
+ return 1 << i;
+}
+
+/* About CNAME handling in addr queries.
+ *
+ * A user-level addr query is translated into a number of protocol-level
+ * queries, and its job is to reassemble the results. This gets tricky if
+ * the answers aren't consistent. In particular, if the answers report
+ * inconsistent indirection via CNAME records (e.g., different CNAMEs, or
+ * some indirect via a CNAME, and some don't) then we have trouble.
+ *
+ * Once we've received an answer, even if it was NODATA, we set
+ * adns__qf_addr_answer on the parent query. This will let us detect a
+ * conflict between a no-CNAME-with-NODATA reply and a subsequent CNAME.
+ *
+ * If we detect a conflict of any kind, then at least one answer came back
+ * with a CNAME record, so we pick the first such answer (somewhat
+ * arbitrarily) as being the `right' canonical name, and set this in the
+ * parent query's answer->cname slot. We discard address records from the
+ * wrong name. And finally we cancel the outstanding child queries, and
+ * resubmit address queries for the address families we don't yet have, with
+ * adns__qf_addr_cname set so that we know that we're in the fixup state.
+ */
+
+static adns_status pap_addr(const parseinfo *pai, int rrty, size_t rrsz,
+ int *cbyte_io, int max, adns_rr_addr *storeto)
+{
+ const byte *dgram= pai->dgram;
+ int af, addrlen, salen;
+ struct in6_addr v6map;
+ const void *oaddr = dgram + *cbyte_io;
+ int avail = max - *cbyte_io;
+ int step = -1;
+ void *addrp = 0;
+
+ switch (rrty) {
+ case adns_r_a:
+ if (pai->qu->flags & adns_qf_domapv4) {
+ if (avail < 4) return adns_s_invaliddata;
+ memset(v6map.s6_addr + 0, 0x00, 10);
+ memset(v6map.s6_addr + 10, 0xff, 2);
+ memcpy(v6map.s6_addr + 12, oaddr, 4);
+ oaddr = v6map.s6_addr; avail = sizeof(v6map.s6_addr);
+ if (step < 0) step = 4;
+ goto aaaa;
+ }
+ af = AF_INET; addrlen = 4;
+ addrp = &storeto->addr.inet.sin_addr;
+ salen = sizeof(storeto->addr.inet);
+ break;
+ case adns_r_aaaa:
+ aaaa:
+ af = AF_INET6; addrlen = 16;
+ addrp = storeto->addr.inet6.sin6_addr.s6_addr;
+ salen = sizeof(storeto->addr.inet6);
+ break;
+ }
+ assert(addrp);
+
+ assert(offsetof(adns_rr_addr, addr) + salen <= rrsz);
+ if (addrlen < avail) return adns_s_invaliddata;
+ if (step < 0) step = addrlen;
+ *cbyte_io += step;
+ memset(&storeto->addr, 0, salen);
+ storeto->len = salen;
+ storeto->addr.sa.sa_family = af;
+ memcpy(addrp, oaddr, addrlen);
+
+ return adns_s_ok;
+}
+
+static adns_status pa_addr(const parseinfo *pai, int cbyte,
+ int max, void *datap) {
+ int err = pap_addr(pai, pai->qu->answer->type & adns_rrt_typemask,
+ pai->qu->answer->rrsz, &cbyte, max, datap);
+ if (err) return err;
+ if (cbyte != max) return adns_s_invaliddata;
+ return adns_s_ok;
+}
+
+static int search_sortlist_sa(adns_state ads, const struct sockaddr *sa)
+{
+ return search_sortlist(ads, sa->sa_family, adns__sockaddr_to_inaddr(sa));
+}
+
+static int dip_sockaddr(adns_state ads,
+ const struct sockaddr *sa,
+ const struct sockaddr *sb)
+{
+ if (!ads->sortlist) return 0;
+ return search_sortlist_sa(ads, sa) > search_sortlist_sa(ads, sb);
+}
+
+static int di_addr(adns_state ads, const void *datap_a, const void *datap_b) {
+ const adns_rr_addr *ap= datap_a, *bp= datap_b;
+
+ return dip_sockaddr(ads, &ap->addr.sa, &bp->addr.sa);
+}
+
+static int div_addr(void *context, const void *datap_a, const void *datap_b) {
+ const adns_state ads= context;
+
+ return di_addr(ads, datap_a, datap_b);
+}
+
+static adns_status csp_addr(vbuf *vb, const adns_rr_addr *rrp) {
+ char buf[128];
+ int err;
+
+ switch (rrp->addr.inet.sin_family) {
+ case AF_INET:
+ CSP_ADDSTR("INET ");
+ goto ntop;
+ case AF_INET6:
+ CSP_ADDSTR("INET6 ");
+ goto ntop;
+ ntop:
+ err= getnameinfo(&rrp->addr.sa, rrp->len, buf, sizeof(buf), 0, 0,
+ NI_NUMERICHOST); assert(!err);
+ CSP_ADDSTR(buf);
+ break;
+ default:
+ sprintf(buf,"AF=%u",rrp->addr.sa.sa_family);
+ CSP_ADDSTR(buf);
+ break;
+ }
+ return adns_s_ok;
+}
+
+static adns_status cs_addr(vbuf *vb, const void *datap) {
+ const adns_rr_addr *rrp= datap;
+
+ return csp_addr(vb,rrp);
+}
+
+static unsigned addr_rrtypes(adns_state ads, adns_rrtype type,
+ adns_queryflags qf)
+{
+ adns_queryflags permitaf = 0, hackaf = 0;
+ unsigned want = 0;
+
+ if (!(qf & (adns_qf_ipv4_only | adns_qf_ipv6_only)))
+ qf |= adns_qf_ipv4_only | adns_qf_ipv6_only;
+ if (!(type & adns__qtf_bigaddr) || !(type & adns__qtf_manyaf))
+ qf = (qf & adns__qf_afmask) | adns_qf_ipv4_only;
+ else if (ads->iflags & adns_if_afmask) {
+ if (ads->iflags & adns_if_af_v4only) {
+ permitaf |= adns_qf_ipv4_only;
+ hackaf |= adns_qf_domapv4;
+ }
+ if (ads->iflags & adns_if_af_v6only)
+ permitaf |= adns_qf_ipv6_only;
+ if (qf & permitaf)
+ qf &= hackaf | permitaf | ~adns__qf_afmask;
+ }
+
+ if (qf & adns_qf_ipv4_only) want |= addr_rf_a;
+ if (qf & adns_qf_ipv6_only) want |= addr_rf_aaaa;
+
+ return want;
+}
+
+static int gsz_addr(adns_rrtype type)
+{
+ return type & adns__qtf_bigaddr ?
+ sizeof(adns_rr_addr) : sizeof(adns_rr_addr_v4only);
+}
+
+static adns_status append_addrs(adns_query qu, adns_query from, size_t rrsz,
+ adns_rr_addr **dp, int *dlen,
+ const adns_rr_addr *sp, int slen)
+{
+ size_t drrsz = *dlen*rrsz, srrsz = slen*rrsz;
+ byte *p;
+
+ if (!slen) return adns_s_ok;
+ p = adns__alloc_interim(qu, drrsz + srrsz);
+ if (!p) R_NOMEM;
+ if (*dlen) {
+ memcpy(p, *dp, drrsz);
+ adns__free_interim(qu, *dp);
+ }
+ memcpy(p + drrsz, sp, srrsz);
+ *dlen += slen;
+ *dp = (adns_rr_addr *)p;
+ if (from && qu->expires > from->expires) qu->expires = from->expires;
+ return adns_s_ok;
+}
+
+static void icb_addr(adns_query parent, adns_query child);
+
+static void addr_subqueries(adns_query qu, struct timeval now,
+ adns_queryflags qf_extra,
+ const byte *qd_dgram, int qd_dglen)
+{
+ int i, err, id;
+ adns_query cqu;
+ adns_queryflags qf = (qu->flags & ~adns_qf_search) | qf_extra;
+ adns_rrtype qtf = qu->answer->type & adns__qtf_deref;
+ unsigned which = qu->ctx.tinfo.addr.want & ~qu->ctx.tinfo.addr.have;
+ qcontext ctx;
+
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.callback = icb_addr;
+ for (i = 0; i < addr_nrrtypes; i++) {
+ if (!(which & (1 << i))) continue;
+ err = adns__mkquery_frdgram(qu->ads, &qu->vb, &id, qd_dgram, qd_dglen,
+ DNS_HDRSIZE, addr_all_rrtypes[i], qf);
+ if (err) goto x_error;
+ err = adns__internal_submit(qu->ads, &cqu, &tinfo_addrsub,
+ addr_all_rrtypes[i] | qtf,
+ &qu->vb, id, qf, now, &ctx);
+ if (err) goto x_error;
+ cqu->answer->rrsz = qu->answer->rrsz;
+ cqu->parent = qu;
+ LIST_LINK_TAIL_PART(qu->children, cqu,siblings.);
+ }
+ qu->state = query_childw;
+ LIST_LINK_TAIL(qu->ads->childw, qu);
+ return;
+
+x_error:
+ adns__query_fail(qu, err);
+}
+
+static adns_status addr_submit(adns_query parent, adns_query *query_r,
+ vbuf *qumsg_vb, int id, unsigned want,
+ adns_queryflags flags, struct timeval now,
+ qcontext *ctx)
+{
+ /* This is effectively a substitute for adns__internal_submit, intended for
+ * the case where the caller (possibly) only wants a subset of the
+ * available record types. The memory management and callback rules are
+ * the same as for adns__internal_submit.
+ *
+ * Some differences: the query is linked onto the parent's children list
+ * before exit (though the parent's state is not changed, and it is not
+ * linked into the childw list queue); and we fiddle with the `tinfo'
+ * portion of the context structure (yes, modifying *ctx), since this is,
+ * in fact, the main purpose of this function.
+ */
+
+ adns_state ads = parent->ads;
+ adns_query qu;
+ adns_status err;
+ adns_rrtype type =
+ (adns_r_addr & adns_rrt_reprmask) |
+ (parent->answer->type & ~adns_rrt_reprmask);
+
+ ctx->tinfo.addr.want = want;
+ ctx->tinfo.addr.have = 0;
+ err = adns__internal_submit(ads, &qu, adns__findtype(adns_r_addr),
+ type, qumsg_vb, id, flags, now, ctx);
+ if (err) return err;
+
+ qu->parent = parent;
+ LIST_LINK_TAIL_PART(parent->children, qu, siblings.);
+ *query_r = qu;
+ return adns_s_ok;
+}
+
+static adns_status copy_cname_from_child(adns_query parent, adns_query child)
+{
+ adns_answer *pans = parent->answer, *cans = child->answer;
+ size_t n = strlen(cans->cname) + 1;
+
+ pans->cname = adns__alloc_preserved(parent, n);
+ if (!pans->cname) R_NOMEM;
+ memcpy(pans->cname, cans->cname, n);
+ return adns_s_ok;