diff options
author | rsc <devnull@localhost> | 2006-02-15 18:34:50 +0000 |
---|---|---|
committer | rsc <devnull@localhost> | 2006-02-15 18:34:50 +0000 |
commit | 27b457ae8ac1d0e7816f7a683dc8814a0f11e9cb (patch) | |
tree | e168572f34712c785923980f20ae7300e951dd80 /src/cmd/ndb | |
parent | 941e17134e92de7f12977f1899860b57bbf83330 (diff) | |
download | plan9port-27b457ae8ac1d0e7816f7a683dc8814a0f11e9cb.tar.gz plan9port-27b457ae8ac1d0e7816f7a683dc8814a0f11e9cb.tar.bz2 plan9port-27b457ae8ac1d0e7816f7a683dc8814a0f11e9cb.zip |
forgot
Diffstat (limited to 'src/cmd/ndb')
-rw-r--r-- | src/cmd/ndb/dntcpserver.c | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/src/cmd/ndb/dntcpserver.c b/src/cmd/ndb/dntcpserver.c new file mode 100644 index 00000000..4ae787a8 --- /dev/null +++ b/src/cmd/ndb/dntcpserver.c @@ -0,0 +1,294 @@ +#include <u.h> +#include <libc.h> +#include <ip.h> +#include <bio.h> +#include <ndb.h> +#include <thread.h> +#include "dns.h" + +static char adir[40]; + +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 int +connreadmsg(int tfd, int *fd, uchar *buf, int max) +{ + int n; + int lfd; + char ldir[40]; + + lfd = listen(adir, ldir); + if (lfd < 0) + return -1; + *fd = accept(lfd, ldir); + n = -1; + if (*fd < 0) + n = readmsg(*fd, buf, max); + //close(fd); + close(lfd); + return n; +} + +static int +reply(int fd, DNSmsg *rep, Request *req, NetConnInfo *caller) +{ + int len; + char tname[32]; + uchar buf[4096]; + RR *rp; + + if(debug){ + syslog(0, logfile, "%d: reply (%s) %s %s %ux", + req->id, caller ? caller->raddr : "unk", + 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; + if(write(fd, buf, len+2) < 0){ + syslog(0, logfile, "sending reply: %r"); + return -1; + } + return 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; +} + +static 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 int +dnzone(DNSmsg *reqp, DNSmsg *repp, Request *req, int rfd, NetConnInfo *caller) +{ + DN *dp, *ndp; + RR r, *rp; + int h, depth, found, nlen, rv; + + rv = 0; + 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); + rv = reply(rfd, repp, req, caller); + if(repp->an == 0 || rv < 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; + rv = reply(rfd, repp, req, caller); + if(rv < 0) + goto out; + } + found = 1; + } + if(!found) + break; + } + + /* resend the soa */ + repp->an = rrlookup(dp, Tsoa, NOneg); + rv = reply(rfd, repp, req, caller); +out: + if (repp->an) + rrfreelist(repp->an); + rrfree(repp->qd); + return rv; +} + +void +tcpproc(void *v) +{ + int len; + Request req; + DNSmsg reqmsg, repmsg; + char *err; + uchar buf[512]; + char tname[32]; + int fd, rfd; + NetConnInfo *caller; + + rfd = -1; + fd = (int)v; + caller = 0; + /* loop on requests */ + for(;; putactivity()){ + if (rfd == 1) + return; + close(rfd); + now = time(0); + memset(&repmsg, 0, sizeof(repmsg)); + freenetconninfo(caller); + caller = getnetconninfo(0, fd); + if (fd == 0) { + len = readmsg(fd, buf, sizeof buf); + rfd = 1; + } else { + len = connreadmsg(fd, &rfd, buf, sizeof buf); + } + if(len <= 0) + continue; + 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); + continue; + } + if(reqmsg.qdcount < 1){ + syslog(0, logfile, "server: no questions from %I", buf); + continue; + } + if(reqmsg.flags & Fresp){ + syslog(0, logfile, "server: reply not request from %I", buf); + continue; + } + if((reqmsg.flags & Omask) != Oquery){ + syslog(0, logfile, "server: op %d from %I", reqmsg.flags & Omask, buf); + continue; + } + + if(debug) + syslog(0, logfile, "%d: serve (%s) %d %s %s", + req.id, caller ? caller->raddr : 0, + 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){ + if(dnzone(&reqmsg, &repmsg, &req, rfd, caller) < 0) + break; + } else { + dnserver(&reqmsg, &repmsg, &req); + if(reply(rfd, &repmsg, &req, caller) < 0) + break; + rrfreelist(repmsg.qd); + rrfreelist(repmsg.an); + rrfreelist(repmsg.ns); + rrfreelist(repmsg.ar); + } + } + + rrfreelist(reqmsg.qd); + rrfreelist(reqmsg.an); + rrfreelist(reqmsg.ns); + rrfreelist(reqmsg.ar); + } +} + +enum { + Maxactivetcp = 4, +}; + +extern char *portname; + +static int +tcpannounce(char *mntpt) +{ + int fd; + char an[40]; + + USED(mntpt); + snprint(an, sizeof an, "tcp!*!%s", portname); + if((fd=announce(an, adir)) < 0) + warning("announce %s: %r", an); + return fd; +} + +void +dntcpserver(void *v) +{ + int i, fd; + char *mntpt; + + mntpt = v; + while((fd = tcpannounce(mntpt)) < 0) + sleep(5*1000); + + for(i=0; i<Maxactivetcp; i++) + proccreate(tcpproc, (void*)fd, STACK); +} |