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