chiark / gitweb /
Support v6-mapping IPv4 addresses found doing addr lookups.
[adns.git] / src / types.c
index 28db797eb7a7e7a938a8484a28b7ef3391a45c8c..d4693c251bcf54cb2b262b6e34f94c1523beb794 100644 (file)
@@ -24,6 +24,7 @@
  *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 
  */
 
+#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)
@@ -343,13 +344,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,
@@ -376,16 +378,59 @@ static unsigned addr_rrtypeflag(adns_rrtype type) {
   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;
+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_ipv6_mapv4) &&
+       (pai->qu->answer->type & adns__qtf_bigaddr)) {
+      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);
 
-  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);
+  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;
 }
 
@@ -489,6 +534,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 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, 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) {
@@ -661,42 +738,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 +785,31 @@ 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->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 +825,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 +835,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 +858,12 @@ 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_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;