11 Nibmask = (1<<Nibwidth) - 1,
12 V6maxrevdomdepth = 128 / Nibwidth, /* bits / bits-per-nibble */
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.
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*);
40 static int implemented[Tall] =
54 /* straddle server configuration */
55 static Ndbtuple *indoms, *innmsrvs, *outnmsrvs;
58 nstrcpy(char *to, char *from, int len)
60 strncpy(to, from, len);
73 xdb = ndbopen(dbfile); /* /lib/ndb */
75 snprint(netdbnm, sizeof netdbnm, "%s/ndb", mntpt);
76 for(netdb = xdb; netdb; netdb = netdb->next)
77 if(strcmp(netdb->file, netdbnm) == 0){
82 netdb = ndbopen(netdbnm); /* /net/ndb */
86 db = ndbcat(netdb, xdb); /* both */
91 * lookup an RR in the network database, look for matches
92 * against both the domain name and the wildcarded domain name.
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
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.
103 dblookup(char *name, int class, int type, int auth, int ttl)
106 char buf[Domlen], *wild;
110 /* so far only internet lookups are implemented */
118 for (type = Ta; type < Tall; type++)
119 if(implemented[type])
120 rrcat(&rp, dblookup(name, class, type, auth, ttl));
126 dp = idnlookup(name, class, 1);
128 if(opendatabase() < 0)
133 /* first try the given name */
135 rp = rrlookup(dp, type, NOneg);
137 rp = dblookup1(name, type, auth, ttl);
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);
148 rp = rrlookup(ndp, type, NOneg);
150 rp = dblookup1(buf, type, auth, ttl);
155 /* add owner to uncached records */
157 for(tp = rp; tp; tp = tp->next)
161 * don't call it non-existent if it's not ours
162 * (unless we're a resolver).
164 if(err == Rname && (!inmyarea(dp->name) || cfg.resolver))
174 intval(Ndbtuple *entry, Ndbtuple *pair, char *attr, ulong def)
176 Ndbtuple *t = look(entry, pair, attr);
178 return (t? strtoul(t->val, 0, 10): def);
189 cp += runetochar(cp, &r);
194 * lookup an RR in the network database
197 dblookup1(char *name, int type, int auth, int ttl)
205 RR *(*f)(Ndbtuple*, Ndbtuple*);
248 return doaxfr(db, name);
250 // dnslog("dblookup1(%s) bad type", name);
255 * find a matching entry in the database
257 nstrcpy(dname, name, sizeof dname);
260 case 1: /* try unicode */
261 if(idn2utf(name, dname, sizeof dname) == nil){
262 nstrcpy(dname, name, sizeof dname);
265 if(strcmp(name, dname) == 0)
268 case 3: /* try ascii (lower case) */
269 if(utf2idn(name, dname, sizeof dname) == nil)
273 if(strcmp(name, dname) == 0)
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));
286 // dnslog("dblookup1(%s) name not found", name);
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);
297 /* ttl is maximum of soa minttl and entry's ttl ala rfc883 */
298 x = intval(t, s.t, "ttl", 0);
302 /* default ttl is one day */
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.
315 if(found == 0 && strcmp(nt->attr, "dom") == 0){
316 nstrcpy(dname, nt->val, sizeof dname);
319 if(strcmp(attr, nt->attr) == 0){
326 dp = idnlookup(dname, Cin, 1);
337 /* search whole entry */
338 for(nt = t; nt; nt = nt->entry)
339 if(nt->ptr == 0 && strcmp(attr, nt->attr) == 0){
346 dp = idnlookup(dname, Cin, 1);
353 // dnslog("dblookup1(%s) -> %#p", name, list);
358 * make various types of resource records from a database entry
361 addrrr(Ndbtuple *entry, Ndbtuple *pair)
364 uchar addr[IPaddrlen];
367 parseip(addr, pair->val);
372 rp->ip = dnlookup(pair->val, Cin, 1);
376 nullrr(Ndbtuple *entry, Ndbtuple *pair)
382 rp->null->data = (uchar*)estrdup(pair->val);
383 rp->null->dlen = strlen((char*)rp->null->data);
387 * txt rr strings are at most 255 bytes long. one
388 * can represent longer strings by multiple concatenated
392 txtrr(Ndbtuple *entry, Ndbtuple *pair)
402 len = strlen(pair->val);
405 t = emalloc(sizeof(*t));
413 memmove(t->p, pair->val+sofar, i);
423 cnamerr(Ndbtuple *entry, Ndbtuple *pair)
428 rp = rralloc(Tcname);
429 rp->host = idnlookup(pair->val, Cin, 1);
433 mxrr(Ndbtuple *entry, Ndbtuple *pair)
438 rp->host = idnlookup(pair->val, Cin, 1);
439 rp->pref = intval(entry, pair, "pref", 1);
443 nsrr(Ndbtuple *entry, Ndbtuple *pair)
449 rp->host = idnlookup(pair->val, Cin, 1);
450 t = look(entry, pair, "soa");
451 if(t && t->val[0] == 0)
456 ptrrr(Ndbtuple *entry, Ndbtuple *pair)
462 rp->ptr = dnlookup(pair->val, Cin, 1);
466 soarr(Ndbtuple *entry, Ndbtuple *pair)
469 Ndbtuple *ns, *mb, *t;
470 char mailbox[Domlen];
476 for(ndb = db; ndb; ndb = ndb->next)
477 if(ndb->mtime > rp->soa->serial)
478 rp->soa->serial = ndb->mtime;
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);
486 ns = look(entry, pair, "ns");
488 ns = look(entry, pair, "dom");
489 rp->host = idnlookup(ns->val, Cin, 1);
493 * mbox=person@machine.dom
494 * mbox=person.machine.dom
496 mb = look(entry, pair, "mbox");
498 mb = look(entry, pair, "mb");
500 if(strchr(mb->val, '.')) {
501 p = strchr(mb->val, '@');
504 rp->rmb = idnlookup(mb->val, Cin, 1);
506 snprint(mailbox, sizeof mailbox, "%s.%s",
508 rp->rmb = idnlookup(mailbox, Cin, 1);
511 snprint(mailbox, sizeof mailbox, "postmaster.%s", ns->val);
512 rp->rmb = idnlookup(mailbox, Cin, 1);
516 * hang dns slaves off of the soa. this is
517 * for managing the area.
519 for(t = entry; t != nil; t = t->entry)
520 if(strcmp(t->attr, "dnsslave") == 0)
521 addserver(&rp->soa->slaves, t->val);
527 srvrr(Ndbtuple *entry, Ndbtuple *pair)
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);
541 * Look for a pair with the given attribute. look first on the same line,
542 * then in the whole entry.
545 look(Ndbtuple *entry, Ndbtuple *line, char *attr)
549 /* first look on same line (closer binding) */
551 if(strcmp(attr, nt->attr) == 0)
557 /* search whole tuple */
558 for(nt = entry; nt; nt = nt->entry)
559 if(strcmp(attr, nt->attr) == 0)
565 linkrr(RR *rp, DN *dp, RR **l)
574 /* these are answered specially by the tcp version */
576 doaxfr(Ndb *db, char *name)
584 * read the all the soa's from the database to determine area's.
585 * this is only used when we're not caching the database.
593 dnslog("rereading %s", db->file);
595 while(t = ndbparse(db))
600 * read the database into the cache
603 dbpair2cache(DN *dp, Ndbtuple *entry, Ndbtuple *pair)
609 if(strcmp(pair->attr, "ip") == 0 ||
610 strcmp(pair->attr, "ipv6") == 0) {
612 rp = addrrr(entry, pair);
614 else if(strcmp(pair->attr, "ns") == 0)
615 rp = nsrr(entry, pair);
616 else if(strcmp(pair->attr, "soa") == 0) {
617 rp = soarr(entry, pair);
618 addarea(dp, rp, pair);
620 else if(strcmp(pair->attr, "mx") == 0)
621 rp = mxrr(entry, pair);
622 else if(strcmp(pair->attr, "srv") == 0)
623 rp = srvrr(entry, pair);
624 else if(strcmp(pair->attr, "cname") == 0)
625 rp = cnamerr(entry, pair);
626 else if(strcmp(pair->attr, "nullrr") == 0)
627 rp = nullrr(entry, pair);
628 else if(strcmp(pair->attr, "txtrr") == 0)
629 rp = txtrr(entry, pair);
635 rp->ttl = intval(entry, pair, "ttl", rp->ttl);
636 rrattach(rp, Notauthoritative);
640 dbtuple2cache(Ndbtuple *t)
645 for(et = t; et; et = et->entry)
646 if(strcmp(et->attr, "dom") == 0){
647 dp = idnlookup(et->val, Cin, 1);
649 /* first same line */
650 for(nt = et->line; nt != et; nt = nt->line){
651 dbpair2cache(dp, t, nt);
655 /* then rest of entry */
656 for(nt = t; nt; nt = nt->entry){
658 dbpair2cache(dp, t, nt);
664 dbfile2cache(Ndb *db)
669 dnslog("rereading %s", db->file);
671 while(t = ndbparse(db)){
677 /* called with dblock held */
683 if (!cfg.inside || !cfg.straddle || !cfg.serve)
689 indoms = innmsrvs = outnmsrvs = nil;
693 free(ndbgetvalue(db, &s, "sys", "inside-dom", "dom", &indoms));
694 free(ndbgetvalue(db, &s, "sys", "inside-ns", "ip", &innmsrvs));
695 free(ndbgetvalue(db, &s, "sys", "outside-ns", "ip", &outnmsrvs));
696 dnslog("[%d] ndb changed: reloaded inside-dom, inside-ns, outside-ns",
706 static ulong lastcheck, lastyoungest;
708 /* no faster than once every 2 minutes */
709 if(now < lastcheck + 2*Min && !doit)
712 refresh_areas(owned);
716 if(opendatabase() < 0){
722 * file may be changing as we are reading it, so loop till
723 * mod times are consistent.
725 * we don't use the times in the ndb records because they may
726 * change outside of refreshing our cached knowledge.
731 for(ndb = db; ndb; ndb = ndb->next)
732 /* dirfstat avoids walking the mount table each time */
733 if((d = dirfstat(Bfildes(&ndb->b))) != nil ||
734 (d = dirstat(ndb->file)) != nil){
735 if(d->mtime > youngest)
739 if(!doit && youngest == lastyoungest)
742 /* forget our area definition */
744 freearea(&delegated);
746 /* reopen all the files (to get oldest for time stamp) */
747 for(ndb = db; ndb; ndb = ndb->next)
750 /* reload straddle-server configuration */
754 /* mark all db records as timed out */
757 /* read in new entries */
758 for(ndb = db; ndb; ndb = ndb->next)
761 /* mark as authoritative anything in our domain */
764 /* remove old entries */
767 /* read all the soa's to get database defaults */
768 for(ndb = db; ndb; ndb = ndb->next)
772 lastyoungest = youngest;
779 extern char mntpt[Maxpath]; /* net mountpoint */
780 static uchar ipaddr[IPaddrlen]; /* my ip address */
784 * caller ndbfrees the result
787 lookupinfo(char *attr)
793 if(ipcmp(ipaddr, IPnoaddr) == 0)
794 if(myipaddr(ipaddr, mntpt) < 0)
797 snprint(buf, sizeof buf, "%I", ipaddr);
801 if(opendatabase() < 0){
805 t = ndbipinfo(db, "ip", buf, a, 1);
811 * return non-zero if this is a bad delegation
814 baddelegation(RR *rp, RR *nsrp, uchar *addr)
824 t = lookupinfo("dom");
826 /* see if delegating to us what we don't own */
827 for(nt = t; nt != nil; nt = nt->entry)
828 if(rp->host && cistrcmp(rp->host->name, nt->val) == 0)
831 if(nt != nil && !inmyarea(rp->owner->name)){
834 dnslog("bad delegation %R from %I/%s; "
835 "no further logging of them",
836 rp, addr, nsrp->host->name);
851 if(ipcmp(ipaddr, IPnoaddr) == 0)
852 if(myipaddr(ipaddr, mntpt) < 0)
855 snprint(buf, sizeof buf, "%I", ipaddr);
856 if (strcmp(addr, buf) == 0) {
857 dnslog("rejecting my ip %s as local dns server", addr);
861 snprint(buf, sizeof buf, "%s/ipselftab", mntpt);
862 bp = Bopen(buf, OREAD);
864 while ((line = Brdline(bp, '\n')) != nil) {
865 line[Blinelen(bp) - 1] = '\0';
866 sp = strchr(line, ' ');
869 if (strcmp(addr, line) == 0) {
870 dnslog("rejecting my ip %s as local dns server",
881 static char *locdns[20];
882 static QLock locdnslck;
885 addlocaldnsserver(DN *dp, int class, char *ipaddr, int i)
893 /* reject our own ip addresses so we don't query ourselves via udp */
898 for (n = 0; n < i && n < nelem(locdns) && locdns[n]; n++)
899 if (strcmp(locdns[n], ipaddr) == 0) {
900 dnslog("rejecting duplicate local dns server ip %s",
905 if (n < nelem(locdns))
906 if (locdns[n] == nil || ++n < nelem(locdns))
907 locdns[n] = strdup(ipaddr); /* remember 1st few local ns */
910 /* ns record for name server, make up an impossible name */
912 snprint(buf, sizeof buf, "local#dns#server%d", i);
913 nsdp = dnlookup(buf, class, 1);
915 rp->owner = dp; /* e.g., local#dns#servers */
919 rrattach(rp, Authoritative); /* will not attach rrs in my area */
922 /* A or AAAA record */
923 if (parseip(ip, ipaddr) >= 0 && isv4(ip))
927 rp->ip = dnlookup(ipaddr, class, 1);
932 rrattach(rp, Authoritative); /* will not attach rrs in my area */
935 dnslog("added local dns server %s at %s", buf, ipaddr);
939 * return list of dns server addresses to use when
940 * acting just as a resolver.
943 dnsservers(int class)
952 dp = dnlookup("local#dns#servers", class, 1);
953 nsrp = rrlookup(dp, Tns, NOneg);
957 p = getenv("DNSSERVER"); /* list of ip addresses */
959 n = tokenize(p, args, nelem(args));
960 for(i = 0; i < n; i++)
961 addlocaldnsserver(dp, class, args[i], i);
964 t = lookupinfo("@dns"); /* @dns=ip1 @dns=ip2 ... */
968 for(nt = t; nt != nil; nt = nt->entry){
969 addlocaldnsserver(dp, class, nt->val, i);
975 return rrlookup(dp, Tns, NOneg);
979 addlocaldnsdomain(DN *dp, int class, char *domain)
985 rp->ptr = dnlookup(domain, class, 1);
989 rrattach(rp, Authoritative);
994 * return list of domains to use when resolving names without '.'s
997 domainlist(int class)
1003 dp = dnlookup("local#dns#domains", class, 1);
1004 rp = rrlookup(dp, Tptr, NOneg);
1008 t = lookupinfo("dnsdomain");
1011 for(nt = t; nt != nil; nt = nt->entry)
1012 addlocaldnsdomain(dp, class, nt->val);
1015 return rrlookup(dp, Tptr, NOneg);
1018 char *v4ptrdom = ".in-addr.arpa";
1019 char *v6ptrdom = ".ip6.arpa"; /* ip6.int deprecated, rfc 3152 */
1027 * create ptrs that are in our v4 areas
1034 char buf[Domlen], ipa[48];
1036 uchar net[IPaddrlen], mask[IPaddrlen];
1040 dlen = strlen(v4ptrdom);
1041 for(s = owned; s; s = s->next){
1042 dom = s->soarr->owner->name;
1044 if((len <= dlen || cistrcmp(dom+len-dlen, v4ptrdom) != 0) &&
1045 cistrcmp(dom, v4ptrdom+1) != 0)
1048 /* get mask and net value */
1049 nstrcpy(buf, dom, sizeof buf);
1050 /* buf contains something like 178.204.in-addr.arpa (n==4) */
1051 n = getfields(buf, f, nelem(f), 0, ".");
1052 memset(mask, 0xff, IPaddrlen);
1053 ipmove(net, v4prefix);
1056 net[IPv4off] = atoi(f[0]);
1057 mask[IPv4off+1] = 0;
1058 mask[IPv4off+2] = 0;
1059 mask[IPv4off+3] = 0;
1062 net[IPv4off] = atoi(f[1]);
1063 net[IPv4off+1] = atoi(f[0]);
1064 mask[IPv4off+2] = 0;
1065 mask[IPv4off+3] = 0;
1068 net[IPv4off] = atoi(f[2]);
1069 net[IPv4off+1] = atoi(f[1]);
1070 net[IPv4off+2] = atoi(f[0]);
1071 mask[IPv4off+3] = 0;
1073 case 6: /* rfc2317: classless in-addr.arpa delegation */
1074 net[IPv4off] = atoi(f[3]);
1075 net[IPv4off+1] = atoi(f[2]);
1076 net[IPv4off+2] = atoi(f[1]);
1077 net[IPv4off+3] = atoi(f[0]);
1078 sprint(ipa, "%I", net);
1079 t = ndbipinfo(db, "ip", ipa, attribs, 1);
1080 if(t == nil) /* could be a reverse with no forward */
1082 nt = look(t, t, "ipmask");
1083 if(nt == nil){ /* we're confused */
1087 parseipmask(mask, nt->val);
1096 * go through all domain entries looking for RR's
1097 * in this network and create ptrs.
1098 * +2 for ".in-addr.arpa".
1100 dnptr(net, mask, dom, Ta, 4+2-n, Ptrttl);
1104 /* convert bytes to nibbles, big-endian */
1106 bytes2nibbles(uchar *nibbles, uchar *bytes, int nbytes)
1108 while (nbytes-- > 0) {
1109 *nibbles++ = *bytes >> Nibwidth;
1110 *nibbles++ = *bytes++ & Nibmask;
1115 nibbles2bytes(uchar *bytes, uchar *nibbles, int nnibs)
1117 for (; nnibs >= 2; nnibs -= 2) {
1118 *bytes++ = nibbles[0] << Nibwidth | (nibbles[1]&Nibmask);
1122 *bytes = nibbles[0] << Nibwidth;
1126 * create ptrs that are in our v6 areas. see rfc3596
1131 int len, dlen, i, n, pfxnibs;
1135 uchar net[IPaddrlen], mask[IPaddrlen];
1136 uchar nibnet[IPaddrlen*2], nibmask[IPaddrlen*2];
1139 dlen = strlen(v6ptrdom);
1140 for(s = owned; s; s = s->next){
1141 dom = s->soarr->owner->name;
1143 if((len <= dlen || cistrcmp(dom+len-dlen, v6ptrdom) != 0) &&
1144 cistrcmp(dom, v6ptrdom+1) != 0)
1147 /* get mask and net value */
1148 nstrcpy(buf, dom, sizeof buf);
1149 /* buf contains something like 2.0.0.2.ip6.arpa (n==6) */
1150 n = getfields(buf, f, nelem(f), 0, ".");
1151 pfxnibs = n - 2; /* 2 for .ip6.arpa */
1152 if (pfxnibs < 0 || pfxnibs > V6maxrevdomdepth)
1155 memset(net, 0, IPaddrlen);
1156 memset(mask, 0xff, IPaddrlen);
1157 bytes2nibbles(nibnet, net, IPaddrlen);
1158 bytes2nibbles(nibmask, mask, IPaddrlen);
1160 /* copy prefix of f, in reverse order, to start of net. */
1161 for (i = 0; i < pfxnibs; i++)
1162 nibnet[i] = strtol(f[pfxnibs - 1 - i], nil, 16);
1163 /* zero nibbles of mask after prefix in net */
1164 memset(nibmask + pfxnibs, 0, V6maxrevdomdepth - pfxnibs);
1166 nibbles2bytes(net, nibnet, 2*IPaddrlen);
1167 nibbles2bytes(mask, nibmask, 2*IPaddrlen);
1170 * go through all domain entries looking for RR's
1171 * in this network and create ptrs.
1173 dnptr(net, mask, dom, Taaaa, V6maxrevdomdepth - pfxnibs, Ptrttl);
1178 * create ptrs that are in our areas
1188 * is this domain (or DOMAIN or Domain or dOMAIN)
1189 * internal to our organisation (behind our firewall)?
1190 * only inside straddling servers care, everybody else gets told `yes',
1191 * so they'll use mntpt for their queries.
1194 insideaddr(char *dom)
1196 int domlen, vallen, rv;
1199 if (!cfg.inside || !cfg.straddle || !cfg.serve)
1201 if (dom[0] == '\0' || strcmp(dom, ".") == 0) /* dns root? */
1202 return 1; /* hack for initialisation */
1207 if (indoms == nil) {
1209 return 1; /* no "inside-dom" sys, try inside nameservers */
1213 domlen = strlen(dom);
1214 for (t = indoms; t != nil; t = t->entry) {
1215 if (strcmp(t->attr, "dom") != 0)
1217 vallen = strlen(t->val);
1218 if (cistrcmp(dom, t->val) == 0 ||
1220 cistrcmp(dom + domlen - vallen, t->val) == 0 &&
1221 dom[domlen - vallen - 1] == '.') {
1233 uchar ipa[IPaddrlen];
1236 for (t = innmsrvs; t != nil; t = t->entry)
1237 if (strcmp(t->attr, "ip") == 0) {
1238 parseip(ipa, t->val);
1239 if (memcmp(ipa, ip, sizeof ipa) == 0)
1246 outsidensip(int n, uchar *ip)
1252 for (t = outnmsrvs; t != nil; t = t->entry)
1253 if (strcmp(t->attr, "ip") == 0 && i++ == n) {
1254 parseip(ip, t->val);