]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ndb/dblookup.c
ndb/dns: double Maxretries for long cname redirection chains
[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 /* these are answered specially by the tcp version */
565 static RR*
566 doaxfr(Ndb *db, char *name)
567 {
568         USED(db, name);
569         return 0;
570 }
571
572 /*
573  *  read the database into the cache
574  */
575 static void
576 dbpair2cache(DN *dp, Ndbtuple *entry, Ndbtuple *pair)
577 {
578         RR *rp;
579         static ulong ord;
580
581         rp = 0;
582         if(strcmp(pair->attr, "ip") == 0 ||
583            strcmp(pair->attr, "ipv6") == 0) {
584                 dp->ordinal = ord++;
585                 rp = addrrr(entry, pair);
586         }
587         else if(strcmp(pair->attr, "ns") == 0)
588                 rp = nsrr(entry, pair);
589         else if(strcmp(pair->attr, "soa") == 0) {
590                 rp = soarr(entry, pair);
591                 addarea(dp, rp, pair);
592         }
593         else if(strcmp(pair->attr, "mx") == 0)
594                 rp = mxrr(entry, pair);
595         else if(strcmp(pair->attr, "srv") == 0)
596                 rp = srvrr(entry, pair);
597         else if(strcmp(pair->attr, "cname") == 0)
598                 rp = cnamerr(entry, pair);
599         else if(strcmp(pair->attr, "nullrr") == 0)
600                 rp = nullrr(entry, pair);
601         else if(strcmp(pair->attr, "txtrr") == 0)
602                 rp = txtrr(entry, pair);
603         if(rp == nil)
604                 return;
605
606         rp->owner = dp;
607         rp->db = 1;
608         rp->ttl = intval(entry, pair, "ttl", rp->ttl);
609         rrattach(rp, Notauthoritative);
610         dnagenever(dp);
611 }
612 static void
613 dbtuple2cache(Ndbtuple *t)
614 {
615         Ndbtuple *et, *nt;
616         DN *dp;
617
618         for(et = t; et; et = et->entry)
619                 if(strcmp(et->attr, "dom") == 0){
620                         dp = idnlookup(et->val, Cin, 1);
621
622                         /* first same line */
623                         for(nt = et->line; nt != et; nt = nt->line){
624                                 dbpair2cache(dp, t, nt);
625                                 nt->ptr = 1;
626                         }
627
628                         /* then rest of entry */
629                         for(nt = t; nt; nt = nt->entry){
630                                 if(nt->ptr == 0)
631                                         dbpair2cache(dp, t, nt);
632                                 nt->ptr = 0;
633                         }
634                 }
635 }
636 static void
637 dbfile2cache(Ndb *db)
638 {
639         Ndbtuple *t;
640
641         if(debug)
642                 dnslog("rereading %s", db->file);
643         Bseek(&db->b, 0, 0);
644         while(t = ndbparse(db)){
645                 dbtuple2cache(t);
646                 ndbfree(t);
647         }
648 }
649
650 /* called with dblock held */
651 static void
652 loaddomsrvs(void)
653 {
654         Ndbs s;
655
656         if (!cfg.inside || !cfg.straddle || !cfg.serve)
657                 return;
658         if (indoms) {
659                 ndbfree(indoms);
660                 ndbfree(innmsrvs);
661                 ndbfree(outnmsrvs);
662                 indoms = innmsrvs = outnmsrvs = nil;
663         }
664         if (db == nil)
665                 opendatabase();
666         free(ndbgetvalue(db, &s, "sys", "inside-dom", "dom", &indoms));
667         free(ndbgetvalue(db, &s, "sys", "inside-ns",  "ip",  &innmsrvs));
668         free(ndbgetvalue(db, &s, "sys", "outside-ns", "ip",  &outnmsrvs));
669         dnslog("[%d] ndb changed: reloaded inside-dom, inside-ns, outside-ns",
670                 getpid());
671 }
672
673 void
674 db2cache(int doit)
675 {
676         ulong youngest;
677         Ndb *ndb;
678         Dir *d;
679         static ulong lastcheck, lastyoungest;
680
681         /* no faster than once every 2 minutes */
682         if(now < lastcheck + 2*Min && !doit)
683                 return;
684
685         refresh_areas(owned);
686
687         lock(&dblock);
688
689         if(opendatabase() < 0){
690                 unlock(&dblock);
691                 return;
692         }
693
694         /*
695          *  file may be changing as we are reading it, so loop till
696          *  mod times are consistent.
697          *
698          *  we don't use the times in the ndb records because they may
699          *  change outside of refreshing our cached knowledge.
700          */
701         for(;;){
702                 lastcheck = now;
703                 youngest = 0;
704                 for(ndb = db; ndb; ndb = ndb->next)
705                         /* dirfstat avoids walking the mount table each time */
706                         if((d = dirfstat(Bfildes(&ndb->b))) != nil ||
707                            (d = dirstat(ndb->file)) != nil){
708                                 if(d->mtime > youngest)
709                                         youngest = d->mtime;
710                                 free(d);
711                         }
712                 if(!doit && youngest == lastyoungest)
713                         break;
714
715                 /* forget our area definition */
716                 freearea(&owned);
717                 freearea(&delegated);
718
719                 /* reopen all the files (to get oldest for time stamp) */
720                 for(ndb = db; ndb; ndb = ndb->next)
721                         ndbreopen(ndb);
722
723                 /* reload straddle-server configuration */
724                 loaddomsrvs();
725
726                 /* mark all db records as timed out */
727                 dnagedb();
728
729                 if(cfg.cachedb){
730                         /* read in new entries */
731                         for(ndb = db; ndb; ndb = ndb->next)
732                                 dbfile2cache(ndb);
733                 }
734
735                 /*
736                  * mark as authoritative anything in our domain,
737                  * delete timed out db records
738                  */
739                 dnauthdb();
740
741                 /* remove old entries */
742                 dnageall(1);
743
744                 doit = 0;
745                 lastyoungest = youngest;
746                 createptrs();
747         }
748
749         unlock(&dblock);
750 }
751
752 extern char     mntpt[Maxpath];         /* net mountpoint */
753 static uchar    ipaddr[IPaddrlen];      /* my ip address */
754
755 /*
756  *  get all my xxx
757  *  caller ndbfrees the result
758  */
759 Ndbtuple*
760 lookupinfo(char *attr)
761 {
762         char buf[64];
763         char *a[2];
764         Ndbtuple *t;
765
766         if(ipcmp(ipaddr, IPnoaddr) == 0)
767                 if(myipaddr(ipaddr, mntpt) < 0)
768                         return nil;
769
770         snprint(buf, sizeof buf, "%I", ipaddr);
771         a[0] = attr;
772
773         lock(&dblock);
774         if(opendatabase() < 0){
775                 unlock(&dblock);
776                 return nil;
777         }
778         t = ndbipinfo(db, "ip", buf, a, 1);
779         unlock(&dblock);
780         return t;
781 }
782
783 /*
784  *  return non-zero if this is a bad delegation
785  */
786 int
787 baddelegation(RR *rp, RR *nsrp, uchar *addr)
788 {
789         static int whined;
790         static Ndbtuple *t;
791         Ndbtuple *nt;
792
793         if(rp->type != Tns)
794                 return 0;
795
796         if(t == nil)
797                 t = lookupinfo("dom");
798         if(t != nil){
799                 /* see if delegating to us what we don't own */
800                 for(nt = t; nt != nil; nt = nt->entry)
801                         if(rp->host && cistrcmp(rp->host->name, nt->val) == 0)
802                                 break;
803
804                 if(nt != nil && !inmyarea(rp->owner->name)){
805                         if (!whined) {
806                                 whined = 1;
807                                 dnslog("bad delegation %R from %I/%s; "
808                                         "no further logging of them",
809                                         rp, addr, nsrp->host->name);
810                         }
811                         return 1;
812                 }
813         }
814         return 0;
815 }
816
817 int
818 myaddr(char *addr)
819 {
820         char *line, *sp;
821         char buf[64];
822         Biobuf *bp;
823
824         if(ipcmp(ipaddr, IPnoaddr) == 0)
825                 if(myipaddr(ipaddr, mntpt) < 0)
826                         return -1;
827
828         snprint(buf, sizeof buf, "%I", ipaddr);
829         if (strcmp(addr, buf) == 0) {
830                 dnslog("rejecting my ip %s as local dns server", addr);
831                 return 1;
832         }
833
834         snprint(buf, sizeof buf, "%s/ipselftab", mntpt);
835         bp = Bopen(buf, OREAD);
836         if (bp != nil) {
837                 while ((line = Brdline(bp, '\n')) != nil) {
838                         line[Blinelen(bp) - 1] = '\0';
839                         sp = strchr(line, ' ');
840                         if (sp) {
841                                 *sp = '\0';
842                                 if (strcmp(addr, line) == 0) {
843                                         dnslog("rejecting my ip %s as local dns server",
844                                                 addr);
845                                         return 1;
846                                 }
847                         }
848                 }
849                 Bterm(bp);
850         }
851         return 0;
852 }
853
854 static char *locdns[20];
855 static QLock locdnslck;
856
857 static void
858 addlocaldnsserver(DN *dp, int class, char *ipaddr, int i)
859 {
860         int n;
861         DN *nsdp;
862         RR *rp;
863         char buf[32];
864         uchar ip[IPaddrlen];
865
866         /* reject our own ip addresses so we don't query ourselves via udp */
867         if (myaddr(ipaddr))
868                 return;
869
870         qlock(&locdnslck);
871         for (n = 0; n < i && n < nelem(locdns) && locdns[n]; n++)
872                 if (strcmp(locdns[n], ipaddr) == 0) {
873                         dnslog("rejecting duplicate local dns server ip %s",
874                                 ipaddr);
875                         qunlock(&locdnslck);
876                         return;
877                 }
878         if (n < nelem(locdns))
879                 if (locdns[n] == nil || ++n < nelem(locdns))
880                         locdns[n] = strdup(ipaddr); /* remember 1st few local ns */
881         qunlock(&locdnslck);
882
883         /* ns record for name server, make up an impossible name */
884         rp = rralloc(Tns);
885         snprint(buf, sizeof buf, "local#dns#server%d", i);
886         nsdp = dnlookup(buf, class, 1);
887         rp->host = nsdp;
888         rp->owner = dp;                 /* e.g., local#dns#servers */
889         rp->local = 1;
890         rp->db = 1;
891         rp->ttl = 10*Min;
892         rrattach(rp, Authoritative);    /* will not attach rrs in my area */
893         dnagenever(dp);
894
895         /* A or AAAA record */
896         if (parseip(ip, ipaddr) >= 0 && isv4(ip))
897                 rp = rralloc(Ta);
898         else
899                 rp = rralloc(Taaaa);
900         rp->ip = dnlookup(ipaddr, class, 1);
901         rp->owner = nsdp;
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(nsdp);
907
908         dnslog("added local dns server %s at %s", buf, ipaddr);
909 }
910
911 /*
912  *  return list of dns server addresses to use when
913  *  acting just as a resolver.
914  */
915 RR*
916 dnsservers(int class)
917 {
918         int i, n;
919         char *p;
920         char *args[5];
921         Ndbtuple *t, *nt;
922         RR *nsrp;
923         DN *dp;
924
925         dp = dnlookup("local#dns#servers", class, 1);
926         nsrp = rrlookup(dp, Tns, NOneg);
927         if(nsrp != nil)
928                 return nsrp;
929
930         p = getenv("DNSSERVER");                /* list of ip addresses */
931         if(p != nil){
932                 n = tokenize(p, args, nelem(args));
933                 for(i = 0; i < n; i++)
934                         addlocaldnsserver(dp, class, args[i], i);
935                 free(p);
936         } else {
937                 t = lookupinfo("@dns");         /* @dns=ip1 @dns=ip2 ... */
938                 if(t == nil)
939                         return nil;
940                 i = 0;
941                 for(nt = t; nt != nil; nt = nt->entry){
942                         addlocaldnsserver(dp, class, nt->val, i);
943                         i++;
944                 }
945                 ndbfree(t);
946         }
947
948         return rrlookup(dp, Tns, NOneg);
949 }
950
951 static void
952 addlocaldnsdomain(DN *dp, int class, char *domain)
953 {
954         RR *rp;
955
956         /* ptr record */
957         rp = rralloc(Tptr);
958         rp->ptr = dnlookup(domain, class, 1);
959         rp->owner = dp;
960         rp->db = 1;
961         rp->ttl = 10*Min;
962         rrattach(rp, Authoritative);
963         dnagenever(dp);
964 }
965
966 /*
967  *  return list of domains to use when resolving names without '.'s
968  */
969 RR*
970 domainlist(int class)
971 {
972         Ndbtuple *t, *nt;
973         RR *rp;
974         DN *dp;
975
976         dp = dnlookup("local#dns#domains", class, 1);
977         rp = rrlookup(dp, Tptr, NOneg);
978         if(rp != nil)
979                 return rp;
980
981         t = lookupinfo("dnsdomain");
982         if(t == nil)
983                 return nil;
984         for(nt = t; nt != nil; nt = nt->entry)
985                 addlocaldnsdomain(dp, class, nt->val);
986         ndbfree(t);
987
988         return rrlookup(dp, Tptr, NOneg);
989 }
990
991 char *v4ptrdom = ".in-addr.arpa";
992 char *v6ptrdom = ".ip6.arpa";           /* ip6.int deprecated, rfc 3152 */
993
994 char *attribs[] = {
995         "ipmask",
996         0
997 };
998
999 /*
1000  *  create ptrs that are in our v4 areas
1001  */
1002 static void
1003 createv4ptrs(void)
1004 {
1005         int len, dlen, n;
1006         char *dom;
1007         char buf[Domlen], ipa[48];
1008         char *f[40];
1009         uchar net[IPaddrlen], mask[IPaddrlen];
1010         Area *s;
1011         Ndbtuple *t, *nt;
1012
1013         dlen = strlen(v4ptrdom);
1014         for(s = owned; s; s = s->next){
1015                 dom = s->soarr->owner->name;
1016                 len = strlen(dom);
1017                 if((len <= dlen || cistrcmp(dom+len-dlen, v4ptrdom) != 0) &&
1018                     cistrcmp(dom, v4ptrdom+1) != 0)
1019                         continue;
1020
1021                 /* get mask and net value */
1022                 nstrcpy(buf, dom, sizeof buf);
1023                 /* buf contains something like 178.204.in-addr.arpa (n==4) */
1024                 n = getfields(buf, f, nelem(f), 0, ".");
1025                 memset(mask, 0xff, IPaddrlen);
1026                 ipmove(net, v4prefix);
1027                 switch(n){
1028                 case 3:                 /* /8 */
1029                         net[IPv4off] = atoi(f[0]);
1030                         mask[IPv4off+1] = 0;
1031                         mask[IPv4off+2] = 0;
1032                         mask[IPv4off+3] = 0;
1033                         break;
1034                 case 4:                 /* /16 */
1035                         net[IPv4off] = atoi(f[1]);
1036                         net[IPv4off+1] = atoi(f[0]);
1037                         mask[IPv4off+2] = 0;
1038                         mask[IPv4off+3] = 0;
1039                         break;
1040                 case 5:                 /* /24 */
1041                         net[IPv4off] = atoi(f[2]);
1042                         net[IPv4off+1] = atoi(f[1]);
1043                         net[IPv4off+2] = atoi(f[0]);
1044                         mask[IPv4off+3] = 0;
1045                         break;
1046                 case 6:         /* rfc2317: classless in-addr.arpa delegation */
1047                         net[IPv4off] = atoi(f[3]);
1048                         net[IPv4off+1] = atoi(f[2]);
1049                         net[IPv4off+2] = atoi(f[1]);
1050                         net[IPv4off+3] = atoi(f[0]);
1051                         sprint(ipa, "%I", net);
1052                         t = ndbipinfo(db, "ip", ipa, attribs, 1);
1053                         if(t == nil)    /* could be a reverse with no forward */
1054                                 continue;
1055                         nt = look(t, t, "ipmask");
1056                         if(nt == nil){          /* we're confused */
1057                                 ndbfree(t);
1058                                 continue;
1059                         }
1060                         parseipmask(mask, nt->val);
1061                         ndbfree(t);
1062                         n = 5;
1063                         break;
1064                 default:
1065                         continue;
1066                 }
1067
1068                 /*
1069                  * go through all domain entries looking for RR's
1070                  * in this network and create ptrs.
1071                  * +2 for ".in-addr.arpa".
1072                  */
1073                 dnptr(net, mask, dom, Ta, 4+2-n, Ptrttl);
1074         }
1075 }
1076
1077 /* convert bytes to nibbles, big-endian */
1078 void
1079 bytes2nibbles(uchar *nibbles, uchar *bytes, int nbytes)
1080 {
1081         while (nbytes-- > 0) {
1082                 *nibbles++ = *bytes >> Nibwidth;
1083                 *nibbles++ = *bytes++ & Nibmask;
1084         }
1085 }
1086
1087 void
1088 nibbles2bytes(uchar *bytes, uchar *nibbles, int nnibs)
1089 {
1090         for (; nnibs >= 2; nnibs -= 2) {
1091                 *bytes++ = nibbles[0] << Nibwidth | (nibbles[1]&Nibmask);
1092                 nibbles += 2;
1093         }
1094         if (nnibs > 0)
1095                 *bytes = nibbles[0] << Nibwidth;
1096 }
1097
1098 /*
1099  *  create ptrs that are in our v6 areas.  see rfc3596
1100  */
1101 static void
1102 createv6ptrs(void)
1103 {
1104         int len, dlen, i, n, pfxnibs;
1105         char *dom;
1106         char buf[Domlen];
1107         char *f[40];
1108         uchar net[IPaddrlen], mask[IPaddrlen];
1109         uchar nibnet[IPaddrlen*2], nibmask[IPaddrlen*2];
1110         Area *s;
1111
1112         dlen = strlen(v6ptrdom);
1113         for(s = owned; s; s = s->next){
1114                 dom = s->soarr->owner->name;
1115                 len = strlen(dom);
1116                 if((len <= dlen || cistrcmp(dom+len-dlen, v6ptrdom) != 0) &&
1117                     cistrcmp(dom, v6ptrdom+1) != 0)
1118                         continue;
1119
1120                 /* get mask and net value */
1121                 nstrcpy(buf, dom, sizeof buf);
1122                 /* buf contains something like 2.0.0.2.ip6.arpa (n==6) */
1123                 n = getfields(buf, f, nelem(f), 0, ".");
1124                 pfxnibs = n - 2;                /* 2 for .ip6.arpa */
1125                 if (pfxnibs < 0 || pfxnibs > V6maxrevdomdepth)
1126                         continue;
1127
1128                 memset(net, 0, IPaddrlen);
1129                 memset(mask, 0xff, IPaddrlen);
1130                 bytes2nibbles(nibnet, net, IPaddrlen);
1131                 bytes2nibbles(nibmask, mask, IPaddrlen);
1132
1133                 /* copy prefix of f, in reverse order, to start of net. */
1134                 for (i = 0; i < pfxnibs; i++)
1135                         nibnet[i] = strtol(f[pfxnibs - 1 - i], nil, 16);
1136                 /* zero nibbles of mask after prefix in net */
1137                 memset(nibmask + pfxnibs, 0, V6maxrevdomdepth - pfxnibs);
1138
1139                 nibbles2bytes(net, nibnet, 2*IPaddrlen);
1140                 nibbles2bytes(mask, nibmask, 2*IPaddrlen);
1141
1142                 /*
1143                  * go through all domain entries looking for RR's
1144                  * in this network and create ptrs.
1145                  */
1146                 dnptr(net, mask, dom, Taaaa, V6maxrevdomdepth - pfxnibs, Ptrttl);
1147         }
1148 }
1149
1150 /*
1151  *  create ptrs that are in our areas
1152  */
1153 static void
1154 createptrs(void)
1155 {
1156         createv4ptrs();
1157         createv6ptrs();
1158 }
1159
1160 /*
1161  * is this domain (or DOMAIN or Domain or dOMAIN)
1162  * internal to our organisation (behind our firewall)?
1163  * only inside straddling servers care, everybody else gets told `yes',
1164  * so they'll use mntpt for their queries.
1165  */
1166 int
1167 insideaddr(char *dom)
1168 {
1169         int domlen, vallen, rv;
1170         Ndbtuple *t;
1171
1172         if (!cfg.inside || !cfg.straddle || !cfg.serve)
1173                 return 1;
1174         if (dom[0] == '\0' || strcmp(dom, ".") == 0)    /* dns root? */
1175                 return 1;                       /* hack for initialisation */
1176
1177         lock(&dblock);
1178         if (indoms == nil)
1179                 loaddomsrvs();
1180         if (indoms == nil) {
1181                 unlock(&dblock);
1182                 return 1;  /* no "inside-dom" sys, try inside nameservers */
1183         }
1184
1185         rv = 0;
1186         domlen = strlen(dom);
1187         for (t = indoms; t != nil; t = t->entry) {
1188                 if (strcmp(t->attr, "dom") != 0)
1189                         continue;
1190                 vallen = strlen(t->val);
1191                 if (cistrcmp(dom, t->val) == 0 ||
1192                     domlen > vallen &&
1193                      cistrcmp(dom + domlen - vallen, t->val) == 0 &&
1194                      dom[domlen - vallen - 1] == '.') {
1195                         rv = 1;
1196                         break;
1197                 }
1198         }
1199         unlock(&dblock);
1200         return rv;
1201 }
1202
1203 int
1204 insidens(uchar *ip)
1205 {
1206         uchar ipa[IPaddrlen];
1207         Ndbtuple *t;
1208
1209         for (t = innmsrvs; t != nil; t = t->entry)
1210                 if (strcmp(t->attr, "ip") == 0) {
1211                         parseip(ipa, t->val);
1212                         if (memcmp(ipa, ip, sizeof ipa) == 0)
1213                                 return 1;
1214                 }
1215         return 0;
1216 }
1217
1218 int
1219 outsidensip(int n, uchar *ip)
1220 {
1221         int i;
1222         Ndbtuple *t;
1223
1224         i = 0;
1225         for (t = outnmsrvs; t != nil; t = t->entry)
1226                 if (strcmp(t->attr, "ip") == 0 && i++ == n) {
1227                         parseip(ip, t->val);
1228                         return 0;
1229                 }
1230         return -1;
1231 }