/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #include <u.h>#include <libc.h>#include <auth.h>#include <fcall.h>#include <bio.h>#include <ctype.h>#include <ip.h>#include <pool.h>#include "dns.h" enum{ Maxrequest= 1024, Maxreply= 8192, /* was 512 */ Maxrrr= 32, /* was 16 */ Maxfdata= 8192, Defmaxage= 60*60, /* default domain name max. age */ Qdir= 0, Qdns= 1,}; typedef struct Mfile Mfile;typedef struct Job Job;typedef struct Network Network; extern ulong start; int vers; /* incremented each clone/attach */ static volatile int stop; /* holds data to be returned via read of /net/dns, perhaps multiple reads */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; Mfile *inuse; /* active mfile's */} mfalloc; Cfg cfg;int debug;uchar ipaddr[IPaddrlen]; /* my ip address */int maxage = Defmaxage;int mfd[2];int needrefresh;ulong now;vlong nowns;int sendnotifies;int testing;char *trace;int traceactivity;char *zonerefreshprogram; char *logfile = "dns"; /* or "dns.test" */char *dbfile;char mntpt[Maxpath]; int addforwtarg(char *);int fillreply(Mfile*, int);void freejob(Job*);void io(void);void mountinit(char*, char*);Job* newjob(void);void rattach(Job*, Mfile*);void rauth(Job*);void rclunk(Job*, Mfile*);void rcreate(Job*, Mfile*);void rflush(Job*);void ropen(Job*, Mfile*);void rread(Job*, Mfile*);void rremove(Job*, Mfile*);void rstat(Job*, Mfile*);void rversion(Job*);char* rwalk(Job*, Mfile*);void rwrite(Job*, Mfile*, Request*);void rwstat(Job*, Mfile*);void sendmsg(Job*, char*);void setext(char*, int, char*); static char *lookupqueryold(Job*, Mfile*, Request*, char*, char*, int, int);static char *lookupquerynew(Job*, Mfile*, Request*, char*, char*, int, int);static char *respond(Job*, Mfile*, RR*, char*, int, int); voidusage(void){ fprint(2, "usage: %s [-FnorRst] [-a maxage] [-f ndb-file] [-N target] " "[-T forwip] [-x netmtpt] [-z refreshprog]\n", argv0); exits("usage");} voidjustremount(char *service, char *mntpt){ int f; f = open(service, ORDWR); if(f < 0) abort(); /* service */; while (mount(f, -1, mntpt, MAFTER, "") < 0) { dnslog("dns mount -a on %s failed: %r", mntpt); sleep(5000); }} voidmain(int argc, char *argv[]){ int kid, pid; char servefile[Maxpath], ext[Maxpath]; Dir *dir; setnetmtpt(mntpt, sizeof mntpt, nil); ext[0] = 0; ARGBEGIN{ case 'a': maxage = atol(EARGF(usage())); if (maxage <= 0) maxage = Defmaxage; break; case 'd': debug = 1; traceactivity = 1; break; case 'f': dbfile = EARGF(usage()); break; case 'F': cfg.justforw = cfg.resolver = 1; break; case 'n': sendnotifies = 1; break; case 'N': target = atol(EARGF(usage())); if (target < 1000) target = 1000; break; case 'o': cfg.straddle = 1; /* straddle inside & outside networks */ break; case 'r': cfg.resolver = 1; break; case 'R': norecursion = 1; break; case 's': cfg.serve = 1; /* serve network */ cfg.cachedb = 1; break; case 't': testing = 1; break; case 'T': addforwtarg(EARGF(usage())); break; case 'x': setnetmtpt(mntpt, sizeof mntpt, EARGF(usage())); setext(ext, sizeof ext, mntpt); break; case 'z': zonerefreshprogram = EARGF(usage()); break; default: usage(); break; }ARGEND if(argc != 0) usage(); if(testing) mainmem->flags |= POOL_NOREUSE | POOL_ANTAGONISM; mainmem->flags |= POOL_ANTAGONISM; rfork(RFREND|RFNOTEG); cfg.inside = (*mntpt == '\0' || strcmp(mntpt, "/net") == 0); /* start syslog before we fork */ fmtinstall('F', fcallfmt); dninit(); /* this really shouldn't be fatal */ if(myipaddr(ipaddr, mntpt) < 0) sysfatal("can't read my ip address"); dnslog("starting %s%sdns %s%s%son %I's %s", (cfg.straddle? "straddling ": ""), (cfg.cachedb? "caching ": ""), (cfg.serve? "udp server ": ""), (cfg.justforw? "forwarding-only ": ""), (cfg.resolver? "resolver ": ""), ipaddr, mntpt); opendatabase(); now = time(nil); /* open time files before we fork */ nowns = nsec(); snprint(servefile, sizeof servefile, "#s/dns%s", ext); dir = dirstat(servefile); if (dir) sysfatal("%s exists; another dns instance is running", servefile); free(dir); /* don't unmount here; could deadlock */// while (unmount(servefile, mntpt) >= 0)// ; mountinit(servefile, mntpt); /* forks, parent exits */ srand(now*getpid()); db2cache(1);// dnageallnever(); if (cfg.straddle && !seerootns()) dnslog("straddle server misconfigured; can't resolve root name servers"); /* * fork without sharing heap. * parent waits around for child to die, then forks & restarts. * child may spawn udp server, notify procs, etc.; when it gets too * big or too old, it kills itself and any children. * * /srv/dns remains open and valid, but /net/dns was only mounted in * a child's separate namespace from 9p service, to avoid a deadlock * from serving our own namespace, so we must remount it upon restart, * in a separate process and namespace. */ for (;;) { start = time(nil); /* don't unmount here; could deadlock */// unmount(servefile, mntpt); kid = rfork(RFPROC|RFFDG|RFNOTEG|RFNAMEG); switch (kid) { case -1: sysfatal("fork failed: %r"); case 0: if(cfg.serve) dnudpserver(mntpt); if(sendnotifies) notifyproc(); io(); /* serve 9p; return implies restart */ _exits("restart"); } sleep(1000); /* wait for 9p service to start */ justremount(servefile, mntpt); while ((pid = waitpid()) != kid && pid != -1) continue; dnslog("restarting"); }} /* * if a mount point is specified, set the cs extension to be the mount point * with '_'s replacing '/'s */voidsetext(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;} voidmountinit(char *service, char *mntpt){ int f; int p[2]; char buf[32]; if(pipe(p) < 0) abort(); /* "pipe failed" */; switch(rfork(RFFDG|RFPROC)){ case 0: /* child: hang around and (re)start main proc */ close(p[1]); procsetname("%s restarter", mntpt); mfd[0] = mfd[1] = p[0]; break; case -1: abort(); /* "fork failed\n" */; default: /* parent: make /srv/dns, mount it, exit */ close(p[0]); /* * make a /srv/dns */ f = create(service, 1, 0666); if(f < 0) abort(); /* service */; snprint(buf, sizeof buf, "%d", p[1]); if(write(f, buf, strlen(buf)) != strlen(buf)) abort(); /* "write %s", service */; close(f); /* * put ourselves into the file system * it's too soon; we need 9p service running. */// if(mount(p[1], -1, mntpt, MAFTER, "") < 0)// dnslog("dns mount -a on %s failed: %r", mntpt); close(p[1]); _exits(0); }} Mfile*newfid(int fid, int needunused){ Mfile *mf; lock(&mfalloc); for(mf = mfalloc.inuse; mf != nil; mf = mf->next) if(mf->fid == fid){ unlock(&mfalloc); if(needunused) return nil; return mf; } mf = emalloc(sizeof(*mf)); mf->fid = fid; mf->user = estrdup("dummy"); mf->next = mfalloc.inuse; mfalloc.inuse = mf; unlock(&mfalloc); return mf;} voidfreefid(Mfile *mf){ Mfile **l; lock(&mfalloc); for(l = &mfalloc.inuse; *l != nil; l = &(*l)->next) if(*l == mf){ *l = mf->next; if(mf->user) free(mf->user); memset(mf, 0, sizeof *mf); /* cause trouble */ free(mf); unlock(&mfalloc); return; } unlock(&mfalloc); sysfatal("freeing unused fid");} Mfile*copyfid(Mfile *mf, int fid){ Mfile *nmf; nmf = newfid(fid, 1); if(nmf == nil) return nil; nmf->fid = fid; free(nmf->user); /* estrdup("dummy") */ 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;} voidfreejob(Job *job){ Job **l; lock(&joblock); for(l = &joblist; *l; l = &(*l)->next) if(*l == job){ *l = job->next; memset(job, 0, sizeof *job); /* cause trouble */ free(job); break; } unlock(&joblock);} voidflushjob(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);} voidio(void){ volatile long n; volatile uchar mdata[IOHDRSZ + Maxfdata]; Job *volatile job; Mfile *volatile mf; volatile Request req; memset(&req, 0, sizeof req); /* * 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(0); req.isslave = 0; stop = 0; while(!stop){ procsetname("%d %s/dns Twrites of %d 9p rpcs read; %d alarms", stats.qrecvd9p, mntpt, stats.qrecvd9prpc, stats.alarms); n = read9pmsg(mfd[0], mdata, sizeof mdata); if(n<=0){ dnslog("error reading 9P from %s: %r", mntpt); sleep(2000); /* don't thrash after read error */ return; } stats.qrecvd9prpc++; job = newjob(); if(convM2S(mdata, n, &job->request) != n){ freejob(job); continue; } mf = newfid(job->request.fid, 0); if(debug) dnslog("%F", &job->request); getactivity(&req, 0); req.aborttime = timems() + Maxreqtm; req.from = "9p"; switch(job->request.type){ default: warning("unknown request type %d", job->request.type); break; case Tversion: rversion(job);Lines 1–500 of 1063. Load more
/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #include <u.h> #include <libc.h> #include <auth.h> #include <fcall.h> #include <bio.h> #include <ctype.h> #include <ip.h> #include <pool.h> #include "dns.h" enum { Maxrequest= 1024, Maxreply= 8192, /* was 512 */ Maxrrr= 32, /* was 16 */ Maxfdata= 8192, Defmaxage= 60*60, /* default domain name max. age */ Qdir= 0, Qdns= 1, }; typedef struct Mfile Mfile; typedef struct Job Job; typedef struct Network Network; extern ulong start; int vers; /* incremented each clone/attach */ static volatile int stop; /* holds data to be returned via read of /net/dns, perhaps multiple reads */ 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; Mfile *inuse; /* active mfile's */ } mfalloc; Cfg cfg; int debug; uchar ipaddr[IPaddrlen]; /* my ip address */ int maxage = Defmaxage; int mfd[2]; int needrefresh; ulong now; vlong nowns; int sendnotifies; int testing; char *trace; int traceactivity; char *zonerefreshprogram; char *logfile = "dns"; /* or "dns.test" */ char *dbfile; char mntpt[Maxpath]; int addforwtarg(char *); int fillreply(Mfile*, int); void freejob(Job*); void io(void); void mountinit(char*, char*); Job* newjob(void); void rattach(Job*, Mfile*); void rauth(Job*); void rclunk(Job*, Mfile*); void rcreate(Job*, Mfile*); void rflush(Job*); void ropen(Job*, Mfile*); void rread(Job*, Mfile*); void rremove(Job*, Mfile*); void rstat(Job*, Mfile*); void rversion(Job*); char* rwalk(Job*, Mfile*); void rwrite(Job*, Mfile*, Request*); void rwstat(Job*, Mfile*); void sendmsg(Job*, char*); void setext(char*, int, char*); static char *lookupqueryold(Job*, Mfile*, Request*, char*, char*, int, int); static char *lookupquerynew(Job*, Mfile*, Request*, char*, char*, int, int); static char *respond(Job*, Mfile*, RR*, char*, int, int); void usage(void) { fprint(2, "usage: %s [-FnorRst] [-a maxage] [-f ndb-file] [-N target] " "[-T forwip] [-x netmtpt] [-z refreshprog]\n", argv0); exits("usage"); } void justremount(char *service, char *mntpt) { int f; f = open(service, ORDWR); if(f < 0) abort(); /* service */; while (mount(f, -1, mntpt, MAFTER, "") < 0) { dnslog("dns mount -a on %s failed: %r", mntpt); sleep(5000); } } void main(int argc, char *argv[]) { int kid, pid; char servefile[Maxpath], ext[Maxpath]; Dir *dir; setnetmtpt(mntpt, sizeof mntpt, nil); ext[0] = 0; ARGBEGIN{ case 'a': maxage = atol(EARGF(usage())); if (maxage <= 0) maxage = Defmaxage; break; case 'd': debug = 1; traceactivity = 1; break; case 'f': dbfile = EARGF(usage()); break; case 'F': cfg.justforw = cfg.resolver = 1; break; case 'n': sendnotifies = 1; break; case 'N': target = atol(EARGF(usage())); if (target < 1000) target = 1000; break; case 'o': cfg.straddle = 1; /* straddle inside & outside networks */ break; case 'r': cfg.resolver = 1; break; case 'R': norecursion = 1; break; case 's': cfg.serve = 1; /* serve network */ cfg.cachedb = 1; break; case 't': testing = 1; break; case 'T': addforwtarg(EARGF(usage())); break; case 'x': setnetmtpt(mntpt, sizeof mntpt, EARGF(usage())); setext(ext, sizeof ext, mntpt); break; case 'z': zonerefreshprogram = EARGF(usage()); break; default: usage(); break; }ARGEND if(argc != 0) usage(); if(testing) mainmem->flags |= POOL_NOREUSE | POOL_ANTAGONISM; mainmem->flags |= POOL_ANTAGONISM; rfork(RFREND|RFNOTEG); cfg.inside = (*mntpt == '\0' || strcmp(mntpt, "/net") == 0); /* start syslog before we fork */ fmtinstall('F', fcallfmt); dninit(); /* this really shouldn't be fatal */ if(myipaddr(ipaddr, mntpt) < 0) sysfatal("can't read my ip address"); dnslog("starting %s%sdns %s%s%son %I's %s", (cfg.straddle? "straddling ": ""), (cfg.cachedb? "caching ": ""), (cfg.serve? "udp server ": ""), (cfg.justforw? "forwarding-only ": ""), (cfg.resolver? "resolver ": ""), ipaddr, mntpt); opendatabase(); now = time(nil); /* open time files before we fork */ nowns = nsec(); snprint(servefile, sizeof servefile, "#s/dns%s", ext); dir = dirstat(servefile); if (dir) sysfatal("%s exists; another dns instance is running", servefile); free(dir); /* don't unmount here; could deadlock */ // while (unmount(servefile, mntpt) >= 0) // ; mountinit(servefile, mntpt); /* forks, parent exits */ srand(now*getpid()); db2cache(1); // dnageallnever(); if (cfg.straddle && !seerootns()) dnslog("straddle server misconfigured; can't resolve root name servers"); /* * fork without sharing heap. * parent waits around for child to die, then forks & restarts. * child may spawn udp server, notify procs, etc.; when it gets too * big or too old, it kills itself and any children. * * /srv/dns remains open and valid, but /net/dns was only mounted in * a child's separate namespace from 9p service, to avoid a deadlock * from serving our own namespace, so we must remount it upon restart, * in a separate process and namespace. */ for (;;) { start = time(nil); /* don't unmount here; could deadlock */ // unmount(servefile, mntpt); kid = rfork(RFPROC|RFFDG|RFNOTEG|RFNAMEG); switch (kid) { case -1: sysfatal("fork failed: %r"); case 0: if(cfg.serve) dnudpserver(mntpt); if(sendnotifies) notifyproc(); io(); /* serve 9p; return implies restart */ _exits("restart"); } sleep(1000); /* wait for 9p service to start */ justremount(servefile, mntpt); while ((pid = waitpid()) != kid && pid != -1) continue; dnslog("restarting"); } } /* * if a mount point is specified, set the cs extension 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 f; int p[2]; char buf[32]; if(pipe(p) < 0) abort(); /* "pipe failed" */; switch(rfork(RFFDG|RFPROC)){ case 0: /* child: hang around and (re)start main proc */ close(p[1]); procsetname("%s restarter", mntpt); mfd[0] = mfd[1] = p[0]; break; case -1: abort(); /* "fork failed\n" */; default: /* parent: make /srv/dns, mount it, exit */ close(p[0]); /* * make a /srv/dns */ f = create(service, 1, 0666); if(f < 0) abort(); /* service */; snprint(buf, sizeof buf, "%d", p[1]); if(write(f, buf, strlen(buf)) != strlen(buf)) abort(); /* "write %s", service */; close(f); /* * put ourselves into the file system * it's too soon; we need 9p service running. */ // if(mount(p[1], -1, mntpt, MAFTER, "") < 0) // dnslog("dns mount -a on %s failed: %r", mntpt); close(p[1]); _exits(0); } } Mfile* newfid(int fid, int needunused) { Mfile *mf; lock(&mfalloc); for(mf = mfalloc.inuse; mf != nil; mf = mf->next) if(mf->fid == fid){ unlock(&mfalloc); if(needunused) return nil; return mf; } mf = emalloc(sizeof(*mf)); mf->fid = fid; mf->user = estrdup("dummy"); mf->next = mfalloc.inuse; mfalloc.inuse = mf; unlock(&mfalloc); return mf; } void freefid(Mfile *mf) { Mfile **l; lock(&mfalloc); for(l = &mfalloc.inuse; *l != nil; l = &(*l)->next) if(*l == mf){ *l = mf->next; if(mf->user) free(mf->user); memset(mf, 0, sizeof *mf); /* cause trouble */ free(mf); unlock(&mfalloc); return; } unlock(&mfalloc); sysfatal("freeing unused fid"); } Mfile* copyfid(Mfile *mf, int fid) { Mfile *nmf; nmf = newfid(fid, 1); if(nmf == nil) return nil; nmf->fid = fid; free(nmf->user); /* estrdup("dummy") */ 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; memset(job, 0, sizeof *job); /* cause trouble */ 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) { volatile long n; volatile uchar mdata[IOHDRSZ + Maxfdata]; Job *volatile job; Mfile *volatile mf; volatile Request req; memset(&req, 0, sizeof req); /* * 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(0); req.isslave = 0; stop = 0; while(!stop){ procsetname("%d %s/dns Twrites of %d 9p rpcs read; %d alarms", stats.qrecvd9p, mntpt, stats.qrecvd9prpc, stats.alarms); n = read9pmsg(mfd[0], mdata, sizeof mdata); if(n<=0){ dnslog("error reading 9P from %s: %r", mntpt); sleep(2000); /* don't thrash after read error */ return; } stats.qrecvd9prpc++; job = newjob(); if(convM2S(mdata, n, &job->request) != n){ freejob(job); continue; } mf = newfid(job->request.fid, 0); if(debug) dnslog("%F", &job->request); getactivity(&req, 0); req.aborttime = timems() + Maxreqtm; req.from = "9p"; switch(job->request.type){ default: warning("unknown request type %d", job->request.type); break; case Tversion: rversion(job);