+#define ADDR_MAXRRTYPES 2
+
+static void addr_rrtypes(adns_state ads, adns_rrtype type,
+ adns_queryflags qf,
+ adns_rrtype *rrty, size_t *nrrty)
+{
+ size_t n = 0;
+ adns_rrtype qtf = type & adns__qtf_deref;
+
+ if ((qf & adns__qf_afmask) != adns_qf_ipv6_only)
+ rrty[n++] = adns_r_a | qtf;
+ if ((qf & adns__qf_afmask) != adns_qf_ipv4_only)
+ rrty[n++] = adns_r_aaaa | qtf;
+
+ *nrrty = n;
+}
+
+static size_t addr_rrsz(adns_query qu)
+{
+ return qu->answer->type & adns__qtf_bigaddr ?
+ sizeof(adns_rr_addr) : sizeof(adns_rr_addr_v4only);
+}
+
+static void icb_addr(adns_query parent, adns_query child)
+{
+ adns_state ads = parent->ads;
+ adns_answer *pans = parent->answer, *cans = child->answer;
+ struct timeval tvbuf;
+ const struct timeval *now = 0;
+ size_t prrsz, crrsz;
+ unsigned char *rrs;
+
+ /* Must handle CNAMEs correctly. This gets very hairy if the answers we
+ * get are inconsistent.
+ */
+
+ if ((parent->flags & adns_qf_search) &&
+ cans->status == adns_s_nxdomain) {
+ if (parent->expires > child->expires) parent->expires = child->expires;
+ adns__cancel_children(parent);
+ adns__free_interim(parent, pans->rrs.bytes);
+ pans->rrs.bytes = 0; pans->nrrs = 0;
+ adns__must_gettimeofday(ads, &now, &tvbuf);
+ if (now) adns__search_next(ads, parent, *now);
+ return;
+ }
+
+ if (cans->status) {
+ adns__query_fail(parent, cans->status);
+ return;
+ }
+
+ assert(pans->rrsz == cans->rrsz);
+ prrsz = pans->rrsz*pans->nrrs;
+ crrsz = cans->rrsz*cans->nrrs;
+ rrs = adns__alloc_interim(parent, prrsz + crrsz);
+ if (!rrs) {
+ adns__query_fail(parent, adns_s_nomemory);
+ return;
+ }
+ if (prrsz) {
+ memcpy(rrs, pans->rrs.bytes, prrsz);
+ adns__free_interim(parent, pans->rrs.bytes);
+ }
+ memcpy(rrs + prrsz, cans->rrs.bytes, crrsz);
+
+ if (parent->expires > child->expires) parent->expires = child->expires;
+ pans->rrs.bytes = rrs;
+ pans->nrrs += cans->nrrs;
+
+ if (parent->children.head) LIST_LINK_TAIL(ads->childw, parent);
+ else adns__query_done(parent);
+}
+
+static void qs_addr(adns_query qu, struct timeval now)
+{
+ adns_rrtype rrty[ADDR_MAXRRTYPES];
+ int i, nrrty, err, id;
+ adns_query cqu;
+ adns_queryflags qf =
+ (qu->flags | adns__qf_senddirect) &
+ ~(adns_qf_search);
+ qcontext ctx;
+
+ addr_rrtypes(qu->ads, qu->answer->type, qu->flags, rrty, &nrrty);
+
+ if (!(qu->answer->type & adns__qtf_bigaddr))
+ qu->answer->rrsz = sizeof(adns_rr_addr_v4only);
+
+ /* This always makes child queries, even if there's only the one. This
+ * seems wasteful, but there's only one case where it'd be safe -- namely
+ * IPv4-only -- and that's not the case I want to optimize.
+ */
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.callback = icb_addr;
+ for (i = 0; i < nrrty; i++) {
+ err = adns__mkquery_frdgram(qu->ads, &qu->vb, &id, qu->query_dgram,
+ qu->query_dglen, DNS_HDRSIZE, rrty[i], qf);
+ if (err) goto x_error;
+ err = adns__internal_submit(qu->ads, &cqu, qu->typei, rrty[i],
+ &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.);
+ }
+ return;
+
+x_error:
+ adns__query_fail(qu, err);
+}
+