chiark / gitweb /
Support IPv6 PTR lookups.
[adns] / src / types.c
index 36ff87930a06f176652419f3aee5e8914465d941..a34f046cd111165d55f47707407d1223727c9be6 100644 (file)
@@ -47,8 +47,9 @@
  * _intstr                    (mf,csp,cs)
  * _manyistr                  (mf,cs)
  * _txt                       (pa)
- * _inaddr                    (pa,dip,di)
- * _addr                      (pa,di,csp,cs)
+ * _inaddr                    (pa,cs,di, +search_sortlist, dip_genaddr)
+ * _in6addr                   (pa,cs,di)
+ * _addr                      (pa,di,csp,cs, +search_sortlist_sa, dip_sockaddr)
  * _domain                    (pap)
  * _host_raw                  (pa)
  * _hostaddr                  (pap,pa,dip,di,mfp,mf,csp,cs +pap_findaddrs)
@@ -239,7 +240,7 @@ static adns_status cs_hinfo(vbuf *vb, const void *datap) {
 }
 
 /*
- * _inaddr   (pa,dip,di)
+ * _inaddr   (pa,di,cs +search_sortlist, dip_genaddr)
  */
 
 static adns_status pa_inaddr(const parseinfo *pai, int cbyte,
@@ -251,32 +252,47 @@ static adns_status pa_inaddr(const parseinfo *pai, int cbyte,
   return adns_s_ok;
 }
 
-static int search_sortlist(adns_state ads, struct in_addr ad) {
+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;
   int i;
-  
+  int v6mappedp = 0;
+
+  if (af == AF_INET6) {
+    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));
+      v6mappedp = 1;
+    }
+  }
+
   for (i=0, slp=ads->sortlist;
        i<ads->nsortlist &&
-        !((ad.s_addr & slp->mask.s_addr) == slp->base.s_addr);
+       !(af == slp->ai->af &&
+        slp->ai->matchp(ad, &slp->base, &slp->mask)) &&
+       !(v6mappedp && slp->ai->af == AF_INET &&
+        slp->ai->matchp(&a, &slp->base, &slp->mask));
        i++, slp++);
   return i;
 }
 
-static int dip_inaddr(adns_state ads, struct in_addr a, struct in_addr b) {
+static int dip_genaddr(adns_state ads, int af, const void *a, const void *b) {
   int ai, bi;
   
   if (!ads->nsortlist) return 0;
 
-  ai= search_sortlist(ads,a);
-  bi= search_sortlist(ads,b);
+  ai= search_sortlist(ads,af,a);
+  bi= search_sortlist(ads,af,b);
   return bi<ai;
 }
 
 static int di_inaddr(adns_state ads,
                     const void *datap_a, const void *datap_b) {
-  const struct in_addr *ap= datap_a, *bp= datap_b;
-
-  return dip_inaddr(ads,*ap,*bp);
+  return dip_genaddr(ads,AF_INET,datap_a,datap_b);
 }
 
 static adns_status cs_inaddr(vbuf *vb, const void *datap) {
@@ -289,7 +305,34 @@ static adns_status cs_inaddr(vbuf *vb, const void *datap) {
 }
 
 /*
- * _addr   (pa,di,csp,cs)
+ * _in6addr   (pa,di,cs)
+ */
+
+static adns_status pa_in6addr(const parseinfo *pai, int cbyte,
+                            int max, void *datap) {
+  struct in6_addr *storeto= datap;
+
+  if (max-cbyte != 16) return adns_s_invaliddata;
+  memcpy(storeto->s6_addr, pai->dgram + cbyte, 16);
+  return adns_s_ok;
+}
+
+static int di_in6addr(adns_state ads,
+                    const void *datap_a, const void *datap_b) {
+  return dip_genaddr(ads,AF_INET6,datap_a,AF_INET6,datap_b);
+}
+
+static adns_status cs_in6addr(vbuf *vb, const void *datap) {
+  char buf[INET6_ADDRSTRLEN];
+  const char *ia;
+
+  ia= inet_ntop(AF_INET6, datap, buf, sizeof(buf)); assert(ia);
+  CSP_ADDSTR(ia);
+  return adns_s_ok;
+}
+
+/*
+ * _addr   (pa,di,csp,cs, +search_sortlist_sa, dip_sockaddr)
  */
 
 static adns_status pa_addr(const parseinfo *pai, int cbyte,
@@ -305,11 +348,31 @@ static adns_status pa_addr(const parseinfo *pai, int cbyte,
   return adns_s_ok;
 }
 
+static int search_sortlist_sa(adns_state ads, const struct sockaddr *sa)
+{
+  const struct afinfo *ai = 0;
+
+  switch (sa->sa_family) {
+    case AF_INET: ai = &adns__inet_afinfo; break;
+    case AF_INET6: ai = &adns__inet6_afinfo; break;
+  }
+  assert(ai);
+
+  return search_sortlist(ads, sa->sa_family, ai->sockaddr_to_inaddr(sa));
+}
+
+static int dip_sockaddr(adns_state ads,
+                       const struct sockaddr *sa,
+                       const struct sockaddr *sb)
+{
+  if (!ads->sortlist) return 0;
+  return search_sortlist_sa(ads, sa) > search_sortlist_sa(ads, sb);
+}
+
 static int di_addr(adns_state ads, const void *datap_a, const void *datap_b) {
   const adns_rr_addr *ap= datap_a, *bp= datap_b;
 
-  assert(ap->addr.sa.sa_family == AF_INET);
-  return dip_inaddr(ads, ap->addr.inet.sin_addr, bp->addr.inet.sin_addr);
+  return dip_sockaddr(ads, &ap->addr.sa, &bp->addr.sa);
 }
 
 static int div_addr(void *context, const void *datap_a, const void *datap_b) {
@@ -539,11 +602,7 @@ static int dip_hostaddr(adns_state ads,
   if (ap->astatus != bp->astatus) return ap->astatus;
   if (ap->astatus) return 0;
 
-  assert(ap->addrs[0].addr.sa.sa_family == AF_INET);
-  assert(bp->addrs[0].addr.sa.sa_family == AF_INET);
-  return dip_inaddr(ads,
-                   ap->addrs[0].addr.inet.sin_addr,
-                   bp->addrs[0].addr.inet.sin_addr);
+  return dip_sockaddr(ads, &ap->addrs[0].addr.sa, &bp->addrs[0].addr.sa);
 }
 
 static int di_hostaddr(adns_state ads,
@@ -705,7 +764,8 @@ static adns_status cs_inthost(vbuf *vb, const void *datap) {
 
 static void icb_ptr(adns_query parent, adns_query child) {
   adns_answer *cans= child->answer;
-  const adns_rr_addr *queried, *found;
+  const union gen_addr *queried;
+  const unsigned char *found;
   adns_state ads= parent->ads;
   int i;
 
@@ -717,10 +777,9 @@ static void icb_ptr(adns_query parent, adns_query child) {
     return;
   }
 
-  queried= &parent->ctx.info.ptr_parent_addr;
-  for (i=0, found=cans->rrs.addr; i<cans->nrrs; i++, found++) {
-    if (queried->len == found->len &&
-       !memcmp(&queried->addr,&found->addr,queried->len)) {
+  queried= &parent->ctx.info.ptr_parent_addr.addr;
+  for (i=0, found=cans->rrs.bytes; i<cans->nrrs; i++, found += cans->rrsz) {
+    if (!memcmp(queried,found,cans->rrsz)) {
       if (!parent->children.head) {
        adns__query_done(parent);
        return;
@@ -736,16 +795,22 @@ static void icb_ptr(adns_query parent, adns_query child) {
 
 static adns_status pa_ptr(const parseinfo *pai, int dmstart,
                          int max, void *datap) {
-  static const char *const (expectdomain[])= { DNS_INADDR_ARPA };
+  static const struct {
+    const afinfo *ai;
+    const char *const tail[3];
+  } expectdomain[] = {
+    { &adns__inet_afinfo, { DNS_INADDR_ARPA, 0 } },
+    { &adns__inet6_afinfo, { DNS_IP6_ARPA, 0 } }
+  };
+  enum { n_ed = sizeof(expectdomain)/sizeof(expectdomain[0]) };
   
   char **rrp= datap;
   adns_status st;
-  adns_rr_addr *ap;
+  struct afinfo_addr *ap;
   findlabel_state fls;
-  char *ep;
-  byte ipv[4];
-  char labbuf[4];
-  int cbyte, i, lablen, labstart, l, id;
+  byte ipv[n_ed][32];
+  int cbyte, i, j, foundj = -1, lablen, labstart, id, f, ac;
+  const char *tp;
   adns_query nqu;
   qcontext ctx;
 
@@ -756,46 +821,62 @@ static adns_status pa_ptr(const parseinfo *pai, int dmstart,
   if (cbyte != max) return adns_s_invaliddata;
 
   ap= &pai->qu->ctx.info.ptr_parent_addr;
-  if (!ap->len) {
+  if (!ap->ai) {
     adns__findlabel_start(&fls, pai->ads, -1, pai->qu,
                          pai->qu->query_dgram, pai->qu->query_dglen,
                          pai->qu->query_dglen, DNS_HDRSIZE, 0);
-    for (i=0; i<4; i++) {
+
+    f = (1 << n_ed) - 1; /* superposition of address types */
+    for (i = 0;; i++) {
       st= adns__findlabel_next(&fls,&lablen,&labstart); assert(!st);
-      if (lablen<=0 || lablen>3) return adns_s_querydomainwrong;
-      memcpy(labbuf, pai->qu->query_dgram + labstart, lablen);
-      labbuf[lablen]= 0;
-      ipv[3-i]= strtoul(labbuf,&ep,10);
-      if (*ep) return adns_s_querydomainwrong;
-      if (lablen>1 && pai->qu->query_dgram[labstart]=='0')
-       return adns_s_querydomainwrong;
+      if (lablen <= 0) break;
+      for (j = 0; j < n_ed; j++) {
+       if (!(f & (1 << j))) continue;
+       if (i < expectdomain[j].ai->nrevcomp) {
+         ac = expectdomain[j].ai->rev_parsecomp(
+           pai->qu->query_dgram + labstart, lablen);
+         if (ac < 0) goto mismatch;
+         assert(i < sizeof(ipv[j]));
+         ipv[j][i] = ac;
+       } else {
+         tp = expectdomain[j].tail[i - expectdomain[j].ai->nrevcomp];
+         if (!tp ||
+             strncmp(pai->qu->query_dgram + labstart, tp, lablen) != 0 ||
+             tp[lablen] != 0)
+           goto mismatch;
+       }
+       continue;
+
+      mismatch:
+       f &= ~(1 << j);
+       if (!f) return adns_s_querydomainwrong;
+      }
     }
-    for (i=0; i<sizeof(expectdomain)/sizeof(*expectdomain); i++) {
-      st= adns__findlabel_next(&fls,&lablen,&labstart); assert(!st);
-      l= strlen(expectdomain[i]);
-      if (lablen != l ||
-         memcmp(pai->qu->query_dgram + labstart, expectdomain[i], l))
-       return adns_s_querydomainwrong;
+
+    if (lablen < 0) return adns_s_querydomainwrong;
+    for (j = 0; j < n_ed; j++) {
+      if (!(f & (1 << j))) continue;
+      if (i >= expectdomain[j].ai->nrevcomp &&
+         !expectdomain[j].tail[i - expectdomain[j].ai->nrevcomp])
+       { foundj = j; continue; }
+      f &= ~(1 << j);
+      if (!f) return adns_s_querydomainwrong;
     }
-    st= adns__findlabel_next(&fls,&lablen,0); assert(!st);
-    if (lablen) return adns_s_querydomainwrong;
-    
-    ap->len= sizeof(struct sockaddr_in);
-    memset(&ap->addr,0,sizeof(ap->addr.inet));
-    ap->addr.inet.sin_family= AF_INET;
-    ap->addr.inet.sin_addr.s_addr=
-      htonl((ipv[0]<<24) | (ipv[1]<<16) | (ipv[2]<<8) | (ipv[3]));
+    assert(foundj >= 0 && f == (1 << foundj)); /* collapsed to a single type */
+
+    ap->ai = expectdomain[foundj].ai;
+    ap->ai->rev_mkaddr(&ap->addr, ipv[foundj]);
   }
 
   st= adns__mkquery_frdgram(pai->ads, &pai->qu->vb, &id,
                            pai->dgram, pai->dglen, dmstart,
-                           adns_r_addr, adns_qf_quoteok_query);
+                           ap->ai->rrtype, adns_qf_quoteok_query);
   if (st) return st;
 
   ctx.ext= 0;
   ctx.callback= icb_ptr;
   memset(&ctx.info,0,sizeof(ctx.info));
-  st= adns__internal_submit(pai->ads, &nqu, adns__findtype(adns_r_addr),
+  st= adns__internal_submit(pai->ads, &nqu, adns__findtype(ap->ai->rrtype),
                            &pai->qu->vb, id,
                            adns_qf_quoteok_query, pai->now, &ctx);
   if (st) return st;
@@ -1271,6 +1352,7 @@ DEEP_TYPE(hinfo,  "HINFO", 0, intstrpair,pa_hinfo,   0,        cs_hinfo      ),
 DEEP_TYPE(mx_raw, "MX",   "raw",intstr,  pa_mx_raw,  di_mx_raw,cs_inthost    ),
 DEEP_TYPE(txt,    "TXT",   0,   manyistr,pa_txt,     0,        cs_txt        ),
 DEEP_TYPE(rp_raw, "RP",   "raw",strpair, pa_rp,      0,        cs_rp         ),
+FLAT_TYPE(aaaa,   "AAAA",  0,   in6addr, pa_in6addr, di_in6addr,cs_in6addr   ),
 XTRA_TYPE(srv_raw,"SRV",  "raw",srvraw , pa_srvraw,  di_srv,   cs_srvraw,
                                                       qdpl_srv, postsort_srv),