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