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