|  | #include <u.h> | 
|  | #include <libc.h> | 
|  | #include <ip.h> | 
|  | #include <bio.h> | 
|  | #include <ndb.h> | 
|  | #include <thread.h> | 
|  | #include "dns.h" | 
|  |  | 
|  | static int	udpannounce(char*); | 
|  | static void	reply(int, uchar*, DNSmsg*, Request*); | 
|  |  | 
|  | extern char *logfile; | 
|  |  | 
|  | typedef struct Inprogress Inprogress; | 
|  | struct Inprogress | 
|  | { | 
|  | int	inuse; | 
|  | Udphdr	uh; | 
|  | DN	*owner; | 
|  | int	type; | 
|  | int	id; | 
|  | }; | 
|  | Inprogress inprog[Maxactive+2]; | 
|  | QLock inproglk; | 
|  |  | 
|  | /* | 
|  | *  record client id and ignore retransmissions. | 
|  | */ | 
|  | static Inprogress* | 
|  | clientrxmit(DNSmsg *req, uchar *buf) | 
|  | { | 
|  | Inprogress *p, *empty; | 
|  | Udphdr *uh; | 
|  |  | 
|  | qlock(&inproglk); | 
|  | uh = (Udphdr *)buf; | 
|  | empty = 0; | 
|  | for(p = inprog; p < &inprog[Maxactive]; p++){ | 
|  | if(p->inuse == 0){ | 
|  | if(empty == 0) | 
|  | empty = p; | 
|  | continue; | 
|  | } | 
|  | if(req->id == p->id) | 
|  | if(req->qd->owner == p->owner) | 
|  | if(req->qd->type == p->type) | 
|  | if(memcmp(uh, &p->uh, Udphdrsize) == 0){ | 
|  | qunlock(&inproglk); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | if(empty == 0){ | 
|  | qunlock(&inproglk); | 
|  | return 0;	/* shouldn't happen - see slave() and definition of Maxactive */ | 
|  | } | 
|  |  | 
|  | empty->id = req->id; | 
|  | empty->owner = req->qd->owner; | 
|  | empty->type = req->qd->type; | 
|  | memmove(&empty->uh, uh, Udphdrsize); | 
|  | empty->inuse = 1; | 
|  | qunlock(&inproglk); | 
|  | return empty; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  a process to act as a dns server for outside reqeusts | 
|  | */ | 
|  | static void | 
|  | udpproc(void *v) | 
|  | { | 
|  | int fd, len, op; | 
|  | Request req; | 
|  | DNSmsg reqmsg, repmsg; | 
|  | uchar buf[Udphdrsize + Maxudp + 1024]; | 
|  | char *err; | 
|  | Inprogress *p; | 
|  | char tname[32]; | 
|  | Udphdr *uh; | 
|  |  | 
|  | fd = (uintptr)v; | 
|  |  | 
|  | /* loop on requests */ | 
|  | for(;; putactivity()){ | 
|  | memset(&repmsg, 0, sizeof(repmsg)); | 
|  | memset(&reqmsg, 0, sizeof(reqmsg)); | 
|  | len = udpread(fd, (Udphdr*)buf, buf+Udphdrsize, sizeof(buf)-Udphdrsize); | 
|  | if(len <= 0) | 
|  | continue; | 
|  | uh = (Udphdr*)buf; | 
|  | getactivity(&req); | 
|  | req.aborttime = now + 30;	/* don't spend more than 30 seconds */ | 
|  | err = convM2DNS(&buf[Udphdrsize], 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); | 
|  | goto freereq; | 
|  | } | 
|  | if(reqmsg.flags & Fresp){ | 
|  | syslog(0, logfile, "server: reply not request from %I", buf); | 
|  | goto freereq; | 
|  | } | 
|  | op = reqmsg.flags & Omask; | 
|  | if(op != Oquery && op != Onotify){ | 
|  | syslog(0, logfile, "server: op %d from %I", reqmsg.flags & Omask, buf); | 
|  | goto freereq; | 
|  | } | 
|  |  | 
|  | if(debug || (trace && subsume(trace, reqmsg.qd->owner->name))){ | 
|  | syslog(0, logfile, "%d: serve (%I/%d) %d %s %s", | 
|  | req.id, buf, ((uh->rport[0])<<8)+uh->rport[1], | 
|  | reqmsg.id, | 
|  | reqmsg.qd->owner->name, | 
|  | rrname(reqmsg.qd->type, tname, sizeof tname)); | 
|  | } | 
|  |  | 
|  | p = clientrxmit(&reqmsg, buf); | 
|  | if(p == 0){ | 
|  | if(debug) | 
|  | syslog(0, logfile, "%d: duplicate", req.id); | 
|  | goto freereq; | 
|  | } | 
|  |  | 
|  | /* loop through each question */ | 
|  | while(reqmsg.qd){ | 
|  | memset(&repmsg, 0, sizeof(repmsg)); | 
|  | switch(op){ | 
|  | case Oquery: | 
|  | dnserver(&reqmsg, &repmsg, &req); | 
|  | break; | 
|  | case Onotify: | 
|  | dnnotify(&reqmsg, &repmsg, &req); | 
|  | break; | 
|  | } | 
|  | reply(fd, buf, &repmsg, &req); | 
|  | rrfreelist(repmsg.qd); | 
|  | rrfreelist(repmsg.an); | 
|  | rrfreelist(repmsg.ns); | 
|  | rrfreelist(repmsg.ar); | 
|  | } | 
|  |  | 
|  | p->inuse = 0; | 
|  |  | 
|  | freereq: | 
|  | rrfreelist(reqmsg.qd); | 
|  | rrfreelist(reqmsg.an); | 
|  | rrfreelist(reqmsg.ns); | 
|  | rrfreelist(reqmsg.ar); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  announce on udp port | 
|  | */ | 
|  | static int | 
|  | udpannounce(char *mntpt) | 
|  | { | 
|  | int fd; | 
|  | char buf[40]; | 
|  | USED(mntpt); | 
|  |  | 
|  | if((fd=announce(udpaddr, buf)) < 0) | 
|  | warning("announce %s: %r", buf); | 
|  | return fd; | 
|  | } | 
|  |  | 
|  | static void | 
|  | reply(int fd, uchar *buf, DNSmsg *rep, Request *reqp) | 
|  | { | 
|  | int len; | 
|  | char tname[32]; | 
|  | RR *rp; | 
|  |  | 
|  | if(debug || (trace && subsume(trace, rep->qd->owner->name))) | 
|  | syslog(0, logfile, "%d: reply (%I/%d) %d %s %s an %R ns %R ar %R", | 
|  | reqp->id, buf, ((buf[4])<<8)+buf[5], | 
|  | rep->id, rep->qd->owner->name, | 
|  | rrname(rep->qd->type, tname, sizeof tname), rep->an, rep->ns, rep->ar); | 
|  |  | 
|  | len = convDNS2M(rep, &buf[Udphdrsize], Maxudp); | 
|  | if(len <= 0){ | 
|  | syslog(0, logfile, "error converting reply: %s %d", rep->qd->owner->name, | 
|  | rep->qd->type); | 
|  | 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); | 
|  | return; | 
|  | } | 
|  | if(udpwrite(fd, (Udphdr*)buf, buf+Udphdrsize, len) != len) | 
|  | syslog(0, logfile, "error sending reply: %r"); | 
|  | } | 
|  |  | 
|  | void | 
|  | dnudpserver(void *v) | 
|  | { | 
|  | int i, fd; | 
|  |  | 
|  | while((fd = udpannounce(v)) < 0) | 
|  | sleep(5*1000); | 
|  | for(i=0; i<Maxactive; i++) | 
|  | proccreate(udpproc, (void*)(uintptr)fd, STACK); | 
|  | } | 
|  |  |