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