1 /* Common utility routines for rbldnsd.
2  */
3 
4 #include <stdio.h>
5 #include <string.h>
6 #include <stdlib.h>
7 #include <stdarg.h>
8 #include <syslog.h>
9 #include <unistd.h>
10 #include <time.h>
11 #include "rbldnsd.h"
12 
13 #define digit(c) ((c) >= '0' && (c) <= '9')
14 #define d2n(c) ((unsigned)((c) - '0'))
15 
parse_uint32_s(char * s,unsigned * np)16 static char *parse_uint32_s(char *s, unsigned *np) {
17   unsigned char *t = (unsigned char*)s;
18   unsigned n = 0;
19   if (!digit(*t))
20     return NULL;
21   do {
22     if (n > 0xffffffffu / 10) return 0;
23     if (n * 10 > 0xffffffffu - d2n(*t)) return 0;
24     n = n * 10 + d2n(*t++);
25   } while(digit(*t));
26   *np = n;
27   return (char*)t;
28 }
29 
parse_uint32(char * s,unsigned * np)30 char *parse_uint32(char *s, unsigned *np) {
31   if (!(s = parse_uint32_s(s, np))) return NULL;
32   if (*s) {
33     if (!ISSPACE(*s)) return NULL;
34     ++s; SKIPSPACE(s);
35   }
36   return s;
37 }
38 
parse_uint32_nb(char * s,unsigned char nb[4])39 char *parse_uint32_nb(char *s, unsigned char nb[4]) {
40   unsigned n;
41   if (!(s = parse_uint32(s, &n))) return NULL;
42   PACK32(nb, n);
43   return s;
44 }
45 
parse_time(char * s,unsigned * tp)46 char *parse_time(char *s, unsigned *tp) {
47   unsigned m = 1;
48   if (!(s = parse_uint32_s(s, tp))) return NULL;
49   switch(*s) {
50     case 'w': case 'W': m *= 7;		/* week */
51     case 'd': case 'D': m *= 24;	/* day */
52     case 'h': case 'H': m *= 60;	/* hours */
53     case 'm': case 'M': m *= 60;	/* minues */
54       if (0xffffffffu / m < *tp) return NULL;
55       *tp *= m;
56     case 's': case 'S':			/* secounds */
57       ++s;
58       break;
59   }
60   if (*s && *s != ':') {
61     if (!ISSPACE(*s)) return NULL;
62     ++s; SKIPSPACE(s);
63   }
64   return s;
65 }
66 
parse_time_nb(char * s,unsigned char nb[4])67 char *parse_time_nb(char *s, unsigned char nb[4]) {
68   unsigned t;
69   if (!(s = parse_time(s, &t))) return NULL;
70   PACK32(nb, t);
71   return s;
72 }
73 
parse_ttl(char * s,unsigned * ttlp,unsigned defttl)74 char *parse_ttl(char *s, unsigned *ttlp, unsigned defttl) {
75   s = parse_time(s, ttlp);
76   if (*ttlp == 0)
77     *ttlp = defttl;
78   else if (min_ttl && *ttlp < min_ttl)
79     *ttlp = min_ttl;
80   else if (max_ttl && *ttlp > max_ttl)
81     *ttlp = max_ttl;
82   return s;
83 }
84 
85 static const unsigned char mday[12] = {
86   31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
87 };
88 
89 #define isleap(year) \
90   ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
91 
92 static char *
parse_tsp(char * s,unsigned * np,unsigned min,unsigned max,unsigned w)93 parse_tsp(char *s, unsigned *np, unsigned min, unsigned max, unsigned w) {
94   unsigned n = 0;
95   if (!digit(*s)) return NULL;
96   do n = n * 10 + d2n(*s++);
97   while(digit(*s) && --w);
98   if (n < min || n > max) return NULL;
99   if (*s == ':' || *s == '-') ++s;
100   *np = n;
101   return s;
102 }
103 
parse_timestamp(char * s,time_t * tsp)104 char *parse_timestamp(char *s, time_t *tsp) {
105   unsigned year, mon, day, hour, min, sec;
106 
107   if ((*s == '0' || *s == '-' || *s == ':') &&
108       (ISSPACE(s[1]) || !s[1])) {
109     *tsp = 0;
110     ++s;
111     SKIPSPACE(s);
112     return s;
113   }
114   if (!(s = parse_tsp(s, &year, 1970, 2038, 4))) return NULL;
115   if (!(s = parse_tsp(s, &mon, 1, 12, 2))) return NULL;
116   mon -= 1;
117   day = mon == 1 && isleap(year) ? 29 : mday[mon];
118   if (!(s = parse_tsp(s, &day, 1, day, 2))) return NULL;
119   hour = min = sec = 0;
120   if (*s && !ISSPACE(*s))
121     if (!(s = parse_tsp(s, &hour, 0, 23, 2))) return NULL;
122   if (*s && !ISSPACE(*s))
123     if (!(s = parse_tsp(s, &min, 0, 59, 2))) return NULL;
124   if (*s && !ISSPACE(*s))
125     if (!(s = parse_tsp(s, &sec, 0, 59, 2))) return NULL;
126   if (*s) {
127     if (!ISSPACE(*s)) return NULL;
128     ++s; SKIPSPACE(s);
129   }
130 
131   {
132     unsigned y4 = (year / 4) - !(year & 3);
133     unsigned y100 = y4 / 25;
134     unsigned y400 = y100 / 4;
135     day =
136       365 * (year - 1970) +
137       (y4 - 492) - (y100 - 19) + (y400 - 4) +
138       day - 1;
139     if (isleap(year) && mon > 1)
140       ++day;
141     while(mon)
142       day += mday[--mon];
143     *tsp = ((day * 24 + hour) * 60 + min) * 60 + sec;
144   }
145 
146   return s;
147 }
148 
parse_dn(char * s,unsigned char * dn,unsigned * dnlenp)149 char *parse_dn(char *s, unsigned char *dn, unsigned *dnlenp) {
150   char *n = s;
151   unsigned l;
152   while(*n && !ISSPACE(*n)) ++n;
153   if (*n) *n++ = '\0';
154   if (!*s) return NULL;
155   if ((l = dns_ptodn(s, dn, DNS_MAXDN)) == 0)
156     return NULL;
157   if (dnlenp) *dnlenp = l;
158   SKIPSPACE(n);
159   return n;
160 }
161 
parse_a_txt(char * str,const char ** rrp,const char * def_rr,struct dsctx * dsc)162 int parse_a_txt(char *str, const char **rrp, const char *def_rr,
163                 struct dsctx *dsc) {
164   char *rr;
165   static char rrbuf[4+256];	/*XXX static buffer */
166   if (*str == ':') {
167     ip4addr_t a;
168     int bits = ip4addr(str + 1, &a, &str);
169     if (!a || bits <= 0) {
170       dswarn(dsc, "invalid A RR");
171       return 0;
172     }
173     if (bits == 8)
174       a |= IP4A_LOOPBACK; /* only last digit in 127.0.0.x */
175     SKIPSPACE(str);
176     if (*str == ':') {	/* A+TXT */
177       ++str;
178       SKIPSPACE(str);
179       rr = str - 4;
180       PACK32(rr, a);
181     }
182     else if (*str) {
183       dswarn(dsc, "unrecognized value for an entry");
184       return 0;
185     }
186     else {	/* only A - take TXT from default entry */
187       unsigned tlen = strlen(def_rr+4);	/* tlen is <= 255 */
188       rr = rrbuf;
189       PACK32(rr, a);
190       memcpy(rr+4, def_rr+4, tlen+1);
191       *rrp = rr;
192       return tlen + 5;
193     }
194   }
195   else {
196     rr = str - 4;
197     memcpy(rr, def_rr, 4);
198   }
199   if (*str) {
200     unsigned len = strlen(str);
201     if (len > 255) {
202       dswarn(dsc, "TXT RR truncated to 255 bytes");
203       str += 255;
204     }
205     else
206       str += len;
207     *str = '\0';
208   }
209   *rrp = rr;
210   return 1 + (str - rr);
211 }
212 
unpack32(const unsigned char p[4])213 unsigned unpack32(const unsigned char p[4]) {
214   unsigned n = p[0];
215   n = (n << 8) | p[1];
216   n = (n << 8) | p[2];
217   n = (n << 8) | p[3];
218   return n;
219 }
220 
221 /* return pointer to the next word in line if first word is the same
222  * as word_lc (ignoring case), or NULL if not.
223  */
firstword_lc(char * line,const char * word_lc)224 char *firstword_lc(char *line, const char *word_lc) {
225   while(*word_lc)
226     if (dns_dnlc(*line) != *word_lc)
227       return NULL;
228     else
229       ++word_lc, ++line;
230   if (!ISSPACE(*line))
231     return NULL;
232   SKIPSPACE(line);
233  return line;
234 }
235 
236 /* implement TXT substitutions.
237  * `sb' is a buffer where the result will be stored -
238  * at least 255 + 3 characters long */
txtsubst(char sb[TXTBUFSIZ],const char * txt,const char * s0,const struct dataset * ds)239 int txtsubst(char sb[TXTBUFSIZ], const char *txt,
240 	     const char *s0, const struct dataset *ds) {
241   char *const *sn = ds->ds_subst;
242   unsigned sl;
243   char *const e = sb + 254;
244   char *lp = sb;
245   const char *s, *si, *sx;
246   if (txt[0] == '=')
247     sx = ++txt;
248   else if (sn[SUBST_BASE_TEMPLATE] && *sn[SUBST_BASE_TEMPLATE]) {
249     if (*txt) s0 = txt;
250     sx = s0;
251     txt = sn[SUBST_BASE_TEMPLATE];
252   }
253   else
254     sx = txt;
255   while(lp < e) {
256     if ((s = strchr(txt, '$')) == NULL)
257       s = (char*)txt + strlen(txt);
258     sl = s - txt;
259     if (lp + sl > e)
260       sl = e - lp;
261     memcpy(lp, txt, sl);
262     lp += sl;
263     if (!*s++) break;
264     if (*s == '$') { si = s++; sl = 1; }
265     else if (*s >= '0' && *s <= '9') { /* $n var */
266       si = sn[*s - '0'];
267       if (!si) { si = s - 1; sl = 2; }
268       else sl = strlen(si);
269       ++s;
270     }
271     else if (*s == '=') {
272       si = sx;
273       sl = strlen(si);
274       ++s;
275     }
276     else
277       sl = strlen(si = s0);
278     if (lp + sl > e) /* silently truncate TXT RR >255 bytes */
279       sl = e - lp;
280     memcpy(lp, si, sl);
281     lp += sl;
282     txt = s;
283   }
284   sl = lp - sb;
285   if (sl > 254) sl = 254;
286   return sl;
287 }
288 
289 #ifndef NO_MASTER_DUMP
290 
dump_ip4(ip4addr_t a,const char * rr,const struct dataset * ds,FILE * f)291 void dump_ip4(ip4addr_t a, const char *rr, const struct dataset *ds, FILE *f) {
292   char name[sizeof("255.255.254.255")];
293   sprintf(name, "%u.%u.%u.%u", a&255, (a>>8)&255, (a>>16)&255, (a>>24));
294   dump_a_txt(name, rr, ip4atos(a), ds, f);
295 }
296 
297 static void
dump_ip4octets(FILE * f,unsigned idx,ip4addr_t a,unsigned cnt,const char * rr,const struct dataset * ds)298 dump_ip4octets(FILE *f, unsigned idx, ip4addr_t a, unsigned cnt,
299 	       const char *rr, const struct dataset *ds) {
300   char name[16];
301   static const char * const fmt[4] = {
302      "%u.%u.%u.%u", "*.%u.%u.%u", "*.%u.%u", "*.%u"
303   };
304   const unsigned bits = 8 * idx;
305   for(;;) {
306     sprintf(name, fmt[idx], a&255, (a>>8)&255, (a>>16)&255, (a>>24));
307     dump_a_txt(name, rr, ip4atos(a<<bits), ds, f);
308     if (!--cnt)
309       break;
310     ++a;
311   }
312 }
313 
dump_ip4range(ip4addr_t a,ip4addr_t b,const char * rr,const struct dataset * ds,FILE * f)314 void dump_ip4range(ip4addr_t a, ip4addr_t b, const char *rr,
315 		   const struct dataset *ds, FILE *f) {
316 
317 #define fn(idx,start,count) \
318 	dump_ip4octets(f, idx, start, count, rr, ds)
319 #define ip4range_expand_octet(bits)               \
320   if ((a | 255u) >= b) {                          \
321     if (b - a == 255u)                            \
322       fn((bits>>3)+1, a>>8, 1);                   \
323     else                                          \
324       fn(bits>>3, a, b - a + 1);                  \
325     return;                                       \
326   }                                               \
327   if (a & 255u) {                                 \
328     fn(bits>>3, a, 256u - (a & 255u));            \
329     a = (a >> 8) + 1;                             \
330   }                                               \
331   else                                            \
332     a >>= 8;                                      \
333   if ((b & 255u) != 255u) {                       \
334     fn((bits>>3), (b & ~255u), (b&255u)+1);       \
335     b = (b >> 8) - 1;                             \
336   }                                               \
337   else                                            \
338     b >>= 8
339 
340   ip4range_expand_octet(0);
341   ip4range_expand_octet(8);
342   ip4range_expand_octet(16);
343   fn(3, a, b - a + 1);
344 
345 #undef fn
346 #undef ip4range_expand_octet
347 
348 }
349 
350 static inline unsigned
ip6nibble(const ip6oct_t addr[IP6ADDR_FULL],unsigned i)351 ip6nibble(const ip6oct_t addr[IP6ADDR_FULL], unsigned i)
352 {
353   ip6oct_t byte = addr[i / 2];
354   return (i % 2) ? (byte & 0xf) : (byte >> 4);
355 }
356 
357 /* format DNS name for ip6 address (with some nibbles possibly wild-carded) */
358 static const char *
ip6name(const ip6oct_t * addr,unsigned wild_nibbles)359 ip6name(const ip6oct_t *addr, unsigned wild_nibbles)
360 {
361   static char hexdigits[] = "0123456789abcdef";
362   static char name[IP6ADDR_FULL * 4 + 2] = "*";
363   char *np = name + 1;
364   unsigned n = 32 - wild_nibbles;
365 
366   /* don't write past end of buffer, even if passed invalid args */
367   if (n > 32) n = 32;
368   while (n-- > 0) {
369     *np++ = '.';
370     *np++ = hexdigits[ip6nibble(addr, n)];
371   }
372   *np = '\0';
373 
374   return wild_nibbles ? name : name + 2;
375 }
376 
377 /* dump an ip6 address, with some nibbles possible wild-carded */
378 void
dump_ip6(const ip6oct_t * addr,unsigned wild_nibbles,const char * rr,const struct dataset * ds,FILE * f)379 dump_ip6(const ip6oct_t *addr, unsigned wild_nibbles, const char *rr,
380          const struct dataset *ds, FILE *f)
381 {
382   const char *dns_name = ip6name(addr, wild_nibbles);
383   const char *ipsubst = NULL;
384 
385   if (rr) {
386     /* careful: addr may point to a short array (e.g. IP6ADDR_HALF) */
387     ipsubst = ip6atos(addr, IP6ADDR_FULL - wild_nibbles / 2);
388   }
389   dump_a_txt(dns_name, rr, ipsubst, ds, f);
390 }
391 
392 /* dump an ip6 address range.
393  *
394  * BEG is the first address in the range, END is one past the last
395  * address included in the range.  END = NULL means no end limit.
396  *
397  * NB: The semantics of END are different than for dump_ip4range!
398  */
399 void
dump_ip6range(const ip6oct_t * beg,const ip6oct_t * end,const char * rr,const struct dataset * ds,FILE * f)400 dump_ip6range(const ip6oct_t *beg, const ip6oct_t *end, const char *rr,
401               const struct dataset *ds, FILE *f)
402 {
403   ip6oct_t addr[IP6ADDR_FULL];
404 
405   memcpy(addr, beg, IP6ADDR_FULL);
406   while (1) {
407     unsigned nwild, i;
408     unsigned maxwild = 32;
409     if (end) {
410       /* find first nibble of end which is greater than addr */
411       for (i = 0; i < 32; i++) {
412         if (ip6nibble(end, i) != ip6nibble(addr, i))
413           break;
414       }
415       if (i == 32 || ip6nibble(end, i) < ip6nibble(addr, i))
416         return;                   /* end <= addr */
417       /* we can only wildcard after this nibble */
418       maxwild = 31 - i;
419     }
420     /* can only wildcard nibbles where we're starting from zero */
421     for (nwild = 0; nwild < maxwild; nwild++)
422       if (ip6nibble(addr, 31 - nwild) != 0)
423         break;
424 
425     dump_ip6(addr, nwild, rr, ds, f);
426 
427     /* advance address to one past end of wildcarded range */
428     /* Increment right-most non-wildcarded nibble */
429     i = 15 -  nwild / 2;
430     addr[i] += (nwild % 2) ? 0x10 : 0x01;
431     while (addr[i] == 0) {      /* propagate carry */
432       if (i == 0) return;       /* wrapped */
433       addr[--i]++;
434     }
435   }
436 }
437 
438 void
dump_a_txt(const char * name,const char * rr,const char * subst,const struct dataset * ds,FILE * f)439 dump_a_txt(const char *name, const char *rr,
440            const char *subst, const struct dataset *ds, FILE *f) {
441   if (!rr)
442     fprintf(f, "%s\tCNAME\texcluded\n", name);
443   else {
444     const unsigned char *a = (const unsigned char*)rr;
445     char sb[TXTBUFSIZ];
446     unsigned sl = txtsubst(sb, rr + 4, subst, ds);
447     fprintf(f, "%s\tA\t%u.%u.%u.%u\n", name, a[0], a[1], a[2], a[3]);
448     if (sl) {
449       char *p, *n;
450       sb[sl] = '\0';
451       fprintf(f, "\tTXT\t\"");
452       for(p = sb; (n = strchr(p, '"')) != NULL; p = n + 1) {
453         fwrite(p, 1, n - p, f);
454         putc('\\', f); putc('"', f);
455       }
456       fprintf(f, "%s\"\n", p);
457     }
458   }
459 }
460 
461 #endif
462 
emalloc(unsigned size)463 char *emalloc(unsigned size) {
464   void *ptr = malloc(size);
465   if (!ptr)
466     oom();
467   return ptr;
468 }
469 
ezalloc(unsigned size)470 char *ezalloc(unsigned size) {
471   void *ptr = calloc(1, size);
472   if (!ptr)
473     oom();
474   return ptr;
475 }
476 
erealloc(void * ptr,unsigned size)477 char *erealloc(void *ptr, unsigned size) {
478   void *nptr = realloc(ptr, size);
479   if (!nptr)
480     oom();
481   return nptr;
482 }
483 
ememdup(const void * buf,unsigned len)484 char *ememdup(const void *buf, unsigned len) {
485   char *b = emalloc(len);
486   if (b)
487     memcpy(b, buf, len);
488   return b;
489 }
490 
estrdup(const char * str)491 char *estrdup(const char *str) {
492   return ememdup(str, strlen(str) + 1);
493 }
494 
495 /* what a mess... this routine is to work around various snprintf
496  * implementations.  It never return <1 or value greather than
497  * size of buffer: i.e. it returns number of chars _actually written_
498  * to a buffer.
499  * Maybe replace this with an alternative (simplistic) implementation,
500  * only %d/%u/%s, with additional %S to print untrusted data replacing
501  * control chars with something sane, and to print `...' for arguments
502  * that aren't fit (e.g. "%.5s", "1234567" will print `12...') ?
503  */
504 
vssprintf(char * buf,int bufsz,const char * fmt,va_list ap)505 int vssprintf(char *buf, int bufsz, const char *fmt, va_list ap) {
506   int r = vsnprintf(buf, bufsz, fmt, ap);
507   return r < 0 ? 0 : r >= bufsz ? buf[bufsz-1] = '\0', bufsz - 1 : r;
508 }
509 
ssprintf(char * buf,int bufsz,const char * fmt,...)510 int ssprintf(char *buf, int bufsz, const char *fmt, ...) {
511   va_list ap;
512   va_start(ap, fmt);
513   bufsz = vssprintf(buf, bufsz, fmt, ap);
514   va_end(ap);
515   return bufsz;
516 }
517 
518 /* logging */
519 
520 static void
vdslog(int level,struct dsctx * dsc,const char * fmt,va_list ap)521 vdslog(int level, struct dsctx *dsc, const char *fmt, va_list ap) {
522   char buf[1024];
523   int pl, l;
524   if ((logto & LOGTO_STDOUT) ||
525       (level <= LOG_WARNING && (logto & LOGTO_STDERR)))
526     l = pl = ssprintf(buf, sizeof(buf), "%.30s: ", progname);
527   else if (logto & LOGTO_SYSLOG)
528     l = pl = 0;
529   else
530     return;
531   if (dsc) {
532     if (dsc->dsc_fname) {
533       l += ssprintf(buf + l, sizeof(buf) - l, "file %.60s", dsc->dsc_fname);
534       l += ssprintf(buf + l, sizeof(buf) - l,
535                     dsc->dsc_lineno ? "(%d): " : ": ", dsc->dsc_lineno);
536     }
537     else {
538       l += ssprintf(buf + l, sizeof(buf) - l, "%s:%.60s:",
539                     dsc->dsc_ds->ds_type->dst_name, dsc->dsc_ds->ds_spec);
540       if (dsc->dsc_subset) {
541         l += ssprintf(buf + l, sizeof(buf) - l, "%s:",
542                       dsc->dsc_subset->ds_type->dst_name);
543 	if (dsc->dsc_subset->ds_spec)
544           l += ssprintf(buf + l, sizeof(buf) - l, "%s:",
545                         dsc->dsc_subset->ds_spec);
546       }
547       l += ssprintf(buf + l, sizeof(buf) - l, " ");
548     }
549   }
550   l += vssprintf(buf + l, sizeof(buf) - l, fmt, ap);
551   if (logto & LOGTO_SYSLOG) {
552     fmt = buf + pl;
553     syslog(level, strchr(fmt, '%') ? "%s" : fmt, fmt);
554   }
555   buf[l++] = '\n';
556   if (level <= LOG_WARNING) {
557     if (logto & (LOGTO_STDERR|LOGTO_STDOUT))
558       write(2, buf, l);
559   }
560   else if (logto & LOGTO_STDOUT)
561     write(1, buf, l);
562 }
563 
dslog(int level,struct dsctx * dsc,const char * fmt,...)564 void dslog(int level, struct dsctx *dsc, const char *fmt, ...) {
565   va_list ap;
566   va_start(ap, fmt);
567   vdslog(level, dsc, fmt, ap);
568   va_end(ap);
569 }
570 
571 #define MAXWARN 5
572 
dswarn(struct dsctx * dsc,const char * fmt,...)573 void dswarn(struct dsctx *dsc, const char *fmt, ...) {
574   if (++dsc->dsc_warns <= MAXWARN) { /* prevent syslog flood */
575     va_list ap;
576     va_start(ap, fmt);
577     vdslog(LOG_WARNING, dsc, fmt, ap);
578     va_end(ap);
579   }
580 }
581 
dsloaded(struct dsctx * dsc,const char * fmt,...)582 void dsloaded(struct dsctx *dsc, const char *fmt, ...) {
583   va_list ap;
584   if (dsc->dsc_warns > MAXWARN)
585     dslog(LOG_WARNING, dsc, "%d more warnings suppressed",
586           dsc->dsc_warns - MAXWARN);
587   va_start(ap, fmt);
588   if (dsc->dsc_subset)
589      vdslog(LOG_INFO, dsc, fmt, ap);
590   else {
591     struct tm *tm = gmtime(&dsc->dsc_ds->ds_stamp);
592     char buf[128];
593     vssprintf(buf, sizeof(buf), fmt, ap);
594     dslog(LOG_INFO, dsc, "%04d%02d%02d %02d%02d%02d: %s",
595           tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
596           tm->tm_hour, tm->tm_min, tm->tm_sec,
597           buf);
598   }
599   va_end(ap);
600 }
601 
zlog(int level,const struct zone * zone,const char * fmt,...)602 void zlog(int level, const struct zone *zone, const char *fmt, ...) {
603   va_list ap;
604   char buf[128];
605   char name[DNS_MAXDOMAIN+1];
606 
607   va_start(ap, fmt);
608   vssprintf(buf, sizeof(buf), fmt, ap);
609   va_end(ap);
610   dns_dntop(zone->z_dn, name, sizeof(name));
611   dslog(level, 0, "zone %.70s: %s", name, buf);
612 }
613