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 <ndb.h>
9 #include <thread.h>
10 #include "dns.h"
11 
12 enum
13 {
14 	Maxrequest=		1024,
15 	Ncache=			8,
16 	Maxpath=		128,
17 	Maxreply=		512,
18 	Maxrrr=			16,
19 	Maxfdata=		8192,
20 
21 	Qdir=			0,
22 	Qdns=			1
23 };
24 
25 typedef struct Mfile	Mfile;
26 typedef struct Job	Job;
27 typedef struct Network	Network;
28 
29 int vers;		/* incremented each clone/attach */
30 
31 struct Mfile
32 {
33 	Mfile		*next;		/* next free mfile */
34 	int		ref;
35 
36 	char		*user;
37 	Qid		qid;
38 	int		fid;
39 
40 	int		type;		/* reply type */
41 	char		reply[Maxreply];
42 	ushort		rr[Maxrrr];	/* offset of rr's */
43 	ushort		nrr;		/* number of rr's */
44 };
45 
46 /*
47  * active local requests
48  */
49 struct Job
50 {
51 	Job	*next;
52 	int	flushed;
53 	Fcall	request;
54 	Fcall	reply;
55 };
56 Lock	joblock;
57 Job	*joblist;
58 
59 struct {
60 	Lock	lk;
61 	Mfile	*inuse;		/* active mfile's */
62 } mfalloc;
63 
64 int	mfd[2];
65 int	debug;
66 int traceactivity;
67 int	cachedb;
68 ulong	now;
69 int	testing;
70 char	*trace;
71 int	needrefresh;
72 int	resolver;
73 uchar	ipaddr[IPaddrlen];	/* my ip address */
74 int	maxage;
75 char	*zonerefreshprogram;
76 int	sendnotifies;
77 
78 void	rversion(Job*);
79 void	rauth(Job*);
80 void	rflush(Job*);
81 void	rattach(Job*, Mfile*);
82 char*	rwalk(Job*, Mfile*);
83 void	ropen(Job*, Mfile*);
84 void	rcreate(Job*, Mfile*);
85 void	rread(Job*, Mfile*);
86 void	rwrite(Job*, Mfile*, Request*);
87 void	rclunk(Job*, Mfile*);
88 void	rremove(Job*, Mfile*);
89 void	rstat(Job*, Mfile*);
90 void	rwstat(Job*, Mfile*);
91 void	sendmsg(Job*, char*);
92 void	mountinit(char*);
93 void	io(void);
94 int	fillreply(Mfile*, int);
95 Job*	newjob(void);
96 void	freejob(Job*);
97 void	setext(char*, int, char*);
98 
99 char *tcpaddr = "tcp!*!domain";
100 char *udpaddr = "udp!*!domain";
101 char	*logfile = "dns";
102 char	*dbfile;
103 char	mntpt[Maxpath];
104 char	*LOG;
105 
106 void
usage(void)107 usage(void)
108 {
109 	fprint(2, "usage: dns [-dnrst] [-a maxage] [-f ndb-file] [-T tcpaddr] [-U udpaddr] [-x service] [-z zoneprog]\n");
110 	threadexitsall("usage");
111 }
112 
113 void
checkaddress(void)114 checkaddress(void)
115 {
116 	char *u, *t;
117 
118 	u = strchr(udpaddr, '!');
119 	t = strchr(tcpaddr, '!');
120 	if(u && t && strcmp(u, t) != 0)
121 		fprint(2, "warning: announce mismatch %s %s\n", udpaddr, tcpaddr);
122 }
123 
124 void
threadmain(int argc,char * argv[])125 threadmain(int argc, char *argv[])
126 {
127 	int serveudp, servetcp;
128 	char *service;
129 
130 	serveudp = 0;
131 	servetcp = 0;
132 	service = "dns";
133 	ARGBEGIN{
134 	case 'd':
135 		debug = 1;
136 		traceactivity = 1;
137 		break;
138 	case 'f':
139 		dbfile = EARGF(usage());
140 		break;
141 	case 'x':
142 		service = EARGF(usage());
143 		break;
144 	case 'r':
145 		resolver = 1;
146 		break;
147 	case 's':
148 		serveudp = 1;
149 		cachedb = 1;
150 		break;
151 	case 't':
152 		servetcp = 1;
153 		cachedb = 1;
154 		break;
155 	case 'a':
156 		maxage = atoi(EARGF(usage()));
157 		break;
158 	case 'z':
159 		zonerefreshprogram = EARGF(usage());
160 		break;
161 	case 'n':
162 		sendnotifies = 1;
163 		break;
164 	case 'U':
165 		udpaddr = estrdup(netmkaddr(EARGF(usage()), "udp", "domain"));
166 		break;
167 	case 'T':
168 		tcpaddr = estrdup(netmkaddr(EARGF(usage()), "tcp", "domain"));
169 		break;
170 	default:
171 		usage();
172 	}ARGEND
173 
174 	if(argc)
175 		usage();
176 	if(serveudp && servetcp)
177 		checkaddress();
178 
179 	rfork(RFNOTEG);
180 
181 	/* start syslog before we fork */
182 	fmtinstall('F', fcallfmt);
183 	dninit();
184 	if(myipaddr(ipaddr, mntpt) < 0)
185 		sysfatal("can't read my ip address");
186 
187 	syslog(0, logfile, "starting dns on %I", ipaddr);
188 
189 	opendatabase();
190 
191 	mountinit(service);
192 
193 	now = time(0);
194 	srand(now*getpid());
195 	db2cache(1);
196 
197 	if(serveudp)
198 		proccreate(dnudpserver, nil, STACK);
199 	if(servetcp)
200 		proccreate(dntcpserver, nil, STACK);
201 	if(sendnotifies)
202 		proccreate(notifyproc, nil, STACK);
203 
204 	io();
205 }
206 
207 /*
208  *  if a mount point is specified, set the cs extention to be the mount point
209  *  with '_'s replacing '/'s
210  */
211 void
setext(char * ext,int n,char * p)212 setext(char *ext, int n, char *p)
213 {
214 	int i, c;
215 
216 	n--;
217 	for(i = 0; i < n; i++){
218 		c = p[i];
219 		if(c == 0)
220 			break;
221 		if(c == '/')
222 			c = '_';
223 		ext[i] = c;
224 	}
225 	ext[i] = 0;
226 }
227 
228 void
mountinit(char * service)229 mountinit(char *service)
230 {
231 	int p[2];
232 
233 	if(pipe(p) < 0)
234 		abort(); /* "pipe failed" */;
235 	if(post9pservice(p[1], service, nil) < 0)
236 		fprint(2, "post9pservice dns: %r\n");
237 	close(p[1]);
238 	mfd[0] = mfd[1] = p[0];
239 }
240 
241 Mfile*
newfid(int fid,int needunused)242 newfid(int fid, int needunused)
243 {
244 	Mfile *mf;
245 
246 	lock(&mfalloc.lk);
247 	for(mf = mfalloc.inuse; mf != nil; mf = mf->next){
248 		if(mf->fid == fid){
249 			unlock(&mfalloc.lk);
250 			if(needunused)
251 				return nil;
252 			return mf;
253 		}
254 	}
255 	if(!needunused){
256 		unlock(&mfalloc.lk);
257 		return nil;
258 	}
259 	mf = emalloc(sizeof(*mf));
260 	if(mf == nil)
261 		sysfatal("out of memory");
262 	mf->fid = fid;
263 	mf->next = mfalloc.inuse;
264 	mfalloc.inuse = mf;
265 	unlock(&mfalloc.lk);
266 	return mf;
267 }
268 
269 void
freefid(Mfile * mf)270 freefid(Mfile *mf)
271 {
272 	Mfile **l;
273 
274 	lock(&mfalloc.lk);
275 	for(l = &mfalloc.inuse; *l != nil; l = &(*l)->next){
276 		if(*l == mf){
277 			*l = mf->next;
278 			if(mf->user)
279 				free(mf->user);
280 			free(mf);
281 			unlock(&mfalloc.lk);
282 			return;
283 		}
284 	}
285 	sysfatal("freeing unused fid");
286 }
287 
288 Mfile*
copyfid(Mfile * mf,int fid)289 copyfid(Mfile *mf, int fid)
290 {
291 	Mfile *nmf;
292 
293 	nmf = newfid(fid, 1);
294 	if(nmf == nil)
295 		return nil;
296 	nmf->fid = fid;
297 	nmf->user = estrdup(mf->user);
298 	nmf->qid.type = mf->qid.type;
299 	nmf->qid.path = mf->qid.path;
300 	nmf->qid.vers = vers++;
301 	return nmf;
302 }
303 
304 Job*
newjob(void)305 newjob(void)
306 {
307 	Job *job;
308 
309 	job = emalloc(sizeof(*job));
310 	lock(&joblock);
311 	job->next = joblist;
312 	joblist = job;
313 	job->request.tag = -1;
314 	unlock(&joblock);
315 	return job;
316 }
317 
318 void
freejob(Job * job)319 freejob(Job *job)
320 {
321 	Job **l;
322 
323 	lock(&joblock);
324 	for(l = &joblist; *l; l = &(*l)->next){
325 		if((*l) == job){
326 			*l = job->next;
327 			free(job);
328 			break;
329 		}
330 	}
331 	unlock(&joblock);
332 }
333 
334 void
flushjob(int tag)335 flushjob(int tag)
336 {
337 	Job *job;
338 
339 	lock(&joblock);
340 	for(job = joblist; job; job = job->next){
341 		if(job->request.tag == tag && job->request.type != Tflush){
342 			job->flushed = 1;
343 			break;
344 		}
345 	}
346 	unlock(&joblock);
347 }
348 
349 void
ioproc0(void * v)350 ioproc0(void *v)
351 {
352 	long n;
353 	Mfile *mf;
354 	uchar mdata[IOHDRSZ + Maxfdata];
355 	Request req;
356 	Job *job;
357 
358 	USED(v);
359 
360 	for(;;){
361 		n = read9pmsg(mfd[0], mdata, sizeof mdata);
362 		if(n <= 0){
363 			syslog(0, logfile, "error reading mntpt: %r");
364 			break;
365 		}
366 		job = newjob();
367 		if(convM2S(mdata, n, &job->request) != n){
368 			freejob(job);
369 			continue;
370 		}
371 		if(debug)
372 			syslog(0, logfile, "%F", &job->request);
373 
374 		getactivity(&req);
375 		req.aborttime = now + 60;	/* don't spend more than 60 seconds */
376 
377 		mf = nil;
378 		switch(job->request.type){
379 		case Tversion:
380 		case Tauth:
381 		case Tflush:
382 			break;
383 		case Tattach:
384 			mf = newfid(job->request.fid, 1);
385 			if(mf == nil){
386 				sendmsg(job, "fid in use");
387 				goto skip;
388 			}
389 			break;
390 		default:
391 			mf = newfid(job->request.fid, 0);
392 			if(mf == nil){
393 				sendmsg(job, "unknown fid");
394 				goto skip;
395 			}
396 			break;
397 		}
398 
399 		switch(job->request.type){
400 		default:
401 			syslog(1, logfile, "unknown request type %d", job->request.type);
402 			break;
403 		case Tversion:
404 			rversion(job);
405 			break;
406 		case Tauth:
407 			rauth(job);
408 			break;
409 		case Tflush:
410 			rflush(job);
411 			break;
412 		case Tattach:
413 			rattach(job, mf);
414 			break;
415 		case Twalk:
416 			rwalk(job, mf);
417 			break;
418 		case Topen:
419 			ropen(job, mf);
420 			break;
421 		case Tcreate:
422 			rcreate(job, mf);
423 			break;
424 		case Tread:
425 			rread(job, mf);
426 			break;
427 		case Twrite:
428 			rwrite(job, mf, &req);
429 			break;
430 		case Tclunk:
431 			rclunk(job, mf);
432 			break;
433 		case Tremove:
434 			rremove(job, mf);
435 			break;
436 		case Tstat:
437 			rstat(job, mf);
438 			break;
439 		case Twstat:
440 			rwstat(job, mf);
441 			break;
442 		}
443 skip:
444 		freejob(job);
445 		putactivity();
446 	}
447 }
448 
449 void
io(void)450 io(void)
451 {
452 	int i;
453 
454 	for(i=0; i<Maxactive; i++)
455 		proccreate(ioproc0, 0, STACK);
456 }
457 
458 void
rversion(Job * job)459 rversion(Job *job)
460 {
461 	if(job->request.msize > IOHDRSZ + Maxfdata)
462 		job->reply.msize = IOHDRSZ + Maxfdata;
463 	else
464 		job->reply.msize = job->request.msize;
465 	if(strncmp(job->request.version, "9P2000", 6) != 0)
466 		sendmsg(job, "unknown 9P version");
467 	else{
468 		job->reply.version = "9P2000";
469 		sendmsg(job, 0);
470 	}
471 }
472 
473 void
rauth(Job * job)474 rauth(Job *job)
475 {
476 	sendmsg(job, "dns: authentication not required");
477 }
478 
479 /*
480  *  don't flush till all the slaves are done
481  */
482 void
rflush(Job * job)483 rflush(Job *job)
484 {
485 	flushjob(job->request.oldtag);
486 	sendmsg(job, 0);
487 }
488 
489 void
rattach(Job * job,Mfile * mf)490 rattach(Job *job, Mfile *mf)
491 {
492 	if(mf->user != nil)
493 		free(mf->user);
494 	mf->user = estrdup(job->request.uname);
495 	mf->qid.vers = vers++;
496 	mf->qid.type = QTDIR;
497 	mf->qid.path = 0LL;
498 	job->reply.qid = mf->qid;
499 	sendmsg(job, 0);
500 }
501 
502 char*
rwalk(Job * job,Mfile * mf)503 rwalk(Job *job, Mfile *mf)
504 {
505 	char *err;
506 	char **elems;
507 	int nelems;
508 	int i;
509 	Mfile *nmf;
510 	Qid qid;
511 
512 	err = 0;
513 	nmf = nil;
514 	elems = job->request.wname;
515 	nelems = job->request.nwname;
516 	job->reply.nwqid = 0;
517 
518 	if(job->request.newfid != job->request.fid){
519 		/* clone fid */
520 		nmf = copyfid(mf, job->request.newfid);
521 		if(nmf == nil){
522 			err = "clone bad newfid";
523 			goto send;
524 		}
525 		mf = nmf;
526 	}
527 	/* else nmf will be nil */
528 
529 	qid = mf->qid;
530 	if(nelems > 0){
531 		/* walk fid */
532 		for(i=0; i<nelems && i<MAXWELEM; i++){
533 			if((qid.type & QTDIR) == 0){
534 				err = "not a directory";
535 				break;
536 			}
537 			if(strcmp(elems[i], "..") == 0 || strcmp(elems[i], ".") == 0){
538 				qid.type = QTDIR;
539 				qid.path = Qdir;
540     Found:
541 				job->reply.wqid[i] = qid;
542 				job->reply.nwqid++;
543 				continue;
544 			}
545 			if(strcmp(elems[i], "dns") == 0){
546 				qid.type = QTFILE;
547 				qid.path = Qdns;
548 				goto Found;
549 			}
550 			err = "file does not exist";
551 			break;
552 		}
553 	}
554 
555     send:
556 	if(nmf != nil && (err!=nil || job->reply.nwqid<nelems))
557 		freefid(nmf);
558 	if(err == nil)
559 		mf->qid = qid;
560 	sendmsg(job, err);
561 	return err;
562 }
563 
564 void
ropen(Job * job,Mfile * mf)565 ropen(Job *job, Mfile *mf)
566 {
567 	int mode;
568 	char *err;
569 
570 	err = 0;
571 	mode = job->request.mode;
572 	if(mf->qid.type & QTDIR){
573 		if(mode)
574 			err = "permission denied";
575 	}
576 	job->reply.qid = mf->qid;
577 	job->reply.iounit = 0;
578 	sendmsg(job, err);
579 }
580 
581 void
rcreate(Job * job,Mfile * mf)582 rcreate(Job *job, Mfile *mf)
583 {
584 	USED(mf);
585 	sendmsg(job, "creation permission denied");
586 }
587 
588 void
rread(Job * job,Mfile * mf)589 rread(Job *job, Mfile *mf)
590 {
591 	int i, n, cnt;
592 	long off;
593 	Dir dir;
594 	uchar buf[Maxfdata];
595 	char *err;
596 	long clock;
597 
598 	n = 0;
599 	err = 0;
600 	off = job->request.offset;
601 	cnt = job->request.count;
602 	if(mf->qid.type & QTDIR){
603 		clock = time(0);
604 		if(off == 0){
605 			dir.name = "dns";
606 			dir.qid.type = QTFILE;
607 			dir.qid.vers = vers;
608 			dir.qid.path = Qdns;
609 			dir.mode = 0666;
610 			dir.length = 0;
611 			dir.uid = mf->user;
612 			dir.gid = mf->user;
613 			dir.muid = mf->user;
614 			dir.atime = clock;	/* wrong */
615 			dir.mtime = clock;	/* wrong */
616 			n = convD2M(&dir, buf, sizeof buf);
617 		}
618 		job->reply.data = (char*)buf;
619 	} else {
620 		for(i = 1; i <= mf->nrr; i++)
621 			if(mf->rr[i] > off)
622 				break;
623 		if(i > mf->nrr)
624 			goto send;
625 		if(off + cnt > mf->rr[i])
626 			n = mf->rr[i] - off;
627 		else
628 			n = cnt;
629 		job->reply.data = mf->reply + off;
630 	}
631 send:
632 	job->reply.count = n;
633 	sendmsg(job, err);
634 }
635 
636 void
rwrite(Job * job,Mfile * mf,Request * req)637 rwrite(Job *job, Mfile *mf, Request *req)
638 {
639 	int cnt, rooted, status;
640 	long n;
641 	char *err, *p, *atype;
642 	RR *rp, *tp, *neg;
643 	int wantsav;
644 	static char *dumpfile;
645 
646 	err = 0;
647 	cnt = job->request.count;
648 	if(mf->qid.type & QTDIR){
649 		err = "can't write directory";
650 		goto send;
651 	}
652 	if(cnt >= Maxrequest){
653 		err = "request too long";
654 		goto send;
655 	}
656 	job->request.data[cnt] = 0;
657 	if(cnt > 0 && job->request.data[cnt-1] == '\n')
658 		job->request.data[cnt-1] = 0;
659 
660 	/*
661 	 *  special commands
662 	 */
663 	p = job->request.data;
664 	if(strcmp(p, "debug")==0){
665 		debug ^= 1;
666 		goto send;
667 	} else if(strcmp(p, "dump")==0){
668 		if(dumpfile == nil)
669 			dumpfile = unsharp("#9/ndb/dnsdump");
670 		dndump(dumpfile);
671 		goto send;
672 	} else if(strncmp(p, "dump ", 5) == 0){
673 		if(*(p+5))
674 			dndump(p+5);
675 		else
676 			err = "bad filename";
677 		goto send;
678 	} else if(strcmp(p, "refresh")==0){
679 		needrefresh = 1;
680 		goto send;
681 	}
682 
683 	/*
684 	 *  kill previous reply
685 	 */
686 	mf->nrr = 0;
687 	mf->rr[0] = 0;
688 
689 	/*
690 	 *  break up request (into a name and a type)
691 	 */
692 	atype = strchr(job->request.data, ' ');
693 	if(atype == 0){
694 		err = "illegal request";
695 		goto send;
696 	} else
697 		*atype++ = 0;
698 
699 	/*
700 	 *  tracing request
701 	 */
702 	if(strcmp(atype, "trace") == 0){
703 		if(trace)
704 			free(trace);
705 		if(*job->request.data)
706 			trace = estrdup(job->request.data);
707 		else
708 			trace = 0;
709 		goto send;
710 	}
711 
712 	mf->type = rrtype(atype);
713 	if(mf->type < 0){
714 		err = "unknown type";
715 		goto send;
716 	}
717 
718 	p = atype - 2;
719 	if(p >= job->request.data && *p == '.'){
720 		rooted = 1;
721 		*p = 0;
722 	} else
723 		rooted = 0;
724 
725 	p = job->request.data;
726 	if(*p == '!'){
727 		wantsav = 1;
728 		p++;
729 	} else
730 		wantsav = 0;
731 	dncheck(0, 1);
732 	rp = dnresolve(p, Cin, mf->type, req, 0, 0, Recurse, rooted, &status);
733 	dncheck(0, 1);
734 	neg = rrremneg(&rp);
735 	if(neg){
736 		status = neg->negrcode;
737 		rrfreelist(neg);
738 	}
739 	if(rp == 0){
740 		switch(status){
741 		case Rname:
742 			err = "name does not exist";
743 			break;
744 		case Rserver:
745 			err = "dns failure";
746 			break;
747 		default:
748 			err = "resource does not exist";
749 			break;
750 		}
751 	} else {
752 		lock(&joblock);
753 		if(!job->flushed){
754 			/* format data to be read later */
755 			n = 0;
756 			mf->nrr = 0;
757 			for(tp = rp; mf->nrr < Maxrrr-1 && n < Maxreply && tp &&
758 					tsame(mf->type, tp->type); tp = tp->next){
759 				mf->rr[mf->nrr++] = n;
760 				if(wantsav)
761 					n += snprint(mf->reply+n, Maxreply-n, "%Q", tp);
762 				else
763 					n += snprint(mf->reply+n, Maxreply-n, "%R", tp);
764 			}
765 			mf->rr[mf->nrr] = n;
766 		}
767 		unlock(&joblock);
768 		rrfreelist(rp);
769 	}
770 
771     send:
772 	dncheck(0, 1);
773 	job->reply.count = cnt;
774 	sendmsg(job, err);
775 }
776 
777 void
rclunk(Job * job,Mfile * mf)778 rclunk(Job *job, Mfile *mf)
779 {
780 	freefid(mf);
781 	sendmsg(job, 0);
782 }
783 
784 void
rremove(Job * job,Mfile * mf)785 rremove(Job *job, Mfile *mf)
786 {
787 	USED(mf);
788 	sendmsg(job, "remove permission denied");
789 }
790 
791 void
rstat(Job * job,Mfile * mf)792 rstat(Job *job, Mfile *mf)
793 {
794 	Dir dir;
795 	uchar buf[IOHDRSZ+Maxfdata];
796 
797 	if(mf->qid.type & QTDIR){
798 		dir.name = ".";
799 		dir.mode = DMDIR|0555;
800 	} else {
801 		dir.name = "dns";
802 		dir.mode = 0666;
803 	}
804 	dir.qid = mf->qid;
805 	dir.length = 0;
806 	dir.uid = mf->user;
807 	dir.gid = mf->user;
808 	dir.muid = mf->user;
809 	dir.atime = dir.mtime = time(0);
810 	job->reply.nstat = convD2M(&dir, buf, sizeof buf);
811 	job->reply.stat = buf;
812 	sendmsg(job, 0);
813 }
814 
815 void
rwstat(Job * job,Mfile * mf)816 rwstat(Job *job, Mfile *mf)
817 {
818 	USED(mf);
819 	sendmsg(job, "wstat permission denied");
820 }
821 
822 void
sendmsg(Job * job,char * err)823 sendmsg(Job *job, char *err)
824 {
825 	int n;
826 	uchar mdata[IOHDRSZ + Maxfdata];
827 	char ename[ERRMAX];
828 
829 	if(err){
830 		job->reply.type = Rerror;
831 		snprint(ename, sizeof(ename), "dns: %s", err);
832 		job->reply.ename = ename;
833 	}else{
834 		job->reply.type = job->request.type+1;
835 	}
836 	job->reply.tag = job->request.tag;
837 	n = convS2M(&job->reply, mdata, sizeof mdata);
838 	if(n == 0){
839 		syslog(1, logfile, "sendmsg convS2M of %F returns 0", &job->reply);
840 		abort();
841 	}
842 	lock(&joblock);
843 	if(job->flushed == 0)
844 		if(write(mfd[1], mdata, n)!=n)
845 			sysfatal("mount write");
846 	unlock(&joblock);
847 	if(debug)
848 		syslog(0, logfile, "%F %d", &job->reply, n);
849 }
850 
851 /*
852  *  the following varies between dnsdebug and dns
853  */
854 void
logreply(int id,uchar * addr,DNSmsg * mp)855 logreply(int id, uchar *addr, DNSmsg *mp)
856 {
857 	RR *rp;
858 
859 	syslog(0, LOG, "%d: rcvd %I flags:%s%s%s%s%s", id, addr,
860 		mp->flags & Fauth ? " auth" : "",
861 		mp->flags & Ftrunc ? " trunc" : "",
862 		mp->flags & Frecurse ? " rd" : "",
863 		mp->flags & Fcanrec ? " ra" : "",
864 		mp->flags & (Fauth|Rname) == (Fauth|Rname) ?
865 		" nx" : "");
866 	for(rp = mp->qd; rp != nil; rp = rp->next)
867 		syslog(0, LOG, "%d: rcvd %I qd %s", id, addr, rp->owner->name);
868 	for(rp = mp->an; rp != nil; rp = rp->next)
869 		syslog(0, LOG, "%d: rcvd %I an %R", id, addr, rp);
870 	for(rp = mp->ns; rp != nil; rp = rp->next)
871 		syslog(0, LOG, "%d: rcvd %I ns %R", id, addr, rp);
872 	for(rp = mp->ar; rp != nil; rp = rp->next)
873 		syslog(0, LOG, "%d: rcvd %I ar %R", id, addr, rp);
874 }
875 
876 void
logsend(int id,int subid,uchar * addr,char * sname,char * rname,int type)877 logsend(int id, int subid, uchar *addr, char *sname, char *rname, int type)
878 {
879 	char buf[12];
880 
881 	syslog(0, LOG, "%d.%d: sending to %I/%s %s %s",
882 		id, subid, addr, sname, rname, rrname(type, buf, sizeof buf));
883 }
884 
885 RR*
getdnsservers(int class)886 getdnsservers(int class)
887 {
888 	return dnsservers(class);
889 }
890