chiark / gitweb /
resolv.conf parsing: Rename OPTION_IS etc. to WORD_IS
[adns.git] / src / setup.c
index e853ab797e36139b015a3f1bb628fd6a38fa3377..c52966d22547089cd9226fa080a391b537734625 100644 (file)
@@ -5,14 +5,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,
@@ -21,8 +22,7 @@
  *  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 <stdlib.h>
 
 static void readconfig(adns_state ads, const char *filename, int warnmissing);
 
-static void addserver(adns_state ads, const struct sockaddr *sa, int n) {
+static void addserver(adns_state ads, const struct sockaddr *sa, int salen) {
   int i;
   adns_rr_addr *ss;
   char buf[ADNS_ADDR2TEXT_BUFLEN];
   
   for (i=0; i<ads->nservers; i++) {
-    if (adns__sockaddr_equal_p(sa, &ads->servers[i].addr.sa)) {
+    if (adns__sockaddrs_equal(sa, &ads->servers[i].addr.sa)) {
       adns__debug(ads,-1,0,"duplicate nameserver %s ignored",
                  adns__sockaddr_ntoa(sa, buf));
       return;
@@ -61,9 +61,9 @@ static void addserver(adns_state ads, const struct sockaddr *sa, int n) {
   }
 
   ss= ads->servers+ads->nservers;
-  assert(n <= sizeof(ss->addr));
-  ss->len = n;
-  memcpy(&ss->addr, sa, n);
+  assert(salen <= sizeof(ss->addr));
+  ss->len = salen;
+  memcpy(&ss->addr, sa, salen);
   ads->nservers++;
 }
 
@@ -165,17 +165,15 @@ static void ccf_search(adns_state ads, const char *fn,
   ads->searchlist= newptrs;
 }
 
-static int gen_pton(const char *text, int *af_io, union gen_addr *a) {
-  adns_rr_addr addr;
+static int gen_pton(const char *text, int want_af, adns_sockaddr *a) {
   int err;
+  int len;
 
-  addr.len= sizeof(addr.addr);
+  len= sizeof(*a);
   err= adns_text2addr(text,0, adns_qf_addrlit_scope_forbid,
-                     &addr.addr.sa, &addr.len);
+                     &a->sa, &len);
   if (err) { assert(err == EINVAL); return 0; }
-  if (*af_io == AF_UNSPEC) *af_io= addr.addr.sa.sa_family;
-  else if (*af_io != addr.addr.sa.sa_family) return 0;
-  adns__sockaddr_extract(&addr.addr.sa, a, 0);
+  if (want_af != AF_UNSPEC && a->sa.sa_family != want_af) return 0;
   return 1;
 }
 
@@ -186,7 +184,6 @@ static void ccf_sortlist(adns_state ads, const char *fn,
   const char *maskwhat;
   struct sortlist *sl;
   int l;
-  int af;
   int initial= -1;
 
   if (!buf) return;
@@ -209,8 +206,7 @@ static void ccf_sortlist(adns_state ads, const char *fn,
     if (slash) *slash++= 0;
 
     sl= &ads->sortlist[ads->nsortlist];
-    af= AF_UNSPEC;
-    if (!gen_pton(tbuf, &af, &sl->base)) {
+    if (!gen_pton(tbuf, AF_UNSPEC, &sl->base)) {
       configparseerr(ads,fn,lno,"invalid address `%s' in sortlist",tbuf);
       continue;
     }
@@ -218,32 +214,36 @@ static void ccf_sortlist(adns_state ads, const char *fn,
     if (slash) {
       if (slash[strspn(slash, "0123456789")]) {
        maskwhat = "mask";
-       if (!gen_pton(slash,&af,&sl->mask)) {
+       if (!gen_pton(slash, sl->base.sa.sa_family, &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>adns__addr_width(af)) {
+       if (*ep || initial>adns__addr_width(sl->base.sa.sa_family)) {
          configparseerr(ads,fn,lno,"mask length `%s' invalid",slash);
          continue;
        }
-       adns__prefix_mask(af, initial, &sl->mask);
+       sl->mask.sa.sa_family= sl->base.sa.sa_family;
+       adns__prefix_mask(&sl->mask, initial);
       }
     } else {
       maskwhat = "implied prefix length";
-      initial= adns__guess_prefix_length(af, &sl->base);
+      initial= adns__guess_prefix_length(&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;
       }
-      adns__prefix_mask(af, initial, &sl->mask);
+      sl->mask.sa.sa_family= sl->base.sa.sa_family;
+      adns__prefix_mask(&sl->mask, initial);
     }
 
-    if (!adns__addr_match_p(af,&sl->base, af,&sl->base,&sl->mask)) {
+    if (!adns__addr_matches(sl->base.sa.sa_family,
+                           adns__sockaddr_addr(&sl->base.sa),
+                           &sl->base,&sl->mask)) {
       if (initial >= 0) {
        configparseerr(ads,fn,lno, "%s %d in sortlist"
                       " overlaps address `%s'",maskwhat,initial,tbuf);
@@ -254,52 +254,99 @@ static void ccf_sortlist(adns_state ads, const char *fn,
       continue;
     }
 
-    sl->af= af;
     ads->nsortlist++;
   }
 }
 
 static void ccf_options(adns_state ads, const char *fn,
                        int lno, const char *buf) {
-  const char *word;
+  const char *opt, *word, *endword, *endopt;
   char *ep;
   unsigned long v;
   int l;
 
   if (!buf) return;
 
+#define WORD__IS(s,op) ((endword-word) op (sizeof(s)-1) && \
+                         !memcmp(word,s,(sizeof(s)-1)))
+#define WORD_IS(s)     (WORD__IS(s,==))
+#define WORD_STARTS(s) (WORD__IS(s,>=) ? ((word+=sizeof(s)-1)) : 0)
+
   while (nextword(&buf,&word,&l)) {
-    if (l==5 && !memcmp(word,"debug",5)) {
+    opt=word;
+    endopt=endword=word+l;
+    if (WORD_IS("debug")) {
       ads->iflags |= adns_if_debug;
       continue;
     }
-    if (l>=6 && !memcmp(word,"ndots:",6)) {
-      v= strtoul(word+6,&ep,10);
-      if (l==6 || ep != word+l || v > INT_MAX) {
+    if (WORD_STARTS("ndots:")) {
+      v= strtoul(word,&ep,10);
+      if (ep==word || ep != endword || v > INT_MAX) {
        configparseerr(ads,fn,lno,"option `%.*s' malformed"
-                      " or has bad value",l,word);
+                      " or has bad value",l,opt);
        continue;
       }
       ads->searchndots= v;
       continue;
     }
-    if (l>=12 && !memcmp(word,"adns_checkc:",12)) {
-      if (!strcmp(word+12,"none")) {
+    if (WORD_STARTS("adns_checkc:")) {
+      if (WORD_IS("none")) {
        ads->iflags &= ~adns_if_checkc_freq;
        ads->iflags |= adns_if_checkc_entex;
-      } else if (!strcmp(word+12,"entex")) {
+      } else if (WORD_IS("entex")) {
        ads->iflags &= ~adns_if_checkc_freq;
        ads->iflags |= adns_if_checkc_entex;
-      } else if (!strcmp(word+12,"freq")) {
+      } else if (WORD_IS("freq")) {
        ads->iflags |= adns_if_checkc_freq;
       } else {
        configparseerr(ads,fn,lno, "option adns_checkc has bad value `%s' "
-                      "(must be none, entex or freq", word+12);
+                      "(must be none, entex or freq", word);
+      }
+      continue;
+    }
+    if (WORD_STARTS("adns_af:")) {
+      ads->iflags &= ~adns_if_afmask;
+      if (!WORD_IS("any")) for (;;) {
+       const char *comma= memchr(word,',',endopt-word);
+       endword=comma?comma:endopt;
+       if (WORD_IS("ipv4"))
+         ads->iflags |= adns_if_permit_ipv4;
+       else if (WORD_IS("ipv6"))
+         ads->iflags |= adns_if_permit_ipv6;
+       else {
+         configparseerr(ads,fn,lno, "option adns_af has bad value `%.*s' "
+                        "(must be `any' or list {`ipv4',`ipv6'},...)",
+                        (int)(endword-word), word);
+         break;
+       }
+       if (!comma) break;
+       word= comma+1;
       }
       continue;
     }
-    adns__diag(ads,-1,0,"%s:%d: unknown option `%.*s'", fn,lno, l,word);
+    if (WORD_IS("adns_ignoreunkcfg")) {
+      ads->config_report_unknown=0;
+      continue;
+    }
+    if (/* adns's query strategy is not configurable */
+       WORD_STARTS("timeout:") ||
+       WORD_STARTS("attempts:") ||
+       WORD_IS("rotate") ||
+       /* adns provides the application with knob for this */
+       WORD_IS("no-check-names") ||
+       /* adns normally does IPv6 if the application wants it; control
+        * this with the adns_af: option if you like */
+       WORD_IS("inet6") ||
+       /* adns does not do edns0 and this is not a problem */
+       WORD_IS("edns0"))
+      continue;
+    if (ads->config_report_unknown)
+      adns__diag(ads,-1,0,"%s:%d: unknown option `%.*s'", fn,lno, l,opt);
   }
+
+#undef WORD__IS
+#undef WORD_IS
+#undef WORD_STARTS
 }
 
 static void ccf_clearnss(adns_state ads, const char *fn,
@@ -336,8 +383,9 @@ static void ccf_lookup(adns_state ads, const char *fn, int lno,
       adns__diag(ads,-1,0,"%s:%d: yp lookups not supported by adns", fn,lno);
       found_bind=-1;
     } else {
-      adns__diag(ads,-1,0,"%s:%d: unknown `lookup' database `%.*s'",
-                fn,lno, l,word);
+      if (ads->config_report_unknown)
+       adns__diag(ads,-1,0,"%s:%d: unknown `lookup' database `%.*s'",
+                  fn,lno, l,word);
       found_bind=-1;
     }
   }
@@ -345,6 +393,10 @@ static void ccf_lookup(adns_state ads, const char *fn, int lno,
     adns__diag(ads,-1,0,"%s:%d: `lookup' specified, but not `bind'", fn,lno);
 }
 
+static void ccf_ignore(adns_state ads, const char *fn, int lno,
+                      const char *buf) {
+}
+
 static const struct configcommandinfo {
   const char *name;
   void (*fn)(adns_state ads, const char *fn, int lno, const char *buf);
@@ -357,6 +409,7 @@ static const struct configcommandinfo {
   { "clearnameservers",  ccf_clearnss    },
   { "include",           ccf_include     },
   { "lookup",            ccf_lookup      }, /* OpenBSD */
+  { "lwserver",          ccf_ignore      }, /* BIND9 lwresd */
   {  0                                   }
 };
 
@@ -462,8 +515,9 @@ static void readconfiggeneric(adns_state ads, const char *filename,
           !(strlen(ccip->name)==dirl && !memcmp(ccip->name,p,q-p));
         ccip++);
     if (!ccip->name) {
-      adns__diag(ads,-1,0,"%s:%d: unknown configuration directive `%.*s'",
-                filename,lno,(int)(q-p),p);
+      if (ads->config_report_unknown)
+       adns__diag(ads,-1,0,"%s:%d: unknown configuration directive `%.*s'",
+                  filename,lno,(int)(q-p),p);
       continue;
     }
     while (ctype_whitespace(*q)) q++;
@@ -548,6 +602,10 @@ static int init_begin(adns_state *ads_r, adns_initflags flags,
   adns_state ads;
   pid_t pid;
   
+  if (flags & ~(adns_initflags)(0x4fff))
+    /* 0x4000 is reserved for `harmless' future expansion */
+    return ENOSYS;
+
   ads= malloc(sizeof(*ads)); if (!ads) return errno;
 
   ads->iflags= flags;
@@ -561,7 +619,7 @@ static int init_begin(adns_state *ads_r, adns_initflags flags,
   LIST_INIT(ads->intdone);
   ads->forallnext= 0;
   ads->nextid= 0x311f;
-  ads->nudp= 0;
+  ads->nudpsockets= 0;
   ads->tcpsocket= -1;
   adns__vbuf_init(&ads->tcpsend);
   adns__vbuf_init(&ads->tcprecv);
@@ -571,6 +629,7 @@ static int init_begin(adns_state *ads_r, adns_initflags flags,
   ads->tcpstate= server_disconnected;
   timerclear(&ads->tcptimeout);
   ads->searchlist= 0;
+  ads->config_report_unknown=1;
 
   pid= getpid();
   ads->rand48xsubi[0]= pid;
@@ -585,7 +644,7 @@ static int init_finish(adns_state ads) {
   struct sockaddr_in sin;
   struct protoent *proto;
   struct udpsocket *udp;
-  int i, j;
+  int i;
   int r;
   
   if (!ads->nservers) {
@@ -599,16 +658,16 @@ static int init_finish(adns_state ads) {
   }
 
   proto= getprotobyname("udp"); if (!proto) { r= ENOPROTOOPT; goto x_free; }
-  ads->nudp= 0;
+  ads->nudpsockets= 0;
   for (i=0; i<ads->nservers; i++) {
     if (adns__udpsocket_by_af(ads, ads->servers[i].addr.sa.sa_family))
       continue;
-    assert(ads->nudp < MAXUDP);
-    udp= &ads->udpsocket[ads->nudp];
+    assert(ads->nudpsockets < MAXUDP);
+    udp= &ads->udpsockets[ads->nudpsockets];
     udp->af= ads->servers[i].addr.sa.sa_family;
     udp->fd= socket(udp->af,SOCK_DGRAM,proto->p_proto);
     if (udp->fd < 0) { r= errno; goto x_free; }
-    ads->nudp++;
+    ads->nudpsockets++;
     r= adns__setnonblock(ads,udp->fd);
     if (r) { r= errno; goto x_closeudp; }
   }
@@ -616,7 +675,7 @@ static int init_finish(adns_state ads) {
   return 0;
 
  x_closeudp:
-  for (j=0; j<ads->nudp; j++) close(ads->udpsocket[j].fd);
+  for (i=0; i<ads->nudpsockets; i++) close(ads->udpsockets[i].fd);
  x_free:
   free(ads);
   return r;
@@ -733,7 +792,7 @@ void adns_finish(adns_state ads) {
     else if (ads->intdone.head) adns__cancel(ads->output.head);
     else break;
   }
-  for (i=0; i<ads->nudp; i++) close(ads->udpsocket[i].fd);
+  for (i=0; i<ads->nudpsockets; i++) close(ads->udpsockets[i].fd);
   if (ads->tcpsocket >= 0) close(ads->tcpsocket);
   adns__vbuf_free(&ads->tcpsend);
   adns__vbuf_free(&ads->tcprecv);