diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/ndb/dnstcp.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/ndb/dnstcp.c')
-rwxr-xr-x | sys/src/cmd/ndb/dnstcp.c | 382 |
1 files changed, 382 insertions, 0 deletions
diff --git a/sys/src/cmd/ndb/dnstcp.c b/sys/src/cmd/ndb/dnstcp.c new file mode 100755 index 000000000..837a77461 --- /dev/null +++ b/sys/src/cmd/ndb/dnstcp.c @@ -0,0 +1,382 @@ +/* + * dnstcp - serve dns via tcp + */ +#include <u.h> +#include <libc.h> +#include <ip.h> +#include "dns.h" + +Cfg cfg; + +char *caller = ""; +char *dbfile; +int debug; +uchar ipaddr[IPaddrlen]; /* my ip address */ +char *logfile = "dns"; +int maxage = 60*60; +char mntpt[Maxpath]; +int needrefresh; +ulong now; +vlong nowns; +int testing; +int traceactivity; +char *zonerefreshprogram; + +static int readmsg(int, uchar*, int); +static void reply(int, DNSmsg*, Request*); +static void dnzone(DNSmsg*, DNSmsg*, Request*); +static void getcaller(char*); +static void refreshmain(char*); + +void +usage(void) +{ + fprint(2, "usage: %s [-rR] [-f ndb-file] [-x netmtpt] [conndir]\n", argv0); + exits("usage"); +} + +void +main(int argc, char *argv[]) +{ + volatile int len, rcode; + volatile char tname[32]; + char *volatile err, *volatile ext = ""; + volatile uchar buf[64*1024], callip[IPaddrlen]; + volatile DNSmsg reqmsg, repmsg; + volatile Request req; + + alarm(2*60*1000); + cfg.cachedb = 1; + ARGBEGIN{ + case 'd': + debug++; + break; + case 'f': + dbfile = EARGF(usage()); + break; + case 'r': + cfg.resolver = 1; + break; + case 'R': + norecursion = 1; + break; + case 'x': + ext = EARGF(usage()); + break; + default: + usage(); + break; + }ARGEND + + if(debug < 2) + debug = 0; + + if(argc > 0) + getcaller(argv[0]); + + cfg.inside = 1; + dninit(); + + snprint(mntpt, sizeof mntpt, "/net%s", ext); + if(myipaddr(ipaddr, mntpt) < 0) + sysfatal("can't read my ip address"); + dnslog("dnstcp call from %s to %I", caller, ipaddr); + memset(callip, 0, sizeof callip); + parseip(callip, caller); + + db2cache(1); + + memset(&req, 0, sizeof req); + setjmp(req.mret); + req.isslave = 0; + procsetname("main loop"); + + /* loop on requests */ + for(;; putactivity(0)){ + now = time(nil); + memset(&repmsg, 0, sizeof repmsg); + len = readmsg(0, buf, sizeof buf); + if(len <= 0) + break; + + getactivity(&req, 0); + req.aborttime = timems() + S2MS(15*Min); + rcode = 0; + memset(&reqmsg, 0, sizeof reqmsg); + err = convM2DNS(buf, len, &reqmsg, &rcode); + if(err){ + dnslog("server: input error: %s from %s", err, caller); + free(err); + break; + } + if (rcode == 0) + if(reqmsg.qdcount < 1){ + dnslog("server: no questions from %s", caller); + break; + } else if(reqmsg.flags & Fresp){ + dnslog("server: reply not request from %s", + caller); + break; + } else if((reqmsg.flags & Omask) != Oquery){ + dnslog("server: op %d from %s", + reqmsg.flags & Omask, caller); + break; + } + if(debug) + dnslog("[%d] %d: serve (%s) %d %s %s", + getpid(), req.id, caller, + reqmsg.id, reqmsg.qd->owner->name, + rrname(reqmsg.qd->type, tname, sizeof tname)); + + /* loop through each question */ + while(reqmsg.qd) + if(reqmsg.qd->type == Taxfr) + dnzone(&reqmsg, &repmsg, &req); + else { + dnserver(&reqmsg, &repmsg, &req, callip, rcode); + reply(1, &repmsg, &req); + rrfreelist(repmsg.qd); + rrfreelist(repmsg.an); + rrfreelist(repmsg.ns); + rrfreelist(repmsg.ar); + } + rrfreelist(reqmsg.qd); /* qd will be nil */ + rrfreelist(reqmsg.an); + rrfreelist(reqmsg.ns); + rrfreelist(reqmsg.ar); + + if(req.isslave){ + putactivity(0); + _exits(0); + } + } + refreshmain(mntpt); +} + +static int +readmsg(int fd, uchar *buf, int max) +{ + int n; + uchar x[2]; + + if(readn(fd, x, 2) != 2) + return -1; + n = x[0]<<8 | x[1]; + if(n > max) + return -1; + if(readn(fd, buf, n) != n) + return -1; + return n; +} + +static void +reply(int fd, DNSmsg *rep, Request *req) +{ + int len, rv; + char tname[32]; + uchar buf[64*1024]; + RR *rp; + + if(debug){ + dnslog("%d: reply (%s) %s %s %ux", + req->id, caller, + rep->qd->owner->name, + rrname(rep->qd->type, tname, sizeof tname), + rep->flags); + for(rp = rep->an; rp; rp = rp->next) + dnslog("an %R", rp); + for(rp = rep->ns; rp; rp = rp->next) + dnslog("ns %R", rp); + for(rp = rep->ar; rp; rp = rp->next) + dnslog("ar %R", rp); + } + + + len = convDNS2M(rep, buf+2, sizeof(buf) - 2); + buf[0] = len>>8; + buf[1] = len; + rv = write(fd, buf, len+2); + if(rv != len+2){ + dnslog("[%d] sending reply: %d instead of %d", getpid(), rv, + len+2); + exits(0); + } +} + +/* + * Hash table for domain names. The hash is based only on the + * first element of the domain name. + */ +extern DN *ht[HTLEN]; + +static int +numelem(char *name) +{ + int i; + + i = 1; + for(; *name; name++) + if(*name == '.') + i++; + return i; +} + +int +inzone(DN *dp, char *name, int namelen, int depth) +{ + int n; + + if(dp->name == nil) + return 0; + if(numelem(dp->name) != depth) + return 0; + n = strlen(dp->name); + if(n < namelen) + return 0; + if(strcmp(name, dp->name + n - namelen) != 0) + return 0; + if(n > namelen && dp->name[n - namelen - 1] != '.') + return 0; + return 1; +} + +static void +dnzone(DNSmsg *reqp, DNSmsg *repp, Request *req) +{ + DN *dp, *ndp; + RR r, *rp; + int h, depth, found, nlen; + + memset(repp, 0, sizeof(*repp)); + repp->id = reqp->id; + repp->qd = reqp->qd; + reqp->qd = reqp->qd->next; + repp->qd->next = 0; + repp->flags = Fauth | Fresp | Oquery; + if(!norecursion) + repp->flags |= Fcanrec; + dp = repp->qd->owner; + + /* send the soa */ + repp->an = rrlookup(dp, Tsoa, NOneg); + reply(1, repp, req); + if(repp->an == 0) + goto out; + rrfreelist(repp->an); + repp->an = nil; + + nlen = strlen(dp->name); + + /* construct a breadth-first search of the name space (hard with a hash) */ + repp->an = &r; + for(depth = numelem(dp->name); ; depth++){ + found = 0; + for(h = 0; h < HTLEN; h++) + for(ndp = ht[h]; ndp; ndp = ndp->next) + if(inzone(ndp, dp->name, nlen, depth)){ + for(rp = ndp->rr; rp; rp = rp->next){ + /* + * there shouldn't be negatives, + * but just in case. + * don't send any soa's, + * ns's are enough. + */ + if (rp->negative || + rp->type == Tsoa) + continue; + r = *rp; + r.next = 0; + reply(1, repp, req); + } + found = 1; + } + if(!found) + break; + } + + /* resend the soa */ + repp->an = rrlookup(dp, Tsoa, NOneg); + reply(1, repp, req); + rrfreelist(repp->an); + repp->an = nil; +out: + rrfree(repp->qd); + repp->qd = nil; +} + +static void +getcaller(char *dir) +{ + int fd, n; + static char remote[128]; + + snprint(remote, sizeof(remote), "%s/remote", dir); + fd = open(remote, OREAD); + if(fd < 0) + return; + n = read(fd, remote, sizeof remote - 1); + close(fd); + if(n <= 0) + return; + if(remote[n-1] == '\n') + n--; + remote[n] = 0; + caller = remote; +} + +static void +refreshmain(char *net) +{ + int fd; + char file[128]; + + snprint(file, sizeof(file), "%s/dns", net); + if(debug) + dnslog("refreshing %s", file); + fd = open(file, ORDWR); + if(fd < 0) + dnslog("can't refresh %s", file); + else { + fprint(fd, "refresh"); + close(fd); + } +} + +/* + * the following varies between dnsdebug and dns + */ +void +logreply(int id, uchar *addr, DNSmsg *mp) +{ + RR *rp; + + dnslog("%d: rcvd %I flags:%s%s%s%s%s", id, addr, + mp->flags & Fauth? " auth": "", + mp->flags & Ftrunc? " trunc": "", + mp->flags & Frecurse? " rd": "", + mp->flags & Fcanrec? " ra": "", + (mp->flags & (Fauth|Rmask)) == (Fauth|Rname)? " nx": ""); + for(rp = mp->qd; rp != nil; rp = rp->next) + dnslog("%d: rcvd %I qd %s", id, addr, rp->owner->name); + for(rp = mp->an; rp != nil; rp = rp->next) + dnslog("%d: rcvd %I an %R", id, addr, rp); + for(rp = mp->ns; rp != nil; rp = rp->next) + dnslog("%d: rcvd %I ns %R", id, addr, rp); + for(rp = mp->ar; rp != nil; rp = rp->next) + dnslog("%d: rcvd %I ar %R", id, addr, rp); +} + +void +logsend(int id, int subid, uchar *addr, char *sname, char *rname, int type) +{ + char buf[12]; + + dnslog("%d.%d: sending to %I/%s %s %s", + id, subid, addr, sname, rname, rrname(type, buf, sizeof buf)); +} + +RR* +getdnsservers(int class) +{ + return dnsservers(class); +} |