chiark / gitweb /
src/: Support IPv6 addresses in `sortlist'.
authorMark Wooding <mdw@distorted.org.uk>
Sat, 3 May 2014 10:46:12 +0000 (11:46 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Sat, 10 May 2014 13:31:40 +0000 (14:31 +0100)
This involves a fair amount of messing about.  There's a new file
`addrfam.c' containing a number of address-family-specific facts and
utilities, packaged together in a handy structure.  The `sortlist'
representation now attaches one of these to each entry, and now keeps
a union of possible address types.

Lookup of addresses in the sortlist is also modified, to check the
address family before going any further.

Signed-off-by: Mark Wooding <mdw@distorted.org.uk>
client/adh-main.c
client/adh-opts.c
src/addrfam.c [new file with mode: 0644]
src/adns.h
src/adns.make
src/check.c
src/internal.h
src/setup.c
src/types.c

index b6f3bd4d0d2ada0b3658b7b41a0b51f893467b34..dc9c46d2ad8e83b6ed282b0b4f112c780968f408 100644 (file)
@@ -99,6 +99,7 @@ void of_type(const struct optioninfo *oi, const char *arg, const char *arg2) {
     
     /* raw versions */
     { adns_r_a,        "a"    },
+    { adns_r_aaaa,     "aaaa" },
     { adns_r_ns_raw,   "ns-"  },
     { adns_r_soa_raw,  "soa-" },
     { adns_r_ptr_raw,  "ptr-" },
index 08310e089d19939c7672d6bd7156f73b57ec700e..34ce33477f4f3645eb4ea8961a2f90cfedc07d3f 100644 (file)
@@ -262,10 +262,10 @@ static void printusage(void) {
        " 11   usage problems\n"
        "\n"
        "Query types (see adns.h; default is addr):\n"
-       "  ns  soa  ptr  mx  rp  srv  addr       - enhanced versions\n"
-       "  cname  hinfo  txt                     - types with only one version\n"
-       "  a  ns-  soa-  ptr-  mx-  rp-  srv-    - _raw versions\n"
-       "  type<number>                          - `unknown' type, RFC3597\n"
+       "  ns  soa  ptr  mx  rp  srv  addr           - enhanced versions\n"
+       "  cname  hinfo  txt                         - types with only one version\n"
+       "  a  aaaa  ns-  soa-  ptr-  mx-  rp-  srv-  - _raw versions\n"
+       "  type<number>                              - `unknown' type, RFC3597\n"
        "Default is addr, or ptr for -i/--ptr queries\n",
        stdout);
   if (ferror(stdout)) sysfail("write usage message",errno);
diff --git a/src/addrfam.c b/src/addrfam.c
new file mode 100644 (file)
index 0000000..40b8f2b
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * addrfam.c
+ * - address-family specific code
+ */
+/*
+ *  This file is part of adns, which is
+ *    Copyright (C) 1997-2000,2003,2006  Ian Jackson
+ *    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)
+ *  any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  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. 
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "internal.h"
+
+/*
+ * IPv4
+ */
+
+#define SIN(sa) ((struct sockaddr_in *)(sa))
+
+static void *inet_sockaddr_to_inaddr(struct sockaddr *sa)
+  { return &SIN(sa)->sin_addr; }
+
+static void inet_prefix_mask(int len, union gen_addr *mask)
+  { mask->v4.s_addr = htonl(!len ? 0 : 0xffffffff << (32 - len)); }
+
+static int inet_guess_len(const union gen_addr *addr)
+{
+  unsigned a = (ntohl(addr->v4.s_addr) >> 24) & 0xff;
+
+  if (a < 128) return 8;
+  else if (a < 192) return 16;
+  else if (a < 224) return 24;
+  else return -1;
+}
+
+static int inet_matchp(const union gen_addr *addr,
+                      const union gen_addr *base,
+                      const union gen_addr *mask)
+  { return (addr->v4.s_addr & mask->v4.s_addr) == base->v4.s_addr; }
+
+const afinfo adns__inet_afinfo = {
+  AF_INET, 32, '.',
+  inet_sockaddr_to_inaddr, inet_prefix_mask, inet_guess_len, inet_matchp
+};
+
+/*
+ * IPv6
+ */
+
+#define SIN6(sa) ((struct sockaddr_in6 *)(sa))
+
+static void *inet6_sockaddr_to_inaddr(struct sockaddr *sa)
+  { return &SIN6(sa)->sin6_addr; }
+
+static void inet6_prefix_mask(int len, union gen_addr *mask)
+{
+  int i = len/8, j = len%8;
+  unsigned char *m = mask->v6.s6_addr;
+
+  assert(len < 128);
+  memset(m, 0xff, i);
+  if (j) m[i++] = (0xff << (8-j)) & 0xff;
+  memset(m + i, 0, 16-i);
+}
+
+static int inet6_guess_len(const union gen_addr *addr)
+  { return 64; }
+
+static int inet6_matchp(const union gen_addr *addr,
+                       const union gen_addr *base,
+                       const union gen_addr *mask)
+{
+  int i;
+  const char *a = addr->v6.s6_addr;
+  const char *b = base->v6.s6_addr;
+  const char *m = mask->v6.s6_addr;
+
+  for (i = 0; i < 16; i++)
+    if ((a[i] & m[i]) != b[i]) return 0;
+  return 1;
+}
+
+const afinfo adns__inet6_afinfo = {
+  AF_INET6, 128, ':',
+  inet6_sockaddr_to_inaddr, inet6_prefix_mask, inet6_guess_len, inet6_matchp
+};
index 34f9f4978020f9f4b80c85406a0e6b0cdceb1a92..79f601dc3d9360e5529dfd9d1b2f11f17f98d13f 100644 (file)
@@ -152,6 +152,8 @@ typedef enum {
  adns_r_rp_raw=          17,
  adns_r_rp=                  adns_r_rp_raw|adns__qtf_mail822,
 
+ adns_r_aaaa=           28,
+
  /* For SRV records, query domain without _qf_quoteok_query must look
   * as expected from SRV RFC with hostname-like Name.  _With_
   * _quoteok_query, any query domain is allowed. */
@@ -355,6 +357,7 @@ typedef struct {
     adns_rr_intstr *(*manyistr);     /* txt (list strs ends with i=-1, str=0)*/
     adns_rr_addr *addr;              /* addr */
     struct in_addr *inaddr;          /* a */
+    struct in6_addr *in6addr;       /* aaaa */
     adns_rr_hostaddr *hostaddr;      /* ns */
     adns_rr_intstrpair *intstrpair;  /* hinfo */
     adns_rr_strpair *strpair;        /* rp, rp_raw */
index b0ba693185e727f1ea00479e4a9c819642b77a1a..1c35eb1b21082ade1a904326b51d608580f1d466 100644 (file)
@@ -21,4 +21,4 @@
 #  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 
 
 LIBOBJS=       types.o event.o query.o reply.o general.o setup.o transmit.o \
-               parse.o poll.o check.o
+               parse.o poll.o check.o addrfam.o
index 41cdde5ff3592d09f86d3e0bd4b9d8abcc114896..6a02ee89987bdd0fbf70547ccb4ed5f4d6f2be9f 100644 (file)
@@ -75,12 +75,15 @@ static void checkc_notcpbuf(adns_state ads) {
 }
 
 static void checkc_global(adns_state ads) {
+  const struct sortlist *sl;
   int i;
   
   assert(ads->udpsocket >= 0);
 
-  for (i=0; i<ads->nsortlist; i++)
-    assert(!(ads->sortlist[i].base.s_addr & ~ads->sortlist[i].mask.s_addr));
+  for (i=0; i<ads->nsortlist; i++) {
+    sl = &ads->sortlist[i];
+    assert(sl->ai->matchp(&sl->base, &sl->base, &sl->mask));
+  }
 
   assert(ads->tcpserver >= 0 && ads->tcpserver < ads->nservers);
   
index 58cd15d2fba69ae80e8f09bf99b71580b8cce07a..4f4f87eb0c4ddebd4f7d5b0efea01fb2cf3d6f8b 100644 (file)
@@ -112,6 +112,22 @@ typedef struct {
   struct timeval now;
 } parseinfo;
 
+union gen_addr {
+  struct in_addr v4;
+  struct in6_addr v6;
+};
+
+typedef struct {
+  int af;
+  int width;
+  int delim;
+  void *(*sockaddr_to_inaddr)(struct sockaddr *sa);
+  void (*prefix_mask)(int len, union gen_addr *a);
+  int (*guess_len)(const union gen_addr *a);
+  int (*matchp)(const union gen_addr *addr,
+               const union gen_addr *base, const union gen_addr *mask);
+} afinfo;
+
 typedef struct typeinfo {
   adns_rrtype typekey;
   const char *rrtname;
@@ -333,12 +349,17 @@ struct adns__state {
     struct in_addr addr;
   } servers[MAXSERVERS];
   struct sortlist {
-    struct in_addr base, mask;
+    const afinfo *ai;
+    union gen_addr base, mask;
   } sortlist[MAXSORTLIST];
   char **searchlist;
   unsigned short rand48xsubi[3];
 };
 
+/* From addrfam.c: */
+
+extern const afinfo adns__inet_afinfo, adns__inet6_afinfo;
+
 /* From setup.c: */
 
 int adns__setnonblock(adns_state ads, int fd); /* => errno value */
index e6f1e55d85993f264bdfacf5c9711e87ad4e0dbd..61f4f67c4b88a43d21273f9c73643062c1e4adca 100644 (file)
@@ -153,9 +153,10 @@ static void ccf_sortlist(adns_state ads, const char *fn,
   const char *word;
   char tbuf[200], *slash, *ep;
   const char *maskwhat;
-  struct in_addr base, mask;
+  struct sortlist *sl;
   int l;
-  unsigned long initial, baselocal;
+  const afinfo *ai;
+  int initial = -1;
 
   if (!buf) return;
   
@@ -175,54 +176,59 @@ static void ccf_sortlist(adns_state ads, const char *fn,
     memcpy(tbuf,word,l); tbuf[l]= 0;
     slash= strchr(tbuf,'/');
     if (slash) *slash++= 0;
+
+    sl= &ads->sortlist[ads->nsortlist];
+
+    if (strchr(tbuf, ':'))
+      ai= &adns__inet6_afinfo;
+    else
+      ai= &adns__inet_afinfo;
     
-    if (!inet_aton(tbuf,&base)) {
+    if (!inet_pton(ai->af, tbuf, &sl->base)) {
       configparseerr(ads,fn,lno,"invalid address `%s' in sortlist",tbuf);
       continue;
     }
 
     if (slash) {
-      if (strchr(slash,'.')) {
+      if (strchr(slash,ai->delim)) {
        maskwhat = "mask";
-       if (!inet_aton(slash,&mask)) {
+       if (!inet_pton(ai->af,slash,&sl->mask)) {
          configparseerr(ads,fn,lno,"invalid mask `%s' in sortlist",slash);
          continue;
        }
       } else {
        maskwhat = "prefix length";
        initial= strtoul(slash,&ep,10);
-       if (*ep || initial>32) {
+       if (*ep || initial>ai->width) {
          configparseerr(ads,fn,lno,"mask length `%s' invalid",slash);
          continue;
        }
-       mask.s_addr= htonl((0x0ffffffffUL) << (32-initial));
+       ai->prefix_mask(initial, &sl->mask);
       }
     } else {
-      maskwhat = "implied mask";
-      baselocal= ntohl(base.s_addr);
-      if (!baselocal & 0x080000000UL) /* class A */
-       mask.s_addr= htonl(0x0ff000000UL);
-      else if ((baselocal & 0x0c0000000UL) == 0x080000000UL)
-       mask.s_addr= htonl(0x0ffff0000UL); /* class B */
-      else if ((baselocal & 0x0f0000000UL) == 0x0e0000000UL)
-       mask.s_addr= htonl(0x0ff000000UL); /* class C */
-      else {
+      maskwhat = "implied prefix length";
+      initial = ai->guess_len(&sl->base);
+      if (initial < 0) {
        configparseerr(ads,fn,lno, "network address `%s'"
                       " in sortlist is not in classed ranges,"
                       " must specify mask explicitly", tbuf);
        continue;
       }
+      ai->prefix_mask(initial, &sl->mask);
     }
 
-    if (base.s_addr & ~mask.s_addr) {
-      configparseerr(ads,fn,lno, "%s `%s' in sortlist"
-                    " overlaps address `%s'",maskwhat,
-                    slash ? slash : inet_ntoa(mask), tbuf);
+    if (!ai->matchp(&sl->base, &sl->base, &sl->mask)) {
+      if (initial >= 0) {
+       configparseerr(ads,fn,lno, "%s %d in sortlist"
+                      " overlaps address `%s'",maskwhat,initial,tbuf);
+      } else {
+       configparseerr(ads,fn,lno, "%s `%s' in sortlist"
+                      " overlaps address `%s'",maskwhat,slash,tbuf);
+      }
       continue;
     }
 
-    ads->sortlist[ads->nsortlist].base= base;
-    ads->sortlist[ads->nsortlist].mask= mask;
+    sl->ai = ai;
     ads->nsortlist++;
   }
 }
index 36ff87930a06f176652419f3aee5e8914465d941..a2ddee31069f4c890160c9a4ecefe5a9ffec4172 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,30 @@ 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;
   int i;
   
   for (i=0, slp=ads->sortlist;
-       i<ads->nsortlist &&
-        !((ad.s_addr & slp->mask.s_addr) == slp->base.s_addr);
+       i<ads->nsortlist && (af != slp->ai->af ||
+                           !slp->ai->matchp(ad, &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 +288,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 +331,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 +585,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,
@@ -1271,6 +1313,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),