xref: /plan9/sys/src/cmd/ndb/dblookup.c (revision c361718002866812b3dd559475f979bcacdb2c1e)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <ndb.h>
5 #include <ip.h>
6 #include "dns.h"
7 
8 enum {
9 	Nibwidth = 4,
10 	Nibmask = (1<<Nibwidth) - 1,
11 	V6maxrevdomdepth = 128 / Nibwidth,	/* bits / bits-per-nibble */
12 
13 	/*
14 	 * ttl for generated ptr records.  it was zero, which might seem
15 	 * like a good idea, but some dns implementations seem to be
16 	 * confused by a zero ttl, and instead of using the data and then
17 	 * discarding the RR, they conclude that they don't have valid data.
18 	 */
19 	Ptrttl = 120,
20 };
21 
22 static Ndb *db;
23 static Lock	dblock;
24 
25 static RR*	addrrr(Ndbtuple*, Ndbtuple*);
26 static RR*	cnamerr(Ndbtuple*, Ndbtuple*);
27 static void	createptrs(void);
28 static RR*	dblookup1(char*, int, int, int);
29 static RR*	doaxfr(Ndb*, char*);
30 static Ndbtuple*look(Ndbtuple*, Ndbtuple*, char*);
31 static RR*	mxrr(Ndbtuple*, Ndbtuple*);
32 static RR*	nsrr(Ndbtuple*, Ndbtuple*);
33 static RR*	nullrr(Ndbtuple*, Ndbtuple*);
34 static RR*	ptrrr(Ndbtuple*, Ndbtuple*);
35 static RR*	soarr(Ndbtuple*, Ndbtuple*);
36 static RR*	srvrr(Ndbtuple*, Ndbtuple*);
37 static RR*	txtrr(Ndbtuple*, Ndbtuple*);
38 
39 static int	implemented[Tall] =
40 {
41 	[Ta]		1,
42 	[Taaaa]		1,
43 	[Tcname]	1,
44 	[Tmx]		1,
45 	[Tns]		1,
46 	[Tnull]		1,
47 	[Tptr]		1,
48 	[Tsoa]		1,
49 	[Tsrv]		1,
50 	[Ttxt]		1,
51 };
52 
53 /* straddle server configuration */
54 static Ndbtuple *indoms, *innmsrvs, *outnmsrvs;
55 
56 static void
nstrcpy(char * to,char * from,int len)57 nstrcpy(char *to, char *from, int len)
58 {
59 	strncpy(to, from, len);
60 	to[len-1] = 0;
61 }
62 
63 int
opendatabase(void)64 opendatabase(void)
65 {
66 	char netdbnm[256];
67 	Ndb *xdb, *netdb;
68 
69 	if (db)
70 		return 0;
71 
72 	xdb = ndbopen(dbfile);		/* /lib/ndb */
73 
74 	snprint(netdbnm, sizeof netdbnm, "%s/ndb", mntpt);
75 	netdb = ndbopen(netdbnm);	/* /net/ndb */
76 	if(netdb)
77 		netdb->nohash = 1;
78 
79 	db = ndbcat(netdb, xdb);	/* both */
80 	return db? 0: -1;
81 }
82 
83 /*
84  *  lookup an RR in the network database, look for matches
85  *  against both the domain name and the wildcarded domain name.
86  *
87  *  the lock makes sure only one process can be accessing the data
88  *  base at a time.  This is important since there's a lot of
89  *  shared state there.
90  *
91  *  e.g. for x.research.bell-labs.com, first look for a match against
92  *       the x.research.bell-labs.com.  If nothing matches,
93  *	 try *.research.bell-labs.com.
94  */
95 RR*
dblookup(char * name,int class,int type,int auth,int ttl)96 dblookup(char *name, int class, int type, int auth, int ttl)
97 {
98 	int err;
99 	char *wild, *cp;
100 	char buf[256];
101 	RR *rp, *tp;
102 	DN *dp, *ndp;
103 
104 	/* so far only internet lookups are implemented */
105 	if(class != Cin)
106 		return 0;
107 
108 	err = Rname;
109 	rp = nil;
110 
111 	if(type == Tall){
112 		for (type = Ta; type < Tall; type++)
113 			if(implemented[type]) {
114 				tp = dblookup(name, class, type, auth, ttl);
115 				lock(&dnlock);
116 				rrcat(&rp, tp);
117 				unlock(&dnlock);
118 			}
119 		return rp;
120 	}
121 
122 	lock(&dblock);
123 	dp = dnlookup(name, class, 1);
124 
125 	if(opendatabase() < 0)
126 		goto out;
127 	if(dp->rr)
128 		err = 0;
129 
130 	/* first try the given name */
131 	if(cfg.cachedb)
132 		rp = rrlookup(dp, type, NOneg);
133 	else
134 		rp = dblookup1(name, type, auth, ttl);
135 	if(rp)
136 		goto out;
137 
138 	/* try lower case version */
139 	for(cp = name; *cp; cp++)
140 		*cp = tolower(*cp);
141 	if(cfg.cachedb)
142 		rp = rrlookup(dp, type, NOneg);
143 	else
144 		rp = dblookup1(name, type, auth, ttl);
145 	if(rp)
146 		goto out;
147 
148 	/* walk the domain name trying the wildcard '*' at each position */
149 	for(wild = strchr(name, '.'); wild; wild = strchr(wild+1, '.')){
150 		snprint(buf, sizeof buf, "*%s", wild);
151 		ndp = dnlookup(buf, class, 1);
152 		if(ndp->rr)
153 			err = 0;
154 		if(cfg.cachedb)
155 			rp = rrlookup(ndp, type, NOneg);
156 		else
157 			rp = dblookup1(buf, type, auth, ttl);
158 		if(rp)
159 			break;
160 	}
161 out:
162 	/* add owner to uncached records */
163 	if(rp)
164 		for(tp = rp; tp; tp = tp->next)
165 			tp->owner = dp;
166 	else {
167 		/*
168 		 * don't call it non-existent if it's not ours
169 		 * (unless we're a resolver).
170 		 */
171 		if(err == Rname && (!inmyarea(name) || cfg.resolver))
172 			err = Rserver;
173 		dp->respcode = err;
174 	}
175 
176 	unlock(&dblock);
177 	return rp;
178 }
179 
180 static ulong
intval(Ndbtuple * entry,Ndbtuple * pair,char * attr,ulong def)181 intval(Ndbtuple *entry, Ndbtuple *pair, char *attr, ulong def)
182 {
183 	Ndbtuple *t = look(entry, pair, attr);
184 
185 	return (t? strtoul(t->val, 0, 10): def);
186 }
187 
188 /*
189  *  lookup an RR in the network database
190  */
191 static RR*
dblookup1(char * name,int type,int auth,int ttl)192 dblookup1(char *name, int type, int auth, int ttl)
193 {
194 	Ndbtuple *t, *nt;
195 	RR *rp, *list, **l;
196 	Ndbs s;
197 	char dname[Domlen];
198 	char *attr;
199 	DN *dp;
200 	RR *(*f)(Ndbtuple*, Ndbtuple*);
201 	int found, x;
202 
203 	dp = nil;
204 	switch(type){
205 	case Tptr:
206 		attr = "ptr";
207 		f = ptrrr;
208 		break;
209 	case Ta:
210 		attr = "ip";
211 		f = addrrr;
212 		break;
213 	case Taaaa:
214 		attr = "ipv6";
215 		f = addrrr;
216 		break;
217 	case Tnull:
218 		attr = "nullrr";
219 		f = nullrr;
220 		break;
221 	case Tns:
222 		attr = "ns";
223 		f = nsrr;
224 		break;
225 	case Tsoa:
226 		attr = "soa";
227 		f = soarr;
228 		break;
229 	case Tsrv:
230 		attr = "srv";
231 		f = srvrr;
232 		break;
233 	case Tmx:
234 		attr = "mx";
235 		f = mxrr;
236 		break;
237 	case Tcname:
238 		attr = "cname";
239 		f = cnamerr;
240 		break;
241 	case Taxfr:
242 	case Tixfr:
243 		return doaxfr(db, name);
244 	default:
245 //		dnslog("dnlookup1(%s) bad type", name);
246 		return nil;
247 	}
248 
249 	/*
250 	 *  find a matching entry in the database
251 	 */
252 	t = nil;
253 	free(ndbgetvalue(db, &s, "dom", name, attr, &t));
254 
255 	/*
256 	 *  hack for local names
257 	 */
258 	if(t == nil && strchr(name, '.') == nil)
259 		free(ndbgetvalue(db, &s, "sys", name, attr, &t));
260 	if(t == nil) {
261 //		dnslog("dnlookup1(%s) name not found", name);
262 		return nil;
263 	}
264 
265 	/* search whole entry for default domain name */
266 	strncpy(dname, name, sizeof dname);
267 	for(nt = t; nt; nt = nt->entry)
268 		if(strcmp(nt->attr, "dom") == 0){
269 			nstrcpy(dname, nt->val, sizeof dname);
270 			break;
271 		}
272 
273 	/* ttl is maximum of soa minttl and entry's ttl ala rfc883 */
274 	x = intval(t, s.t, "ttl", 0);
275 	if(x > ttl)
276 		ttl = x;
277 
278 	/* default ttl is one day */
279 	if(ttl < 0)
280 		ttl = DEFTTL;
281 
282 	/*
283 	 *  The database has 2 levels of precedence; line and entry.
284 	 *  Pairs on the same line bind tighter than pairs in the
285 	 *  same entry, so we search the line first.
286 	 */
287 	found = 0;
288 	list = 0;
289 	l = &list;
290 	for(nt = s.t;; ){
291 		if(found == 0 && strcmp(nt->attr, "dom") == 0){
292 			nstrcpy(dname, nt->val, sizeof dname);
293 			found = 1;
294 		}
295 		if(cistrcmp(attr, nt->attr) == 0){
296 			rp = (*f)(t, nt);
297 			rp->auth = auth;
298 			rp->db = 1;
299 			if(ttl)
300 				rp->ttl = ttl;
301 			if(dp == nil)
302 				dp = dnlookup(dname, Cin, 1);
303 			rp->owner = dp;
304 			*l = rp;
305 			l = &rp->next;
306 			nt->ptr = 1;
307 		}
308 		nt = nt->line;
309 		if(nt == s.t)
310 			break;
311 	}
312 
313 	/* search whole entry */
314 	for(nt = t; nt; nt = nt->entry)
315 		if(nt->ptr == 0 && cistrcmp(attr, nt->attr) == 0){
316 			rp = (*f)(t, nt);
317 			rp->db = 1;
318 			if(ttl)
319 				rp->ttl = ttl;
320 			rp->auth = auth;
321 			if(dp == nil)
322 				dp = dnlookup(dname, Cin, 1);
323 			rp->owner = dp;
324 			*l = rp;
325 			l = &rp->next;
326 		}
327 	ndbfree(t);
328 
329 //	dnslog("dnlookup1(%s) -> %#p", name, list);
330 	return list;
331 }
332 
333 /*
334  *  make various types of resource records from a database entry
335  */
336 static RR*
addrrr(Ndbtuple * entry,Ndbtuple * pair)337 addrrr(Ndbtuple *entry, Ndbtuple *pair)
338 {
339 	RR *rp;
340 	uchar addr[IPaddrlen];
341 
342 	USED(entry);
343 	parseip(addr, pair->val);
344 	if(isv4(addr))
345 		rp = rralloc(Ta);
346 	else
347 		rp = rralloc(Taaaa);
348 	rp->ip = dnlookup(pair->val, Cin, 1);
349 	return rp;
350 }
351 static RR*
nullrr(Ndbtuple * entry,Ndbtuple * pair)352 nullrr(Ndbtuple *entry, Ndbtuple *pair)
353 {
354 	RR *rp;
355 
356 	USED(entry);
357 	rp = rralloc(Tnull);
358 	rp->null->data = (uchar*)estrdup(pair->val);
359 	rp->null->dlen = strlen((char*)rp->null->data);
360 	return rp;
361 }
362 /*
363  *  txt rr strings are at most 255 bytes long.  one
364  *  can represent longer strings by multiple concatenated
365  *  <= 255 byte ones.
366  */
367 static RR*
txtrr(Ndbtuple * entry,Ndbtuple * pair)368 txtrr(Ndbtuple *entry, Ndbtuple *pair)
369 {
370 	RR *rp;
371 	Txt *t, **l;
372 	int i, len, sofar;
373 
374 	USED(entry);
375 	rp = rralloc(Ttxt);
376 	l = &rp->txt;
377 	rp->txt = nil;
378 	len = strlen(pair->val);
379 	sofar = 0;
380 	while(len > sofar){
381 		t = emalloc(sizeof(*t));
382 		t->next = nil;
383 
384 		i = len-sofar;
385 		if(i > 255)
386 			i = 255;
387 
388 		t->p = emalloc(i+1);
389 		memmove(t->p, pair->val+sofar, i);
390 		t->p[i] = 0;
391 		sofar += i;
392 
393 		*l = t;
394 		l = &t->next;
395 	}
396 	return rp;
397 }
398 static RR*
cnamerr(Ndbtuple * entry,Ndbtuple * pair)399 cnamerr(Ndbtuple *entry, Ndbtuple *pair)
400 {
401 	RR *rp;
402 
403 	USED(entry);
404 	rp = rralloc(Tcname);
405 	rp->host = dnlookup(pair->val, Cin, 1);
406 	return rp;
407 }
408 static RR*
mxrr(Ndbtuple * entry,Ndbtuple * pair)409 mxrr(Ndbtuple *entry, Ndbtuple *pair)
410 {
411 	RR *rp;
412 
413 	rp = rralloc(Tmx);
414 	rp->host = dnlookup(pair->val, Cin, 1);
415 	rp->pref = intval(entry, pair, "pref", 1);
416 	return rp;
417 }
418 static RR*
nsrr(Ndbtuple * entry,Ndbtuple * pair)419 nsrr(Ndbtuple *entry, Ndbtuple *pair)
420 {
421 	RR *rp;
422 	Ndbtuple *t;
423 
424 	rp = rralloc(Tns);
425 	rp->host = dnlookup(pair->val, Cin, 1);
426 	t = look(entry, pair, "soa");
427 	if(t && t->val[0] == 0)
428 		rp->local = 1;
429 	return rp;
430 }
431 static RR*
ptrrr(Ndbtuple * entry,Ndbtuple * pair)432 ptrrr(Ndbtuple *entry, Ndbtuple *pair)
433 {
434 	RR *rp;
435 
436 	USED(entry);
437 	rp = rralloc(Tns);
438 	rp->ptr = dnlookup(pair->val, Cin, 1);
439 	return rp;
440 }
441 static RR*
soarr(Ndbtuple * entry,Ndbtuple * pair)442 soarr(Ndbtuple *entry, Ndbtuple *pair)
443 {
444 	RR *rp;
445 	Ndbtuple *ns, *mb, *t;
446 	char mailbox[Domlen];
447 	Ndb *ndb;
448 	char *p;
449 
450 	rp = rralloc(Tsoa);
451 	rp->soa->serial = 1;
452 	for(ndb = db; ndb; ndb = ndb->next)
453 		if(ndb->mtime > rp->soa->serial)
454 			rp->soa->serial = ndb->mtime;
455 
456 	rp->soa->retry  = intval(entry, pair, "retry", Hour);
457 	rp->soa->expire = intval(entry, pair, "expire", Day);
458 	rp->soa->minttl = intval(entry, pair, "ttl", Day);
459 	rp->soa->refresh = intval(entry, pair, "refresh", Day);
460 	rp->soa->serial = intval(entry, pair, "serial", rp->soa->serial);
461 
462 	ns = look(entry, pair, "ns");
463 	if(ns == nil)
464 		ns = look(entry, pair, "dom");
465 	rp->host = dnlookup(ns->val, Cin, 1);
466 
467 	/* accept all of:
468 	 *  mbox=person
469 	 *  mbox=person@machine.dom
470 	 *  mbox=person.machine.dom
471 	 */
472 	mb = look(entry, pair, "mbox");
473 	if(mb == nil)
474 		mb = look(entry, pair, "mb");
475 	if(mb)
476 		if(strchr(mb->val, '.')) {
477 			p = strchr(mb->val, '@');
478 			if(p != nil)
479 				*p = '.';
480 			rp->rmb = dnlookup(mb->val, Cin, 1);
481 		} else {
482 			snprint(mailbox, sizeof mailbox, "%s.%s",
483 				mb->val, ns->val);
484 			rp->rmb = dnlookup(mailbox, Cin, 1);
485 		}
486 	else {
487 		snprint(mailbox, sizeof mailbox, "postmaster.%s", ns->val);
488 		rp->rmb = dnlookup(mailbox, Cin, 1);
489 	}
490 
491 	/*
492 	 *  hang dns slaves off of the soa.  this is
493 	 *  for managing the area.
494 	 */
495 	for(t = entry; t != nil; t = t->entry)
496 		if(strcmp(t->attr, "dnsslave") == 0)
497 			addserver(&rp->soa->slaves, t->val);
498 
499 	return rp;
500 }
501 
502 static RR*
srvrr(Ndbtuple * entry,Ndbtuple * pair)503 srvrr(Ndbtuple *entry, Ndbtuple *pair)
504 {
505 	RR *rp;
506 
507 	rp = rralloc(Tsrv);
508 	rp->host = dnlookup(pair->val, Cin, 1);
509 	rp->srv->pri = intval(entry, pair, "pri", 0);
510 	rp->srv->weight = intval(entry, pair, "weight", 0);
511 	/* TODO: translate service name to port # */
512 	rp->port = intval(entry, pair, "port", 0);
513 	return rp;
514 }
515 
516 /*
517  *  Look for a pair with the given attribute.  look first on the same line,
518  *  then in the whole entry.
519  */
520 static Ndbtuple*
look(Ndbtuple * entry,Ndbtuple * line,char * attr)521 look(Ndbtuple *entry, Ndbtuple *line, char *attr)
522 {
523 	Ndbtuple *nt;
524 
525 	/* first look on same line (closer binding) */
526 	for(nt = line;;){
527 		if(cistrcmp(attr, nt->attr) == 0)
528 			return nt;
529 		nt = nt->line;
530 		if(nt == line)
531 			break;
532 	}
533 	/* search whole tuple */
534 	for(nt = entry; nt; nt = nt->entry)
535 		if(cistrcmp(attr, nt->attr) == 0)
536 			return nt;
537 	return 0;
538 }
539 
540 static RR**
linkrr(RR * rp,DN * dp,RR ** l)541 linkrr(RR *rp, DN *dp, RR **l)
542 {
543 	rp->owner = dp;
544 	rp->auth = 1;
545 	rp->db = 1;
546 	*l = rp;
547 	return &rp->next;
548 }
549 
550 /* these are answered specially by the tcp version */
551 static RR*
doaxfr(Ndb * db,char * name)552 doaxfr(Ndb *db, char *name)
553 {
554 	USED(db, name);
555 	return 0;
556 }
557 
558 
559 /*
560  *  read the all the soa's from the database to determine area's.
561  *  this is only used when we're not caching the database.
562  */
563 static void
dbfile2area(Ndb * db)564 dbfile2area(Ndb *db)
565 {
566 	Ndbtuple *t;
567 
568 	if(debug)
569 		dnslog("rereading %s", db->file);
570 	Bseek(&db->b, 0, 0);
571 	while(t = ndbparse(db))
572 		ndbfree(t);
573 }
574 
575 /*
576  *  read the database into the cache
577  */
578 static void
dbpair2cache(DN * dp,Ndbtuple * entry,Ndbtuple * pair)579 dbpair2cache(DN *dp, Ndbtuple *entry, Ndbtuple *pair)
580 {
581 	RR *rp;
582 	static ulong ord;
583 
584 	rp = 0;
585 	if(cistrcmp(pair->attr, "ip") == 0 ||
586 	   cistrcmp(pair->attr, "ipv6") == 0){
587 		dp->ordinal = ord++;
588 		rp = addrrr(entry, pair);
589 	} else 	if(cistrcmp(pair->attr, "ns") == 0)
590 		rp = nsrr(entry, pair);
591 	else if(cistrcmp(pair->attr, "soa") == 0) {
592 		rp = soarr(entry, pair);
593 		addarea(dp, rp, pair);
594 	} else if(cistrcmp(pair->attr, "mx") == 0)
595 		rp = mxrr(entry, pair);
596 	else if(cistrcmp(pair->attr, "srv") == 0)
597 		rp = srvrr(entry, pair);
598 	else if(cistrcmp(pair->attr, "cname") == 0)
599 		rp = cnamerr(entry, pair);
600 	else if(cistrcmp(pair->attr, "nullrr") == 0)
601 		rp = nullrr(entry, pair);
602 	else if(cistrcmp(pair->attr, "txtrr") == 0)
603 		rp = txtrr(entry, pair);
604 	if(rp == nil)
605 		return;
606 
607 	rp->owner = dp;
608 	dnagenever(dp, 1);
609 	rp->db = 1;
610 	rp->ttl = intval(entry, pair, "ttl", rp->ttl);
611 	rrattach(rp, Notauthoritative);
612 }
613 static void
dbtuple2cache(Ndbtuple * t)614 dbtuple2cache(Ndbtuple *t)
615 {
616 	Ndbtuple *et, *nt;
617 	DN *dp;
618 
619 	for(et = t; et; et = et->entry)
620 		if(strcmp(et->attr, "dom") == 0){
621 			dp = dnlookup(et->val, Cin, 1);
622 
623 			/* first same line */
624 			for(nt = et->line; nt != et; nt = nt->line){
625 				dbpair2cache(dp, t, nt);
626 				nt->ptr = 1;
627 			}
628 
629 			/* then rest of entry */
630 			for(nt = t; nt; nt = nt->entry){
631 				if(nt->ptr == 0)
632 					dbpair2cache(dp, t, nt);
633 				nt->ptr = 0;
634 			}
635 		}
636 }
637 static void
dbfile2cache(Ndb * db)638 dbfile2cache(Ndb *db)
639 {
640 	Ndbtuple *t;
641 
642 	if(debug)
643 		dnslog("rereading %s", db->file);
644 	Bseek(&db->b, 0, 0);
645 	while(t = ndbparse(db)){
646 		dbtuple2cache(t);
647 		ndbfree(t);
648 	}
649 }
650 
651 /* called with dblock held */
652 static void
loaddomsrvs(void)653 loaddomsrvs(void)
654 {
655 	Ndbs s;
656 
657 	if (!cfg.inside || !cfg.straddle || !cfg.serve)
658 		return;
659 	if (indoms) {
660 		ndbfree(indoms);
661 		ndbfree(innmsrvs);
662 		ndbfree(outnmsrvs);
663 		indoms = innmsrvs = outnmsrvs = nil;
664 	}
665 	if (db == nil)
666 		opendatabase();
667 	free(ndbgetvalue(db, &s, "sys", "inside-dom", "dom", &indoms));
668 	free(ndbgetvalue(db, &s, "sys", "inside-ns",  "ip",  &innmsrvs));
669 	free(ndbgetvalue(db, &s, "sys", "outside-ns", "ip",  &outnmsrvs));
670 	dnslog("[%d] ndb changed: reloaded inside-dom, inside-ns, outside-ns",
671 		getpid());
672 }
673 
674 void
db2cache(int doit)675 db2cache(int doit)
676 {
677 	ulong youngest;
678 	Ndb *ndb;
679 	Dir *d;
680 	static ulong lastcheck, lastyoungest;
681 
682 	/* no faster than once every 2 minutes */
683 	if(now < lastcheck + 2*Min && !doit)
684 		return;
685 
686 	refresh_areas(owned);
687 
688 	lock(&dblock);
689 
690 	if(opendatabase() < 0){
691 		unlock(&dblock);
692 		return;
693 	}
694 
695 	/*
696 	 *  file may be changing as we are reading it, so loop till
697 	 *  mod times are consistent.
698 	 *
699 	 *  we don't use the times in the ndb records because they may
700 	 *  change outside of refreshing our cached knowledge.
701 	 */
702 	for(;;){
703 		lastcheck = now;
704 		youngest = 0;
705 		for(ndb = db; ndb; ndb = ndb->next)
706 			/* dirfstat avoids walking the mount table each time */
707 			if((d = dirfstat(Bfildes(&ndb->b))) != nil ||
708 			   (d = dirstat(ndb->file)) != nil){
709 				if(d->mtime > youngest)
710 					youngest = d->mtime;
711 				free(d);
712 			}
713 		if(!doit && youngest == lastyoungest)
714 			break;
715 
716 		/* forget our area definition */
717 		freearea(&owned);
718 		freearea(&delegated);
719 
720 		/* reopen all the files (to get oldest for time stamp) */
721 		for(ndb = db; ndb; ndb = ndb->next)
722 			ndbreopen(ndb);
723 
724 		/* reload straddle-server configuration */
725 		loaddomsrvs();
726 
727 		if(cfg.cachedb){
728 			/* mark all db records as timed out */
729 			dnagedb();
730 
731 			/* read in new entries */
732 			for(ndb = db; ndb; ndb = ndb->next)
733 				dbfile2cache(ndb);
734 
735 			/* mark as authoritative anything in our domain */
736 			dnauthdb();
737 
738 			/* remove old entries */
739 			dnageall(1);
740 		} else
741 			/* read all the soa's to get database defaults */
742 			for(ndb = db; ndb; ndb = ndb->next)
743 				dbfile2area(ndb);
744 
745 		doit = 0;
746 		lastyoungest = youngest;
747 		createptrs();
748 	}
749 
750 	unlock(&dblock);
751 }
752 
753 void
dnforceage(void)754 dnforceage(void)
755 {
756 	lock(&dblock);
757 	dnageall(1);
758 	unlock(&dblock);
759 }
760 
761 extern uchar	ipaddr[IPaddrlen];	/* my ip address */
762 
763 /*
764  *  get all my xxx
765  *  caller ndbfrees the result
766  */
767 Ndbtuple*
lookupinfo(char * attr)768 lookupinfo(char *attr)
769 {
770 	char buf[64];
771 	char *a[2];
772 	Ndbtuple *t;
773 
774 	snprint(buf, sizeof buf, "%I", ipaddr);
775 	a[0] = attr;
776 
777 	lock(&dblock);
778 	if(opendatabase() < 0){
779 		unlock(&dblock);
780 		return nil;
781 	}
782 	t = ndbipinfo(db, "ip", buf, a, 1);
783 	unlock(&dblock);
784 	return t;
785 }
786 
787 char *localservers =	  "local#dns#servers";
788 char *localserverprefix = "local#dns#server";
789 
790 /*
791  *  return non-zero if this is a bad delegation
792  */
793 int
baddelegation(RR * rp,RR * nsrp,uchar * addr)794 baddelegation(RR *rp, RR *nsrp, uchar *addr)
795 {
796 	Ndbtuple *nt;
797 	static int whined;
798 	static Ndbtuple *t;
799 
800 	if(t == nil)
801 		t = lookupinfo("dom");
802 	if(t == nil)
803 		return 0;
804 
805 	for(; rp; rp = rp->next){
806 		if(rp->type != Tns)
807 			continue;
808 
809 		/* see if delegation is looping */
810 		if(nsrp)
811 		if(rp->owner != nsrp->owner)
812 		if(subsume(rp->owner->name, nsrp->owner->name) &&
813 		   strcmp(nsrp->owner->name, localservers) != 0){
814 			dnslog("delegation loop %R -> %R from %I",
815 				nsrp, rp, addr);
816 			return 1;
817 		}
818 
819 		/* see if delegating to us what we don't own */
820 		for(nt = t; nt != nil; nt = nt->entry)
821 			if(rp->host && cistrcmp(rp->host->name, nt->val) == 0)
822 				break;
823 		if(nt != nil && !inmyarea(rp->owner->name)){
824 			if (!whined) {
825 				whined = 1;
826 				dnslog("bad delegation %R from %I; "
827 					"no further logging of them", rp, addr);
828 			}
829 			return 1;
830 		}
831 	}
832 
833 	return 0;
834 }
835 
836 int
myaddr(char * addr)837 myaddr(char *addr)
838 {
839 	char *name, *line, *sp;
840 	char buf[64];
841 	Biobuf *bp;
842 
843 	snprint(buf, sizeof buf, "%I", ipaddr);
844 	if (strcmp(addr, buf) == 0) {
845 		dnslog("rejecting my ip %s as local dns server", addr);
846 		return 1;
847 	}
848 
849 	name = smprint("%s/ipselftab", mntpt);
850 	bp = Bopen(name, OREAD);
851 	free(name);
852 	if (bp != nil) {
853 		while ((line = Brdline(bp, '\n')) != nil) {
854 			line[Blinelen(bp) - 1] = '\0';
855 			sp = strchr(line, ' ');
856 			if (sp) {
857 				*sp = '\0';
858 				if (strcmp(addr, line) == 0) {
859 					dnslog("rejecting my ip %s as local dns server",
860 						addr);
861 					return 1;
862 				}
863 			}
864 		}
865 		Bterm(bp);
866 	}
867 	return 0;
868 }
869 
870 static char *locdns[20];
871 static QLock locdnslck;
872 
873 static void
addlocaldnsserver(DN * dp,int class,char * ipaddr,int i)874 addlocaldnsserver(DN *dp, int class, char *ipaddr, int i)
875 {
876 	int n;
877 	DN *nsdp;
878 	RR *rp;
879 	char buf[32];
880 	uchar ip[IPaddrlen];
881 
882 	/* reject our own ip addresses so we don't query ourselves via udp */
883 	if (myaddr(ipaddr))
884 		return;
885 
886 	qlock(&locdnslck);
887 	for (n = 0; n < i && n < nelem(locdns) && locdns[n]; n++)
888 		if (strcmp(locdns[n], ipaddr) == 0) {
889 			dnslog("rejecting duplicate local dns server ip %s",
890 				ipaddr);
891 			qunlock(&locdnslck);
892 			return;
893 		}
894 	if (n < nelem(locdns))
895 		if (locdns[n] == nil || ++n < nelem(locdns))
896 			locdns[n] = strdup(ipaddr); /* remember 1st few local ns */
897 	qunlock(&locdnslck);
898 
899 	/* ns record for name server, make up an impossible name */
900 	rp = rralloc(Tns);
901 	snprint(buf, sizeof buf, "%s%d", localserverprefix, i);
902 	nsdp = dnlookup(buf, class, 1);
903 	rp->host = nsdp;
904 	rp->owner = dp;			/* e.g., local#dns#servers */
905 	rp->local = 1;
906 	rp->db = 1;
907 //	rp->ttl = 10*Min;		/* seems too short */
908 	rp->ttl = (1UL<<31)-1;
909 	rrattach(rp, Authoritative);	/* will not attach rrs in my area */
910 
911 	/* A or AAAA record */
912 	if (parseip(ip, ipaddr) >= 0 && isv4(ip))
913 		rp = rralloc(Ta);
914 	else
915 		rp = rralloc(Taaaa);
916 	rp->ip = dnlookup(ipaddr, class, 1);
917 	rp->owner = nsdp;
918 	rp->local = 1;
919 	rp->db = 1;
920 //	rp->ttl = 10*Min;		/* seems too short */
921 	rp->ttl = (1UL<<31)-1;
922 	rrattach(rp, Authoritative);	/* will not attach rrs in my area */
923 
924 	dnslog("added local dns server %s at %s", buf, ipaddr);
925 }
926 
927 /*
928  *  return list of dns server addresses to use when
929  *  acting just as a resolver.
930  */
931 RR*
dnsservers(int class)932 dnsservers(int class)
933 {
934 	int i, n;
935 	char *p;
936 	char *args[5];
937 	Ndbtuple *t, *nt;
938 	RR *nsrp;
939 	DN *dp;
940 
941 	dp = dnlookup(localservers, class, 1);
942 	nsrp = rrlookup(dp, Tns, NOneg);
943 	if(nsrp != nil)
944 		return nsrp;
945 
946 	p = getenv("DNSSERVER");		/* list of ip addresses */
947 	if(p != nil){
948 		n = tokenize(p, args, nelem(args));
949 		for(i = 0; i < n; i++)
950 			addlocaldnsserver(dp, class, args[i], i);
951 		free(p);
952 	} else {
953 		t = lookupinfo("@dns");		/* @dns=ip1 @dns=ip2 ... */
954 		if(t == nil)
955 			return nil;
956 		i = 0;
957 		for(nt = t; nt != nil; nt = nt->entry){
958 			addlocaldnsserver(dp, class, nt->val, i);
959 			i++;
960 		}
961 		ndbfree(t);
962 	}
963 
964 	return rrlookup(dp, Tns, NOneg);
965 }
966 
967 static void
addlocaldnsdomain(DN * dp,int class,char * domain)968 addlocaldnsdomain(DN *dp, int class, char *domain)
969 {
970 	RR *rp;
971 
972 	/* ptr record */
973 	rp = rralloc(Tptr);
974 	rp->ptr = dnlookup(domain, class, 1);
975 	rp->owner = dp;
976 	rp->db = 1;
977 	rp->ttl = 10*Min;
978 	rrattach(rp, Authoritative);
979 }
980 
981 /*
982  *  return list of domains to use when resolving names without '.'s
983  */
984 RR*
domainlist(int class)985 domainlist(int class)
986 {
987 	Ndbtuple *t, *nt;
988 	RR *rp;
989 	DN *dp;
990 
991 	dp = dnlookup("local#dns#domains", class, 1);
992 	rp = rrlookup(dp, Tptr, NOneg);
993 	if(rp != nil)
994 		return rp;
995 
996 	t = lookupinfo("dnsdomain");
997 	if(t == nil)
998 		return nil;
999 	for(nt = t; nt != nil; nt = nt->entry)
1000 		addlocaldnsdomain(dp, class, nt->val);
1001 	ndbfree(t);
1002 
1003 	return rrlookup(dp, Tptr, NOneg);
1004 }
1005 
1006 char *v4ptrdom = ".in-addr.arpa";
1007 char *v6ptrdom = ".ip6.arpa";		/* ip6.int deprecated, rfc 3152 */
1008 
1009 char *attribs[] = {
1010 	"ipmask",
1011 	0
1012 };
1013 
1014 /*
1015  *  create ptrs that are in our v4 areas
1016  */
1017 static void
createv4ptrs(void)1018 createv4ptrs(void)
1019 {
1020 	int len, dlen, n;
1021 	char *dom;
1022 	char buf[Domlen+1], ipa[48];
1023 	char *f[40];
1024 	uchar net[IPaddrlen], mask[IPaddrlen];
1025 	Area *s;
1026 	Ndbtuple *t, *nt;
1027 
1028 	dlen = strlen(v4ptrdom);
1029 	for(s = owned; s; s = s->next){
1030 		dom = s->soarr->owner->name;
1031 		len = strlen(dom);
1032 		if((len <= dlen || cistrcmp(dom+len-dlen, v4ptrdom) != 0) &&
1033 		    cistrcmp(dom, v4ptrdom+1) != 0)
1034 			continue;
1035 
1036 		/* get mask and net value */
1037 		strncpy(buf, dom, sizeof buf);
1038 		buf[sizeof buf-1] = 0;
1039 		/* buf contains something like 178.204.in-addr.arpa (n==4) */
1040 		n = getfields(buf, f, nelem(f), 0, ".");
1041 		memset(mask, 0xff, IPaddrlen);
1042 		ipmove(net, v4prefix);
1043 		switch(n){
1044 		case 3:			/* /8 */
1045 			net[IPv4off] = atoi(f[0]);
1046 			mask[IPv4off+1] = 0;
1047 			mask[IPv4off+2] = 0;
1048 			mask[IPv4off+3] = 0;
1049 			break;
1050 		case 4:			/* /16 */
1051 			net[IPv4off] = atoi(f[1]);
1052 			net[IPv4off+1] = atoi(f[0]);
1053 			mask[IPv4off+2] = 0;
1054 			mask[IPv4off+3] = 0;
1055 			break;
1056 		case 5:			/* /24 */
1057 			net[IPv4off] = atoi(f[2]);
1058 			net[IPv4off+1] = atoi(f[1]);
1059 			net[IPv4off+2] = atoi(f[0]);
1060 			mask[IPv4off+3] = 0;
1061 			break;
1062 		case 6:		/* rfc2317: classless in-addr.arpa delegation */
1063 			net[IPv4off] = atoi(f[3]);
1064 			net[IPv4off+1] = atoi(f[2]);
1065 			net[IPv4off+2] = atoi(f[1]);
1066 			net[IPv4off+3] = atoi(f[0]);
1067 			sprint(ipa, "%I", net);
1068 			t = ndbipinfo(db, "ip", ipa, attribs, 1);
1069 			if(t == nil)	/* could be a reverse with no forward */
1070 				continue;
1071 			nt = look(t, t, "ipmask");
1072 			if(nt == nil){		/* we're confused */
1073 				ndbfree(t);
1074 				continue;
1075 			}
1076 			parseipmask(mask, nt->val);
1077 			ndbfree(t);
1078 			n = 5;
1079 			break;
1080 		default:
1081 			continue;
1082 		}
1083 
1084 		/*
1085 		 * go through all domain entries looking for RR's
1086 		 * in this network and create ptrs.
1087 		 * +2 for ".in-addr.arpa".
1088 		 */
1089 		dnptr(net, mask, dom, Ta, 4+2-n, Ptrttl);
1090 	}
1091 }
1092 
1093 /* convert bytes to nibbles, big-endian */
1094 void
bytes2nibbles(uchar * nibbles,uchar * bytes,int nbytes)1095 bytes2nibbles(uchar *nibbles, uchar *bytes, int nbytes)
1096 {
1097 	while (nbytes-- > 0) {
1098 		*nibbles++ = *bytes >> Nibwidth;
1099 		*nibbles++ = *bytes++ & Nibmask;
1100 	}
1101 }
1102 
1103 void
nibbles2bytes(uchar * bytes,uchar * nibbles,int nnibs)1104 nibbles2bytes(uchar *bytes, uchar *nibbles, int nnibs)
1105 {
1106 	for (; nnibs >= 2; nnibs -= 2) {
1107 		*bytes++ = nibbles[0] << Nibwidth | (nibbles[1]&Nibmask);
1108 		nibbles += 2;
1109 	}
1110 	if (nnibs > 0)
1111 		*bytes = nibbles[0] << Nibwidth;
1112 }
1113 
1114 /*
1115  *  create ptrs that are in our v6 areas.  see rfc3596
1116  */
1117 static void
createv6ptrs(void)1118 createv6ptrs(void)
1119 {
1120 	int len, dlen, i, n, pfxnibs;
1121 	char *dom;
1122 	char buf[Domlen+1];
1123 	char *f[40];
1124 	uchar net[IPaddrlen], mask[IPaddrlen];
1125 	uchar nibnet[IPaddrlen*2], nibmask[IPaddrlen*2];
1126 	Area *s;
1127 
1128 	dlen = strlen(v6ptrdom);
1129 	for(s = owned; s; s = s->next){
1130 		dom = s->soarr->owner->name;
1131 		len = strlen(dom);
1132 		if((len <= dlen || cistrcmp(dom+len-dlen, v6ptrdom) != 0) &&
1133 		    cistrcmp(dom, v6ptrdom+1) != 0)
1134 			continue;
1135 
1136 		/* get mask and net value */
1137 		strncpy(buf, dom, sizeof buf);
1138 		buf[sizeof buf-1] = 0;
1139 		/* buf contains something like 2.0.0.2.ip6.arpa (n==6) */
1140 		n = getfields(buf, f, nelem(f), 0, ".");
1141 		pfxnibs = n - 2;		/* 2 for .ip6.arpa */
1142 		if (pfxnibs < 0 || pfxnibs > V6maxrevdomdepth)
1143 			continue;
1144 
1145 		memset(net, 0, IPaddrlen);
1146 		memset(mask, 0xff, IPaddrlen);
1147 		bytes2nibbles(nibnet, net, IPaddrlen);
1148 		bytes2nibbles(nibmask, mask, IPaddrlen);
1149 
1150 		/* copy prefix of f, in reverse order, to start of net. */
1151 		for (i = 0; i < pfxnibs; i++)
1152 			nibnet[i] = strtol(f[pfxnibs - 1 - i], nil, 16);
1153 		/* zero nibbles of mask after prefix in net */
1154 		memset(nibmask + pfxnibs, 0, V6maxrevdomdepth - pfxnibs);
1155 
1156 		nibbles2bytes(net, nibnet, 2*IPaddrlen);
1157 		nibbles2bytes(mask, nibmask, 2*IPaddrlen);
1158 
1159 		/*
1160 		 * go through all domain entries looking for RR's
1161 		 * in this network and create ptrs.
1162 		 */
1163 		dnptr(net, mask, dom, Taaaa, V6maxrevdomdepth - pfxnibs, Ptrttl);
1164 	}
1165 }
1166 
1167 /*
1168  *  create ptrs that are in our areas
1169  */
1170 static void
createptrs(void)1171 createptrs(void)
1172 {
1173 	createv4ptrs();
1174 	createv6ptrs();
1175 }
1176 
1177 /*
1178  * is this domain (or DOMAIN or Domain or dOMAIN)
1179  * internal to our organisation (behind our firewall)?
1180  * only inside straddling servers care, everybody else gets told `yes',
1181  * so they'll use mntpt for their queries.
1182  */
1183 int
insideaddr(char * dom)1184 insideaddr(char *dom)
1185 {
1186 	int domlen, vallen, rv;
1187 	Ndbtuple *t;
1188 
1189 	if (!cfg.inside || !cfg.straddle || !cfg.serve)
1190 		return 1;
1191 	if (dom[0] == '\0' || strcmp(dom, ".") == 0)	/* dns root? */
1192 		return 1;			/* hack for initialisation */
1193 
1194 	lock(&dblock);
1195 	if (indoms == nil)
1196 		loaddomsrvs();
1197 	if (indoms == nil) {
1198 		unlock(&dblock);
1199 		return 1;  /* no "inside-dom" sys, try inside nameservers */
1200 	}
1201 
1202 	rv = 0;
1203 	domlen = strlen(dom);
1204 	for (t = indoms; t != nil; t = t->entry) {
1205 		if (strcmp(t->attr, "dom") != 0)
1206 			continue;
1207 		vallen = strlen(t->val);
1208 		if (cistrcmp(dom, t->val) == 0 ||
1209 		    domlen > vallen &&
1210 		     cistrcmp(dom + domlen - vallen, t->val) == 0 &&
1211 		     dom[domlen - vallen - 1] == '.') {
1212 			rv = 1;
1213 			break;
1214 		}
1215 	}
1216 	unlock(&dblock);
1217 	return rv;
1218 }
1219 
1220 int
insidens(uchar * ip)1221 insidens(uchar *ip)
1222 {
1223 	uchar ipa[IPaddrlen];
1224 	Ndbtuple *t;
1225 
1226 	for (t = innmsrvs; t != nil; t = t->entry)
1227 		if (strcmp(t->attr, "ip") == 0) {
1228 			parseip(ipa, t->val);
1229 			if (memcmp(ipa, ip, sizeof ipa) == 0)
1230 				return 1;
1231 		}
1232 	return 0;
1233 }
1234 
1235 uchar *
outsidens(int n)1236 outsidens(int n)
1237 {
1238 	int i;
1239 	Ndbtuple *t;
1240 	static uchar ipa[IPaddrlen];
1241 
1242 	i = 0;
1243 	for (t = outnmsrvs; t != nil; t = t->entry)
1244 		if (strcmp(t->attr, "ip") == 0 && i++ == n) {
1245 			parseip(ipa, t->val);
1246 			return ipa;
1247 		}
1248 	return nil;
1249 }
1250