chiark / gitweb /
src/types.c: Do not crash when one of several addr subqueries fails
[adns.git] / src / types.c
index 28db797eb7a7e7a938a8484a28b7ef3391a45c8c..142a942c1a2f3fa003e30fe53f4ac81d4c560108 100644 (file)
@@ -4,14 +4,15 @@
  */
 /*
  *  This file is part of adns, which is
- *    Copyright (C) 1997-2000,2003,2006  Ian Jackson
+ *    Copyright (C) 1997-2000,2003,2006,2014  Ian Jackson
+ *    Copyright (C) 2014  Mark Wooding
  *    Copyright (C) 1999-2000,2003,2006  Tony Finch
  *    Copyright (C) 1991 Massachusetts Institute of Technology
  *  (See the file INSTALL for full details.)
  *  
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2, or (at your option)
+ *  the Free Software Foundation; either version 3, or (at your option)
  *  any later version.
  *  
  *  This program is distributed in the hope that it will be useful,
  *  GNU General Public License for more details.
  *  
  *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software Foundation,
- *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 
+ *  along with this program; if not, write to the Free Software Foundation.
  */
 
+#include <stddef.h>
 #include <stdlib.h>
 
 #include <sys/types.h>
@@ -50,9 +51,9 @@
  * _inaddr                    (pa,di,cs
  *                             +search_sortlist, dip_genaddr, csp_genaddr)
  * _in6addr                  (pa,di,cs)
- * _addr                      (pa,di,div,csp,cs,gsz,qs
- *                             +search_sortlst_sa, dip_sockaddr,
- *                              addr_rrtypes, icb_addr)
+ * _addr                      (pap,pa,di,div,csp,cs,gsz,qs
+ *                             +search_sortlist_sa, dip_sockaddr,
+ *                              addr_rrtypes, addr_submit, icb_addr)
  * _domain                    (pap,csp,cs)
  * _dom_raw                  (pa)
  * _host_raw                  (pa)
@@ -262,27 +263,26 @@ static adns_status pa_inaddr(const parseinfo *pai, int cbyte,
 
 static int search_sortlist(adns_state ads, int af, const void *ad) {
   const struct sortlist *slp;
-  const struct in6_addr *a6;
-  union gen_addr a;
+  struct in_addr a4;
   int i;
   int v6mappedp= 0;
 
   if (af == AF_INET6) {
-    a6= ad;
+    const struct in6_addr *a6= ad;
     if (IN6_IS_ADDR_V4MAPPED(a6)) {
-      a.v4.s_addr= htonl(((unsigned long)a6->s6_addr[12] << 24) |
-                        ((unsigned long)a6->s6_addr[13] << 16) |
-                        ((unsigned long)a6->s6_addr[14] <<  8) |
-                        ((unsigned long)a6->s6_addr[15] <<  0));
+      a4.s_addr= htonl(((unsigned long)a6->s6_addr[12] << 24) |
+                      ((unsigned long)a6->s6_addr[13] << 16) |
+                      ((unsigned long)a6->s6_addr[14] <<  8) |
+                      ((unsigned long)a6->s6_addr[15] <<  0));
       v6mappedp= 1;
     }
   }
 
   for (i=0, slp=ads->sortlist;
        i<ads->nsortlist &&
-        !adns__addr_match_p(af,ad, slp->af,&slp->base,&slp->mask) &&
+        !adns__addr_matches(af,ad, &slp->base,&slp->mask) &&
         !(v6mappedp &&
-          adns__addr_match_p(AF_INET,&a, slp->af,&slp->base,&slp->mask));
+          adns__addr_matches(AF_INET,&a4, &slp->base,&slp->mask));
        i++, slp++);
   return i;
 }
@@ -310,7 +310,7 @@ static adns_status csp_genaddr(vbuf *vb, int af, const void *p) {
 
   memset(&a, 0, sizeof(a));
   a.addr.sa.sa_family= af;
-  adns__sockaddr_inject(p, 0, &a.addr.sa);
+  adns__addr_inject(p, &a.addr);
   err= adns_addr2text(&a.addr.sa,0, buf,&len, 0); assert(!err);
   CSP_ADDSTR(buf);
   return adns_s_ok;
@@ -343,13 +343,14 @@ static adns_status cs_in6addr(vbuf *vb, const void *datap) {
 }
 
 /*
- * _addr   (pa,di,div,csp,cs,gsz,qs
- *             +search_sortlist_sa, dip_sockaddr, addr_rrtypes, icb_addr)
+ * _addr   (pap,pa,di,div,csp,cs,gsz,qs
+ *             +search_sortlist_sa, dip_sockaddr, addr_rrtypes,
+ *              addr_submit, icb_addr)
  */
 
 static const typeinfo tinfo_addrsub;
 
-#define ADDR_RRTYPES(_) _(a)
+#define ADDR_RRTYPES(_) _(a) _(aaaa)
 
 static const adns_rrtype addr_all_rrtypes[] = {
 #define RRTY_CODE(ty) adns_r_##ty,
@@ -364,7 +365,7 @@ enum {
   addr_nrrtypes,
 #define RRTY_FLAG(ty) addr_rf_##ty = 1 << addr__ri_##ty,
   ADDR_RRTYPES(RRTY_FLAG)
-  addr__rrty_hunoz
+  addr__rrty_eat_final_comma
 #undef RRTY_FLAG
 };
 
@@ -372,27 +373,88 @@ 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;
+  for (i=0; i<addr_nrrtypes; i++)
+     if (type==addr_all_rrtypes[i])
+       return 1 << i;
+  return 0;
+}
+
+/* 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 in_rrty, size_t out_rrsz,
+                           int *cbyte_io, int cbyte_max, adns_rr_addr *out) {
+  int in_addrlen;
+  int out_af, out_salen;
+  struct in6_addr v6map;
+
+  const void *use_addr= pai->dgram + *cbyte_io;
+
+  switch (in_rrty) {
+  case adns_r_a:    in_addrlen= 4;  out_af= AF_INET;  break;
+  case adns_r_aaaa: in_addrlen= 16; out_af= AF_INET6; break;
+  default: abort();
+  }
+
+  if ((*cbyte_io + in_addrlen) != cbyte_max) return adns_s_invaliddata;
+
+  if (out_af==AF_INET &&
+      (pai->qu->flags & adns_qf_ipv6_mapv4) &&
+      (pai->qu->answer->type & adns__qtf_bigaddr)) {
+    memset(v6map.s6_addr +  0, 0x00,    10);
+    memset(v6map.s6_addr + 10, 0xff,     2);
+    memcpy(v6map.s6_addr + 12, use_addr, 4);
+    use_addr= v6map.s6_addr;
+    out_af= AF_INET6;
+  }
+
+  switch (out_af) {
+  case AF_INET:  out_salen= sizeof(out->addr.inet);  break;
+  case AF_INET6: out_salen= sizeof(out->addr.inet6); break;
+  default: abort();
+  }
+
+  assert(offsetof(adns_rr_addr, addr) + out_salen <= out_rrsz);
+
+  memset(&out->addr, 0, out_salen);
+  out->len= out_salen;
+  out->addr.sa.sa_family= out_af;
+  adns__addr_inject(use_addr, &out->addr);
+
+  *cbyte_io += in_addrlen;
+  return adns_s_ok;
 }
 
 static adns_status pa_addr(const parseinfo *pai, int cbyte,
                           int max, void *datap) {
-  adns_rr_addr *storeto= datap;
-  const byte *dgram= pai->dgram;
-
-  if (max-cbyte != 4) return adns_s_invaliddata;
-  storeto->len= sizeof(storeto->addr.inet);
-  memset(&storeto->addr,0,sizeof(storeto->addr.inet));
-  storeto->addr.inet.sin_family= AF_INET;
-  memcpy(&storeto->addr.inet.sin_addr,dgram+cbyte,4);
+  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) {
-  union gen_addr a;
-  adns__sockaddr_extract(sa, &a, 0);
-  return search_sortlist(ads, sa->sa_family, &a);
+  const void *pa = adns__sockaddr_addr(sa);
+  return search_sortlist(ads, sa->sa_family, pa);
 }
 
 static int dip_sockaddr(adns_state ads,
@@ -453,7 +515,26 @@ static unsigned addr_rrtypes(adns_state ads, adns_rrtype type,
   /* Return a mask of addr_rf_... flags indicating which address families are
    * wanted, given a query type and flags.
    */
-  return addr_rf_a;
+
+  adns_queryflags permitaf= 0;
+  unsigned want= 0;
+
+  if (!(type & adns__qtf_bigaddr))
+    qf= (qf & ~adns_qf_want_allaf) | adns_qf_want_ipv4;
+  else {
+    if (!(qf & adns_qf_want_allaf)) {
+      qf |= (type & adns__qtf_manyaf) ?
+       adns_qf_want_allaf : adns_qf_want_ipv4;
+    }
+    if (ads->iflags & adns_if_permit_ipv4) permitaf |= adns_qf_want_ipv4;
+    if (ads->iflags & adns_if_permit_ipv6) permitaf |= adns_qf_want_ipv6;
+    if (qf & permitaf) qf &= permitaf | ~adns_qf_want_allaf;
+  }
+
+  if (qf & adns_qf_want_ipv4) want |= addr_rf_a;
+  if (qf & adns_qf_want_ipv6) want |= addr_rf_aaaa;
+
+  return want;
 }
 
 static void icb_addr(adns_query parent, adns_query child);
@@ -489,6 +570,38 @@ 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 set 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, parent, adns__findtype(adns_r_addr),
+                            type, qumsg_vb, id, flags, now, ctx);
+  if (err) return err;
+
+  *query_r= qu;
+  return adns_s_ok;
+}
+
 static adns_status append_addrs(adns_query qu, size_t rrsz,
                                adns_rr_addr **dp, int *dlen,
                                const adns_rr_addr *sp, int slen) {
@@ -536,9 +649,55 @@ static void icb_addr(adns_query parent, adns_query child) {
   adns_answer *pans= parent->answer, *cans= child->answer;
   struct timeval now;
   adns_status err;
+  adns_queryflags qf;
+  int id, r;
 
   propagate_ttl(parent, child);
 
+  if (!(child->flags & adns__qf_addr_cname) &&
+      (parent->flags & adns__qf_addr_answer) &&
+      (!!pans->cname != !!cans->cname ||
+       (pans->cname && strcmp(pans->cname, cans->cname)))) {
+    /* We've detected an inconsistency in CNAME records, and must deploy
+     * countermeasures.
+     */
+
+    if (!pans->cname) {
+      /* The child has a CNAME record, but the parent doesn't.  We must
+       * discard all of the parent's addresses, and substitute the child's.
+       */
+
+      assert(pans->rrsz == cans->rrsz);
+      adns__free_interim(parent, pans->rrs.bytes);
+      adns__transfer_interim(child, parent, cans->rrs.bytes);
+      pans->rrs.bytes= cans->rrs.bytes;
+      pans->nrrs= cans->nrrs;
+      parent->ctx.tinfo.addr.have= 0;
+      done_addr_type(parent, cans->type);
+      err= copy_cname_from_child(parent, child); if (err) goto x_err;
+    }
+
+    /* We've settled on the CNAME (now) associated with the parent, which
+     * already has appropriate address records.  Build a query datagram for
+     * this name so that we can issue child queries for the missing address
+     * families.  The child's vbuf looks handy for this.
+     */
+    err= adns__mkquery(ads, &child->vb, &id, pans->cname,
+                      strlen(pans->cname), &tinfo_addrsub,
+                      adns_r_addr, parent->flags);
+    if (err) goto x_err;
+
+    /* Now cancel the remaining children, and try again with the CNAME we've
+     * settled on.
+     */
+    adns__cancel_children(parent);
+    r= gettimeofday(&now, 0);  if (r) goto x_gtod;
+    qf= adns__qf_addr_cname;
+    if (!(parent->flags & adns_qf_cname_loose)) qf |= adns_qf_cname_forbid;
+    addr_subqueries(parent, now, qf, child->vb.buf, child->vb.used);
+    return;
+  }
+
   if (cans->cname && !pans->cname) {
     err= copy_cname_from_child(parent, child);
     if (err) goto x_err;
@@ -553,7 +712,7 @@ static void icb_addr(adns_query parent, adns_query child) {
     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;
+    r= gettimeofday(&now, 0);  if (r) goto x_gtod;
     adns__search_next(ads, parent, now);
     return;
   }
@@ -571,9 +730,13 @@ static void icb_addr(adns_query parent, adns_query child) {
   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);
+  parent->flags |= adns__qf_addr_answer;
   return;
 
 x_gtod:
+  /* We have our own error handling, because adns__must_gettimeofday
+   * handles errors by calling adns_globalsystemfailure, which would
+   * reenter the query processing logic. */
   adns__diag(ads, -1, parent, "gettimeofday failed: %s", strerror(errno));
   err= adns_s_systemfail;
   goto x_err;
@@ -661,42 +824,45 @@ static adns_status pa_host_raw(const parseinfo *pai, int cbyte,
  */
 
 static adns_status pap_findaddrs(const parseinfo *pai, adns_rr_hostaddr *ha,
-                                size_t addrsz,
+                                unsigned *want_io, size_t addrsz,
                                 int *cbyte_io, int count, int dmstart) {
   int rri, naddrs;
-  int type, class, rdlen, rdstart, ownermatched;
+  unsigned typef, want= *want_io, need= want;
+  int type, class, rdlen, rdend, rdstart, ownermatched;
   unsigned long ttl;
   adns_status st;
   
-  for (rri=0, naddrs=-1; rri<count; rri++) {
+  for (rri=0, naddrs=0; rri<count; rri++) {
     st= adns__findrr_anychk(pai->qu, pai->serv, pai->dgram,
                            pai->dglen, cbyte_io,
                            &type, &class, &ttl, &rdlen, &rdstart,
                            pai->dgram, pai->dglen, dmstart, &ownermatched);
     if (st) return st;
-    if (!ownermatched || class != DNS_CLASS_IN || type != adns_r_a) {
-      if (naddrs>0) break; else continue;
-    }
-    if (naddrs == -1) {
-      naddrs= 0;
-    }
-    if (!adns__vbuf_ensure(&pai->qu->vb, (naddrs+1)*addrsz))
-      R_NOMEM;
+    if (!ownermatched || class != DNS_CLASS_IN) continue;
+    typef= addr_rrtypeflag(type);
+    if (!(want & typef)) continue;
+    need &= ~typef;
+    if (!adns__vbuf_ensure(&pai->qu->vb, (naddrs+1)*addrsz)) R_NOMEM;
     adns__update_expires(pai->qu,ttl,pai->now);
-    st= pa_addr(pai, rdstart,rdstart+rdlen, pai->qu->vb.buf + naddrs*addrsz);
+    rdend= rdstart + rdlen;
+    st= pap_addr(pai, type, addrsz, &rdstart, rdend,
+                (adns_rr_addr *)(pai->qu->vb.buf + naddrs*addrsz));
     if (st) return st;
+    if (rdstart != rdend) return adns_s_invaliddata;
     naddrs++;
   }
-  if (naddrs >= 0) {
-    ha->addrs= adns__alloc_interim(pai->qu, naddrs*addrsz);
-    if (!ha->addrs) R_NOMEM;
-    memcpy(ha->addrs, pai->qu->vb.buf, naddrs*addrsz);
-    ha->naddrs= naddrs;
+  if (naddrs > 0) {
+    st= append_addrs(pai->qu, addrsz, &ha->addrs, &ha->naddrs,
+                    (const adns_rr_addr *)pai->qu->vb.buf, naddrs);
+    if (st) return st;
     ha->astatus= adns_s_ok;
 
-    adns__isort(ha->addrs, naddrs, addrsz, pai->qu->vb.buf,
-               div_addr, pai->ads);
+    if (!need) {
+      adns__isort(ha->addrs, naddrs, addrsz, pai->qu->vb.buf,
+                 div_addr, pai->ads);
+    }
   }
+  *want_io= need;
   return adns_s_ok;
 }
 
@@ -705,13 +871,32 @@ static void icb_hostaddr(adns_query parent, adns_query child) {
   adns_rr_hostaddr *rrp= child->ctx.pinfo.hostaddr;
   adns_state ads= parent->ads;
   adns_status st;
+  size_t addrsz= gsz_addr(0, parent->answer->type);
 
-  st= cans->status;
-  rrp->astatus= st;
-  rrp->naddrs= (st>0 && st<=adns_s_max_tempfail) ? -1 : cans->nrrs;
-  rrp->addrs= cans->rrs.addr;
-  adns__transfer_interim(child, parent, rrp->addrs);
+  st= cans->status == adns_s_nodata ? adns_s_ok : cans->status;
+  if (st) goto done;
+  propagate_ttl(parent, child);
+
+  assert(addrsz == cans->rrsz);
+  st= append_addrs(parent, addrsz,
+                  &rrp->addrs, &rrp->naddrs,
+                  cans->rrs.addr, cans->nrrs);
+  if (st) goto done;
+  if (!rrp->naddrs) { st= adns_s_nodata; goto done; }
+
+  if (!adns__vbuf_ensure(&parent->vb, addrsz))
+    { st= adns_s_nomemory; goto done; }
+  adns__isort(rrp->addrs, rrp->naddrs, addrsz, parent->vb.buf,
+             div_addr, ads);
+
+done:
+  if (st) {
+    adns__free_interim(parent, rrp->addrs);
+    rrp->addrs = 0;
+    rrp->naddrs= (st>0 && st<=adns_s_max_tempfail) ? -1 : 0;
+  }
 
+  rrp->astatus= st;
   if (parent->children.head) {
     LIST_LINK_TAIL(ads->childw,parent);
   } else {
@@ -727,6 +912,7 @@ static adns_status pap_hostaddr(const parseinfo *pai, int *cbyte_io,
   int id;
   adns_query nqu;
   adns_queryflags nflags;
+  unsigned want;
   size_t addrsz= gsz_addr(0, pai->qu->answer->type);
 
   dmstart= cbyte= *cbyte_io;
@@ -736,18 +922,20 @@ static adns_status pap_hostaddr(const parseinfo *pai, int *cbyte_io,
   *cbyte_io= cbyte;
 
   rrp->astatus= adns_s_ok;
-  rrp->naddrs= -1;
+  rrp->naddrs= 0;
   rrp->addrs= 0;
 
   cbyte= pai->nsstart;
 
-  st= pap_findaddrs(pai, rrp,addrsz, &cbyte, pai->nscount, dmstart);
+  want= addr_rrtypes(pai->ads, pai->qu->answer->type, pai->qu->flags);
+
+  st= pap_findaddrs(pai, rrp, &want, addrsz, &cbyte, pai->nscount, dmstart);
   if (st) return st;
-  if (rrp->naddrs != -1) return adns_s_ok;
+  if (!want) return adns_s_ok;
 
-  st= pap_findaddrs(pai, rrp,addrsz, &cbyte, pai->arcount, dmstart);
+  st= pap_findaddrs(pai, rrp, &want, addrsz, &cbyte, pai->arcount, dmstart);
   if (st) return st;
-  if (rrp->naddrs != -1) return adns_s_ok;
+  if (!want) return adns_s_ok;
 
   st= adns__mkquery_frdgram(pai->ads, &pai->qu->vb, &id,
                            pai->dgram, pai->dglen, dmstart,
@@ -757,16 +945,13 @@ static adns_status pap_hostaddr(const parseinfo *pai, int *cbyte_io,
   ctx.ext= 0;
   ctx.callback= icb_hostaddr;
   ctx.pinfo.hostaddr= rrp;
-  memset(&ctx.tinfo, 0, sizeof(ctx.tinfo));
   
-  nflags= adns_qf_quoteok_query;
+  nflags= adns_qf_quoteok_query | (pai->qu->flags & (adns_qf_want_allaf |
+                                                    adns_qf_ipv6_mapv4));
   if (!(pai->qu->flags & adns_qf_cname_loose)) nflags |= adns_qf_cname_forbid;
   
-  st= adns__internal_submit(pai->ads, &nqu, pai->qu,
-                           adns__findtype(adns_r_addr),
-                           ((adns_r_addr & adns_rrt_reprmask) |
-                            (pai->qu->answer->type & ~adns_rrt_reprmask)),
-                           &pai->qu->vb, id, nflags, pai->now, &ctx);
+  st= addr_submit(pai->qu, &nqu, &pai->qu->vb, id, want,
+                 nflags, pai->now, &ctx);
   if (st) return st;
 
   return adns_s_ok;
@@ -952,14 +1137,15 @@ static adns_status cs_inthost(vbuf *vb, const void *datap) {
 
 static adns_status ckl_ptr(adns_state ads, adns_queryflags flags,
                           union checklabel_state *cls, qcontext *ctx,
-                          int labnum, const char *label, int lablen) {
+                          int labnum, const char *dgram,
+                          int labstart, int lablen) {
   if (lablen) {
-    if (adns__revparse_label(&cls->ptr, labnum, label,lablen))
+    if (!adns__revparse_label(&cls->ptr, labnum, dgram,labstart,lablen))
       return adns_s_querydomainwrong;
   } else {
-    if (adns__revparse_done(&cls->ptr, labnum,
-                           &ctx->tinfo.ptr.rev_rrtype,
-                           &ctx->tinfo.ptr.addr))
+    if (!adns__revparse_done(&cls->ptr, dgram, labnum,
+                            &ctx->tinfo.ptr.rev_rrtype,
+                            &ctx->tinfo.ptr.addr))
       return adns_s_querydomainwrong;
   }
   return adns_s_ok;
@@ -967,7 +1153,7 @@ static adns_status ckl_ptr(adns_state ads, adns_queryflags flags,
 
 static void icb_ptr(adns_query parent, adns_query child) {
   adns_answer *cans= child->answer;
-  const struct af_addr *queried;
+  const adns_sockaddr *queried;
   const unsigned char *found;
   adns_state ads= parent->ads;
   int i;
@@ -982,8 +1168,8 @@ static void icb_ptr(adns_query parent, adns_query child) {
 
   queried= &parent->ctx.tinfo.ptr.addr;
   for (i=0, found=cans->rrs.bytes; i<cans->nrrs; i++, found+=cans->rrsz) {
-    if (adns__genaddr_equal_p(queried->af,&queried->addr,
-                             parent->ctx.tinfo.ptr.addr.af,found)) {
+    if (adns__addrs_equal_raw(&queried->sa,
+                         parent->ctx.tinfo.ptr.addr.sa.sa_family,found)) {
       if (!parent->children.head) {
        adns__query_done(parent);
        return;
@@ -1235,12 +1421,15 @@ static adns_status cs_soa(vbuf *vb, const void *datap) {
 
 static adns_status ckl_srv(adns_state ads, adns_queryflags flags,
                           union checklabel_state *cls, qcontext *ctx,
-                          int labnum, const char *label, int lablen) {
-  if (labnum < 2 && !(flags & adns_qf_quoteok_query)) {
+                          int labnum, const char *dgram,
+                          int labstart, int lablen) {
+  const char *label = dgram+labstart;
+  if (labnum < 2) {
+    if (flags & adns_qf_quoteok_query) return adns_s_ok;
     if (!lablen || label[0] != '_') return adns_s_querydomaininvalid;
     return adns_s_ok;
   }
-  return adns__ckl_hostname(ads, flags, cls, ctx, labnum, label, lablen);
+  return adns__ckl_hostname(ads,flags, cls,ctx, labnum, dgram,labstart,lablen);
 }
 
 static adns_status pap_srv_begin(const parseinfo *pai, int *cbyte_io, int max,
@@ -1505,6 +1694,10 @@ DEEP_TYPE(unknown,0, "unknown",byteblock,opaque,  0,     opaque            );
 const typeinfo *adns__findtype(adns_rrtype type) {
   const typeinfo *begin, *end, *mid;
 
+  if (type & ~(adns_rrtype)0x63ffffff)
+    /* 0x60000000 is reserved for `harmless' future expansion */
+    return 0;
+
   if (type & adns_r_unknown) return &typeinfo_unknown;
   type &= adns_rrt_reprmask;