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