|  | #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); | 
|  | if (*fd >= 0) | 
|  | n = readmsg(*fd, buf, max); | 
|  | else | 
|  | n = -1; | 
|  | 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, rv; | 
|  | Request req; | 
|  | DNSmsg reqmsg, repmsg; | 
|  | char *err; | 
|  | uchar buf[512]; | 
|  | char tname[32]; | 
|  | int fd, rfd; | 
|  | NetConnInfo *caller; | 
|  |  | 
|  | rfd = -1; | 
|  | fd = (uintptr)v; | 
|  | caller = 0; | 
|  | /* loop on requests */ | 
|  | for(;; putactivity()){ | 
|  | if (rfd == 1) | 
|  | return; | 
|  | close(rfd); | 
|  | now = time(0); | 
|  | memset(&repmsg, 0, sizeof(repmsg)); | 
|  | if (fd == 0) { | 
|  | len = readmsg(fd, buf, sizeof buf); | 
|  | rfd = 1; | 
|  | } else { | 
|  | len = connreadmsg(fd, &rfd, buf, sizeof buf); | 
|  | } | 
|  | if(len <= 0) | 
|  | continue; | 
|  | freenetconninfo(caller); | 
|  | caller = getnetconninfo(0, fd); | 
|  | 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); | 
|  | rv = reply(rfd, &repmsg, &req, caller); | 
|  | rrfreelist(repmsg.qd); | 
|  | rrfreelist(repmsg.an); | 
|  | rrfreelist(repmsg.ns); | 
|  | rrfreelist(repmsg.ar); | 
|  | if(rv < 0) | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | rrfreelist(reqmsg.qd); | 
|  | rrfreelist(reqmsg.an); | 
|  | rrfreelist(reqmsg.ns); | 
|  | rrfreelist(reqmsg.ar); | 
|  | } | 
|  | } | 
|  |  | 
|  | enum { | 
|  | Maxactivetcp = 4 | 
|  | }; | 
|  |  | 
|  | static int | 
|  | tcpannounce(char *mntpt) | 
|  | { | 
|  | int fd; | 
|  |  | 
|  | USED(mntpt); | 
|  | if((fd=announce(tcpaddr, adir)) < 0) | 
|  | warning("announce %s: %r", tcpaddr); | 
|  | return fd; | 
|  | } | 
|  |  | 
|  | void | 
|  | dntcpserver(void *v) | 
|  | { | 
|  | int i, fd; | 
|  |  | 
|  | while((fd = tcpannounce(v)) < 0) | 
|  | sleep(5*1000); | 
|  |  | 
|  | for(i=0; i<Maxactivetcp; i++) | 
|  | proccreate(tcpproc, (void*)(uintptr)fd, STACK); | 
|  | } |