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.
27 static QLock ipifclock;
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*);
43 static int implemented[Tall] =
57 /* straddle server configuration */
58 static Ndbtuple *indoms, *innmsrvs, *outnmsrvs;
61 nstrcpy(char *to, char *from, int len)
63 strncpy(to, from, len);
76 xdb = ndbopen(dbfile); /* /lib/ndb */
78 snprint(netdbnm, sizeof netdbnm, "%s/ndb", mntpt);
79 for(netdb = xdb; netdb; netdb = netdb->next)
80 if(strcmp(netdb->file, netdbnm) == 0){
85 netdb = ndbopen(netdbnm); /* /net/ndb */
89 db = ndbcat(netdb, xdb); /* both */
90 return db!=nil ? 0: -1;
94 * lookup an RR in the network database, look for matches
95 * against both the domain name and the wildcarded domain name.
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
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.
106 dblookup(char *name, int class, int type, int auth, int ttl)
109 char buf[Domlen], *wild;
113 /* so far only internet lookups are implemented */
121 for (type = Ta; type < Tall; type++)
122 if(implemented[type])
123 rrcat(&rp, dblookup(name, class, type, auth, ttl));
129 dp = idnlookup(name, class, 1);
131 if(opendatabase() < 0)
136 /* first try the given name */
138 rp = rrlookup(dp, type, NOneg);
140 rp = dblookup1(name, type, auth, ttl);
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);
151 rp = rrlookup(ndp, type, NOneg);
153 rp = dblookup1(buf, type, auth, ttl);
158 /* add owner to uncached records */
160 for(tp = rp; tp; tp = tp->next)
164 * don't call it non-existent if it's not ours
165 * (unless we're a resolver).
167 if(err == Rname && (!inmyarea(dp->name) || cfg.resolver))
177 intval(Ndbtuple *entry, Ndbtuple *pair, char *attr, ulong def)
179 Ndbtuple *t = look(entry, pair, attr);
181 return (t? strtoul(t->val, 0, 10): def);
192 cp += runetochar(cp, &r);
197 * lookup an RR in the network database
200 dblookup1(char *name, int type, int auth, int ttl)
208 RR *(*f)(Ndbtuple*, Ndbtuple*);
251 return doaxfr(db, name);
253 // dnslog("dblookup1(%s) bad type", name);
258 * find a matching entry in the database
261 nstrcpy(dname, name, sizeof dname);
264 case 1: /* try unicode */
265 if(idn2utf(name, dname, sizeof dname) < 0){
266 nstrcpy(dname, name, sizeof dname);
269 if(strcmp(name, dname) == 0)
272 case 3: /* try ascii (lower case) */
273 if(utf2idn(name, dname, sizeof dname) < 0)
277 if(strcmp(name, dname) == 0)
281 for(nt = ndbsearch(db, &s, "dom", dname); nt != nil; nt = ndbsnext(&s, "dom", dname)) {
282 if(ndbfindattr(nt, s.t, attr) == nil) {
286 t = ndbconcatenate(t, ndbreorder(nt, s.t));
288 if(t == nil && strchr(dname, '.') == nil) {
289 for(nt = ndbsearch(db, &s, "sys", dname); nt != nil; nt = ndbsnext(&s, "sys", dname)) {
290 if(ndbfindattr(nt, s.t, attr) == nil) {
294 t = ndbconcatenate(t, ndbreorder(nt, s.t));
303 // dnslog("dblookup1(%s) name not found", name);
308 /* search whole entry for default domain name */
309 for(nt = t; nt; nt = nt->entry)
310 if(strcmp(nt->attr, "dom") == 0){
311 nstrcpy(dname, nt->val, sizeof dname);
315 /* ttl is maximum of soa minttl and entry's ttl ala rfc883 */
316 x = intval(t, s.t, "ttl", 0);
320 /* default ttl is one day */
325 * The database has 2 levels of precedence; line and entry.
326 * Pairs on the same line bind tighter than pairs in the
327 * same entry, so we search the line first.
333 if(found == 0 && strcmp(nt->attr, "dom") == 0){
334 nstrcpy(dname, nt->val, sizeof dname);
337 if(strcmp(attr, nt->attr) == 0 && (rp = (*f)(t, nt)) != nil){
343 dp = idnlookup(dname, Cin, 1);
354 /* search whole entry */
355 for(nt = t; nt; nt = nt->entry)
356 if(nt->ptr == 0 && strcmp(attr, nt->attr) == 0 && (rp = (*f)(t, nt)) != nil){
362 dp = idnlookup(dname, Cin, 1);
369 // dnslog("dblookup1(%s) -> %#p", name, list);
374 * make various types of resource records from a database entry
377 addrrr(Ndbtuple*, Ndbtuple *pair)
382 if(parseip(ip, pair->val) == -1)
384 rp = rralloc(isv4(ip) ? Ta : Taaaa);
385 rp->ip = ipalookup(ip, Cin, 1);
389 nullrr(Ndbtuple*, Ndbtuple *pair)
394 rp->null->data = (uchar*)estrdup(pair->val);
395 rp->null->dlen = strlen((char*)rp->null->data);
399 * txt rr strings are at most 255 bytes long. one
400 * can represent longer strings by multiple concatenated
404 txtrr(Ndbtuple*, Ndbtuple *pair)
413 len = strlen(pair->val);
416 t = emalloc(sizeof(*t));
424 memmove(t->p, pair->val+sofar, i);
434 cnamerr(Ndbtuple*, Ndbtuple *pair)
438 rp = rralloc(Tcname);
439 rp->host = idnlookup(pair->val, Cin, 1);
443 mxrr(Ndbtuple *entry, Ndbtuple *pair)
448 rp->host = idnlookup(pair->val, Cin, 1);
449 rp->pref = intval(entry, pair, "pref", 1);
453 nsrr(Ndbtuple *entry, Ndbtuple *pair)
459 rp->host = idnlookup(pair->val, Cin, 1);
460 t = look(entry, pair, "soa");
461 if(t && t->val[0] == 0)
466 ptrrr(Ndbtuple*, Ndbtuple *pair)
471 rp->ptr = dnlookup(pair->val, Cin, 1);
475 soarr(Ndbtuple *entry, Ndbtuple *pair)
478 Ndbtuple *ns, *mb, *t;
479 char mailbox[Domlen];
485 for(ndb = db; ndb; ndb = ndb->next)
486 if(ndb->mtime > rp->soa->serial)
487 rp->soa->serial = ndb->mtime;
489 rp->soa->retry = intval(entry, pair, "retry", Hour);
490 rp->soa->expire = intval(entry, pair, "expire", Day);
491 rp->soa->minttl = intval(entry, pair, "ttl", Day);
492 rp->soa->refresh = intval(entry, pair, "refresh", Day);
493 rp->soa->serial = intval(entry, pair, "serial", rp->soa->serial);
495 ns = look(entry, pair, "ns");
497 ns = look(entry, pair, "dom");
498 rp->host = idnlookup(ns->val, Cin, 1);
502 * mbox=person@machine.dom
503 * mbox=person.machine.dom
505 mb = look(entry, pair, "mbox");
507 mb = look(entry, pair, "mb");
509 if(strchr(mb->val, '.')) {
510 p = strchr(mb->val, '@');
513 rp->rmb = idnlookup(mb->val, Cin, 1);
515 snprint(mailbox, sizeof mailbox, "%s.%s",
517 rp->rmb = idnlookup(mailbox, Cin, 1);
520 snprint(mailbox, sizeof mailbox, "postmaster.%s", ns->val);
521 rp->rmb = idnlookup(mailbox, Cin, 1);
525 * hang dns slaves off of the soa. this is
526 * for managing the area.
528 for(t = entry; t != nil; t = t->entry)
529 if(strcmp(t->attr, "dnsslave") == 0)
530 addserver(&rp->soa->slaves, t->val);
536 srvrr(Ndbtuple *entry, Ndbtuple *pair)
541 rp->host = idnlookup(pair->val, Cin, 1);
542 rp->srv->pri = intval(entry, pair, "pri", 0);
543 rp->srv->weight = intval(entry, pair, "weight", 0);
544 /* TODO: translate service name to port # */
545 rp->port = intval(entry, pair, "port", 0);
550 * Look for a pair with the given attribute. look first on the same line,
551 * then in the whole entry.
554 look(Ndbtuple *entry, Ndbtuple *line, char *attr)
558 /* first look on same line (closer binding) */
560 if(strcmp(attr, nt->attr) == 0)
566 /* search whole tuple */
567 for(nt = entry; nt; nt = nt->entry)
568 if(strcmp(attr, nt->attr) == 0)
573 /* these are answered specially by the tcp version */
575 doaxfr(Ndb *db, char *name)
582 * read the database into the cache
585 dbpair2cache(DN *dp, Ndbtuple *entry, Ndbtuple *pair)
591 if(strcmp(pair->attr, "ip") == 0 ||
592 strcmp(pair->attr, "ipv6") == 0) {
594 rp = addrrr(entry, pair);
596 else if(strcmp(pair->attr, "ns") == 0)
597 rp = nsrr(entry, pair);
598 else if(strcmp(pair->attr, "soa") == 0) {
599 rp = soarr(entry, pair);
600 addarea(dp, rp, pair);
602 else if(strcmp(pair->attr, "mx") == 0)
603 rp = mxrr(entry, pair);
604 else if(strcmp(pair->attr, "srv") == 0)
605 rp = srvrr(entry, pair);
606 else if(strcmp(pair->attr, "cname") == 0)
607 rp = cnamerr(entry, pair);
608 else if(strcmp(pair->attr, "nullrr") == 0)
609 rp = nullrr(entry, pair);
610 else if(strcmp(pair->attr, "txtrr") == 0)
611 rp = txtrr(entry, pair);
617 rp->ttl = intval(entry, pair, "ttl", rp->ttl);
618 rrattach(rp, Notauthoritative);
622 dbtuple2cache(Ndbtuple *t)
627 for(et = t; et; et = et->entry)
628 if(strcmp(et->attr, "dom") == 0){
629 dp = idnlookup(et->val, Cin, 1);
631 /* first same line */
632 for(nt = et->line; nt != et; nt = nt->line){
633 dbpair2cache(dp, t, nt);
637 /* then rest of entry */
638 for(nt = t; nt; nt = nt->entry){
640 dbpair2cache(dp, t, nt);
646 dbfile2cache(Ndb *db)
651 dnslog("rereading %s", db->file);
653 while(t = ndbparse(db)){
659 /* called with dblock held */
665 if (!cfg.inside || !cfg.straddle || !cfg.serve)
671 indoms = innmsrvs = outnmsrvs = nil;
675 free(ndbgetvalue(db, &s, "sys", "inside-dom", "dom", &indoms));
676 free(ndbgetvalue(db, &s, "sys", "inside-ns", "ip", &innmsrvs));
677 free(ndbgetvalue(db, &s, "sys", "outside-ns", "ip", &outnmsrvs));
678 dnslog("[%d] ndb changed: reloaded inside-dom, inside-ns, outside-ns",
688 static ulong lastcheck, lastyoungest;
690 /* no faster than once every 2 minutes */
691 if(now < lastcheck + 2*Min && !doit)
694 refresh_areas(owned);
697 if(opendatabase() < 0){
703 ipifcs = readipifc(mntpt, ipifcs, -1);
707 * file may be changing as we are reading it, so loop till
708 * mod times are consistent.
710 * we don't use the times in the ndb records because they may
711 * change outside of refreshing our cached knowledge.
716 for(ndb = db; ndb; ndb = ndb->next)
717 /* dirfstat avoids walking the mount table each time */
718 if((d = dirfstat(Bfildes(&ndb->b))) != nil ||
719 (d = dirstat(ndb->file)) != nil){
720 if(d->mtime > youngest)
724 if(!doit && youngest == lastyoungest)
727 /* forget our area definition */
729 freearea(&delegated);
731 /* reopen all the files (to get oldest for time stamp) */
732 for(ndb = db; ndb; ndb = ndb->next)
735 /* reload straddle-server configuration */
738 /* mark all db records as timed out */
742 /* read in new entries */
743 for(ndb = db; ndb; ndb = ndb->next)
748 * mark as authoritative anything in our domain,
749 * delete timed out db records
753 /* remove old entries */
757 lastyoungest = youngest;
764 extern char mntpt[Maxpath]; /* net mountpoint */
768 * caller ndbfrees the result
771 lookupinfo(char *attr)
780 if(opendatabase() < 0){
786 ipifcs = readipifc(mntpt, ipifcs, -1);
787 for(ifc = ipifcs; ifc != nil; ifc = ifc->next){
788 for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
789 snprint(ip, sizeof(ip), "%I", lifc->ip);
790 nt = ndbipinfo(db, "ip", ip, &attr, 1);
791 t = ndbconcatenate(t, nt);
801 * return non-zero if this is a bad delegation
804 baddelegation(RR *rp, RR *nsrp, uchar *addr)
814 t = lookupinfo("dom");
816 /* see if delegating to us what we don't own */
817 for(nt = t; nt != nil; nt = nt->entry)
818 if(rp->host && cistrcmp(rp->host->name, nt->val) == 0)
821 if(nt != nil && !inmyarea(rp->owner->name)){
824 dnslog("bad delegation %R from %I/%s; "
825 "no further logging of them",
826 rp, addr, nsrp->host->name);
841 for(ifc = ipifcs; ifc != nil; ifc = ifc->next){
842 for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
843 if(ipcmp(ip, lifc->ip) == 0){
855 addlocaldnsserver(DN *dp, int class, char *addr, int i)
863 if(parseip(ip, addr) == -1 || ipcmp(ip, IPnoaddr) == 0){
864 dnslog("rejecting bad ip %s as local dns server", addr);
868 /* reject our own ip addresses so we don't query ourselves via udp */
870 dnslog("rejecting my ip %I as local dns server", ip);
874 /* A or AAAA record */
875 type = isv4(ip) ? Ta : Taaaa;
876 ipdp = ipalookup(ip, class, 1);
878 /* check duplicate ip */
879 for(n = 0; n < i; n++){
880 snprint(buf, sizeof buf, "local#dns#server%d", n);
881 nsdp = dnlookup(buf, class, 0);
884 rp = rrlookup(nsdp, type, NOneg);
885 for(tp = rp; tp != nil; tp = tp->next){
887 dnslog("rejecting duplicate local dns server ip %I", ip);
895 snprint(buf, sizeof buf, "local#dns#server%d", i);
896 nsdp = dnlookup(buf, class, 1);
898 /* ns record for name server, make up an impossible name */
901 rp->owner = dp; /* e.g., local#dns#servers */
905 rrattach(rp, Authoritative); /* will not attach rrs in my area */
914 rrattach(rp, Authoritative); /* will not attach rrs in my area */
917 dnslog("added local dns server %s at %I", buf, ip);
921 * return list of dns server addresses to use when
922 * acting just as a resolver.
925 dnsservers(int class)
934 dp = dnlookup("local#dns#servers", class, 1);
935 nsrp = rrlookup(dp, Tns, NOneg);
939 p = getenv("DNSSERVER"); /* list of ip addresses */
941 n = tokenize(p, args, nelem(args));
942 for(i = 0; i < n; i++)
943 addlocaldnsserver(dp, class, args[i], i);
946 t = lookupinfo("@dns"); /* @dns=ip1 @dns=ip2 ... */
950 for(nt = t; nt != nil; nt = nt->entry){
951 addlocaldnsserver(dp, class, nt->val, i);
957 return rrlookup(dp, Tns, NOneg);
961 addlocaldnsdomain(DN *dp, int class, char *domain)
967 rp->ptr = dnlookup(domain, class, 1);
971 rrattach(rp, Authoritative);
976 * return list of domains to use when resolving names without '.'s
979 domainlist(int class)
985 dp = dnlookup("local#dns#domains", class, 1);
986 rp = rrlookup(dp, Tptr, NOneg);
990 t = lookupinfo("dnsdomain");
993 for(nt = t; nt != nil; nt = nt->entry)
994 addlocaldnsdomain(dp, class, nt->val);
997 return rrlookup(dp, Tptr, NOneg);
1000 char *v4ptrdom = ".in-addr.arpa";
1001 char *v6ptrdom = ".ip6.arpa"; /* ip6.int deprecated, rfc 3152 */
1009 * create ptrs that are in our v4 areas
1016 char buf[Domlen], ipa[48];
1018 uchar net[IPaddrlen], mask[IPaddrlen];
1022 dlen = strlen(v4ptrdom);
1023 for(s = owned; s; s = s->next){
1024 dom = s->soarr->owner->name;
1026 if((len <= dlen || cistrcmp(dom+len-dlen, v4ptrdom) != 0) &&
1027 cistrcmp(dom, v4ptrdom+1) != 0)
1030 /* get mask and net value */
1031 nstrcpy(buf, dom, sizeof buf);
1032 /* buf contains something like 178.204.in-addr.arpa (n==4) */
1033 n = getfields(buf, f, nelem(f), 0, ".");
1034 memset(mask, 0xff, IPaddrlen);
1035 ipmove(net, v4prefix);
1038 net[IPv4off] = atoi(f[0]);
1039 mask[IPv4off+1] = 0;
1040 mask[IPv4off+2] = 0;
1041 mask[IPv4off+3] = 0;
1044 net[IPv4off] = atoi(f[1]);
1045 net[IPv4off+1] = atoi(f[0]);
1046 mask[IPv4off+2] = 0;
1047 mask[IPv4off+3] = 0;
1050 net[IPv4off] = atoi(f[2]);
1051 net[IPv4off+1] = atoi(f[1]);
1052 net[IPv4off+2] = atoi(f[0]);
1053 mask[IPv4off+3] = 0;
1055 case 6: /* rfc2317: classless in-addr.arpa delegation */
1056 net[IPv4off] = atoi(f[3]);
1057 net[IPv4off+1] = atoi(f[2]);
1058 net[IPv4off+2] = atoi(f[1]);
1059 net[IPv4off+3] = atoi(f[0]);
1060 sprint(ipa, "%I", net);
1061 t = ndbipinfo(db, "ip", ipa, attribs, 1);
1062 if(t == nil) /* could be a reverse with no forward */
1064 nt = look(t, t, "ipmask");
1065 if(nt == nil){ /* we're confused */
1069 parseipmask(mask, nt->val);
1078 * go through all domain entries looking for RR's
1079 * in this network and create ptrs.
1080 * +2 for ".in-addr.arpa".
1082 dnptr(net, mask, dom, Ta, 4+2-n, Ptrttl);
1086 /* convert bytes to nibbles, big-endian */
1088 bytes2nibbles(uchar *nibbles, uchar *bytes, int nbytes)
1090 while (nbytes-- > 0) {
1091 *nibbles++ = *bytes >> Nibwidth;
1092 *nibbles++ = *bytes++ & Nibmask;
1097 nibbles2bytes(uchar *bytes, uchar *nibbles, int nnibs)
1099 for (; nnibs >= 2; nnibs -= 2) {
1100 *bytes++ = nibbles[0] << Nibwidth | (nibbles[1]&Nibmask);
1104 *bytes = nibbles[0] << Nibwidth;
1108 * create ptrs that are in our v6 areas. see rfc3596
1113 int len, dlen, i, n, pfxnibs;
1117 uchar net[IPaddrlen], mask[IPaddrlen];
1118 uchar nibnet[IPaddrlen*2], nibmask[IPaddrlen*2];
1121 dlen = strlen(v6ptrdom);
1122 for(s = owned; s; s = s->next){
1123 dom = s->soarr->owner->name;
1125 if((len <= dlen || cistrcmp(dom+len-dlen, v6ptrdom) != 0) &&
1126 cistrcmp(dom, v6ptrdom+1) != 0)
1129 /* get mask and net value */
1130 nstrcpy(buf, dom, sizeof buf);
1131 /* buf contains something like 2.0.0.2.ip6.arpa (n==6) */
1132 n = getfields(buf, f, nelem(f), 0, ".");
1133 pfxnibs = n - 2; /* 2 for .ip6.arpa */
1134 if (pfxnibs < 0 || pfxnibs > V6maxrevdomdepth)
1137 memset(net, 0, IPaddrlen);
1138 memset(mask, 0xff, IPaddrlen);
1139 bytes2nibbles(nibnet, net, IPaddrlen);
1140 bytes2nibbles(nibmask, mask, IPaddrlen);
1142 /* copy prefix of f, in reverse order, to start of net. */
1143 for (i = 0; i < pfxnibs; i++)
1144 nibnet[i] = strtol(f[pfxnibs - 1 - i], nil, 16);
1145 /* zero nibbles of mask after prefix in net */
1146 memset(nibmask + pfxnibs, 0, V6maxrevdomdepth - pfxnibs);
1148 nibbles2bytes(net, nibnet, 2*IPaddrlen);
1149 nibbles2bytes(mask, nibmask, 2*IPaddrlen);
1152 * go through all domain entries looking for RR's
1153 * in this network and create ptrs.
1155 dnptr(net, mask, dom, Taaaa, V6maxrevdomdepth - pfxnibs, Ptrttl);
1160 * create ptrs that are in our areas
1170 * is this domain (or DOMAIN or Domain or dOMAIN)
1171 * internal to our organisation (behind our firewall)?
1172 * only inside straddling servers care, everybody else gets told `yes',
1173 * so they'll use mntpt for their queries.
1176 insideaddr(char *dom)
1178 int domlen, vallen, rv;
1181 if (!cfg.inside || !cfg.straddle || !cfg.serve)
1183 if (dom[0] == '\0' || strcmp(dom, ".") == 0) /* dns root? */
1184 return 1; /* hack for initialisation */
1189 if (indoms == nil) {
1191 return 1; /* no "inside-dom" sys, try inside nameservers */
1195 domlen = strlen(dom);
1196 for (t = indoms; t != nil; t = t->entry) {
1197 if (strcmp(t->attr, "dom") != 0)
1199 vallen = strlen(t->val);
1200 if (cistrcmp(dom, t->val) == 0 ||
1202 cistrcmp(dom + domlen - vallen, t->val) == 0 &&
1203 dom[domlen - vallen - 1] == '.') {
1215 uchar ipa[IPaddrlen];
1218 for (t = innmsrvs; t != nil; t = t->entry)
1219 if (strcmp(t->attr, "ip") == 0) {
1220 if (parseip(ipa, t->val) != -1 && ipcmp(ipa, ip) == 0)
1227 outsidensip(int n, uchar *ip)
1233 for (t = outnmsrvs; t != nil; t = t->entry)
1234 if (strcmp(t->attr, "ip") == 0 && i++ == n) {
1235 if (parseip(ip, t->val) == -1)