10 Nibmask = (1<<Nibwidth) - 1,
11 V6maxrevdomdepth = 128 / Nibwidth, /* bits / bits-per-nibble */
14 * ttl for generated ptr records. it was zero, which might seem
15 * like a good idea, but some dns implementations seem to be
16 * confused by a zero ttl, and instead of using the data and then
17 * discarding the RR, they conclude that they don't have valid data.
25 static RR* addrrr(Ndbtuple*, Ndbtuple*);
26 static RR* cnamerr(Ndbtuple*, Ndbtuple*);
27 static void createptrs(void);
28 static RR* dblookup1(char*, int, int, int);
29 static RR* doaxfr(Ndb*, char*);
30 static Ndbtuple*look(Ndbtuple*, Ndbtuple*, char*);
31 static RR* mxrr(Ndbtuple*, Ndbtuple*);
32 static RR* nsrr(Ndbtuple*, Ndbtuple*);
33 static RR* nullrr(Ndbtuple*, Ndbtuple*);
34 static RR* ptrrr(Ndbtuple*, Ndbtuple*);
35 static RR* soarr(Ndbtuple*, Ndbtuple*);
36 static RR* srvrr(Ndbtuple*, Ndbtuple*);
37 static RR* txtrr(Ndbtuple*, Ndbtuple*);
39 static int implemented[Tall] =
53 /* straddle server configuration */
54 static Ndbtuple *indoms, *innmsrvs, *outnmsrvs;
57 nstrcpy(char *to, char *from, int len)
59 strncpy(to, from, len);
72 xdb = ndbopen(dbfile); /* /lib/ndb */
74 snprint(netdbnm, sizeof netdbnm, "%s/ndb", mntpt);
75 for(netdb = xdb; netdb; netdb = netdb->next)
76 if(strcmp(netdb->file, netdbnm) == 0){
81 netdb = ndbopen(netdbnm); /* /net/ndb */
85 db = ndbcat(netdb, xdb); /* both */
90 * lookup an RR in the network database, look for matches
91 * against both the domain name and the wildcarded domain name.
93 * the lock makes sure only one process can be accessing the data
94 * base at a time. This is important since there's a lot of
97 * e.g. for x.research.bell-labs.com, first look for a match against
98 * the x.research.bell-labs.com. If nothing matches,
99 * try *.research.bell-labs.com.
102 dblookup(char *name, int class, int type, int auth, int ttl)
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 = dnlookup(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 /* try lower case version */
142 for(cp = name; *cp; cp++)
145 rp = rrlookup(dp, type, NOneg);
147 rp = dblookup1(name, type, auth, ttl);
151 /* walk the domain name trying the wildcard '*' at each position */
152 for(wild = strchr(name, '.'); wild; wild = strchr(wild+1, '.')){
153 snprint(buf, sizeof buf, "*%s", wild);
154 ndp = dnlookup(buf, class, 1);
158 rp = rrlookup(ndp, type, NOneg);
160 rp = dblookup1(buf, type, auth, ttl);
165 /* add owner to uncached records */
167 for(tp = rp; tp; tp = tp->next)
171 * don't call it non-existent if it's not ours
172 * (unless we're a resolver).
174 if(err == Rname && (!inmyarea(name) || cfg.resolver))
184 intval(Ndbtuple *entry, Ndbtuple *pair, char *attr, ulong def)
186 Ndbtuple *t = look(entry, pair, attr);
188 return (t? strtoul(t->val, 0, 10): def);
192 * lookup an RR in the network database
195 dblookup1(char *name, int type, int auth, int ttl)
203 RR *(*f)(Ndbtuple*, Ndbtuple*);
246 return doaxfr(db, name);
248 // dnslog("dnlookup1(%s) bad type", name);
253 * find a matching entry in the database
256 free(ndbgetvalue(db, &s, "dom", name, attr, &t));
259 * hack for local names
261 if(t == nil && strchr(name, '.') == nil)
262 free(ndbgetvalue(db, &s, "sys", name, attr, &t));
264 // dnslog("dnlookup1(%s) name not found", name);
268 /* search whole entry for default domain name */
269 strncpy(dname, name, sizeof dname);
270 for(nt = t; nt; nt = nt->entry)
271 if(strcmp(nt->attr, "dom") == 0){
272 nstrcpy(dname, nt->val, sizeof dname);
276 /* ttl is maximum of soa minttl and entry's ttl ala rfc883 */
277 x = intval(t, s.t, "ttl", 0);
281 /* default ttl is one day */
286 * The database has 2 levels of precedence; line and entry.
287 * Pairs on the same line bind tighter than pairs in the
288 * same entry, so we search the line first.
294 if(found == 0 && strcmp(nt->attr, "dom") == 0){
295 nstrcpy(dname, nt->val, sizeof dname);
298 if(cistrcmp(attr, nt->attr) == 0){
305 dp = dnlookup(dname, Cin, 1);
316 /* search whole entry */
317 for(nt = t; nt; nt = nt->entry)
318 if(nt->ptr == 0 && cistrcmp(attr, nt->attr) == 0){
325 dp = dnlookup(dname, Cin, 1);
332 // dnslog("dnlookup1(%s) -> %#p", name, list);
337 * make various types of resource records from a database entry
340 addrrr(Ndbtuple *entry, Ndbtuple *pair)
343 uchar addr[IPaddrlen];
346 parseip(addr, pair->val);
351 rp->ip = dnlookup(pair->val, Cin, 1);
355 nullrr(Ndbtuple *entry, Ndbtuple *pair)
361 rp->null->data = (uchar*)estrdup(pair->val);
362 rp->null->dlen = strlen((char*)rp->null->data);
366 * txt rr strings are at most 255 bytes long. one
367 * can represent longer strings by multiple concatenated
371 txtrr(Ndbtuple *entry, Ndbtuple *pair)
381 len = strlen(pair->val);
384 t = emalloc(sizeof(*t));
392 memmove(t->p, pair->val+sofar, i);
402 cnamerr(Ndbtuple *entry, Ndbtuple *pair)
407 rp = rralloc(Tcname);
408 rp->host = dnlookup(pair->val, Cin, 1);
412 mxrr(Ndbtuple *entry, Ndbtuple *pair)
417 rp->host = dnlookup(pair->val, Cin, 1);
418 rp->pref = intval(entry, pair, "pref", 1);
422 nsrr(Ndbtuple *entry, Ndbtuple *pair)
428 rp->host = dnlookup(pair->val, Cin, 1);
429 t = look(entry, pair, "soa");
430 if(t && t->val[0] == 0)
435 ptrrr(Ndbtuple *entry, Ndbtuple *pair)
441 rp->ptr = dnlookup(pair->val, Cin, 1);
445 soarr(Ndbtuple *entry, Ndbtuple *pair)
448 Ndbtuple *ns, *mb, *t;
449 char mailbox[Domlen];
455 for(ndb = db; ndb; ndb = ndb->next)
456 if(ndb->mtime > rp->soa->serial)
457 rp->soa->serial = ndb->mtime;
459 rp->soa->retry = intval(entry, pair, "retry", Hour);
460 rp->soa->expire = intval(entry, pair, "expire", Day);
461 rp->soa->minttl = intval(entry, pair, "ttl", Day);
462 rp->soa->refresh = intval(entry, pair, "refresh", Day);
463 rp->soa->serial = intval(entry, pair, "serial", rp->soa->serial);
465 ns = look(entry, pair, "ns");
467 ns = look(entry, pair, "dom");
468 rp->host = dnlookup(ns->val, Cin, 1);
472 * mbox=person@machine.dom
473 * mbox=person.machine.dom
475 mb = look(entry, pair, "mbox");
477 mb = look(entry, pair, "mb");
479 if(strchr(mb->val, '.')) {
480 p = strchr(mb->val, '@');
483 rp->rmb = dnlookup(mb->val, Cin, 1);
485 snprint(mailbox, sizeof mailbox, "%s.%s",
487 rp->rmb = dnlookup(mailbox, Cin, 1);
490 snprint(mailbox, sizeof mailbox, "postmaster.%s", ns->val);
491 rp->rmb = dnlookup(mailbox, Cin, 1);
495 * hang dns slaves off of the soa. this is
496 * for managing the area.
498 for(t = entry; t != nil; t = t->entry)
499 if(strcmp(t->attr, "dnsslave") == 0)
500 addserver(&rp->soa->slaves, t->val);
506 srvrr(Ndbtuple *entry, Ndbtuple *pair)
511 rp->host = dnlookup(pair->val, Cin, 1);
512 rp->srv->pri = intval(entry, pair, "pri", 0);
513 rp->srv->weight = intval(entry, pair, "weight", 0);
514 /* TODO: translate service name to port # */
515 rp->port = intval(entry, pair, "port", 0);
520 * Look for a pair with the given attribute. look first on the same line,
521 * then in the whole entry.
524 look(Ndbtuple *entry, Ndbtuple *line, char *attr)
528 /* first look on same line (closer binding) */
530 if(cistrcmp(attr, nt->attr) == 0)
536 /* search whole tuple */
537 for(nt = entry; nt; nt = nt->entry)
538 if(cistrcmp(attr, nt->attr) == 0)
544 linkrr(RR *rp, DN *dp, RR **l)
553 /* these are answered specially by the tcp version */
555 doaxfr(Ndb *db, char *name)
563 * read the all the soa's from the database to determine area's.
564 * this is only used when we're not caching the database.
572 dnslog("rereading %s", db->file);
574 while(t = ndbparse(db))
579 * read the database into the cache
582 dbpair2cache(DN *dp, Ndbtuple *entry, Ndbtuple *pair)
588 if(cistrcmp(pair->attr, "ip") == 0 ||
589 cistrcmp(pair->attr, "ipv6") == 0){
591 rp = addrrr(entry, pair);
592 } else if(cistrcmp(pair->attr, "ns") == 0)
593 rp = nsrr(entry, pair);
594 else if(cistrcmp(pair->attr, "soa") == 0) {
595 rp = soarr(entry, pair);
596 addarea(dp, rp, pair);
597 } else if(cistrcmp(pair->attr, "mx") == 0)
598 rp = mxrr(entry, pair);
599 else if(cistrcmp(pair->attr, "srv") == 0)
600 rp = srvrr(entry, pair);
601 else if(cistrcmp(pair->attr, "cname") == 0)
602 rp = cnamerr(entry, pair);
603 else if(cistrcmp(pair->attr, "nullrr") == 0)
604 rp = nullrr(entry, pair);
605 else if(cistrcmp(pair->attr, "txtrr") == 0)
606 rp = txtrr(entry, pair);
613 rp->ttl = intval(entry, pair, "ttl", rp->ttl);
614 rrattach(rp, Notauthoritative);
617 dbtuple2cache(Ndbtuple *t)
622 for(et = t; et; et = et->entry)
623 if(strcmp(et->attr, "dom") == 0){
624 dp = dnlookup(et->val, Cin, 1);
626 /* first same line */
627 for(nt = et->line; nt != et; nt = nt->line){
628 dbpair2cache(dp, t, nt);
632 /* then rest of entry */
633 for(nt = t; nt; nt = nt->entry){
635 dbpair2cache(dp, t, nt);
641 dbfile2cache(Ndb *db)
646 dnslog("rereading %s", db->file);
648 while(t = ndbparse(db)){
654 /* called with dblock held */
660 if (!cfg.inside || !cfg.straddle || !cfg.serve)
666 indoms = innmsrvs = outnmsrvs = nil;
670 free(ndbgetvalue(db, &s, "sys", "inside-dom", "dom", &indoms));
671 free(ndbgetvalue(db, &s, "sys", "inside-ns", "ip", &innmsrvs));
672 free(ndbgetvalue(db, &s, "sys", "outside-ns", "ip", &outnmsrvs));
673 dnslog("[%d] ndb changed: reloaded inside-dom, inside-ns, outside-ns",
683 static ulong lastcheck, lastyoungest;
685 /* no faster than once every 2 minutes */
686 if(now < lastcheck + 2*Min && !doit)
689 refresh_areas(owned);
693 if(opendatabase() < 0){
699 * file may be changing as we are reading it, so loop till
700 * mod times are consistent.
702 * we don't use the times in the ndb records because they may
703 * change outside of refreshing our cached knowledge.
708 for(ndb = db; ndb; ndb = ndb->next)
709 /* dirfstat avoids walking the mount table each time */
710 if((d = dirfstat(Bfildes(&ndb->b))) != nil ||
711 (d = dirstat(ndb->file)) != nil){
712 if(d->mtime > youngest)
716 if(!doit && youngest == lastyoungest)
719 /* forget our area definition */
721 freearea(&delegated);
723 /* reopen all the files (to get oldest for time stamp) */
724 for(ndb = db; ndb; ndb = ndb->next)
727 /* reload straddle-server configuration */
731 /* mark all db records as timed out */
734 /* read in new entries */
735 for(ndb = db; ndb; ndb = ndb->next)
738 /* mark as authoritative anything in our domain */
741 /* remove old entries */
744 /* read all the soa's to get database defaults */
745 for(ndb = db; ndb; ndb = ndb->next)
749 lastyoungest = youngest;
756 extern char mntpt[Maxpath]; /* net mountpoint */
757 static uchar ipaddr[IPaddrlen]; /* my ip address */
761 * caller ndbfrees the result
764 lookupinfo(char *attr)
770 if(ipcmp(ipaddr, IPnoaddr) == 0)
771 if(myipaddr(ipaddr, mntpt) < 0)
774 snprint(buf, sizeof buf, "%I", ipaddr);
778 if(opendatabase() < 0){
782 t = ndbipinfo(db, "ip", buf, a, 1);
787 char *localservers = "local#dns#servers";
788 char *localserverprefix = "local#dns#server";
791 * return non-zero if this is a bad delegation
794 baddelegation(RR *rp, RR *nsrp, uchar *addr)
801 t = lookupinfo("dom");
803 for(; rp; rp = rp->next){
807 /* see if delegation is looping */
809 if(rp->owner != nsrp->owner)
810 if(subsume(rp->owner->name, nsrp->owner->name) &&
811 strcmp(nsrp->owner->name, localservers) != 0){
812 dnslog("delegation loop %R -> %R from %I",
820 /* see if delegating to us what we don't own */
821 for(nt = t; nt != nil; nt = nt->entry)
822 if(rp->host && cistrcmp(rp->host->name, nt->val) == 0)
824 if(nt != nil && !inmyarea(rp->owner->name)){
827 dnslog("bad delegation %R from %I; "
828 "no further logging of them", rp, addr);
844 if(ipcmp(ipaddr, IPnoaddr) == 0)
845 if(myipaddr(ipaddr, mntpt) < 0)
848 snprint(buf, sizeof buf, "%I", ipaddr);
849 if (strcmp(addr, buf) == 0) {
850 dnslog("rejecting my ip %s as local dns server", addr);
854 snprint(buf, sizeof buf, "%s/ipselftab", mntpt);
855 bp = Bopen(buf, OREAD);
857 while ((line = Brdline(bp, '\n')) != nil) {
858 line[Blinelen(bp) - 1] = '\0';
859 sp = strchr(line, ' ');
862 if (strcmp(addr, line) == 0) {
863 dnslog("rejecting my ip %s as local dns server",
874 static char *locdns[20];
875 static QLock locdnslck;
878 addlocaldnsserver(DN *dp, int class, char *ipaddr, int i)
886 /* reject our own ip addresses so we don't query ourselves via udp */
891 for (n = 0; n < i && n < nelem(locdns) && locdns[n]; n++)
892 if (strcmp(locdns[n], ipaddr) == 0) {
893 dnslog("rejecting duplicate local dns server ip %s",
898 if (n < nelem(locdns))
899 if (locdns[n] == nil || ++n < nelem(locdns))
900 locdns[n] = strdup(ipaddr); /* remember 1st few local ns */
903 /* ns record for name server, make up an impossible name */
905 snprint(buf, sizeof buf, "%s%d", localserverprefix, i);
906 nsdp = dnlookup(buf, class, 1);
908 rp->owner = dp; /* e.g., local#dns#servers */
911 // rp->ttl = 10*Min; /* seems too short */
912 rp->ttl = (1UL<<31)-1;
913 rrattach(rp, Authoritative); /* will not attach rrs in my area */
915 /* A or AAAA record */
916 if (parseip(ip, ipaddr) >= 0 && isv4(ip))
920 rp->ip = dnlookup(ipaddr, class, 1);
924 // rp->ttl = 10*Min; /* seems too short */
925 rp->ttl = (1UL<<31)-1;
926 rrattach(rp, Authoritative); /* will not attach rrs in my area */
928 dnslog("added local dns server %s at %s", buf, ipaddr);
932 * return list of dns server addresses to use when
933 * acting just as a resolver.
936 dnsservers(int class)
945 dp = dnlookup(localservers, class, 1);
946 nsrp = rrlookup(dp, Tns, NOneg);
950 p = getenv("DNSSERVER"); /* list of ip addresses */
952 n = tokenize(p, args, nelem(args));
953 for(i = 0; i < n; i++)
954 addlocaldnsserver(dp, class, args[i], i);
957 t = lookupinfo("@dns"); /* @dns=ip1 @dns=ip2 ... */
961 for(nt = t; nt != nil; nt = nt->entry){
962 addlocaldnsserver(dp, class, nt->val, i);
968 return rrlookup(dp, Tns, NOneg);
972 addlocaldnsdomain(DN *dp, int class, char *domain)
978 rp->ptr = dnlookup(domain, class, 1);
982 rrattach(rp, Authoritative);
986 * return list of domains to use when resolving names without '.'s
989 domainlist(int class)
995 dp = dnlookup("local#dns#domains", class, 1);
996 rp = rrlookup(dp, Tptr, NOneg);
1000 t = lookupinfo("dnsdomain");
1003 for(nt = t; nt != nil; nt = nt->entry)
1004 addlocaldnsdomain(dp, class, nt->val);
1007 return rrlookup(dp, Tptr, NOneg);
1010 char *v4ptrdom = ".in-addr.arpa";
1011 char *v6ptrdom = ".ip6.arpa"; /* ip6.int deprecated, rfc 3152 */
1019 * create ptrs that are in our v4 areas
1026 char buf[Domlen+1], ipa[48];
1028 uchar net[IPaddrlen], mask[IPaddrlen];
1032 dlen = strlen(v4ptrdom);
1033 for(s = owned; s; s = s->next){
1034 dom = s->soarr->owner->name;
1036 if((len <= dlen || cistrcmp(dom+len-dlen, v4ptrdom) != 0) &&
1037 cistrcmp(dom, v4ptrdom+1) != 0)
1040 /* get mask and net value */
1041 strncpy(buf, dom, sizeof buf);
1042 buf[sizeof buf-1] = 0;
1043 /* buf contains something like 178.204.in-addr.arpa (n==4) */
1044 n = getfields(buf, f, nelem(f), 0, ".");
1045 memset(mask, 0xff, IPaddrlen);
1046 ipmove(net, v4prefix);
1049 net[IPv4off] = atoi(f[0]);
1050 mask[IPv4off+1] = 0;
1051 mask[IPv4off+2] = 0;
1052 mask[IPv4off+3] = 0;
1055 net[IPv4off] = atoi(f[1]);
1056 net[IPv4off+1] = atoi(f[0]);
1057 mask[IPv4off+2] = 0;
1058 mask[IPv4off+3] = 0;
1061 net[IPv4off] = atoi(f[2]);
1062 net[IPv4off+1] = atoi(f[1]);
1063 net[IPv4off+2] = atoi(f[0]);
1064 mask[IPv4off+3] = 0;
1066 case 6: /* rfc2317: classless in-addr.arpa delegation */
1067 net[IPv4off] = atoi(f[3]);
1068 net[IPv4off+1] = atoi(f[2]);
1069 net[IPv4off+2] = atoi(f[1]);
1070 net[IPv4off+3] = atoi(f[0]);
1071 sprint(ipa, "%I", net);
1072 t = ndbipinfo(db, "ip", ipa, attribs, 1);
1073 if(t == nil) /* could be a reverse with no forward */
1075 nt = look(t, t, "ipmask");
1076 if(nt == nil){ /* we're confused */
1080 parseipmask(mask, nt->val);
1089 * go through all domain entries looking for RR's
1090 * in this network and create ptrs.
1091 * +2 for ".in-addr.arpa".
1093 dnptr(net, mask, dom, Ta, 4+2-n, Ptrttl);
1097 /* convert bytes to nibbles, big-endian */
1099 bytes2nibbles(uchar *nibbles, uchar *bytes, int nbytes)
1101 while (nbytes-- > 0) {
1102 *nibbles++ = *bytes >> Nibwidth;
1103 *nibbles++ = *bytes++ & Nibmask;
1108 nibbles2bytes(uchar *bytes, uchar *nibbles, int nnibs)
1110 for (; nnibs >= 2; nnibs -= 2) {
1111 *bytes++ = nibbles[0] << Nibwidth | (nibbles[1]&Nibmask);
1115 *bytes = nibbles[0] << Nibwidth;
1119 * create ptrs that are in our v6 areas. see rfc3596
1124 int len, dlen, i, n, pfxnibs;
1128 uchar net[IPaddrlen], mask[IPaddrlen];
1129 uchar nibnet[IPaddrlen*2], nibmask[IPaddrlen*2];
1132 dlen = strlen(v6ptrdom);
1133 for(s = owned; s; s = s->next){
1134 dom = s->soarr->owner->name;
1136 if((len <= dlen || cistrcmp(dom+len-dlen, v6ptrdom) != 0) &&
1137 cistrcmp(dom, v6ptrdom+1) != 0)
1140 /* get mask and net value */
1141 strncpy(buf, dom, sizeof buf);
1142 buf[sizeof buf-1] = 0;
1143 /* buf contains something like 2.0.0.2.ip6.arpa (n==6) */
1144 n = getfields(buf, f, nelem(f), 0, ".");
1145 pfxnibs = n - 2; /* 2 for .ip6.arpa */
1146 if (pfxnibs < 0 || pfxnibs > V6maxrevdomdepth)
1149 memset(net, 0, IPaddrlen);
1150 memset(mask, 0xff, IPaddrlen);
1151 bytes2nibbles(nibnet, net, IPaddrlen);
1152 bytes2nibbles(nibmask, mask, IPaddrlen);
1154 /* copy prefix of f, in reverse order, to start of net. */
1155 for (i = 0; i < pfxnibs; i++)
1156 nibnet[i] = strtol(f[pfxnibs - 1 - i], nil, 16);
1157 /* zero nibbles of mask after prefix in net */
1158 memset(nibmask + pfxnibs, 0, V6maxrevdomdepth - pfxnibs);
1160 nibbles2bytes(net, nibnet, 2*IPaddrlen);
1161 nibbles2bytes(mask, nibmask, 2*IPaddrlen);
1164 * go through all domain entries looking for RR's
1165 * in this network and create ptrs.
1167 dnptr(net, mask, dom, Taaaa, V6maxrevdomdepth - pfxnibs, Ptrttl);
1172 * create ptrs that are in our areas
1182 * is this domain (or DOMAIN or Domain or dOMAIN)
1183 * internal to our organisation (behind our firewall)?
1184 * only inside straddling servers care, everybody else gets told `yes',
1185 * so they'll use mntpt for their queries.
1188 insideaddr(char *dom)
1190 int domlen, vallen, rv;
1193 if (!cfg.inside || !cfg.straddle || !cfg.serve)
1195 if (dom[0] == '\0' || strcmp(dom, ".") == 0) /* dns root? */
1196 return 1; /* hack for initialisation */
1201 if (indoms == nil) {
1203 return 1; /* no "inside-dom" sys, try inside nameservers */
1207 domlen = strlen(dom);
1208 for (t = indoms; t != nil; t = t->entry) {
1209 if (strcmp(t->attr, "dom") != 0)
1211 vallen = strlen(t->val);
1212 if (cistrcmp(dom, t->val) == 0 ||
1214 cistrcmp(dom + domlen - vallen, t->val) == 0 &&
1215 dom[domlen - vallen - 1] == '.') {
1227 uchar ipa[IPaddrlen];
1230 for (t = innmsrvs; t != nil; t = t->entry)
1231 if (strcmp(t->attr, "ip") == 0) {
1232 parseip(ipa, t->val);
1233 if (memcmp(ipa, ip, sizeof ipa) == 0)
1240 outsidensip(int n, uchar *ip)
1246 for (t = outnmsrvs; t != nil; t = t->entry)
1247 if (strcmp(t->attr, "ip") == 0 && i++ == n) {
1248 parseip(ip, t->val);