diff options
Diffstat (limited to 'src/cmd/ndb/dns.c')
-rwxr-xr-x | src/cmd/ndb/dns.c | 882 |
1 files changed, 882 insertions, 0 deletions
diff --git a/src/cmd/ndb/dns.c b/src/cmd/ndb/dns.c new file mode 100755 index 00000000..5f509f24 --- /dev/null +++ b/src/cmd/ndb/dns.c @@ -0,0 +1,882 @@ +#include <u.h> +#include <libc.h> +#include <auth.h> +#include <fcall.h> +#include <bio.h> +#include <ctype.h> +#include <ip.h> +#include <ndb.h> +#include "dns.h" + +enum +{ + Maxrequest= 1024, + Ncache= 8, + Maxpath= 128, + Maxreply= 512, + Maxrrr= 16, + Maxfdata= 8192, + + Qdir= 0, + Qdns= 1, +}; + +typedef struct Mfile Mfile; +typedef struct Job Job; +typedef struct Network Network; + +int vers; /* incremented each clone/attach */ + +struct Mfile +{ + Mfile *next; /* next free mfile */ + int ref; + + char *user; + Qid qid; + int fid; + + int type; /* reply type */ + char reply[Maxreply]; + ushort rr[Maxrrr]; /* offset of rr's */ + ushort nrr; /* number of rr's */ +}; + +// +// active local requests +// +struct Job +{ + Job *next; + int flushed; + Fcall request; + Fcall reply; +}; +Lock joblock; +Job *joblist; + +struct { + Lock lk; + Mfile *inuse; /* active mfile's */ +} mfalloc; + +int haveip; +int mfd[2]; +int debug; +int traceactivity; +int cachedb; +ulong now; +int testing; +char *trace; +int needrefresh; +int resolver; +uchar ipaddr[IPaddrlen]; /* my ip address */ +int maxage; +char *zonerefreshprogram; +int sendnotifies; + +void rversion(Job*); +void rauth(Job*); +void rflush(Job*); +void rattach(Job*, Mfile*); +char* rwalk(Job*, Mfile*); +void ropen(Job*, Mfile*); +void rcreate(Job*, Mfile*); +void rread(Job*, Mfile*); +void rwrite(Job*, Mfile*, Request*); +void rclunk(Job*, Mfile*); +void rremove(Job*, Mfile*); +void rstat(Job*, Mfile*); +void rwstat(Job*, Mfile*); +void sendmsg(Job*, char*); +void mountinit(char*, char*); +void io(void); +int fillreply(Mfile*, int); +Job* newjob(void); +void freejob(Job*); +void setext(char*, int, char*); + +char *logfile = "dns"; +char *dbfile; +char mntpt[Maxpath]; +char *LOG; + +void +usage(void) +{ + fprint(2, "usage: %s [-rs] [-f ndb-file] [-x netmtpt]\n", argv0); + exits("usage"); +} + +void +main(int argc, char *argv[]) +{ + int serve; + char servefile[Maxpath]; + char ext[Maxpath]; + char *p; + + serve = 0; +// setnetmtpt(mntpt, sizeof(mntpt), nil); + ext[0] = 0; + ARGBEGIN{ + case 'd': + debug = 1; + traceactivity = 1; + break; + case 'f': + p = ARGF(); + if(p == nil) + usage(); + dbfile = p; + break; + case 'i': + haveip = 1; + parseip(ipaddr, EARGF(usage())); + break; +// case 'x': + // p = ARGF(); + // if(p == nil) + // usage(); + // setnetmtpt(mntpt, sizeof(mntpt), p); + // setext(ext, sizeof(ext), mntpt); + // break; + case 'r': + resolver = 1; + break; + case 's': + serve = 1; /* serve network */ + cachedb = 1; + break; + case 'a': + p = ARGF(); + if(p == nil) + usage(); + maxage = atoi(p); + break; + case 't': + testing = 1; + break; + case 'z': + zonerefreshprogram = ARGF(); + break; + case 'n': + sendnotifies = 1; + break; + }ARGEND + USED(argc); + USED(argv); + +//if(testing) mainmem->flags |= POOL_NOREUSE; +#define RFREND 0 + rfork(RFREND|RFNOTEG); + + /* start syslog before we fork */ + fmtinstall('F', fcallfmt); + dninit(); + if(!haveip && myipaddr(ipaddr, mntpt) < 0) + sysfatal("can't read my ip address"); + + syslog(0, logfile, "starting dns on %I", ipaddr); + + opendatabase(); + +/* + snprint(servefile, sizeof(servefile), "#s/dns%s", ext); + unmount(servefile, mntpt); + remove(servefile); +*/ + mountinit(servefile, mntpt); + + now = time(0); + srand(now*getpid()); + db2cache(1); + + if(serve) + proccreate(dnudpserver, mntpt, STACK); + if(sendnotifies) + notifyproc(); + + io(); + syslog(0, logfile, "io returned, exiting"); + exits(0); +} + +int +myipaddr(uchar *ip, char *net) +{ + ipmove(ip, ipaddr); + return 0; +} + +/* + * if a mount point is specified, set the cs extention to be the mount point + * with '_'s replacing '/'s + */ +void +setext(char *ext, int n, char *p) +{ + int i, c; + + n--; + for(i = 0; i < n; i++){ + c = p[i]; + if(c == 0) + break; + if(c == '/') + c = '_'; + ext[i] = c; + } + ext[i] = 0; +} + +void +mountinit(char *service, char *mntpt) +{ + int p[2]; + + if(pipe(p) < 0) + abort(); /* "pipe failed" */; + switch(rfork(RFFDG|RFPROC|RFNAMEG)){ + case 0: + close(p[1]); + break; + case -1: + abort(); /* "fork failed\n" */; + default: + close(p[0]); + + if(post9pservice(p[1], "dns") < 0) + fprint(2, "post9pservice dns: %r\n"); + _exits(0); + } + mfd[0] = mfd[1] = p[0]; +} + +Mfile* +newfid(int fid, int needunused) +{ + Mfile *mf; + + lock(&mfalloc.lk); + for(mf = mfalloc.inuse; mf != nil; mf = mf->next){ + if(mf->fid == fid){ + unlock(&mfalloc.lk); + if(needunused) + return nil; + return mf; + } + } + mf = emalloc(sizeof(*mf)); + if(mf == nil) + sysfatal("out of memory"); + mf->fid = fid; + mf->next = mfalloc.inuse; + mfalloc.inuse = mf; + unlock(&mfalloc.lk); + return mf; +} + +void +freefid(Mfile *mf) +{ + Mfile **l; + + lock(&mfalloc.lk); + for(l = &mfalloc.inuse; *l != nil; l = &(*l)->next){ + if(*l == mf){ + *l = mf->next; + if(mf->user) + free(mf->user); + free(mf); + unlock(&mfalloc.lk); + return; + } + } + sysfatal("freeing unused fid"); +} + +Mfile* +copyfid(Mfile *mf, int fid) +{ + Mfile *nmf; + + nmf = newfid(fid, 1); + if(nmf == nil) + return nil; + nmf->fid = fid; + nmf->user = estrdup(mf->user); + nmf->qid.type = mf->qid.type; + nmf->qid.path = mf->qid.path; + nmf->qid.vers = vers++; + return nmf; +} + +Job* +newjob(void) +{ + Job *job; + + job = emalloc(sizeof(*job)); + lock(&joblock); + job->next = joblist; + joblist = job; + job->request.tag = -1; + unlock(&joblock); + return job; +} + +void +freejob(Job *job) +{ + Job **l; + + lock(&joblock); + for(l = &joblist; *l; l = &(*l)->next){ + if((*l) == job){ + *l = job->next; + free(job); + break; + } + } + unlock(&joblock); +} + +void +flushjob(int tag) +{ + Job *job; + + lock(&joblock); + for(job = joblist; job; job = job->next){ + if(job->request.tag == tag && job->request.type != Tflush){ + job->flushed = 1; + break; + } + } + unlock(&joblock); +} + +void +io(void) +{ + long n; + Mfile *mf; + uchar mdata[IOHDRSZ + Maxfdata]; + Request req; + Job *job; + + /* + * a slave process is sometimes forked to wait for replies from other + * servers. The master process returns immediately via a longjmp + * through 'mret'. + */ + if(setjmp(req.mret)) + putactivity(); + req.isslave = 0; + for(;;){ + n = read9pmsg(mfd[0], mdata, sizeof mdata); + if(n<=0){ + syslog(0, logfile, "error reading mntpt: %r"); + exits(0); + } + job = newjob(); + if(convM2S(mdata, n, &job->request) != n){ + freejob(job); + continue; + } + mf = newfid(job->request.fid, 0); + if(debug) + syslog(0, logfile, "%F", &job->request); + + getactivity(&req); + req.aborttime = now + 60; /* don't spend more than 60 seconds */ + + switch(job->request.type){ + default: + syslog(1, logfile, "unknown request type %d", job->request.type); + break; + case Tversion: + rversion(job); + break; + case Tauth: + rauth(job); + break; + case Tflush: + rflush(job); + break; + case Tattach: + rattach(job, mf); + break; + case Twalk: + rwalk(job, mf); + break; + case Topen: + ropen(job, mf); + break; + case Tcreate: + rcreate(job, mf); + break; + case Tread: + rread(job, mf); + break; + case Twrite: + rwrite(job, mf, &req); + break; + case Tclunk: + rclunk(job, mf); + break; + case Tremove: + rremove(job, mf); + break; + case Tstat: + rstat(job, mf); + break; + case Twstat: + rwstat(job, mf); + break; + } + + freejob(job); + + /* + * slave processes die after replying + */ + if(req.isslave){ + putactivity(); + _exits(0); + } + + putactivity(); + } +} + +void +rversion(Job *job) +{ + if(job->request.msize > IOHDRSZ + Maxfdata) + job->reply.msize = IOHDRSZ + Maxfdata; + else + job->reply.msize = job->request.msize; + if(strncmp(job->request.version, "9P2000", 6) != 0) + sendmsg(job, "unknown 9P version"); + else{ + job->reply.version = "9P2000"; + sendmsg(job, 0); + } +} + +void +rauth(Job *job) +{ + sendmsg(job, "dns: authentication not required"); +} + +/* + * don't flush till all the slaves are done + */ +void +rflush(Job *job) +{ + flushjob(job->request.oldtag); + sendmsg(job, 0); +} + +void +rattach(Job *job, Mfile *mf) +{ + if(mf->user != nil) + free(mf->user); + mf->user = estrdup(job->request.uname); + mf->qid.vers = vers++; + mf->qid.type = QTDIR; + mf->qid.path = 0LL; + job->reply.qid = mf->qid; + sendmsg(job, 0); +} + +char* +rwalk(Job *job, Mfile *mf) +{ + char *err; + char **elems; + int nelems; + int i; + Mfile *nmf; + Qid qid; + + err = 0; + nmf = nil; + elems = job->request.wname; + nelems = job->request.nwname; + job->reply.nwqid = 0; + + if(job->request.newfid != job->request.fid){ + /* clone fid */ + if(job->request.newfid<0){ + err = "clone newfid out of range"; + goto send; + } + nmf = copyfid(mf, job->request.newfid); + if(nmf == nil){ + err = "clone bad newfid"; + goto send; + } + mf = nmf; + } + /* else nmf will be nil */ + + qid = mf->qid; + if(nelems > 0){ + /* walk fid */ + for(i=0; i<nelems && i<MAXWELEM; i++){ + if((qid.type & QTDIR) == 0){ + err = "not a directory"; + break; + } + if(strcmp(elems[i], "..") == 0 || strcmp(elems[i], ".") == 0){ + qid.type = QTDIR; + qid.path = Qdir; + Found: + job->reply.wqid[i] = qid; + job->reply.nwqid++; + continue; + } + if(strcmp(elems[i], "dns") == 0){ + qid.type = QTFILE; + qid.path = Qdns; + goto Found; + } + err = "file does not exist"; + break; + } + } + + send: + if(nmf != nil && (err!=nil || job->reply.nwqid<nelems)) + freefid(nmf); + if(err == nil) + mf->qid = qid; + sendmsg(job, err); + return err; +} + +void +ropen(Job *job, Mfile *mf) +{ + int mode; + char *err; + + err = 0; + mode = job->request.mode; + if(mf->qid.type & QTDIR){ + if(mode) + err = "permission denied"; + } + job->reply.qid = mf->qid; + job->reply.iounit = 0; + sendmsg(job, err); +} + +void +rcreate(Job *job, Mfile *mf) +{ + USED(mf); + sendmsg(job, "creation permission denied"); +} + +void +rread(Job *job, Mfile *mf) +{ + int i, n, cnt; + long off; + Dir dir; + uchar buf[Maxfdata]; + char *err; + long clock; + + n = 0; + err = 0; + off = job->request.offset; + cnt = job->request.count; + if(mf->qid.type & QTDIR){ + clock = time(0); + if(off == 0){ + dir.name = "dns"; + dir.qid.type = QTFILE; + dir.qid.vers = vers; + dir.qid.path = Qdns; + dir.mode = 0666; + dir.length = 0; + dir.uid = mf->user; + dir.gid = mf->user; + dir.muid = mf->user; + dir.atime = clock; /* wrong */ + dir.mtime = clock; /* wrong */ + n = convD2M(&dir, buf, sizeof buf); + } + job->reply.data = (char*)buf; + } else { + for(i = 1; i <= mf->nrr; i++) + if(mf->rr[i] > off) + break; + if(i > mf->nrr) + goto send; + if(off + cnt > mf->rr[i]) + n = mf->rr[i] - off; + else + n = cnt; + job->reply.data = mf->reply + off; + } +send: + job->reply.count = n; + sendmsg(job, err); +} + +void +rwrite(Job *job, Mfile *mf, Request *req) +{ + int cnt, rooted, status; + long n; + char *err, *p, *atype; + RR *rp, *tp, *neg; + int wantsav; + + err = 0; + cnt = job->request.count; + if(mf->qid.type & QTDIR){ + err = "can't write directory"; + goto send; + } + if(cnt >= Maxrequest){ + err = "request too long"; + goto send; + } + job->request.data[cnt] = 0; + if(cnt > 0 && job->request.data[cnt-1] == '\n') + job->request.data[cnt-1] = 0; + + /* + * special commands + */ + if(strncmp(job->request.data, "debug", 5)==0 && job->request.data[5] == 0){ + debug ^= 1; + goto send; + } else if(strncmp(job->request.data, "dump", 4)==0 && job->request.data[4] == 0){ + dndump("/lib/ndb/dnsdump"); + goto send; + } else if(strncmp(job->request.data, "refresh", 7)==0 && job->request.data[7] == 0){ + needrefresh = 1; + goto send; +// } else if(strncmp(job->request.data, "poolcheck", 9)==0 && job->request.data[9] == 0){ +// poolcheck(mainmem); +// goto send; + } + + /* + * kill previous reply + */ + mf->nrr = 0; + mf->rr[0] = 0; + + /* + * break up request (into a name and a type) + */ + atype = strchr(job->request.data, ' '); + if(atype == 0){ + err = "illegal request"; + goto send; + } else + *atype++ = 0; + + /* + * tracing request + */ + if(strcmp(atype, "trace") == 0){ + if(trace) + free(trace); + if(*job->request.data) + trace = estrdup(job->request.data); + else + trace = 0; + goto send; + } + + mf->type = rrtype(atype); + if(mf->type < 0){ + err = "unknown type"; + goto send; + } + + p = atype - 2; + if(p >= job->request.data && *p == '.'){ + rooted = 1; + *p = 0; + } else + rooted = 0; + + p = job->request.data; + if(*p == '!'){ + wantsav = 1; + p++; + } else + wantsav = 0; + dncheck(0, 1); + rp = dnresolve(p, Cin, mf->type, req, 0, 0, Recurse, rooted, &status); + dncheck(0, 1); + neg = rrremneg(&rp); + if(neg){ + status = neg->negrcode; + rrfreelist(neg); + } + if(rp == 0){ + switch(status){ + case Rname: + err = "name does not exist"; + break; + case Rserver: + err = "dns failure"; + break; + default: + err = "resource does not exist"; + break; + } + } else { + lock(&joblock); + if(!job->flushed){ + /* format data to be read later */ + n = 0; + mf->nrr = 0; + for(tp = rp; mf->nrr < Maxrrr-1 && n < Maxreply && tp && + tsame(mf->type, tp->type); tp = tp->next){ + mf->rr[mf->nrr++] = n; + if(wantsav) + n += snprint(mf->reply+n, Maxreply-n, "%Q", tp); + else + n += snprint(mf->reply+n, Maxreply-n, "%R", tp); + } + mf->rr[mf->nrr] = n; + } + unlock(&joblock); + rrfreelist(rp); + } + + send: + dncheck(0, 1); + job->reply.count = cnt; + sendmsg(job, err); +} + +void +rclunk(Job *job, Mfile *mf) +{ + freefid(mf); + sendmsg(job, 0); +} + +void +rremove(Job *job, Mfile *mf) +{ + USED(mf); + sendmsg(job, "remove permission denied"); +} + +void +rstat(Job *job, Mfile *mf) +{ + Dir dir; + uchar buf[IOHDRSZ+Maxfdata]; + + if(mf->qid.type & QTDIR){ + dir.name = "."; + dir.mode = DMDIR|0555; + } else { + dir.name = "dns"; + dir.mode = 0666; + } + dir.qid = mf->qid; + dir.length = 0; + dir.uid = mf->user; + dir.gid = mf->user; + dir.muid = mf->user; + dir.atime = dir.mtime = time(0); + job->reply.nstat = convD2M(&dir, buf, sizeof buf); + job->reply.stat = buf; + sendmsg(job, 0); +} + +void +rwstat(Job *job, Mfile *mf) +{ + USED(mf); + sendmsg(job, "wstat permission denied"); +} + +void +sendmsg(Job *job, char *err) +{ + int n; + uchar mdata[IOHDRSZ + Maxfdata]; + char ename[ERRMAX]; + + if(err){ + job->reply.type = Rerror; + snprint(ename, sizeof(ename), "dns: %s", err); + job->reply.ename = ename; + }else{ + job->reply.type = job->request.type+1; + } + job->reply.tag = job->request.tag; + n = convS2M(&job->reply, mdata, sizeof mdata); + if(n == 0){ + syslog(1, logfile, "sendmsg convS2M of %F returns 0", &job->reply); + abort(); + } + lock(&joblock); + if(job->flushed == 0) + if(write(mfd[1], mdata, n)!=n) + sysfatal("mount write"); + unlock(&joblock); + if(debug) + syslog(0, logfile, "%F %d", &job->reply, n); +} + +/* + * the following varies between dnsdebug and dns + */ +void +logreply(int id, uchar *addr, DNSmsg *mp) +{ + RR *rp; + + syslog(0, LOG, "%d: rcvd %I flags:%s%s%s%s%s", id, addr, + mp->flags & Fauth ? " auth" : "", + mp->flags & Ftrunc ? " trunc" : "", + mp->flags & Frecurse ? " rd" : "", + mp->flags & Fcanrec ? " ra" : "", + mp->flags & (Fauth|Rname) == (Fauth|Rname) ? + " nx" : ""); + for(rp = mp->qd; rp != nil; rp = rp->next) + syslog(0, LOG, "%d: rcvd %I qd %s", id, addr, rp->owner->name); + for(rp = mp->an; rp != nil; rp = rp->next) + syslog(0, LOG, "%d: rcvd %I an %R", id, addr, rp); + for(rp = mp->ns; rp != nil; rp = rp->next) + syslog(0, LOG, "%d: rcvd %I ns %R", id, addr, rp); + for(rp = mp->ar; rp != nil; rp = rp->next) + syslog(0, LOG, "%d: rcvd %I ar %R", id, addr, rp); +} + +void +logsend(int id, int subid, uchar *addr, char *sname, char *rname, int type) +{ + char buf[12]; + + syslog(0, LOG, "%d.%d: sending to %I/%s %s %s", + id, subid, addr, sname, rname, rrname(type, buf, sizeof buf)); +} + +RR* +getdnsservers(int class) +{ + return dnsservers(class); +} |