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