1 /* Nameserver zones: structures and routines
2  */
3 
4 #include <stdio.h>
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #include <stdlib.h>
8 #include <syslog.h>
9 #include <string.h>
10 #include <errno.h>
11 #include <unistd.h>
12 #include <fcntl.h>
13 #include <time.h>
14 #include "rbldnsd.h"
15 #include "istream.h"
16 
17 static struct dataset *ds_list;
18 struct dataset *g_dsacl;
19 
newdataset(char * spec)20 static struct dataset *newdataset(char *spec) {
21   /* type:file,file,file... */
22   struct dataset *ds, **dsp;
23   char *f;
24   struct dsfile **dsfp, *dsf;
25   static const char *const delims = ",:";
26   const struct dstype **dstp;
27 
28   f = strchr(spec, ':');
29   if (!f)
30     error(0, "invalid zone data specification `%.60s'", spec);
31   *f++ = '\0';
32 
33   for(dsp = &ds_list; (ds = *dsp) != NULL; dsp = &ds->ds_next)
34     if (strcmp(ds->ds_type->dst_name, spec) == 0 &&
35         strcmp(ds->ds_spec, f) == 0)
36       return ds;
37 
38   dstp = ds_types;
39   while(strcmp(spec, (*dstp)->dst_name))
40     if (!*++dstp)
41       error(0, "unknown dataset type `%.60s'", spec);
42   ds = (struct dataset*)ezalloc(sizeof(struct dataset) +
43                                      sizeof(struct mempool) +
44                                      (*dstp)->dst_size);
45   ds->ds_type = *dstp;
46   ds->ds_mp = (struct mempool*)(ds + 1);
47   ds->ds_dsd = (struct dsdata*)(ds->ds_mp + 1);
48   ds->ds_spec = estrdup(f);
49 
50   ds->ds_next = NULL;
51   *dsp = ds;
52 
53   dsfp = &ds->ds_dsf;
54   for (f = strtok(f, delims); f; f = strtok(NULL, delims)) {
55     dsf = tmalloc(struct dsfile);
56     dsf->dsf_stamp = 0;
57     dsf->dsf_name = estrdup(f);
58     *dsfp = dsf;
59     dsfp = &dsf->dsf_next;
60   }
61   *dsfp = NULL;
62   if (!ds->ds_dsf)
63     error(0, "missing filenames for %s", spec);
64 
65   return ds;
66 }
67 
newzone(struct zone ** zonelist,unsigned char * dn,unsigned dnlen,struct mempool * mp)68 struct zone *newzone(struct zone **zonelist,
69                      unsigned char *dn, unsigned dnlen,
70                      struct mempool *mp) {
71   struct zone *zone, **zonep, **lastzonep;
72 
73   zonep = zonelist;
74   lastzonep = NULL;
75 
76   for (;;) {
77     if (!(zone = *zonep)) {
78       if (mp)
79         zone = mp_talloc(mp, struct zone);
80       else
81         zone = tmalloc(struct zone);
82       if (!zone)
83         return NULL;
84       memset(zone, 0, sizeof(*zone));
85       if (lastzonep) { zone->z_next = *lastzonep; *lastzonep = zone; }
86       else *zonep = zone;
87       memcpy(zone->z_dn, dn, dnlen);
88       zone->z_dnlen = dnlen;
89       zone->z_dnlab = dns_dnlabels(dn);
90       zone->z_dslp = &zone->z_dsl;
91       break;
92     }
93     else if (zone->z_dnlen == dnlen && memcmp(zone->z_dn, dn, dnlen) == 0)
94       break;
95     else {
96       if (!lastzonep && zone->z_dnlen < dnlen &&
97           memcmp(dn + dnlen - zone->z_dnlen, zone->z_dn, zone->z_dnlen) == 0)
98         lastzonep = zonep;
99       zonep = &zone->z_next;
100     }
101   }
102 
103   return zone;
104 }
105 
connectdataset(struct zone * zone,struct dataset * ds,struct dslist * dsl)106 void connectdataset(struct zone *zone,
107                     struct dataset *ds,
108                     struct dslist *dsl) {
109   dsl->dsl_next = NULL;
110   *zone->z_dslp = dsl;
111   zone->z_dslp = &dsl->dsl_next;
112   dsl->dsl_ds = ds;
113   dsl->dsl_queryfn = ds->ds_type->dst_queryfn;
114   zone->z_dstflags |= ds->ds_type->dst_flags;
115 }
116 
addzone(struct zone * zonelist,const char * spec)117 struct zone *addzone(struct zone *zonelist, const char *spec) {
118   struct zone *zone;
119   char *p;
120   char name[DNS_MAXDOMAIN];
121   unsigned char dn[DNS_MAXDN];
122   unsigned dnlen;
123   struct dataset *ds;
124 
125   p = strchr(spec, ':');
126   if (!p || p - spec >= DNS_MAXDOMAIN)
127     error(0, "invalid zone spec `%.60s'", spec);
128 
129   memcpy(name, spec, p - spec);
130   name[p - spec] = '\0';
131 
132   dnlen = dns_ptodn(name, dn, sizeof(dn));
133   if (!dnlen)
134     error(0, "invalid domain name `%.80s'", name);
135   dns_dntol(dn, dn);
136 
137   p = estrdup(p+1);
138   ds = newdataset(p);
139 
140   if (!dn[0]) {
141     if (!isdstype(ds->ds_type, acl))
142       error(0, "missing domain name in `%.60s'", spec);
143     if (g_dsacl)
144       error(0, "global acl specified more than once");
145     g_dsacl = ds;
146   }
147   else {
148     zone = newzone(&zonelist, dn, dnlen, NULL);
149     if (isdstype(ds->ds_type, acl)) {
150       if (zone->z_dsacl)
151         error(0, "repeated ACL definition for zone `%.60s'", name);
152       zone->z_dsacl = ds;
153     }
154     else
155       connectdataset(zone, ds, tmalloc(struct dslist));
156   }
157   free(p);
158 
159   return zonelist;
160 }
161 
162 /* parse $SPECIAL construct */
ds_special(struct dataset * ds,char * line,struct dsctx * dsc)163 static int ds_special(struct dataset *ds, char *line, struct dsctx *dsc) {
164   char *w;
165 
166   if ((w = firstword_lc(line, "soa"))) {
167     /* SOA record */
168     struct dssoa dssoa;
169     unsigned char odn[DNS_MAXDN], pdn[DNS_MAXDN];
170     unsigned odnlen, pdnlen;
171 
172     if (isdstype(ds->ds_type, acl))
173       return 0;	/* don't allow SOA for ACLs */
174     if (ds->ds_dssoa)
175       return 1; /* ignore if already set */
176 
177     if (!(w = parse_ttl(w, &dssoa.dssoa_ttl, ds->ds_ttl))) return 0;
178     if (!(w = parse_dn(w, odn, &odnlen))) return 0;
179     if (!(w = parse_dn(w, pdn, &pdnlen))) return 0;
180     if (!(w = parse_uint32(w, &dssoa.dssoa_serial))) return 0;
181     if (!(w = parse_time_nb(w, dssoa.dssoa_n+0))) return 0;
182     if (!(w = parse_time_nb(w, dssoa.dssoa_n+4))) return 0;
183     if (!(w = parse_time_nb(w, dssoa.dssoa_n+8))) return 0;
184     if (!(w = parse_time_nb(w, dssoa.dssoa_n+12))) return 0;
185     if (*w) return 0;
186 
187     dssoa.dssoa_odn = mp_memdup(ds->ds_mp, odn, odnlen);
188     dssoa.dssoa_pdn = mp_memdup(ds->ds_mp, pdn, pdnlen);
189     if (!dssoa.dssoa_odn || !dssoa.dssoa_pdn) return -1;
190     ds->ds_dssoa = mp_talloc(ds->ds_mp, struct dssoa);
191     if (!ds->ds_dssoa) return -1;
192     *ds->ds_dssoa = dssoa;
193     return 1;
194   }
195 
196   if ((w = firstword_lc(line, "ns")) ||
197       (w = firstword_lc(line, "nameserver"))) {
198      /* NS records */
199      unsigned char dn[DNS_MAXDN];
200      unsigned dnlen;
201      struct dsns *dsns, **dsnslp;
202      unsigned ttl;
203 
204 #ifndef INCOMPAT_0_99
205 #ifdef __GNUC__
206 /* some compilers don't understand #warning directive */
207 #warning NS record compatibility mode: remove for 1.0 final
208 #endif
209      struct dsns *dsns_first = 0;
210      unsigned cnt;
211      int newformat = 0;
212 #endif
213 
214     if (isdstype(ds->ds_type, acl))
215       return 0;	/* don't allow NSes for ACLs */
216 
217 #ifndef INCOMPAT_0_99
218      if (ds->ds_nsflags & DSF_NEWNS) return 1;
219      if (ds->ds_dsns) {
220        dsns = ds->ds_dsns;
221        while(dsns->dsns_next)
222          dsns = dsns->dsns_next;
223        dsnslp = &dsns->dsns_next;
224      }
225      else
226        dsnslp = &ds->ds_dsns;
227      cnt = 0;
228 #else
229      if (ds->ds_dsns) return 1; /* ignore 2nd nameserver line */
230      dsnslp = &ds->ds_dsns;
231 #endif
232 
233      /*XXX parse options (AndrewSN suggested `-bloat') here */
234 
235      if (!(w = parse_ttl(w, &ttl, ds->ds_ttl))) return 0;
236 
237      do {
238        if (*w == '-') {
239          /* skip nameservers that start with `-' aka 'commented-out' */
240          do ++w; while (*w && !ISSPACE(*w));
241          SKIPSPACE(w);
242 #ifndef INCOMPAT_0_99
243 	 newformat = 1;
244 #endif
245          continue;
246        }
247        if (!(w = parse_dn(w, dn, &dnlen))) return 0;
248        dsns = (struct dsns*)
249          mp_alloc(ds->ds_mp, sizeof(struct dsns) + dnlen - 1, 1);
250        if (!dsns) return -1;
251        memcpy(dsns->dsns_dn, dn, dnlen);
252        *dsnslp = dsns;
253        dsnslp = &dsns->dsns_next;
254        *dsnslp = NULL;
255 #ifndef INCOMPAT_0_99
256        if (!cnt++)
257          dsns_first = dsns;
258 #endif
259      } while(*w);
260 
261 #ifndef INCOMPAT_0_99
262      if (cnt > 1 || newformat) {
263        ds->ds_nsflags |= DSF_NEWNS;
264        ds->ds_dsns = dsns_first; /* throw away all NS recs */
265      }
266      else if (dsns_first != ds->ds_dsns && !(ds->ds_nsflags & DSF_NSWARN)) {
267        dswarn(dsc, "compatibility mode: specify all NS records in ONE line");
268        ds->ds_nsflags |= DSF_NSWARN;
269      }
270      if (!ds->ds_nsttl || ds->ds_nsttl > ttl)
271        ds->ds_nsttl = ttl;
272 #else
273      ds->ds_nsttl = ttl;
274 #endif
275     return 1;
276   }
277 
278   if ((w = firstword_lc(line, "ttl"))) {
279     unsigned ttl;
280     if (!(w = parse_ttl(w, &ttl, def_ttl))) return 0;
281     if (*w) return 0;
282     if (dsc->dsc_subset) dsc->dsc_subset->ds_ttl = ttl;
283     else ds->ds_ttl = ttl;
284     return 1;
285   }
286 
287   if ((w = firstword_lc(line, "maxrange4"))) {
288     unsigned r;
289     int cidr;
290     if (*w == '/') cidr = 1, ++w;
291     else cidr = 0;
292     if (!(w = parse_uint32(w, &r)) || *w || !r)
293       return 0;
294     if (cidr) {
295       if (r > 32) return 0;
296       r = ~ip4mask(r) + 1;
297     }
298     if (dsc->dsc_ip4maxrange && dsc->dsc_ip4maxrange < r)
299       dswarn(dsc, "ignoring attempt to increase $MAXRANGE4 from %u to %u",
300              dsc->dsc_ip4maxrange, r);
301     else
302       dsc->dsc_ip4maxrange = r;
303     return 1;
304   }
305 
306   if (((*(w = line) >= '0' && *w <= '9') || *w == '=') && ISSPACE(w[1])) {
307     /* substitution vars */
308     unsigned n = w[0] == '=' ? SUBST_BASE_TEMPLATE : w[0] - '0';
309     if (dsc->dsc_subset) ds = dsc->dsc_subset;
310     if (ds->ds_subst[n]) return 1; /* ignore second assignment */
311     w += 2;
312     SKIPSPACE(w);
313     if (!*w) return 0;
314     if (!(ds->ds_subst[n] = mp_strdup(ds->ds_mp, w))) return 0;
315     return 1;
316   }
317 
318   if ((w = firstword_lc(line, "dataset"))) {
319     if (!isdstype(ds->ds_type, combined))
320       return 0;	/* $dataset is only allowed for combined dataset */
321     return ds_combined_newset(ds, w, dsc);
322   }
323 
324   if ((w = firstword_lc(line, "timestamp"))) {
325     time_t stamp, expires;
326 
327     if (!(w = parse_timestamp(w, &stamp))) return 0;
328     if (!*w)
329       expires = 0;
330     else if (*w == '+') {       /* relative */
331       unsigned n;
332       if (!(w = parse_time(w + 1, &n)) || *w) return 0;
333       if (!stamp || !n) return 0;
334       expires = stamp + n;
335       if (expires < 0 || expires - (time_t)n != stamp) return 0;
336     }
337     else {
338       if (!(w = parse_timestamp(w, &expires)) || *w) return 0;
339     }
340     if (stamp) {
341       time_t now = time(NULL);
342       if (stamp > now) {
343         dslog(LOG_ERR, dsc,
344               "data timestamp is %u sec in the future, aborting loading",
345               (unsigned)(stamp - now));
346         return -1;
347       }
348     }
349     if (expires &&
350         (!ds->ds_expires || ds->ds_expires > expires))
351       ds->ds_expires = expires;
352     return 1;
353   }
354 
355   return 0;
356 }
357 
358 static int
readdslines(struct istream * sp,struct dataset * ds,struct dsctx * dsc)359 readdslines(struct istream *sp, struct dataset *ds, struct dsctx *dsc) {
360   char *line, *eol;
361   int r;
362   int noeol = 0;
363   struct dataset *dscur = ds;
364   ds_linefn_t *linefn = dscur->ds_type->dst_linefn;
365 
366   while((r = istream_getline(sp, &line, '\n')) > 0) {
367     eol = line + r - 1;
368     if (noeol) {
369       if (*eol == '\n')
370         noeol = 0;
371       continue;
372     }
373     ++dsc->dsc_lineno;
374     if (*eol == '\n')
375       --eol;
376     else {
377       dswarn(dsc, "long line (truncated)");
378       noeol = 1; /* mark it to be read above */
379     }
380     SKIPSPACE(line);
381     while(eol >= line && ISSPACE(*eol))
382       --eol;
383     eol[1] = '\0';
384     if (line[0] == '$' ||
385         ((ISCOMMENT(line[0]) || line[0] == ':') && line[1] == '$')) {
386       int r = ds_special(ds, line[0] == '$' ? line + 1 : line + 2, dsc);
387       if (!r)
388         dswarn(dsc, "invalid or unrecognized special entry");
389       else if (r < 0)
390         return 0;
391       dscur = dsc->dsc_subset ? dsc->dsc_subset : ds;
392       linefn = dscur->ds_type->dst_linefn;
393       continue;
394     }
395     if (line[0] && !ISCOMMENT(line[0]))
396       if (!linefn(dscur, line, dsc))
397         return 0;
398   }
399   if (r < 0)
400     return -1;
401   if (noeol)
402     dslog(LOG_WARNING, dsc, "incomplete last line (ignored)");
403   return 1;
404 }
405 
freedataset(struct dataset * ds)406 static void freedataset(struct dataset *ds) {
407   ds->ds_type->dst_resetfn(ds->ds_dsd, 0);
408   mp_free(ds->ds_mp);
409   ds->ds_dssoa = NULL;
410   ds->ds_ttl = def_ttl;
411   ds->ds_dsns = NULL;
412   ds->ds_nsttl = 0;
413   ds->ds_expires = 0;
414 #ifndef INCOMPAT_0_99
415   ds->ds_nsflags = 0;
416 #endif
417   memset(ds->ds_subst, 0, sizeof(ds->ds_subst));
418 }
419 
loaddataset(struct dataset * ds)420 int loaddataset(struct dataset *ds) {
421   struct dsfile *dsf;
422   time_t stamp = 0;
423   struct istream is;
424   int fd;
425   int r;
426   struct stat st0, st1;
427   struct dsctx dsc;
428 
429   freedataset(ds);
430 
431   memset(&dsc, 0, sizeof(dsc));
432   dsc.dsc_ds = ds;
433 
434   for(dsf = ds->ds_dsf; dsf; dsf = dsf->dsf_next) {
435     dsc.dsc_fname = dsf->dsf_name;
436     fd = open(dsf->dsf_name, O_RDONLY);
437     if (fd < 0 || fstat(fd, &st0) < 0) {
438       dslog(LOG_ERR, &dsc, "unable to open file: %s", strerror(errno));
439       if (fd >= 0) close(fd);
440       goto fail;
441     }
442     ds->ds_type->dst_startfn(ds);
443     istream_init_fd(&is, fd);
444     if (istream_compressed(&is)) {
445       if (nouncompress) {
446         dslog(LOG_ERR, &dsc, "file is compressed, decompression disabled");
447         r = 0;
448       }
449       else {
450 #ifdef NO_ZLIB
451         dslog(LOG_ERR, &dsc,
452               "file is compressed, decompression is not compiled in");
453         r = 0;
454 #else
455         r = istream_uncompress_setup(&is);
456           /* either 1 or -1 but not 0 */
457 #endif
458       }
459     }
460     else
461       r = 1;
462     if (r > 0) r = readdslines(&is, ds, &dsc);
463     if (r > 0) r = fstat(fd, &st1) < 0 ? -1 : 1;
464     dsc.dsc_lineno = 0;
465     istream_destroy(&is);
466     close(fd);
467     if (!r)
468       goto fail;
469     if (r < 0) {
470       dslog(LOG_ERR, &dsc, "error reading file: %s", strerror(errno));
471       goto fail;
472     }
473     if (st0.st_mtime != st1.st_mtime ||
474         st0.st_size  != st1.st_size) {
475       dslog(LOG_ERR, &dsc,
476             "file changed while we where reading it, data load aborted");
477       dslog(LOG_ERR, &dsc,
478             "do not write data files directly, "
479             "use temp file and rename(2) instead");
480       goto fail;
481     }
482     dsf->dsf_stamp = st0.st_mtime;
483     dsf->dsf_size  = st0.st_size;
484     if (dsf->dsf_stamp > stamp)
485       stamp = dsf->dsf_stamp;
486   }
487   ds->ds_stamp = stamp;
488   dsc.dsc_fname = NULL;
489 
490   ds->ds_type->dst_finishfn(ds, &dsc);
491 
492   return 1;
493 
494 fail:
495   freedataset(ds);
496   for (dsf = ds->ds_dsf; dsf; dsf = dsf->dsf_next)
497     dsf->dsf_stamp = 0;
498   ds->ds_stamp = 0;
499   return 0;
500 }
501 
502 /* find next dataset which needs reloading */
nextdataset2reload(struct dataset * ds)503 struct dataset *nextdataset2reload(struct dataset *ds) {
504   struct dsfile *dsf;
505   for (ds = ds ? ds->ds_next : ds_list; ds; ds = ds->ds_next)
506     for(dsf = ds->ds_dsf; dsf; dsf = dsf->dsf_next) {
507       struct stat st;
508       if (stat(dsf->dsf_name, &st) < 0)
509         return ds;
510       if (dsf->dsf_stamp != st.st_mtime ||
511           dsf->dsf_size  != st.st_size)
512         return ds;
513     }
514   return NULL;
515 }
516 
517 #ifndef NO_MASTER_DUMP
dumpzone(const struct zone * z,FILE * f)518 void dumpzone(const struct zone *z, FILE *f) {
519   const struct dslist *dsl;
520   { /* zone header */
521     char name[DNS_MAXDOMAIN+1];
522     const unsigned char *const *nsdna = z->z_nsdna;
523     const struct dssoa *dssoa = z->z_dssoa;
524     unsigned nns = z->z_nns;
525     unsigned n;
526     dns_dntop(z->z_dn, name, sizeof(name));
527     fprintf(f, "$ORIGIN\t%s.\n", name);
528     if (z->z_dssoa) {
529       fprintf(f, "@\t%u\tSOA", dssoa->dssoa_ttl);
530       dns_dntop(dssoa->dssoa_odn, name, sizeof(name));
531       fprintf(f, "\t%s.", name);
532       dns_dntop(dssoa->dssoa_pdn, name, sizeof(name));
533       fprintf(f, "\t%s.", name);
534       fprintf(f, "\t(%u %u %u %u %u)\n",
535           dssoa->dssoa_serial ? dssoa->dssoa_serial : z->z_stamp,
536           unpack32(dssoa->dssoa_n+0),
537           unpack32(dssoa->dssoa_n+4),
538           unpack32(dssoa->dssoa_n+8),
539           unpack32(dssoa->dssoa_n+12));
540     }
541     for(n = 0; n < nns; ++n) {
542       dns_dntop(nsdna[n], name, sizeof(name));
543       fprintf(f, "\t%u\tNS\t%s.\n", z->z_nsttl, name);
544     }
545   }
546   for (dsl = z->z_dsl; dsl; dsl = dsl->dsl_next) {
547     fprintf(f, "$TTL %u\n", dsl->dsl_ds->ds_ttl);
548     dsl->dsl_ds->ds_type->dst_dumpfn(dsl->dsl_ds, z->z_dn, f);
549   }
550 }
551 #endif
552