1063 lines · 20.6 KB · C
RawDownload
/*  * 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.