chiark / gitweb /
src/types.c: Start on IPv6 support in adns_r_addr queries.
authorMark Wooding <mdw@distorted.org.uk>
Tue, 10 Jun 2014 22:44:13 +0000 (23:44 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 19 Oct 2014 20:09:56 +0000 (21:09 +0100)
The way addr queries work internally is now very different.  The
toplevel query is now `virtual', and exists to spawn subqueries for each
applicable address type (currently hardcoded to A only, but this will
change later).

The toplevel query has a callback function which assembles its answer
from the various child queries, and there's a collection of support
machinery too.  (Some of the bookkeeping done during this may seem
unnecessary, but it will come in handy later.)

Because more of the work is now done in internal query callbacks, some
of the answers in the tests are reported in different order.  See
`Reentrancy: Avoid reentrant callbacks' for more detailed explanation
of a similar change.

Signed-off-by: Mark Wooding <mdw@distorted.org.uk>
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
regress/case-abbrevto.out
regress/case-datapluscname.out
regress/case-manyptrwrongrty.out
regress/case-tcpallfail.out
regress/case-tcpblock.out
regress/case-tcpblockbrk.out
regress/case-tcpblockwr.out
src/internal.h
src/transmit.c
src/types.c

index 0bc268bf89fa1a21ddaa872c50cb7d4a952cccd9..cdccc4b08d73e17ab8a6e755f166bc8f0d5d578f 100644 (file)
@@ -24,9 +24,9 @@ greenend.org.uk flags 0 type HINFO(-) ownflags=a: timeout; nrrs=0; cname=$; owne
 greenend.org.uk flags 0 type MX(raw) ownflags=a: timeout; nrrs=0; cname=$; owner=$; ttl=604770
 greenend.org.uk flags 0 type TXT(-) ownflags=a: timeout; nrrs=0; cname=$; owner=$; ttl=604770
 greenend.org.uk flags 0 type RP(raw) ownflags=a: timeout; nrrs=0; cname=$; owner=$; ttl=604770
-greenend.org.uk flags 0 type A(addr) ownflags=a: timeout; nrrs=0; cname=$; owner=$; ttl=604770
 greenend.org.uk flags 0 type NS(+addr) ownflags=a: timeout; nrrs=0; cname=$; owner=$; ttl=604770
 greenend.org.uk flags 0 type MX(+addr) ownflags=a: timeout; nrrs=0; cname=$; owner=$; ttl=604770
 greenend.org.uk flags 0 type SOA(822) ownflags=a: timeout; nrrs=0; cname=$; owner=$; ttl=604770
 greenend.org.uk flags 0 type RP(822) ownflags=a: timeout; nrrs=0; cname=$; owner=$; ttl=604770
+greenend.org.uk flags 0 type A(addr) ownflags=a: timeout; nrrs=0; cname=$; owner=$; ttl=604770
 rc=0
index d5b36af1c9bc5a596d4ec16df93fb916fbadd634..fa83e09692ced4fc3a782acbcd2e7dd89bb976f7 100644 (file)
@@ -27,11 +27,11 @@ adns debug: ignoring RR with an unexpected owner 170.168.99.219.194.in-addr.arpa
 170.99.219.194.in-addr.arpa flags 292 type MX(raw): No such data; nrrs=0; cname=170.168.99.219.194.in-addr.arpa; owner=170.99.219.194.in-addr.arpa; ttl=0
 170.99.219.194.in-addr.arpa flags 292 type TXT(-): No such data; nrrs=0; cname=170.168.99.219.194.in-addr.arpa; owner=170.99.219.194.in-addr.arpa; ttl=0
 170.99.219.194.in-addr.arpa flags 292 type RP(raw): No such data; nrrs=0; cname=170.168.99.219.194.in-addr.arpa; owner=170.99.219.194.in-addr.arpa; ttl=0
-170.99.219.194.in-addr.arpa flags 292 type A(addr): No such data; nrrs=0; cname=170.168.99.219.194.in-addr.arpa; owner=170.99.219.194.in-addr.arpa; ttl=0
 170.99.219.194.in-addr.arpa flags 292 type NS(+addr): No such data; nrrs=0; cname=170.168.99.219.194.in-addr.arpa; owner=170.99.219.194.in-addr.arpa; ttl=0
 170.99.219.194.in-addr.arpa flags 292 type MX(+addr): No such data; nrrs=0; cname=170.168.99.219.194.in-addr.arpa; owner=170.99.219.194.in-addr.arpa; ttl=0
 170.99.219.194.in-addr.arpa flags 292 type SOA(822): No such data; nrrs=0; cname=170.168.99.219.194.in-addr.arpa; owner=170.99.219.194.in-addr.arpa; ttl=0
 170.99.219.194.in-addr.arpa flags 292 type RP(822): No such data; nrrs=0; cname=170.168.99.219.194.in-addr.arpa; owner=170.99.219.194.in-addr.arpa; ttl=0
+170.99.219.194.in-addr.arpa flags 292 type A(addr): No such data; nrrs=0; cname=170.168.99.219.194.in-addr.arpa; owner=170.99.219.194.in-addr.arpa; ttl=0
 170.99.219.194.in-addr.arpa flags 292 type PTR(checked): OK; nrrs=1; cname=$; owner=170.99.219.194.in-addr.arpa; ttl=171727
  proxy.scoplife.gr
 170.99.219.194.in-addr.arpa flags 292 type CNAME(-): OK; nrrs=1; cname=$; owner=170.99.219.194.in-addr.arpa; ttl=171726
index 6621afdabc75b1db05fe8562a54a48bfe782dc4f..0a0d6071d73d3aa0c6f99333b2e061b0177ba187 100644 (file)
@@ -117,11 +117,11 @@ adns debug: TCP connected (NS=172.18.45.6)
  ns.asis.org.nz
  ns.bouquets.co.nz
  agate.co.nz
-254.0.99.203.in-addr.arpa flags 292 type A(addr): No such data; nrrs=0; cname=$; owner=254.0.99.203.in-addr.arpa; ttl=539
 254.0.99.203.in-addr.arpa flags 292 type NS(+addr): No such data; nrrs=0; cname=$; owner=254.0.99.203.in-addr.arpa; ttl=539
 254.0.99.203.in-addr.arpa flags 292 type MX(+addr): No such data; nrrs=0; cname=$; owner=254.0.99.203.in-addr.arpa; ttl=540
 254.0.99.203.in-addr.arpa flags 292 type SOA(822): No such data; nrrs=0; cname=$; owner=254.0.99.203.in-addr.arpa; ttl=540
 254.0.99.203.in-addr.arpa flags 292 type RP(822): No such data; nrrs=0; cname=$; owner=254.0.99.203.in-addr.arpa; ttl=539
+254.0.99.203.in-addr.arpa flags 292 type A(addr): No such data; nrrs=0; cname=$; owner=254.0.99.203.in-addr.arpa; ttl=539
 254.0.99.203.in-addr.arpa flags 292 type PTR(checked): Inconsistent resource records in DNS; nrrs=0; cname=$; owner=254.0.99.203.in-addr.arpa; ttl=86351
 adns debug: reply not found, id 3151, query owner mail.safes.co.nz (NS=172.18.45.6)
 adns debug: reply not found, id 3152, query owner ns.bcc.co.nz (NS=172.18.45.6)
index 799b0b5b5278f507391c2a46886c0c731e74e013..4f11177e223f9e0ae65ee284d4b9066d784287e0 100644 (file)
@@ -26,9 +26,9 @@ test.iwj.relativity.greenend.org.uk. flags 2 type HINFO(-): All nameservers fail
 test.iwj.relativity.greenend.org.uk. flags 2 type MX(raw): All nameservers failed; nrrs=0; cname=$; owner=$; ttl=604800
 test.iwj.relativity.greenend.org.uk. flags 2 type TXT(-): All nameservers failed; nrrs=0; cname=$; owner=$; ttl=604800
 test.iwj.relativity.greenend.org.uk. flags 2 type RP(raw): All nameservers failed; nrrs=0; cname=$; owner=$; ttl=604800
-test.iwj.relativity.greenend.org.uk. flags 2 type A(addr): All nameservers failed; nrrs=0; cname=$; owner=$; ttl=604800
 test.iwj.relativity.greenend.org.uk. flags 2 type NS(+addr): All nameservers failed; nrrs=0; cname=$; owner=$; ttl=604800
 test.iwj.relativity.greenend.org.uk. flags 2 type MX(+addr): All nameservers failed; nrrs=0; cname=$; owner=$; ttl=604800
 test.iwj.relativity.greenend.org.uk. flags 2 type SOA(822): All nameservers failed; nrrs=0; cname=$; owner=$; ttl=604800
 test.iwj.relativity.greenend.org.uk. flags 2 type RP(822): All nameservers failed; nrrs=0; cname=$; owner=$; ttl=604800
+test.iwj.relativity.greenend.org.uk. flags 2 type A(addr): All nameservers failed; nrrs=0; cname=$; owner=$; ttl=604800
 rc=0
index dc628d310507daa530f0fa345f4044b2044890e6..34c8b65bdf7a48facaa93f3690c6079b689b31ff 100644 (file)
@@ -25,9 +25,9 @@ test.iwj.relativity.greenend.org.uk. flags 2 type HINFO(-): DNS query timed out;
 test.iwj.relativity.greenend.org.uk. flags 2 type MX(raw): DNS query timed out; nrrs=0; cname=$; owner=$; ttl=604770
 test.iwj.relativity.greenend.org.uk. flags 2 type TXT(-): DNS query timed out; nrrs=0; cname=$; owner=$; ttl=604770
 test.iwj.relativity.greenend.org.uk. flags 2 type RP(raw): DNS query timed out; nrrs=0; cname=$; owner=$; ttl=604770
-test.iwj.relativity.greenend.org.uk. flags 2 type A(addr): DNS query timed out; nrrs=0; cname=$; owner=$; ttl=604770
 test.iwj.relativity.greenend.org.uk. flags 2 type NS(+addr): DNS query timed out; nrrs=0; cname=$; owner=$; ttl=604770
 test.iwj.relativity.greenend.org.uk. flags 2 type MX(+addr): DNS query timed out; nrrs=0; cname=$; owner=$; ttl=604770
 test.iwj.relativity.greenend.org.uk. flags 2 type SOA(822): DNS query timed out; nrrs=0; cname=$; owner=$; ttl=604770
 test.iwj.relativity.greenend.org.uk. flags 2 type RP(822): DNS query timed out; nrrs=0; cname=$; owner=$; ttl=604770
+test.iwj.relativity.greenend.org.uk. flags 2 type A(addr): DNS query timed out; nrrs=0; cname=$; owner=$; ttl=604770
 rc=0
index 4b7e9bc8bcbaa86324c155cbde69b69e4b76c4d2..2b867e34af99e2ade092ddfb20210e5e96ec2f06 100644 (file)
@@ -27,9 +27,9 @@ test.iwj.relativity.greenend.org.uk. flags 2 type HINFO(-): All nameservers fail
 test.iwj.relativity.greenend.org.uk. flags 2 type MX(raw): All nameservers failed; nrrs=0; cname=$; owner=$; ttl=604798
 test.iwj.relativity.greenend.org.uk. flags 2 type TXT(-): All nameservers failed; nrrs=0; cname=$; owner=$; ttl=604798
 test.iwj.relativity.greenend.org.uk. flags 2 type RP(raw): All nameservers failed; nrrs=0; cname=$; owner=$; ttl=604798
-test.iwj.relativity.greenend.org.uk. flags 2 type A(addr): All nameservers failed; nrrs=0; cname=$; owner=$; ttl=604798
 test.iwj.relativity.greenend.org.uk. flags 2 type NS(+addr): All nameservers failed; nrrs=0; cname=$; owner=$; ttl=604798
 test.iwj.relativity.greenend.org.uk. flags 2 type MX(+addr): All nameservers failed; nrrs=0; cname=$; owner=$; ttl=604798
 test.iwj.relativity.greenend.org.uk. flags 2 type SOA(822): All nameservers failed; nrrs=0; cname=$; owner=$; ttl=604798
 test.iwj.relativity.greenend.org.uk. flags 2 type RP(822): All nameservers failed; nrrs=0; cname=$; owner=$; ttl=604798
+test.iwj.relativity.greenend.org.uk. flags 2 type A(addr): All nameservers failed; nrrs=0; cname=$; owner=$; ttl=604798
 rc=0
index 4b84e30d832476da6f8700cb3b8af960a5fa63db..f05087563f3bbec334c22db3e3a4143940cd20ae 100644 (file)
@@ -59,7 +59,6 @@ test.iwj.relativity.greenend.org.uk. flags 2 type HINFO(-): No such data; nrrs=0
 test.iwj.relativity.greenend.org.uk. flags 2 type MX(raw): No such data; nrrs=0; cname=$; owner=$; ttl=59
 test.iwj.relativity.greenend.org.uk. flags 2 type TXT(-): No such data; nrrs=0; cname=$; owner=$; ttl=59
 test.iwj.relativity.greenend.org.uk. flags 2 type RP(raw): No such data; nrrs=0; cname=$; owner=$; ttl=59
-test.iwj.relativity.greenend.org.uk. flags 2 type A(addr): No such data; nrrs=0; cname=$; owner=$; ttl=59
 test.iwj.relativity.greenend.org.uk. flags 2 type NS(+addr): OK; nrrs=1; cname=$; owner=$; ttl=59
  ns0.relativity.greenend.org.uk ok 0 ok "OK" ( INET 172.18.45.6 )
 test.iwj.relativity.greenend.org.uk. flags 2 type MX(+addr): No such data; nrrs=0; cname=$; owner=$; ttl=59
@@ -77,7 +76,6 @@ test.iwj.relativity.greenend.org.uk. flags 2 type HINFO(-): No such data; nrrs=0
 test.iwj.relativity.greenend.org.uk. flags 2 type MX(raw): No such data; nrrs=0; cname=$; owner=$; ttl=59
 test.iwj.relativity.greenend.org.uk. flags 2 type TXT(-): No such data; nrrs=0; cname=$; owner=$; ttl=59
 test.iwj.relativity.greenend.org.uk. flags 2 type RP(raw): No such data; nrrs=0; cname=$; owner=$; ttl=59
-test.iwj.relativity.greenend.org.uk. flags 2 type A(addr): No such data; nrrs=0; cname=$; owner=$; ttl=59
 test.iwj.relativity.greenend.org.uk. flags 2 type NS(+addr): OK; nrrs=1; cname=$; owner=$; ttl=59
  ns0.relativity.greenend.org.uk ok 0 ok "OK" ( INET 172.18.45.6 )
 test.iwj.relativity.greenend.org.uk. flags 2 type MX(+addr): No such data; nrrs=0; cname=$; owner=$; ttl=59
@@ -95,11 +93,13 @@ test.iwj.relativity.greenend.org.uk. flags 2 type HINFO(-): No such data; nrrs=0
 test.iwj.relativity.greenend.org.uk. flags 2 type MX(raw): No such data; nrrs=0; cname=$; owner=$; ttl=59
 test.iwj.relativity.greenend.org.uk. flags 2 type TXT(-): No such data; nrrs=0; cname=$; owner=$; ttl=59
 test.iwj.relativity.greenend.org.uk. flags 2 type RP(raw): No such data; nrrs=0; cname=$; owner=$; ttl=59
-test.iwj.relativity.greenend.org.uk. flags 2 type A(addr): No such data; nrrs=0; cname=$; owner=$; ttl=59
 test.iwj.relativity.greenend.org.uk. flags 2 type NS(+addr): OK; nrrs=1; cname=$; owner=$; ttl=59
  ns0.relativity.greenend.org.uk ok 0 ok "OK" ( INET 172.18.45.6 )
 test.iwj.relativity.greenend.org.uk. flags 2 type MX(+addr): No such data; nrrs=0; cname=$; owner=$; ttl=59
 test.iwj.relativity.greenend.org.uk. flags 2 type SOA(822): OK; nrrs=1; cname=$; owner=$; ttl=59
  ns0.relativity.greenend.org.uk hostmaster@relativity.greenend.org.uk 42 3600 120 6604800 60
+test.iwj.relativity.greenend.org.uk. flags 2 type A(addr): No such data; nrrs=0; cname=$; owner=$; ttl=59
+test.iwj.relativity.greenend.org.uk. flags 2 type A(addr): No such data; nrrs=0; cname=$; owner=$; ttl=59
+test.iwj.relativity.greenend.org.uk. flags 2 type A(addr): No such data; nrrs=0; cname=$; owner=$; ttl=59
 test.iwj.relativity.greenend.org.uk. flags 2 type RP(822): No such data; nrrs=0; cname=$; owner=$; ttl=60
 rc=0
index 92b0d107fd041f94f59cd975cb35b283a1bbdc22..a95bf4d6a15b79f5ff9955ef015f0a7e863ad835 100644 (file)
@@ -151,6 +151,9 @@ typedef struct {
       adns_rrtype rev_rrtype;
       struct af_addr addr;
     } ptr;
+    struct {
+      unsigned want, have;
+    } addr;
   } tinfo; /* type-specific state for the query itself: zero-init if you
            * don't know better. */
 
index c2efdb595e15c5c49a0e676917e39ef232a60e0b..be72cf237e1f23d6e4a5ef00e286e8bdc2e9605e 100644 (file)
@@ -120,7 +120,6 @@ adns_status adns__mkquery(adns_state ads, vbuf *vb, int *id_r,
   const char *p, *pe;
   adns_status st;
 
-  if (!((type^adns_r_addr) & adns_rrt_reprmask)) ads->nextid++; /* bodge */
   st= mkquery_header(ads,vb,id_r,ol+2); if (st) return st;
   
   MKQUERY_START(vb);
@@ -156,7 +155,6 @@ adns_status adns__mkquery_frdgram(adns_state ads, vbuf *vb, int *id_r,
   int lablen, labstart;
   adns_status st;
 
-  if (!((type^adns_r_addr) & adns_rrt_reprmask)) ads->nextid++; /* bodge */
   st= mkquery_header(ads,vb,id_r,qd_dglen); if (st) return st;
 
   MKQUERY_START(vb);
index cb343beb8d4e6177042a51289147f6df35716f72..28db797eb7a7e7a938a8484a28b7ef3391a45c8c 100644 (file)
@@ -50,8 +50,9 @@
  * _inaddr                    (pa,di,cs
  *                             +search_sortlist, dip_genaddr, csp_genaddr)
  * _in6addr                  (pa,di,cs)
- * _addr                      (pa,di,div,csp,cs,gsz
- *                             +search_sortlist_sa, dip_sockaddr)
+ * _addr                      (pa,di,div,csp,cs,gsz,qs
+ *                             +search_sortlst_sa, dip_sockaddr,
+ *                              addr_rrtypes, icb_addr)
  * _domain                    (pap,csp,cs)
  * _dom_raw                  (pa)
  * _host_raw                  (pa)
@@ -342,9 +343,39 @@ static adns_status cs_in6addr(vbuf *vb, const void *datap) {
 }
 
 /*
- * _addr   (pa,di,div,csp,cs,gsz +search_sortlist_sa, dip_sockaddr)
+ * _addr   (pa,di,div,csp,cs,gsz,qs
+ *             +search_sortlist_sa, dip_sockaddr, addr_rrtypes, icb_addr)
  */
 
+static const typeinfo tinfo_addrsub;
+
+#define ADDR_RRTYPES(_) _(a)
+
+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++);
+  return i < addr_nrrtypes ? 1 << i : 0;
+}
+
 static adns_status pa_addr(const parseinfo *pai, int cbyte,
                           int max, void *datap) {
   adns_rr_addr *storeto= datap;
@@ -417,6 +448,149 @@ static int gsz_addr(const typeinfo *typei, adns_rrtype type) {
     sizeof(adns_rr_addr) : sizeof(adns_rr_addr_v4only);
 }
 
+static unsigned addr_rrtypes(adns_state ads, adns_rrtype type,
+                            adns_queryflags qf) {
+  /* Return a mask of addr_rf_... flags indicating which address families are
+   * wanted, given a query type and flags.
+   */
+  return addr_rf_a;
+}
+
+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, qu, &tinfo_addrsub,
+                              addr_all_rrtypes[i] | qtf,
+                              &qu->vb, id, qf, now, &ctx);
+    if (err) goto x_error;
+    cqu->answer->rrsz= qu->answer->rrsz;
+  }
+  qu->state= query_childw;
+  LIST_LINK_TAIL(qu->ads->childw, qu);
+  return;
+
+x_error:
+  adns__query_fail(qu, err);
+}
+
+static adns_status append_addrs(adns_query qu, size_t rrsz,
+                               adns_rr_addr **dp, int *dlen,
+                               const adns_rr_addr *sp, int slen) {
+  /* Append a vector of slen addr records, each of size rrsz, starting at ap,
+   * to a vector starting at *dp, of length *dlen.  On successful completion,
+   * *dp and *dlen are updated.
+   */
+
+  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;
+  return adns_s_ok;
+}
+
+static void propagate_ttl(adns_query to, adns_query from)
+  { if (to->expires > from->expires) to->expires= from->expires; }
+
+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;
+}
+
+static void done_addr_type(adns_query qu, adns_rrtype type) {
+  unsigned f= addr_rrtypeflag(type);
+  assert(f); qu->ctx.tinfo.addr.have |= f;
+}
+
+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 now;
+  adns_status err;
+
+  propagate_ttl(parent, child);
+
+  if (cans->cname && !pans->cname) {
+    err= copy_cname_from_child(parent, child);
+    if (err) goto x_err;
+  }
+
+  if ((parent->flags & adns_qf_search) &&
+      !pans->cname && cans->status == adns_s_nxdomain) {
+    /* We're searching a list of suffixes, and the name doesn't exist.  Try
+     * the next one.
+     */
+
+    adns__cancel_children(parent);
+    adns__free_interim(parent, pans->rrs.bytes);
+    pans->rrs.bytes= 0; pans->nrrs= 0;
+    if (gettimeofday(&now, 0)) goto x_gtod;
+    adns__search_next(ads, parent, now);
+    return;
+  }
+
+  if (cans->status && cans->status != adns_s_nodata)
+    { err= cans->status; goto x_err; }
+
+  assert(pans->rrsz == cans->rrsz);
+  err= append_addrs(parent, pans->rrsz,
+                   &pans->rrs.addr, &pans->nrrs,
+                   cans->rrs.addr, cans->nrrs);
+  if (err) goto x_err;
+  done_addr_type(parent, cans->type);
+
+  if (parent->children.head) LIST_LINK_TAIL(ads->childw, parent);
+  else if (!pans->nrrs) adns__query_fail(parent, adns_s_nodata);
+  else adns__query_done(parent);
+  return;
+
+x_gtod:
+  adns__diag(ads, -1, parent, "gettimeofday failed: %s", strerror(errno));
+  err= adns_s_systemfail;
+  goto x_err;
+
+x_err:
+  adns__query_fail(parent, err);
+}
+
+static void qs_addr(adns_query qu, struct timeval now) {
+  if (!qu->ctx.tinfo.addr.want) {
+    qu->ctx.tinfo.addr.want= addr_rrtypes(qu->ads, qu->answer->type,
+                                         qu->flags);
+    qu->ctx.tinfo.addr.have= 0;
+  }
+  addr_subqueries(qu, now, 0, qu->query_dgram, qu->query_dglen);
+}
+
 /*
  * _domain      (pap,csp,cs)
  * _dom_raw     (pa)
@@ -1309,7 +1483,7 @@ DEEP_TYPE(srv_raw,"SRV",  "raw",srvraw ,   srvraw,  srv,   srvraw,
                              .checklabel= ckl_srv, .postsort= postsort_srv),
 
 FLAT_TYPE(addr,   "A",  "addr", addr,      addr,    addr,  addr,
-                                                        .getrrsz= gsz_addr),
+                                  .getrrsz= gsz_addr, .query_send= qs_addr),
 DEEP_TYPE(ns,     "NS", "+addr",hostaddr,  hostaddr,hostaddr,hostaddr      ),
 DEEP_TYPE(ptr,    "PTR","checked",str,     ptr,     0,     domain,
                                                       .checklabel= ckl_ptr),
@@ -1321,6 +1495,10 @@ DEEP_TYPE(soa,    "SOA","822",  soa,       soa,     0,     soa             ),
 DEEP_TYPE(rp,     "RP", "822",  strpair,   rp,      0,     rp              ),
 };
 
+static const typeinfo tinfo_addrsub =
+FLAT_TYPE(none,          "<addr>","sub",addr,     addr,    0,     addr,
+                                                        .getrrsz= gsz_addr);
+
 static const typeinfo typeinfo_unknown=
 DEEP_TYPE(unknown,0, "unknown",byteblock,opaque,  0,     opaque            );