#include #include #include "dns.h" #include "ip.h" static int udpport(void); static RR* getipaddr(DN*); static void recurse(DNSmsg*, Request*); static void local(DNSmsg*); static void reply(int, uchar*, DNSmsg*); static void hint(RR**, RR*); /* * a process to act as a dns server for outside reqeusts */ void dnserver(void) { int fd, len; char *err; RR *tp; Request req; DNSmsg reqmsg; DNSmsg repmsg; uchar buf[Udphdrlen + Maxudp]; char tname[32]; /* fork sharing text, data, and bss with parent */ switch(rfork(RFPROC|RFNOTEG|RFMEM|RFNOWAIT)){ case -1: break; case 0: break; default: return; } /* loop on requests */ fd = udpport(); setjmp(req.mret); req.isslave = 0; for(;;){ memset(&repmsg, 0, sizeof(repmsg)); len = read(fd, buf, sizeof(buf)); err = convM2DNS(&buf[Udphdrlen], len, &reqmsg); if(err){ syslog(0, "dns", "server: input error %s from %I", err, buf); continue; } if(reqmsg.qdcount < 1){ syslog(0, "dns", "server: no questions from %I", buf); } else if(reqmsg.flags & Fresp){ syslog(0, "dns", "server: reply not request from %I", buf); } else if(reqmsg.qd->owner->class != Cin){ syslog(0, "dns", "server: class %d from %I", reqmsg.qd->owner->class, buf); memset(&repmsg, 0, sizeof(repmsg)); repmsg.id = reqmsg.id; repmsg.flags = Runimplimented | Fresp | Fcanrec | Oquery; repmsg.qd = reqmsg.qd; reply(fd, buf, &repmsg); } else if((reqmsg.flags & Omask) != Oquery){ syslog(0, "dns", "server: op %d from %I", reqmsg.flags & Omask, buf); } else { /* loop through each question */ while(reqmsg.qd){ if(debug) print("serve %s %s\n", reqmsg.qd->owner->name, rrname(reqmsg.qd->type, tname)); memset(&repmsg, 0, sizeof(repmsg)); repmsg.id = reqmsg.id; repmsg.flags = Fresp | Fcanrec | Oquery; repmsg.qd = reqmsg.qd; reqmsg.qd = reqmsg.qd->next; repmsg.qd->next = 0; /* * get the answer if we can */ if(reqmsg.flags & Frecurse) recurse(&repmsg, &req); else local(&repmsg); /* * add ip addresses as hints */ for(tp = repmsg.ns; tp; tp = tp->next) hint(&repmsg.ar, tp); for(tp = repmsg.an; tp; tp = tp->next) hint(&repmsg.ar, tp); reply(fd, buf, &repmsg); /* * pack and send reply */ 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) _exits(0); } } /* * satisfy a recursive request. dnlookup will handle cnames. */ static void recurse(DNSmsg *mp, Request *req) { int type; char *name; RR *cname; name = mp->qd->owner->name; type = mp->qd->type; cname = 0; rrcat(&mp->an, dnresolve(name, Cin, type, req, &cname), type); if(cname) rrcat(&mp->an, cname, Tcname); if(mp->an && mp->an->auth) mp->flags |= Fauth; } /* * satisfy a request with local info only */ static void local(DNSmsg *mp) { DN *dp, *nsdp; RR *nsrp; char *cp; int type; dp = mp->qd->owner; type = mp->qd->type; mp->flags |= Fauth; /* in-addr.arpa queries are special */ if(type == Tptr){ rrcat(&mp->an, dbinaddr(dp), type); if(mp->an) return; } /* * walk up the domain name looking for * a name server for the domain. */ for(cp = dp->name; mp->an && cp; cp = walkup(cp)){ /* look for ns in cache and database */ nsdp = dnlookup(cp, dp->class, 0); nsrp = 0; if(nsdp) nsrp = rrlookup(nsdp, Tns); if(nsrp == 0) nsrp = dblookup(cp, dp->class, Tns, 0); if(nsrp == 0) continue; if(nsrp->local){ /* local domains resolved from this db */ if(nsrp->db) /* free database rr's */ rrfreelist(nsrp); rrcat(&mp->an, dblookup(dp->name, Cin, type, 1), type); } else { /* just return the name of a name server to use */ rrcat(&mp->ns, nsrp, Tns); } break; } } static void reply(int fd, uchar *buf, DNSmsg *rep) { int len; len = convDNS2M(rep, &buf[Udphdrlen], Maxudp); if(len <= 0) fatal("dnserver: converting reply"); len += Udphdrlen; if(write(fd, buf, len) != len) fatal("dnserver: sending reply"); } static void hint(RR **last, RR *rp) { switch(rp->type){ case Tns: case Tmx: case Tmb: case Tmf: case Tmd: rrcat(last, dblookup(rp->host->name, Cin, Ta, 0), Ta); break; } } /* * get ip addresses without looking off machine */ static RR* getipaddr(DN *dp) { RR *rp; /* first try the cache */ rp = rrlookup(dp, Ta); if(rp) return rp; /* then try on disk db */ return dblookup(dp->name, dp->class, Ta, 0); } /* * announce on udp port and set message style interface */ static char *hmsg = "headers"; static int udpport(void) { int data, ctl; char dir[64]; char datafile[64+6]; /* get a udp port */ ctl = announce("udp!*!dns", dir); if(ctl < 0) fatal("can't announce on udp port"); sprint(datafile, "%s/data", dir); /* turn on header style interface */ if(write(ctl, hmsg, strlen(hmsg)) , 0) fatal(hmsg); data = open(datafile, ORDWR); if(data < 0){ close(ctl); fatal("can't announce on udp port"); } close(ctl); return data; }