xref: /plan9/sys/src/cmd/ndb/dns.c (revision 98813beef1db23409911a4b339e6bb9c03d0a5c0)
1 #include <u.h>
2 #include <libc.h>
3 #include <auth.h>
4 #include <fcall.h>
5 #include <bio.h>
6 #include <ctype.h>
7 #include <ip.h>
8 #include <pool.h>
9 #include "dns.h"
10 
11 enum
12 {
13 	Maxrequest=		1024,
14 	Maxreply=		8192,		/* was 512 */
15 	Maxrrr=			32,		/* was 16 */
16 	Maxfdata=		8192,
17 
18 	Defmaxage=		60*60,	/* default domain name max. age */
19 
20 	Qdir=			0,
21 	Qdns=			1,
22 };
23 
24 typedef struct Mfile	Mfile;
25 typedef struct Job	Job;
26 typedef struct Network	Network;
27 
28 extern	ulong	start;
29 
30 int vers;		/* incremented each clone/attach */
31 
32 static volatile int stop;
33 
34 /* holds data to be returned via read of /net/dns, perhaps multiple reads */
35 struct Mfile
36 {
37 	Mfile		*next;		/* next free mfile */
38 	int		ref;
39 
40 	char		*user;
41 	Qid		qid;
42 	int		fid;
43 
44 	int		type;		/* reply type */
45 	char		reply[Maxreply];
46 	ushort		rr[Maxrrr];	/* offset of rr's */
47 	ushort		nrr;		/* number of rr's */
48 };
49 
50 /*
51  *  active local requests
52  */
53 struct Job
54 {
55 	Job	*next;
56 	int	flushed;
57 	Fcall	request;
58 	Fcall	reply;
59 };
60 Lock	joblock;
61 Job	*joblist;
62 
63 struct {
64 	Lock;
65 	Mfile	*inuse;		/* active mfile's */
66 } mfalloc;
67 
68 Cfg	cfg;
69 int	debug;
70 uchar	ipaddr[IPaddrlen];	/* my ip address */
71 int	maxage = Defmaxage;
72 int	mfd[2];
73 int	needrefresh;
74 ulong	now;
75 vlong	nowns;
76 int	sendnotifies;
77 int	testing;
78 char	*trace;
79 int	traceactivity;
80 char	*zonerefreshprogram;
81 
82 char	*logfile = "dns";	/* or "dns.test" */
83 char	*dbfile;
84 char	mntpt[Maxpath];
85 
86 int	addforwtarg(char *);
87 int	fillreply(Mfile*, int);
88 void	freejob(Job*);
89 void	io(void);
90 void	mountinit(char*, char*);
91 Job*	newjob(void);
92 void	rattach(Job*, Mfile*);
93 void	rauth(Job*);
94 void	rclunk(Job*, Mfile*);
95 void	rcreate(Job*, Mfile*);
96 void	rflush(Job*);
97 void	ropen(Job*, Mfile*);
98 void	rread(Job*, Mfile*);
99 void	rremove(Job*, Mfile*);
100 void	rstat(Job*, Mfile*);
101 void	rversion(Job*);
102 char*	rwalk(Job*, Mfile*);
103 void	rwrite(Job*, Mfile*, Request*);
104 void	rwstat(Job*, Mfile*);
105 void	sendmsg(Job*, char*);
106 void	setext(char*, int, char*);
107 
108 static char *lookupqueryold(Job*, Mfile*, Request*, char*, char*, int, int);
109 static char *lookupquerynew(Job*, Mfile*, Request*, char*, char*, int, int);
110 static char *respond(Job*, Mfile*, RR*, char*, int, int);
111 
112 void
usage(void)113 usage(void)
114 {
115 	fprint(2, "usage: %s [-FnorRst] [-a maxage] [-f ndb-file] [-N target] "
116 		"[-T forwip] [-x netmtpt] [-z refreshprog]\n", argv0);
117 	exits("usage");
118 }
119 
120 void
justremount(char * service,char * mntpt)121 justremount(char *service, char *mntpt)
122 {
123 	int f;
124 
125 	f = open(service, ORDWR);
126 	if(f < 0)
127 		abort(); 	/* service */;
128 	while (mount(f, -1, mntpt, MAFTER, "") < 0) {
129 		dnslog("dns mount -a on %s failed: %r", mntpt);
130 		sleep(5000);
131 	}
132 }
133 
134 void
main(int argc,char * argv[])135 main(int argc, char *argv[])
136 {
137 	int kid, pid;
138 	char servefile[Maxpath], ext[Maxpath];
139 	Dir *dir;
140 
141 	setnetmtpt(mntpt, sizeof mntpt, nil);
142 	ext[0] = 0;
143 	ARGBEGIN{
144 	case 'a':
145 		maxage = atol(EARGF(usage()));
146 		if (maxage <= 0)
147 			maxage = Defmaxage;
148 		break;
149 	case 'd':
150 		debug = 1;
151 		traceactivity = 1;
152 		break;
153 	case 'f':
154 		dbfile = EARGF(usage());
155 		break;
156 	case 'F':
157 		cfg.justforw = cfg.resolver = 1;
158 		break;
159 	case 'n':
160 		sendnotifies = 1;
161 		break;
162 	case 'N':
163 		target = atol(EARGF(usage()));
164 		if (target < 1000)
165 			target = 1000;
166 		break;
167 	case 'o':
168 		cfg.straddle = 1;	/* straddle inside & outside networks */
169 		break;
170 	case 'r':
171 		cfg.resolver = 1;
172 		break;
173 	case 'R':
174 		norecursion = 1;
175 		break;
176 	case 's':
177 		cfg.serve = 1;		/* serve network */
178 		cfg.cachedb = 1;
179 		break;
180 	case 't':
181 		testing = 1;
182 		break;
183 	case 'T':
184 		addforwtarg(EARGF(usage()));
185 		break;
186 	case 'x':
187 		setnetmtpt(mntpt, sizeof mntpt, EARGF(usage()));
188 		setext(ext, sizeof ext, mntpt);
189 		break;
190 	case 'z':
191 		zonerefreshprogram = EARGF(usage());
192 		break;
193 	default:
194 		usage();
195 		break;
196 	}ARGEND
197 	if(argc != 0)
198 		usage();
199 
200 	if(testing)
201 		mainmem->flags |= POOL_NOREUSE | POOL_ANTAGONISM;
202 	mainmem->flags |= POOL_ANTAGONISM;
203 	rfork(RFREND|RFNOTEG);
204 
205 	cfg.inside = (*mntpt == '\0' || strcmp(mntpt, "/net") == 0);
206 
207 	/* start syslog before we fork */
208 	fmtinstall('F', fcallfmt);
209 	dninit();
210 	/* this really shouldn't be fatal */
211 	if(myipaddr(ipaddr, mntpt) < 0)
212 		sysfatal("can't read my ip address");
213 	dnslog("starting %s%sdns %s%s%son %I's %s",
214 		(cfg.straddle? "straddling ": ""),
215 		(cfg.cachedb? "caching ": ""),
216 		(cfg.serve?   "udp server ": ""),
217 		(cfg.justforw? "forwarding-only ": ""),
218 		(cfg.resolver? "resolver ": ""), ipaddr, mntpt);
219 
220 	opendatabase();
221 	now = time(nil);		/* open time files before we fork */
222 	nowns = nsec();
223 
224 	snprint(servefile, sizeof servefile, "#s/dns%s", ext);
225 	dir = dirstat(servefile);
226 	if (dir)
227 		sysfatal("%s exists; another dns instance is running",
228 			servefile);
229 	free(dir);
230 
231 	/* don't unmount here; could deadlock */
232 //	while (unmount(servefile, mntpt) >= 0)
233 //		;
234 	mountinit(servefile, mntpt);	/* forks, parent exits */
235 
236 	srand(now*getpid());
237 	db2cache(1);
238 //	dnageallnever();
239 
240 	if (cfg.straddle && !seerootns())
241 		dnslog("straddle server misconfigured; can't resolve root name servers");
242 	/*
243 	 * fork without sharing heap.
244 	 * parent waits around for child to die, then forks & restarts.
245 	 * child may spawn udp server, notify procs, etc.; when it gets too
246 	 * big or too old, it kills itself and any children.
247 	 *
248 	 * /srv/dns remains open and valid, but /net/dns was only mounted in
249 	 * a child's separate namespace from 9p service, to avoid a deadlock
250 	 * from serving our own namespace, so we must remount it upon restart,
251 	 * in a separate process and namespace.
252 	 */
253 	for (;;) {
254 		start = time(nil);
255 		/* don't unmount here; could deadlock */
256 //		unmount(servefile, mntpt);
257 		kid = rfork(RFPROC|RFFDG|RFNOTEG|RFNAMEG);
258 		switch (kid) {
259 		case -1:
260 			sysfatal("fork failed: %r");
261 		case 0:
262 			if(cfg.serve)
263 				dnudpserver(mntpt);
264 			if(sendnotifies)
265 				notifyproc();
266 			io();		/* serve 9p; return implies restart */
267 			_exits("restart");
268 		}
269 		sleep(1000);	/* wait for 9p service to start */
270 		justremount(servefile, mntpt);
271 		while ((pid = waitpid()) != kid && pid != -1)
272 			continue;
273 		dnslog("restarting");
274 	}
275 }
276 
277 /*
278  *  if a mount point is specified, set the cs extension to be the mount point
279  *  with '_'s replacing '/'s
280  */
281 void
setext(char * ext,int n,char * p)282 setext(char *ext, int n, char *p)
283 {
284 	int i, c;
285 
286 	n--;
287 	for(i = 0; i < n; i++){
288 		c = p[i];
289 		if(c == 0)
290 			break;
291 		if(c == '/')
292 			c = '_';
293 		ext[i] = c;
294 	}
295 	ext[i] = 0;
296 }
297 
298 void
mountinit(char * service,char * mntpt)299 mountinit(char *service, char *mntpt)
300 {
301 	int f;
302 	int p[2];
303 	char buf[32];
304 
305 	if(pipe(p) < 0)
306 		abort(); /* "pipe failed" */;
307 	switch(rfork(RFFDG|RFPROC)){
308 	case 0:			/* child: hang around and (re)start main proc */
309 		close(p[1]);
310 		procsetname("%s restarter", mntpt);
311 		mfd[0] = mfd[1] = p[0];
312 		break;
313 	case -1:
314 		abort(); /* "fork failed\n" */;
315 	default:		/* parent: make /srv/dns, mount it, exit */
316 		close(p[0]);
317 
318 		/*
319 		 *  make a /srv/dns
320 		 */
321 		f = create(service, 1, 0666);
322 		if(f < 0)
323 			abort(); /* service */;
324 		snprint(buf, sizeof buf, "%d", p[1]);
325 		if(write(f, buf, strlen(buf)) != strlen(buf))
326 			abort(); /* "write %s", service */;
327 		close(f);
328 
329 		/*
330 		 *  put ourselves into the file system
331 		 *  it's too soon; we need 9p service running.
332 		 */
333 //		if(mount(p[1], -1, mntpt, MAFTER, "") < 0)
334 //			dnslog("dns mount -a on %s failed: %r", mntpt);
335 		close(p[1]);
336 		_exits(0);
337 	}
338 }
339 
340 Mfile*
newfid(int fid,int needunused)341 newfid(int fid, int needunused)
342 {
343 	Mfile *mf;
344 
345 	lock(&mfalloc);
346 	for(mf = mfalloc.inuse; mf != nil; mf = mf->next)
347 		if(mf->fid == fid){
348 			unlock(&mfalloc);
349 			if(needunused)
350 				return nil;
351 			return mf;
352 		}
353 	mf = emalloc(sizeof(*mf));
354 	mf->fid = fid;
355 	mf->user = estrdup("dummy");
356 	mf->next = mfalloc.inuse;
357 	mfalloc.inuse = mf;
358 	unlock(&mfalloc);
359 	return mf;
360 }
361 
362 void
freefid(Mfile * mf)363 freefid(Mfile *mf)
364 {
365 	Mfile **l;
366 
367 	lock(&mfalloc);
368 	for(l = &mfalloc.inuse; *l != nil; l = &(*l)->next)
369 		if(*l == mf){
370 			*l = mf->next;
371 			if(mf->user)
372 				free(mf->user);
373 			memset(mf, 0, sizeof *mf);	/* cause trouble */
374 			free(mf);
375 			unlock(&mfalloc);
376 			return;
377 		}
378 	unlock(&mfalloc);
379 	sysfatal("freeing unused fid");
380 }
381 
382 Mfile*
copyfid(Mfile * mf,int fid)383 copyfid(Mfile *mf, int fid)
384 {
385 	Mfile *nmf;
386 
387 	nmf = newfid(fid, 1);
388 	if(nmf == nil)
389 		return nil;
390 	nmf->fid = fid;
391 	free(nmf->user);			/* estrdup("dummy") */
392 	nmf->user = estrdup(mf->user);
393 	nmf->qid.type = mf->qid.type;
394 	nmf->qid.path = mf->qid.path;
395 	nmf->qid.vers = vers++;
396 	return nmf;
397 }
398 
399 Job*
newjob(void)400 newjob(void)
401 {
402 	Job *job;
403 
404 	job = emalloc(sizeof *job);
405 	lock(&joblock);
406 	job->next = joblist;
407 	joblist = job;
408 	job->request.tag = -1;
409 	unlock(&joblock);
410 	return job;
411 }
412 
413 void
freejob(Job * job)414 freejob(Job *job)
415 {
416 	Job **l;
417 
418 	lock(&joblock);
419 	for(l = &joblist; *l; l = &(*l)->next)
420 		if(*l == job){
421 			*l = job->next;
422 			memset(job, 0, sizeof *job);	/* cause trouble */
423 			free(job);
424 			break;
425 		}
426 	unlock(&joblock);
427 }
428 
429 void
flushjob(int tag)430 flushjob(int tag)
431 {
432 	Job *job;
433 
434 	lock(&joblock);
435 	for(job = joblist; job; job = job->next)
436 		if(job->request.tag == tag && job->request.type != Tflush){
437 			job->flushed = 1;
438 			break;
439 		}
440 	unlock(&joblock);
441 }
442 
443 void
io(void)444 io(void)
445 {
446 	volatile long n;
447 	volatile uchar mdata[IOHDRSZ + Maxfdata];
448 	Job *volatile job;
449 	Mfile *volatile mf;
450 	volatile Request req;
451 
452 	memset(&req, 0, sizeof req);
453 	/*
454 	 *  a slave process is sometimes forked to wait for replies from other
455 	 *  servers.  The master process returns immediately via a longjmp
456 	 *  through 'mret'.
457 	 */
458 	if(setjmp(req.mret))
459 		putactivity(0);
460 	req.isslave = 0;
461 	stop = 0;
462 	while(!stop){
463 		procsetname("%d %s/dns Twrites of %d 9p rpcs read; %d alarms",
464 			stats.qrecvd9p, mntpt, stats.qrecvd9prpc, stats.alarms);
465 		n = read9pmsg(mfd[0], mdata, sizeof mdata);
466 		if(n<=0){
467 			dnslog("error reading 9P from %s: %r", mntpt);
468 			sleep(2000);	/* don't thrash after read error */
469 			return;
470 		}
471 
472 		stats.qrecvd9prpc++;
473 		job = newjob();
474 		if(convM2S(mdata, n, &job->request) != n){
475 			freejob(job);
476 			continue;
477 		}
478 		mf = newfid(job->request.fid, 0);
479 		if(debug)
480 			dnslog("%F", &job->request);
481 
482 		getactivity(&req, 0);
483 		req.aborttime = timems() + Maxreqtm;
484 		req.from = "9p";
485 
486 		switch(job->request.type){
487 		default:
488 			warning("unknown request type %d", job->request.type);
489 			break;
490 		case Tversion:
491 			rversion(job);
492 			break;
493 		case Tauth:
494 			rauth(job);
495 			break;
496 		case Tflush:
497 			rflush(job);
498 			break;
499 		case Tattach:
500 			rattach(job, mf);
501 			break;
502 		case Twalk:
503 			rwalk(job, mf);
504 			break;
505 		case Topen:
506 			ropen(job, mf);
507 			break;
508 		case Tcreate:
509 			rcreate(job, mf);
510 			break;
511 		case Tread:
512 			rread(job, mf);
513 			break;
514 		case Twrite:
515 			/* &req is handed to dnresolve() */
516 			rwrite(job, mf, &req);
517 			break;
518 		case Tclunk:
519 			rclunk(job, mf);
520 			break;
521 		case Tremove:
522 			rremove(job, mf);
523 			break;
524 		case Tstat:
525 			rstat(job, mf);
526 			break;
527 		case Twstat:
528 			rwstat(job, mf);
529 			break;
530 		}
531 
532 		freejob(job);
533 
534 		/*
535 		 *  slave processes die after replying
536 		 */
537 		if(req.isslave){
538 			putactivity(0);
539 			_exits(0);
540 		}
541 
542 		putactivity(0);
543 	}
544 	/* kill any udp server, notifier, etc. processes */
545 	postnote(PNGROUP, getpid(), "die");
546 	sleep(1000);
547 }
548 
549 void
rversion(Job * job)550 rversion(Job *job)
551 {
552 	if(job->request.msize > IOHDRSZ + Maxfdata)
553 		job->reply.msize = IOHDRSZ + Maxfdata;
554 	else
555 		job->reply.msize = job->request.msize;
556 	if(strncmp(job->request.version, "9P2000", 6) != 0)
557 		sendmsg(job, "unknown 9P version");
558 	else{
559 		job->reply.version = "9P2000";
560 		sendmsg(job, 0);
561 	}
562 }
563 
564 void
rauth(Job * job)565 rauth(Job *job)
566 {
567 	sendmsg(job, "dns: authentication not required");
568 }
569 
570 /*
571  *  don't flush till all the slaves are done
572  */
573 void
rflush(Job * job)574 rflush(Job *job)
575 {
576 	flushjob(job->request.oldtag);
577 	sendmsg(job, 0);
578 }
579 
580 void
rattach(Job * job,Mfile * mf)581 rattach(Job *job, Mfile *mf)
582 {
583 	if(mf->user != nil)
584 		free(mf->user);
585 	mf->user = estrdup(job->request.uname);
586 	mf->qid.vers = vers++;
587 	mf->qid.type = QTDIR;
588 	mf->qid.path = 0LL;
589 	job->reply.qid = mf->qid;
590 	sendmsg(job, 0);
591 }
592 
593 char*
rwalk(Job * job,Mfile * mf)594 rwalk(Job *job, Mfile *mf)
595 {
596 	int i, nelems;
597 	char *err;
598 	char **elems;
599 	Mfile *nmf;
600 	Qid qid;
601 
602 	err = 0;
603 	nmf = nil;
604 	elems  = job->request.wname;
605 	nelems = job->request.nwname;
606 	job->reply.nwqid = 0;
607 
608 	if(job->request.newfid != job->request.fid){
609 		/* clone fid */
610 		nmf = copyfid(mf, job->request.newfid);
611 		if(nmf == nil){
612 			err = "clone bad newfid";
613 			goto send;
614 		}
615 		mf = nmf;
616 	}
617 	/* else nmf will be nil */
618 
619 	qid = mf->qid;
620 	if(nelems > 0)
621 		/* walk fid */
622 		for(i=0; i<nelems && i<MAXWELEM; i++){
623 			if((qid.type & QTDIR) == 0){
624 				err = "not a directory";
625 				break;
626 			}
627 			if (strcmp(elems[i], "..") == 0 ||
628 			    strcmp(elems[i], ".") == 0){
629 				qid.type = QTDIR;
630 				qid.path = Qdir;
631 Found:
632 				job->reply.wqid[i] = qid;
633 				job->reply.nwqid++;
634 				continue;
635 			}
636 			if(strcmp(elems[i], "dns") == 0){
637 				qid.type = QTFILE;
638 				qid.path = Qdns;
639 				goto Found;
640 			}
641 			err = "file does not exist";
642 			break;
643 		}
644 
645 send:
646 	if(nmf != nil && (err!=nil || job->reply.nwqid<nelems))
647 		freefid(nmf);
648 	if(err == nil)
649 		mf->qid = qid;
650 	sendmsg(job, err);
651 	return err;
652 }
653 
654 void
ropen(Job * job,Mfile * mf)655 ropen(Job *job, Mfile *mf)
656 {
657 	int mode;
658 	char *err;
659 
660 	err = 0;
661 	mode = job->request.mode;
662 	if(mf->qid.type & QTDIR)
663 		if(mode)
664 			err = "permission denied";
665 	job->reply.qid = mf->qid;
666 	job->reply.iounit = 0;
667 	sendmsg(job, err);
668 }
669 
670 void
rcreate(Job * job,Mfile * mf)671 rcreate(Job *job, Mfile *mf)
672 {
673 	USED(mf);
674 	sendmsg(job, "creation permission denied");
675 }
676 
677 void
rread(Job * job,Mfile * mf)678 rread(Job *job, Mfile *mf)
679 {
680 	int i, n;
681 	long clock;
682 	ulong cnt;
683 	vlong off;
684 	char *err;
685 	uchar buf[Maxfdata];
686 	Dir dir;
687 
688 	n = 0;
689 	err = nil;
690 	off = job->request.offset;
691 	cnt = job->request.count;
692 	*buf = '\0';
693 	job->reply.data = (char*)buf;
694 	if(mf->qid.type & QTDIR){
695 		clock = time(nil);
696 		if(off == 0){
697 			memset(&dir, 0, sizeof dir);
698 			dir.name = "dns";
699 			dir.qid.type = QTFILE;
700 			dir.qid.vers = vers;
701 			dir.qid.path = Qdns;
702 			dir.mode = 0666;
703 			dir.length = 0;
704 			dir.uid = dir.gid = dir.muid = mf->user;
705 			dir.atime = dir.mtime = clock;		/* wrong */
706 			n = convD2M(&dir, buf, sizeof buf);
707 		}
708 	} else if (off < 0)
709 		err = "negative read offset";
710 	else {
711 		/* first offset will always be zero */
712 		for(i = 1; i <= mf->nrr; i++)
713 			if(mf->rr[i] > off)
714 				break;
715 		if(i <= mf->nrr) {
716 			if(off + cnt > mf->rr[i])
717 				n = mf->rr[i] - off;
718 			else
719 				n = cnt;
720 			assert(n >= 0);
721 			job->reply.data = mf->reply + off;
722 		}
723 	}
724 	job->reply.count = n;
725 	sendmsg(job, err);
726 }
727 
728 void
rwrite(Job * job,Mfile * mf,Request * req)729 rwrite(Job *job, Mfile *mf, Request *req)
730 {
731 	int rooted, wantsav, send;
732 	ulong cnt;
733 	char *err, *p, *atype;
734 	char errbuf[ERRMAX];
735 
736 	err = nil;
737 	cnt = job->request.count;
738 	send = 1;
739 	if(mf->qid.type & QTDIR)
740 		err = "can't write directory";
741 	else if (job->request.offset != 0)
742 		err = "writing at non-zero offset";
743 	else if(cnt >= Maxrequest)
744 		err = "request too long";
745 	else
746 		send = 0;
747 	if (send)
748 		goto send;
749 
750 	job->request.data[cnt] = 0;
751 	if(cnt > 0 && job->request.data[cnt-1] == '\n')
752 		job->request.data[cnt-1] = 0;
753 
754 	/*
755 	 *  special commands
756 	 */
757 //	dnslog("rwrite got: %s", job->request.data);
758 	send = 1;
759 	if(strcmp(job->request.data, "age")==0){
760 		dnslog("dump, age & dump forced");
761 		dndump("/lib/ndb/dnsdump1");
762 		dnforceage();
763 		dndump("/lib/ndb/dnsdump2");
764 	} else if(strcmp(job->request.data, "debug")==0)
765 		debug ^= 1;
766 	else if(strcmp(job->request.data, "dump")==0)
767 		dndump("/lib/ndb/dnsdump");
768 	else if(strcmp(job->request.data, "poolcheck")==0)
769 		poolcheck(mainmem);
770 	else if(strcmp(job->request.data, "refresh")==0)
771 		needrefresh = 1;
772 	else if(strcmp(job->request.data, "restart")==0)
773 		stop = 1;
774 	else if(strcmp(job->request.data, "stats")==0)
775 		dnstats("/lib/ndb/dnsstats");
776 	else if(strncmp(job->request.data, "target ", 7)==0){
777 		target = atol(job->request.data + 7);
778 		dnslog("target set to %ld", target);
779 	} else
780 		send = 0;
781 	if (send)
782 		goto send;
783 
784 	/*
785 	 *  kill previous reply
786 	 */
787 	mf->nrr = 0;
788 	mf->rr[0] = 0;
789 
790 	/*
791 	 *  break up request (into a name and a type)
792 	 */
793 	atype = strchr(job->request.data, ' ');
794 	if(atype == 0){
795 		snprint(errbuf, sizeof errbuf, "illegal request %s",
796 			job->request.data);
797 		err = errbuf;
798 		goto send;
799 	} else
800 		*atype++ = 0;
801 
802 	/*
803 	 *  tracing request
804 	 */
805 	if(strcmp(atype, "trace") == 0){
806 		if(trace)
807 			free(trace);
808 		if(*job->request.data)
809 			trace = estrdup(job->request.data);
810 		else
811 			trace = 0;
812 		goto send;
813 	}
814 
815 	/* normal request: domain [type] */
816 	stats.qrecvd9p++;
817 	mf->type = rrtype(atype);
818 	if(mf->type < 0){
819 		snprint(errbuf, sizeof errbuf, "unknown type %s", atype);
820 		err = errbuf;
821 		goto send;
822 	}
823 
824 	p = atype - 2;
825 	if(p >= job->request.data && *p == '.'){
826 		rooted = 1;
827 		*p = 0;
828 	} else
829 		rooted = 0;
830 
831 	p = job->request.data;
832 	if(*p == '!'){
833 		wantsav = 1;
834 		p++;
835 	} else
836 		wantsav = 0;
837 
838 	err = lookupqueryold(job, mf, req, errbuf, p, wantsav, rooted);
839 send:
840 	dncheck(0, 1);
841 	job->reply.count = cnt;
842 	sendmsg(job, err);
843 }
844 
845 /*
846  * dnsdebug calls
847  *	rr = dnresolve(buf, Cin, type, &req, 0, 0, Recurse, rooted, 0);
848  * which generates a UDP query, which eventually calls
849  *	dnserver(&reqmsg, &repmsg, &req, buf, rcode);
850  * which calls
851  *	rp = dnresolve(name, Cin, type, req, &mp->an, 0, recurse, 1, 0);
852  *
853  * but here we just call dnresolve directly.
854  */
855 static char *
lookupqueryold(Job * job,Mfile * mf,Request * req,char * errbuf,char * p,int wantsav,int rooted)856 lookupqueryold(Job *job, Mfile *mf, Request *req, char *errbuf, char *p,
857 	int wantsav, int rooted)
858 {
859 	int status;
860 	RR *rp, *neg;
861 
862 	dncheck(0, 1);
863 	status = Rok;
864 	rp = dnresolve(p, Cin, mf->type, req, 0, 0, Recurse, rooted, &status);
865 
866 	dncheck(0, 1);
867 	lock(&dnlock);
868 	neg = rrremneg(&rp);
869 	if(neg){
870 		status = neg->negrcode;
871 		rrfreelist(neg);
872 	}
873 	unlock(&dnlock);
874 
875 	return respond(job, mf, rp, errbuf, status, wantsav);
876 }
877 
878 static char *
respond(Job * job,Mfile * mf,RR * rp,char * errbuf,int status,int wantsav)879 respond(Job *job, Mfile *mf, RR *rp, char *errbuf, int status, int wantsav)
880 {
881 	long n;
882 	RR *tp;
883 
884 	if(rp == nil)
885 		switch(status){
886 		case Rname:
887 			return "name does not exist";
888 		case Rserver:
889 			return "dns failure";
890 		case Rok:
891 		default:
892 			snprint(errbuf, ERRMAX,
893 				"resource does not exist; negrcode %d", status);
894 			return errbuf;
895 		}
896 
897 	lock(&joblock);
898 	if(!job->flushed){
899 		/* format data to be read later */
900 		n = 0;
901 		mf->nrr = 0;
902 		for(tp = rp; mf->nrr < Maxrrr-1 && n < Maxreply && tp &&
903 		    tsame(mf->type, tp->type); tp = tp->next){
904 			mf->rr[mf->nrr++] = n;
905 			if(wantsav)
906 				n += snprint(mf->reply+n, Maxreply-n, "%Q", tp);
907 			else
908 				n += snprint(mf->reply+n, Maxreply-n, "%R", tp);
909 		}
910 		mf->rr[mf->nrr] = n;
911 	}
912 	unlock(&joblock);
913 	rrfreelist(rp);
914 	return nil;
915 }
916 
917 #ifdef notused
918 /* simulate what dnsudpserver does */
919 static char *
lookupquerynew(Job * job,Mfile * mf,Request * req,char * errbuf,char * p,int wantsav,int)920 lookupquerynew(Job *job, Mfile *mf, Request *req, char *errbuf, char *p,
921 	int wantsav, int)
922 {
923 	char *err;
924 	uchar buf[Udphdrsize + Maxpayload];
925 	DNSmsg *mp;
926 	DNSmsg repmsg;
927 	RR *rp;
928 
929 	dncheck(0, 1);
930 
931 	memset(&repmsg, 0, sizeof repmsg);
932 	rp = rralloc(mf->type);
933 	rp->owner = dnlookup(p, Cin, 1);
934 	mp = newdnsmsg(rp, Frecurse|Oquery, (ushort)rand());
935 
936 	/* BUG: buf is srcip, yet it's uninitialised */
937 	dnserver(mp, &repmsg, req, buf, Rok);
938 
939 	freeanswers(mp);
940 	err = respond(job, mf, repmsg.an, errbuf, Rok, wantsav);
941 	repmsg.an = nil;		/* freed above */
942 	freeanswers(&repmsg);
943 	return err;
944 }
945 #endif
946 
947 void
rclunk(Job * job,Mfile * mf)948 rclunk(Job *job, Mfile *mf)
949 {
950 	freefid(mf);
951 	sendmsg(job, 0);
952 }
953 
954 void
rremove(Job * job,Mfile * mf)955 rremove(Job *job, Mfile *mf)
956 {
957 	USED(mf);
958 	sendmsg(job, "remove permission denied");
959 }
960 
961 void
rstat(Job * job,Mfile * mf)962 rstat(Job *job, Mfile *mf)
963 {
964 	Dir dir;
965 	uchar buf[IOHDRSZ+Maxfdata];
966 
967 	memset(&dir, 0, sizeof dir);
968 	if(mf->qid.type & QTDIR){
969 		dir.name = ".";
970 		dir.mode = DMDIR|0555;
971 	} else {
972 		dir.name = "dns";
973 		dir.mode = 0666;
974 	}
975 	dir.qid = mf->qid;
976 	dir.length = 0;
977 	dir.uid = dir.gid = dir.muid = mf->user;
978 	dir.atime = dir.mtime = time(nil);
979 	job->reply.nstat = convD2M(&dir, buf, sizeof buf);
980 	job->reply.stat = buf;
981 	sendmsg(job, 0);
982 }
983 
984 void
rwstat(Job * job,Mfile * mf)985 rwstat(Job *job, Mfile *mf)
986 {
987 	USED(mf);
988 	sendmsg(job, "wstat permission denied");
989 }
990 
991 void
sendmsg(Job * job,char * err)992 sendmsg(Job *job, char *err)
993 {
994 	int n;
995 	uchar mdata[IOHDRSZ + Maxfdata];
996 	char ename[ERRMAX];
997 
998 	if(err){
999 		job->reply.type = Rerror;
1000 		snprint(ename, sizeof ename, "dns: %s", err);
1001 		job->reply.ename = ename;
1002 	}else
1003 		job->reply.type = job->request.type+1;
1004 	job->reply.tag = job->request.tag;
1005 	n = convS2M(&job->reply, mdata, sizeof mdata);
1006 	if(n == 0){
1007 		warning("sendmsg convS2M of %F returns 0", &job->reply);
1008 		abort();
1009 	}
1010 	lock(&joblock);
1011 	if(job->flushed == 0)
1012 		if(write(mfd[1], mdata, n)!=n)
1013 			sysfatal("mount write");
1014 	unlock(&joblock);
1015 	if(debug)
1016 		dnslog("%F %d", &job->reply, n);
1017 }
1018 
1019 /*
1020  *  the following varies between dnsdebug and dns
1021  */
1022 void
logreply(int id,uchar * addr,DNSmsg * mp)1023 logreply(int id, uchar *addr, DNSmsg *mp)
1024 {
1025 	RR *rp;
1026 
1027 	dnslog("%d: rcvd %I flags:%s%s%s%s%s", id, addr,
1028 		mp->flags & Fauth? " auth": "",
1029 		mp->flags & Ftrunc? " trunc": "",
1030 		mp->flags & Frecurse? " rd": "",
1031 		mp->flags & Fcanrec? " ra": "",
1032 		(mp->flags & (Fauth|Rmask)) == (Fauth|Rname)? " nx": "");
1033 	for(rp = mp->qd; rp != nil; rp = rp->next)
1034 		dnslog("%d: rcvd %I qd %s", id, addr, rp->owner->name);
1035 	for(rp = mp->an; rp != nil; rp = rp->next)
1036 		dnslog("%d: rcvd %I an %R", id, addr, rp);
1037 	for(rp = mp->ns; rp != nil; rp = rp->next)
1038 		dnslog("%d: rcvd %I ns %R", id, addr, rp);
1039 	for(rp = mp->ar; rp != nil; rp = rp->next)
1040 		dnslog("%d: rcvd %I ar %R", id, addr, rp);
1041 }
1042 
1043 void
logsend(int id,int subid,uchar * addr,char * sname,char * rname,int type)1044 logsend(int id, int subid, uchar *addr, char *sname, char *rname, int type)
1045 {
1046 	char buf[12];
1047 
1048 	dnslog("[%d] %d.%d: sending to %I/%s %s %s",
1049 		getpid(), id, subid, addr, sname, rname,
1050 		rrname(type, buf, sizeof buf));
1051 }
1052 
1053 RR*
getdnsservers(int class)1054 getdnsservers(int class)
1055 {
1056 	return dnsservers(class);
1057 }
1058