diff options
author | rsc <devnull@localhost> | 2005-12-27 04:30:05 +0000 |
---|---|---|
committer | rsc <devnull@localhost> | 2005-12-27 04:30:05 +0000 |
commit | 3e0d8fb3ea83b2b65a6425c65beda887140f9349 (patch) | |
tree | ce1579f6999c0e4adad35b75c498ddc714b40c1e /src/cmd/ndb/dnstcp.c | |
parent | cff43a06f21a674b2b16e72f8853aac5fd24f48d (diff) | |
download | plan9port-3e0d8fb3ea83b2b65a6425c65beda887140f9349.tar.gz plan9port-3e0d8fb3ea83b2b65a6425c65beda887140f9349.tar.bz2 plan9port-3e0d8fb3ea83b2b65a6425c65beda887140f9349.zip |
add dns
Diffstat (limited to 'src/cmd/ndb/dnstcp.c')
-rwxr-xr-x | src/cmd/ndb/dnstcp.c | 362 |
1 files changed, 362 insertions, 0 deletions
diff --git a/src/cmd/ndb/dnstcp.c b/src/cmd/ndb/dnstcp.c new file mode 100755 index 00000000..19a7540d --- /dev/null +++ b/src/cmd/ndb/dnstcp.c @@ -0,0 +1,362 @@ +#include <u.h> +#include <libc.h> +#include <ip.h> +#include "dns.h" + +enum +{ + Maxpath= 128, +}; + +char *logfile = "dns"; +char *dbfile; +int debug; +int cachedb = 1; +int testing; +int traceactivity; +int needrefresh; +int resolver; +char mntpt[Maxpath]; +char *caller = ""; +ulong now; +int maxage; +uchar ipaddr[IPaddrlen]; /* my ip address */ +char *LOG; +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 +main(int argc, char *argv[]) +{ + int len; + Request req; + DNSmsg reqmsg, repmsg; + uchar buf[512]; + char tname[32]; + char *err; + char *ext = ""; + + ARGBEGIN{ + case 'd': + debug++; + break; + case 'f': + dbfile = ARGF(); + break; + case 'r': + resolver = 1; + break; + case 'x': + ext = ARGF(); + break; + }ARGEND + + if(debug < 2) + debug = 0; + + if(argc > 0) + getcaller(argv[0]); + + dninit(); + + snprint(mntpt, sizeof(mntpt), "/net%s", ext); + if(myipaddr(ipaddr, mntpt) < 0) + sysfatal("can't read my ip address"); + syslog(0, logfile, "dnstcp call from %s to %I", caller, ipaddr); + + db2cache(1); + + setjmp(req.mret); + req.isslave = 0; + + /* loop on requests */ + for(;; putactivity()){ + now = time(0); + memset(&repmsg, 0, sizeof(repmsg)); + alarm(10*60*1000); + len = readmsg(0, buf, sizeof(buf)); + alarm(0); + if(len <= 0) + break; + getactivity(&req); + req.aborttime = now + 15*Min; + err = convM2DNS(buf, len, &reqmsg); + if(err){ + syslog(0, logfile, "server: input error: %s from %I", err, buf); + break; + } + if(reqmsg.qdcount < 1){ + syslog(0, logfile, "server: no questions from %I", buf); + break; + } + if(reqmsg.flags & Fresp){ + syslog(0, logfile, "server: reply not request from %I", buf); + break; + } + if((reqmsg.flags & Omask) != Oquery){ + syslog(0, logfile, "server: op %d from %I", reqmsg.flags & Omask, buf); + break; + } + + if(debug) + syslog(0, logfile, "%d: serve (%s) %d %s %s", + 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); + reply(1, &repmsg, &req); + rrfreelist(repmsg.qd); + rrfreelist(repmsg.an); + rrfreelist(repmsg.ns); + rrfreelist(repmsg.ar); + } + } + + rrfreelist(reqmsg.qd); + rrfreelist(reqmsg.an); + rrfreelist(reqmsg.ns); + rrfreelist(reqmsg.ar); + + if(req.isslave){ + putactivity(); + _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[4096]; + RR *rp; + + if(debug){ + syslog(0, logfile, "%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) + syslog(0, logfile, "an %R", rp); + for(rp = rep->ns; rp; rp = rp->next) + syslog(0, logfile, "ns %R", rp); + for(rp = rep->ar; rp; rp = rp->next) + syslog(0, logfile, "ar %R", rp); + } + + + len = convDNS2M(rep, buf+2, sizeof(buf) - 2); + if(len <= 0) + abort(); /* "dnserver: converting reply" */; + buf[0] = len>>8; + buf[1] = len; + rv = write(fd, buf, len+2); + if(rv != len+2){ + syslog(0, logfile, "sending reply: %d instead of %d", 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 == 0) + 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->flags = Fauth | Fresp | Fcanrec | Oquery; + repp->qd = reqp->qd; + reqp->qd = reqp->qd->next; + repp->qd->next = 0; + 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); + + 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 */ + if(rp->negative) + continue; + + /* don't send an soa's, ns's are enough */ + if(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); +out: + rrfree(repp->qd); +} + +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) + syslog(0, logfile, "refreshing %s", file); + fd = open(file, ORDWR); + if(fd < 0){ + syslog(0, logfile, "can't refresh %s", file); + return; + } + fprint(fd, "refresh"); + close(fd); +} + +/* + * the following varies between dnsdebug and dns + */ +void +logreply(int id, uchar *addr, DNSmsg *mp) +{ + RR *rp; + + syslog(0, LOG, "%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|Rname) == (Fauth|Rname) ? + " nx" : ""); + for(rp = mp->qd; rp != nil; rp = rp->next) + syslog(0, LOG, "%d: rcvd %I qd %s", id, addr, rp->owner->name); + for(rp = mp->an; rp != nil; rp = rp->next) + syslog(0, LOG, "%d: rcvd %I an %R", id, addr, rp); + for(rp = mp->ns; rp != nil; rp = rp->next) + syslog(0, LOG, "%d: rcvd %I ns %R", id, addr, rp); + for(rp = mp->ar; rp != nil; rp = rp->next) + syslog(0, LOG, "%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]; + + syslog(0, LOG, "%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); +} |