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 0bc268b..cdccc4b 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 d5b36af..fa83e09 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 6621afd..0a0d607 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 799b0b5..4f11177 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 dc628d3..34c8b65 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 4b7e9bc..2b867e3 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 4b84e30..f050875 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 92b0d10..a95bf4d 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 c2efdb5..be72cf2 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 cb343be..28db797 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            );