]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ndb/dblookup.c
merge
[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  *  read the database into the cache
584  */
585 static void
586 dbpair2cache(DN *dp, Ndbtuple *entry, Ndbtuple *pair)
587 {
588         RR *rp;
589         static ulong ord;
590
591         rp = 0;
592         if(strcmp(pair->attr, "ip") == 0 ||
593            strcmp(pair->attr, "ipv6") == 0) {
594                 dp->ordinal = ord++;
595                 rp = addrrr(entry, pair);
596         }
597         else if(strcmp(pair->attr, "ns") == 0)
598                 rp = nsrr(entry, pair);
599         else if(strcmp(pair->attr, "soa") == 0) {
600                 rp = soarr(entry, pair);
601                 addarea(dp, rp, pair);
602         }
603         else if(strcmp(pair->attr, "mx") == 0)
604                 rp = mxrr(entry, pair);
605         else if(strcmp(pair->attr, "srv") == 0)
606                 rp = srvrr(entry, pair);
607         else if(strcmp(pair->attr, "cname") == 0)
608                 rp = cnamerr(entry, pair);
609         else if(strcmp(pair->attr, "nullrr") == 0)
610                 rp = nullrr(entry, pair);
611         else if(strcmp(pair->attr, "txtrr") == 0)
612                 rp = txtrr(entry, pair);
613         if(rp == nil)
614                 return;
615
616         rp->owner = dp;
617         rp->db = 1;
618         rp->ttl = intval(entry, pair, "ttl", rp->ttl);
619         rrattach(rp, Notauthoritative);
620         dnagenever(dp);
621 }
622 static void
623 dbtuple2cache(Ndbtuple *t)
624 {
625         Ndbtuple *et, *nt;
626         DN *dp;
627
628         for(et = t; et; et = et->entry)
629                 if(strcmp(et->attr, "dom") == 0){
630                         dp = idnlookup(et->val, Cin, 1);
631
632                         /* first same line */
633                         for(nt = et->line; nt != et; nt = nt->line){
634                                 dbpair2cache(dp, t, nt);
635                                 nt->ptr = 1;
636                         }
637
638                         /* then rest of entry */
639                         for(nt = t; nt; nt = nt->entry){
640                                 if(nt->ptr == 0)
641                                         dbpair2cache(dp, t, nt);
642                                 nt->ptr = 0;
643                         }
644                 }
645 }
646 static void
647 dbfile2cache(Ndb *db)
648 {
649         Ndbtuple *t;
650
651         if(debug)
652                 dnslog("rereading %s", db->file);
653         Bseek(&db->b, 0, 0);
654         while(t = ndbparse(db)){
655                 dbtuple2cache(t);
656                 ndbfree(t);
657         }
658 }
659
660 /* called with dblock held */
661 static void
662 loaddomsrvs(void)
663 {
664         Ndbs s;
665
666         if (!cfg.inside || !cfg.straddle || !cfg.serve)
667                 return;
668         if (indoms) {
669                 ndbfree(indoms);
670                 ndbfree(innmsrvs);
671                 ndbfree(outnmsrvs);
672                 indoms = innmsrvs = outnmsrvs = nil;
673         }
674         if (db == nil)
675                 opendatabase();
676         free(ndbgetvalue(db, &s, "sys", "inside-dom", "dom", &indoms));
677         free(ndbgetvalue(db, &s, "sys", "inside-ns",  "ip",  &innmsrvs));
678         free(ndbgetvalue(db, &s, "sys", "outside-ns", "ip",  &outnmsrvs));
679         dnslog("[%d] ndb changed: reloaded inside-dom, inside-ns, outside-ns",
680                 getpid());
681 }
682
683 void
684 db2cache(int doit)
685 {
686         ulong youngest;
687         Ndb *ndb;
688         Dir *d;
689         static ulong lastcheck, lastyoungest;
690
691         /* no faster than once every 2 minutes */
692         if(now < lastcheck + 2*Min && !doit)
693                 return;
694
695         refresh_areas(owned);
696
697         lock(&dblock);
698
699         if(opendatabase() < 0){
700                 unlock(&dblock);
701                 return;
702         }
703
704         /*
705          *  file may be changing as we are reading it, so loop till
706          *  mod times are consistent.
707          *
708          *  we don't use the times in the ndb records because they may
709          *  change outside of refreshing our cached knowledge.
710          */
711         for(;;){
712                 lastcheck = now;
713                 youngest = 0;
714                 for(ndb = db; ndb; ndb = ndb->next)
715                         /* dirfstat avoids walking the mount table each time */
716                         if((d = dirfstat(Bfildes(&ndb->b))) != nil ||
717                            (d = dirstat(ndb->file)) != nil){
718                                 if(d->mtime > youngest)
719                                         youngest = d->mtime;
720                                 free(d);
721                         }
722                 if(!doit && youngest == lastyoungest)
723                         break;
724
725                 /* forget our area definition */
726                 freearea(&owned);
727                 freearea(&delegated);
728
729                 /* reopen all the files (to get oldest for time stamp) */
730                 for(ndb = db; ndb; ndb = ndb->next)
731                         ndbreopen(ndb);
732
733                 /* reload straddle-server configuration */
734                 loaddomsrvs();
735
736                 /* mark all db records as timed out */
737                 dnagedb();
738
739                 if(cfg.cachedb){
740                         /* read in new entries */
741                         for(ndb = db; ndb; ndb = ndb->next)
742                                 dbfile2cache(ndb);
743                 }
744
745                 /*
746                  * mark as authoritative anything in our domain,
747                  * delete timed out db records
748                  */
749                 dnauthdb();
750
751                 /* remove old entries */
752                 dnageall(1);
753
754                 doit = 0;
755                 lastyoungest = youngest;
756                 createptrs();
757         }
758
759         unlock(&dblock);
760 }
761
762 extern char     mntpt[Maxpath];         /* net mountpoint */
763 static uchar    ipaddr[IPaddrlen];      /* my ip address */
764
765 /*
766  *  get all my xxx
767  *  caller ndbfrees the result
768  */
769 Ndbtuple*
770 lookupinfo(char *attr)
771 {
772         char buf[64];
773         char *a[2];
774         Ndbtuple *t;
775
776         if(ipcmp(ipaddr, IPnoaddr) == 0)
777                 if(myipaddr(ipaddr, mntpt) < 0)
778                         return nil;
779
780         snprint(buf, sizeof buf, "%I", ipaddr);
781         a[0] = attr;
782
783         lock(&dblock);
784         if(opendatabase() < 0){
785                 unlock(&dblock);
786                 return nil;
787         }
788         t = ndbipinfo(db, "ip", buf, a, 1);
789         unlock(&dblock);
790         return t;
791 }
792
793 /*
794  *  return non-zero if this is a bad delegation
795  */
796 int
797 baddelegation(RR *rp, RR *nsrp, uchar *addr)
798 {
799         static int whined;
800         static Ndbtuple *t;
801         Ndbtuple *nt;
802
803         if(rp->type != Tns)
804                 return 0;
805
806         if(t == nil)
807                 t = lookupinfo("dom");
808         if(t != nil){
809                 /* see if delegating to us what we don't own */
810                 for(nt = t; nt != nil; nt = nt->entry)
811                         if(rp->host && cistrcmp(rp->host->name, nt->val) == 0)
812                                 break;
813
814                 if(nt != nil && !inmyarea(rp->owner->name)){
815                         if (!whined) {
816                                 whined = 1;
817                                 dnslog("bad delegation %R from %I/%s; "
818                                         "no further logging of them",
819                                         rp, addr, nsrp->host->name);
820                         }
821                         return 1;
822                 }
823         }
824         return 0;
825 }
826
827 int
828 myaddr(char *addr)
829 {
830         char *line, *sp;
831         char buf[64];
832         Biobuf *bp;
833
834         if(ipcmp(ipaddr, IPnoaddr) == 0)
835                 if(myipaddr(ipaddr, mntpt) < 0)
836                         return -1;
837
838         snprint(buf, sizeof buf, "%I", ipaddr);
839         if (strcmp(addr, buf) == 0) {
840                 dnslog("rejecting my ip %s as local dns server", addr);
841                 return 1;
842         }
843
844         snprint(buf, sizeof buf, "%s/ipselftab", mntpt);
845         bp = Bopen(buf, OREAD);
846         if (bp != nil) {
847                 while ((line = Brdline(bp, '\n')) != nil) {
848                         line[Blinelen(bp) - 1] = '\0';
849                         sp = strchr(line, ' ');
850                         if (sp) {
851                                 *sp = '\0';
852                                 if (strcmp(addr, line) == 0) {
853                                         dnslog("rejecting my ip %s as local dns server",
854                                                 addr);
855                                         return 1;
856                                 }
857                         }
858                 }
859                 Bterm(bp);
860         }
861         return 0;
862 }
863
864 static char *locdns[20];
865 static QLock locdnslck;
866
867 static void
868 addlocaldnsserver(DN *dp, int class, char *ipaddr, int i)
869 {
870         int n;
871         DN *nsdp;
872         RR *rp;
873         char buf[32];
874         uchar ip[IPaddrlen];
875
876         /* reject our own ip addresses so we don't query ourselves via udp */
877         if (myaddr(ipaddr))
878                 return;
879
880         qlock(&locdnslck);
881         for (n = 0; n < i && n < nelem(locdns) && locdns[n]; n++)
882                 if (strcmp(locdns[n], ipaddr) == 0) {
883                         dnslog("rejecting duplicate local dns server ip %s",
884                                 ipaddr);
885                         qunlock(&locdnslck);
886                         return;
887                 }
888         if (n < nelem(locdns))
889                 if (locdns[n] == nil || ++n < nelem(locdns))
890                         locdns[n] = strdup(ipaddr); /* remember 1st few local ns */
891         qunlock(&locdnslck);
892
893         /* ns record for name server, make up an impossible name */
894         rp = rralloc(Tns);
895         snprint(buf, sizeof buf, "local#dns#server%d", i);
896         nsdp = dnlookup(buf, class, 1);
897         rp->host = nsdp;
898         rp->owner = dp;                 /* e.g., local#dns#servers */
899         rp->local = 1;
900         rp->db = 1;
901         rp->ttl = 10*Min;
902         rrattach(rp, Authoritative);    /* will not attach rrs in my area */
903         dnagenever(dp);
904
905         /* A or AAAA record */
906         if (parseip(ip, ipaddr) >= 0 && isv4(ip))
907                 rp = rralloc(Ta);
908         else
909                 rp = rralloc(Taaaa);
910         rp->ip = dnlookup(ipaddr, class, 1);
911         rp->owner = nsdp;
912         rp->local = 1;
913         rp->db = 1;
914         rp->ttl = 10*Min;
915         rrattach(rp, Authoritative);    /* will not attach rrs in my area */
916         dnagenever(nsdp);
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("local#dns#servers", 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         dnagenever(dp);
974 }
975
976 /*
977  *  return list of domains to use when resolving names without '.'s
978  */
979 RR*
980 domainlist(int class)
981 {
982         Ndbtuple *t, *nt;
983         RR *rp;
984         DN *dp;
985
986         dp = dnlookup("local#dns#domains", class, 1);
987         rp = rrlookup(dp, Tptr, NOneg);
988         if(rp != nil)
989                 return rp;
990
991         t = lookupinfo("dnsdomain");
992         if(t == nil)
993                 return nil;
994         for(nt = t; nt != nil; nt = nt->entry)
995                 addlocaldnsdomain(dp, class, nt->val);
996         ndbfree(t);
997
998         return rrlookup(dp, Tptr, NOneg);
999 }
1000
1001 char *v4ptrdom = ".in-addr.arpa";
1002 char *v6ptrdom = ".ip6.arpa";           /* ip6.int deprecated, rfc 3152 */
1003
1004 char *attribs[] = {
1005         "ipmask",
1006         0
1007 };
1008
1009 /*
1010  *  create ptrs that are in our v4 areas
1011  */
1012 static void
1013 createv4ptrs(void)
1014 {
1015         int len, dlen, n;
1016         char *dom;
1017         char buf[Domlen], ipa[48];
1018         char *f[40];
1019         uchar net[IPaddrlen], mask[IPaddrlen];
1020         Area *s;
1021         Ndbtuple *t, *nt;
1022
1023         dlen = strlen(v4ptrdom);
1024         for(s = owned; s; s = s->next){
1025                 dom = s->soarr->owner->name;
1026                 len = strlen(dom);
1027                 if((len <= dlen || cistrcmp(dom+len-dlen, v4ptrdom) != 0) &&
1028                     cistrcmp(dom, v4ptrdom+1) != 0)
1029                         continue;
1030
1031                 /* get mask and net value */
1032                 nstrcpy(buf, dom, sizeof buf);
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];
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                 nstrcpy(buf, dom, sizeof buf);
1132                 /* buf contains something like 2.0.0.2.ip6.arpa (n==6) */
1133                 n = getfields(buf, f, nelem(f), 0, ".");
1134                 pfxnibs = n - 2;                /* 2 for .ip6.arpa */
1135                 if (pfxnibs < 0 || pfxnibs > V6maxrevdomdepth)
1136                         continue;
1137
1138                 memset(net, 0, IPaddrlen);
1139                 memset(mask, 0xff, IPaddrlen);
1140                 bytes2nibbles(nibnet, net, IPaddrlen);
1141                 bytes2nibbles(nibmask, mask, IPaddrlen);
1142
1143                 /* copy prefix of f, in reverse order, to start of net. */
1144                 for (i = 0; i < pfxnibs; i++)
1145                         nibnet[i] = strtol(f[pfxnibs - 1 - i], nil, 16);
1146                 /* zero nibbles of mask after prefix in net */
1147                 memset(nibmask + pfxnibs, 0, V6maxrevdomdepth - pfxnibs);
1148
1149                 nibbles2bytes(net, nibnet, 2*IPaddrlen);
1150                 nibbles2bytes(mask, nibmask, 2*IPaddrlen);
1151
1152                 /*
1153                  * go through all domain entries looking for RR's
1154                  * in this network and create ptrs.
1155                  */
1156                 dnptr(net, mask, dom, Taaaa, V6maxrevdomdepth - pfxnibs, Ptrttl);
1157         }
1158 }
1159
1160 /*
1161  *  create ptrs that are in our areas
1162  */
1163 static void
1164 createptrs(void)
1165 {
1166         createv4ptrs();
1167         createv6ptrs();
1168 }
1169
1170 /*
1171  * is this domain (or DOMAIN or Domain or dOMAIN)
1172  * internal to our organisation (behind our firewall)?
1173  * only inside straddling servers care, everybody else gets told `yes',
1174  * so they'll use mntpt for their queries.
1175  */
1176 int
1177 insideaddr(char *dom)
1178 {
1179         int domlen, vallen, rv;
1180         Ndbtuple *t;
1181
1182         if (!cfg.inside || !cfg.straddle || !cfg.serve)
1183                 return 1;
1184         if (dom[0] == '\0' || strcmp(dom, ".") == 0)    /* dns root? */
1185                 return 1;                       /* hack for initialisation */
1186
1187         lock(&dblock);
1188         if (indoms == nil)
1189                 loaddomsrvs();
1190         if (indoms == nil) {
1191                 unlock(&dblock);
1192                 return 1;  /* no "inside-dom" sys, try inside nameservers */
1193         }
1194
1195         rv = 0;
1196         domlen = strlen(dom);
1197         for (t = indoms; t != nil; t = t->entry) {
1198                 if (strcmp(t->attr, "dom") != 0)
1199                         continue;
1200                 vallen = strlen(t->val);
1201                 if (cistrcmp(dom, t->val) == 0 ||
1202                     domlen > vallen &&
1203                      cistrcmp(dom + domlen - vallen, t->val) == 0 &&
1204                      dom[domlen - vallen - 1] == '.') {
1205                         rv = 1;
1206                         break;
1207                 }
1208         }
1209         unlock(&dblock);
1210         return rv;
1211 }
1212
1213 int
1214 insidens(uchar *ip)
1215 {
1216         uchar ipa[IPaddrlen];
1217         Ndbtuple *t;
1218
1219         for (t = innmsrvs; t != nil; t = t->entry)
1220                 if (strcmp(t->attr, "ip") == 0) {
1221                         parseip(ipa, t->val);
1222                         if (memcmp(ipa, ip, sizeof ipa) == 0)
1223                                 return 1;
1224                 }
1225         return 0;
1226 }
1227
1228 int
1229 outsidensip(int n, uchar *ip)
1230 {
1231         int i;
1232         Ndbtuple *t;
1233
1234         i = 0;
1235         for (t = outnmsrvs; t != nil; t = t->entry)
1236                 if (strcmp(t->attr, "ip") == 0 && i++ == n) {
1237                         parseip(ip, t->val);
1238                         return 0;
1239                 }
1240         return -1;
1241 }