#include #include #include #include #include #include #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)); 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); 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