xref: /plan9-contrib/sys/src/cmd/ndb/dnresolve.c (revision adb31a6221c09bb6f8b2c527bf9100e20ffac78f)
1 /*
2  * domain name resolvers, see rfcs 1035 and 1123
3  */
4 #include <u.h>
5 #include <libc.h>
6 #include <ip.h>
7 #include <bio.h>
8 #include <ndb.h>
9 #include "dns.h"
10 
11 #define NS2MS(ns) ((ns) / 1000000L)
12 #define S2MS(s)   ((s)  * 1000)
13 #define MS2S(ms)  ((ms) / 1000)
14 
15 typedef struct Dest Dest;
16 typedef struct Ipaddr Ipaddr;
17 typedef struct Query Query;
18 
19 enum
20 {
21 	Udp, Tcp,
22 	Maxdest=	24,	/* maximum destinations for a request message */
23 	Maxtrans=	3,	/* maximum transmissions to a server */
24 	Destmagic=	0xcafebabe,
25 	Querymagic=	0xdeadbeef,
26 };
27 enum { Hurry, Patient, };
28 enum { Outns, Inns, };
29 enum { Remntretry = 15, };	/* min. sec.s between remount attempts */
30 
31 struct Ipaddr {
32 	Ipaddr *next;
33 	uchar	ip[IPaddrlen];
34 };
35 
36 struct Dest
37 {
38 	uchar	a[IPaddrlen];	/* ip address */
39 	DN	*s;		/* name server */
40 	int	nx;		/* number of transmissions */
41 	int	code;		/* response code; used to clear dp->respcode */
42 
43 	ulong	magic;
44 };
45 
46 /*
47  * Query has a QLock in it, thus it can't be an automatic
48  * variable, since each process would see a separate copy
49  * of the lock on its stack.
50  */
51 struct Query {
52 	DN	*dp;		/* domain */
53 	ushort	type;		/* and type to look up */
54 	Request *req;
55 	RR	*nsrp;		/* name servers to consult */
56 
57 	/* dest must not be on the stack due to forking in slave() */
58 	Dest	*dest;		/* array of destinations */
59 	Dest	*curdest;	/* pointer to one of them */
60 	int	ndest;
61 
62 	int	udpfd;
63 
64 	QLock	tcplock;	/* only one tcp call at a time per query */
65 	int	tcpset;
66 	int	tcpfd;		/* if Tcp, read replies from here */
67 	int	tcpctlfd;
68 	uchar	tcpip[IPaddrlen];
69 
70 	ulong	magic;
71 };
72 
73 /* estimated % probability of such a record existing at all */
74 int likely[] = {
75 	[Ta]		95,
76 	[Taaaa]		10,
77 	[Tcname]	15,
78 	[Tmx]		60,
79 	[Tns]		90,
80 	[Tnull]		5,
81 	[Tptr]		35,
82 	[Tsoa]		90,
83 	[Tsrv]		60,
84 	[Ttxt]		15,
85 	[Tall]		95,
86 };
87 
88 static RR*	dnresolve1(char*, int, int, Request*, int, int);
89 static int	netquery(Query *, int);
90 
91 /*
92  * reading /proc/pid/args yields either "name" or "name [display args]",
93  * so return only display args, if any.
94  */
95 static char *
96 procgetname(void)
97 {
98 	int fd, n;
99 	char *lp, *rp;
100 	char buf[256];
101 
102 	snprint(buf, sizeof buf, "#p/%d/args", getpid());
103 	if((fd = open(buf, OREAD)) < 0)
104 		return strdup("");
105 	*buf = '\0';
106 	n = read(fd, buf, sizeof buf-1);
107 	close(fd);
108 	if (n >= 0)
109 		buf[n] = '\0';
110 	if ((lp = strchr(buf, '[')) == nil ||
111 	    (rp = strrchr(buf, ']')) == nil)
112 		return strdup("");
113 	*rp = '\0';
114 	return strdup(lp+1);
115 }
116 
117 /*
118  *  lookup 'type' info for domain name 'name'.  If it doesn't exist, try
119  *  looking it up as a canonical name.
120  */
121 RR*
122 dnresolve(char *name, int class, int type, Request *req, RR **cn, int depth,
123 	int recurse, int rooted, int *status)
124 {
125 	RR *rp, *nrp, *drp;
126 	DN *dp;
127 	int loops;
128 	char *procname;
129 	char nname[Domlen];
130 
131 	if(status)
132 		*status = 0;
133 
134 	if(depth > 12)			/* in a recursive loop? */
135 		return nil;
136 
137 	procname = procgetname();
138 	/*
139 	 *  hack for systems that don't have resolve search
140 	 *  lists.  Just look up the simple name in the database.
141 	 */
142 	if(!rooted && strchr(name, '.') == nil){
143 		rp = nil;
144 		drp = domainlist(class);
145 		for(nrp = drp; rp == nil && nrp != nil; nrp = nrp->next){
146 			snprint(nname, sizeof nname, "%s.%s", name,
147 				nrp->ptr->name);
148 			rp = dnresolve(nname, class, type, req, cn, depth+1,
149 				recurse, rooted, status);
150 			rrfreelist(rrremneg(&rp));
151 		}
152 		if(drp != nil)
153 			rrfreelist(drp);	/* was rrfree */
154 		procsetname(procname);
155 		free(procname);
156 		return rp;
157 	}
158 
159 	/*
160 	 *  try the name directly
161 	 */
162 	rp = dnresolve1(name, class, type, req, depth, recurse);
163 	if(rp == nil) {
164 		/*
165 		 * try it as a canonical name if we weren't told
166 		 * that the name didn't exist
167 		 */
168 		dp = dnlookup(name, class, 0);
169 		if(type != Tptr && dp->respcode != Rname)
170 			for(loops = 0; rp == nil && loops < 32; loops++){
171 				rp = dnresolve1(name, class, Tcname, req,
172 					depth, recurse);
173 				if(rp == nil)
174 					break;
175 
176 				/* rp->host == nil shouldn't happen, but does */
177 				if(rp->negative || rp->host == nil){
178 					rrfreelist(rp);
179 					rp = nil;
180 					break;
181 				}
182 
183 				name = rp->host->name;
184 				if(cn)
185 					rrcat(cn, rp);
186 				else
187 					rrfreelist(rp);
188 
189 				rp = dnresolve1(name, class, type, req,
190 					depth, recurse);
191 			}
192 
193 		/* distinction between not found and not good */
194 		if(rp == nil && status != nil && dp->respcode != 0)
195 			*status = dp->respcode;
196 	}
197 	procsetname(procname);
198 	free(procname);
199 	return randomize(rp);
200 }
201 
202 static void
203 queryinit(Query *qp, DN *dp, int type, Request *req)
204 {
205 	memset(qp, 0, sizeof *qp);
206 	qp->udpfd = qp->tcpfd = qp->tcpctlfd = -1;
207 	qp->dp = dp;
208 	qp->type = type;
209 	if (qp->type != type)
210 		dnslog("queryinit: bogus type %d", type);
211 	qp->req = req;
212 	qp->nsrp = nil;
213 	qp->dest = qp->curdest = nil;
214 	qp->magic = Querymagic;
215 }
216 
217 static void
218 queryck(Query *qp)
219 {
220 	assert(qp);
221 	assert(qp->magic == Querymagic);
222 }
223 
224 static void
225 querydestroy(Query *qp)
226 {
227 	queryck(qp);
228 	/* leave udpfd alone */
229 	if (qp->tcpfd > 0)
230 		close(qp->tcpfd);
231 	if (qp->tcpctlfd > 0) {
232 		hangup(qp->tcpctlfd);
233 		close(qp->tcpctlfd);
234 	}
235 	free(qp->dest);
236 	memset(qp, 0, sizeof *qp);	/* prevent accidents */
237 	qp->udpfd = qp->tcpfd = qp->tcpctlfd = -1;
238 }
239 
240 static void
241 destinit(Dest *p)
242 {
243 	memset(p, 0, sizeof *p);
244 	p->magic = Destmagic;
245 }
246 
247 static void
248 destck(Dest *p)
249 {
250 	assert(p);
251 	assert(p->magic == Destmagic);
252 }
253 
254 static void
255 destdestroy(Dest *p)
256 {
257 	USED(p);
258 }
259 
260 /*
261  * if the response to a query hasn't arrived within 100 ms.,
262  * it's unlikely to arrive at all.  after 1 s., it's really unlikely.
263  * queries for missing RRs are likely to produce time-outs rather than
264  * negative responses, so cname and aaaa queries are likely to time out,
265  * thus we don't wait very long for them.
266  */
267 static void
268 notestats(vlong start, int tmout, int type)
269 {
270 	qlock(&stats);
271 	if (tmout) {
272 		stats.tmout++;
273 		if (type == Taaaa)
274 			stats.tmoutv6++;
275 		else if (type == Tcname)
276 			stats.tmoutcname++;
277 	} else {
278 		long wait10ths = NS2MS(nsec() - start) / 100;
279 
280 		if (wait10ths <= 0)
281 			stats.under10ths[0]++;
282 		else if (wait10ths >= nelem(stats.under10ths))
283 			stats.under10ths[nelem(stats.under10ths) - 1]++;
284 		else
285 			stats.under10ths[wait10ths]++;
286 	}
287 	qunlock(&stats);
288 }
289 
290 static void
291 noteinmem(void)
292 {
293 	qlock(&stats);
294 	stats.answinmem++;
295 	qunlock(&stats);
296 }
297 
298 static RR*
299 issuequery(Query *qp, RR *rp, char *name, int class, int depth, int recurse)
300 {
301 	char *cp;
302 	DN *nsdp;
303 	RR *nsrp, *dbnsrp;
304 
305 	/*
306 	 *  if we're running as just a resolver, query our
307 	 *  designated name servers
308 	 */
309 	if(cfg.resolver){
310 		nsrp = randomize(getdnsservers(class));
311 		if(nsrp != nil) {
312 			qp->nsrp = nsrp;
313 			if(netquery(qp, depth+1)){
314 				rrfreelist(nsrp);
315 				return rrlookup(qp->dp, qp->type, OKneg);
316 			}
317 			rrfreelist(nsrp);
318 		}
319 	}
320 
321 	/*
322  	 *  walk up the domain name looking for
323 	 *  a name server for the domain.
324 	 */
325 	for(cp = name; cp; cp = walkup(cp)){
326 		/*
327 		 *  if this is a local (served by us) domain,
328 		 *  return answer
329 		 */
330 		dbnsrp = randomize(dblookup(cp, class, Tns, 0, 0));
331 		if(dbnsrp && dbnsrp->local){
332 			rp = dblookup(name, class, qp->type, 1, dbnsrp->ttl);
333 			rrfreelist(dbnsrp);
334 			return rp;
335 		}
336 
337 		/*
338 		 *  if recursion isn't set, just accept local
339 		 *  entries
340 		 */
341 		if(recurse == Dontrecurse){
342 			if(dbnsrp)
343 				rrfreelist(dbnsrp);
344 			continue;
345 		}
346 
347 		/* look for ns in cache */
348 		nsdp = dnlookup(cp, class, 0);
349 		nsrp = nil;
350 		if(nsdp)
351 			nsrp = randomize(rrlookup(nsdp, Tns, NOneg));
352 
353 		/* if the entry timed out, ignore it */
354 		if(nsrp && nsrp->ttl < now){
355 			rrfreelist(nsrp);
356 			nsrp = nil;
357 		}
358 
359 		if(nsrp){
360 			rrfreelist(dbnsrp);
361 
362 			/* query the name servers found in cache */
363 			qp->nsrp = nsrp;
364 			if(netquery(qp, depth+1)){
365 				rrfreelist(nsrp);
366 				return rrlookup(qp->dp, qp->type, OKneg);
367 			}
368 			rrfreelist(nsrp);
369 			continue;
370 		}
371 
372 		/* use ns from db */
373 		if(dbnsrp){
374 			/* try the name servers found in db */
375 			qp->nsrp = dbnsrp;
376 			if(netquery(qp, depth+1)){
377 				/* we got an answer */
378 				rrfreelist(dbnsrp);
379 				return rrlookup(qp->dp, qp->type, NOneg);
380 			}
381 			rrfreelist(dbnsrp);
382 		}
383 	}
384 	return rp;
385 }
386 
387 static RR*
388 dnresolve1(char *name, int class, int type, Request *req, int depth,
389 	int recurse)
390 {
391 	Area *area;
392 	DN *dp;
393 	RR *rp;
394 	Query *qp;
395 
396 	if(debug)
397 		dnslog("[%d] dnresolve1 %s %d %d", getpid(), name, type, class);
398 
399 	/* only class Cin implemented so far */
400 	if(class != Cin)
401 		return nil;
402 
403 	dp = dnlookup(name, class, 1);
404 
405 	/*
406 	 *  Try the cache first
407 	 */
408 	rp = rrlookup(dp, type, OKneg);
409 	if(rp)
410 		if(rp->db){
411 			/* unauthoritative db entries are hints */
412 			if(rp->auth) {
413 				noteinmem();
414 				return rp;
415 			}
416 		} else
417 			/* cached entry must still be valid */
418 			if(rp->ttl > now)
419 				/* but Tall entries are special */
420 				if(type != Tall || rp->query == Tall) {
421 					noteinmem();
422 					return rp;
423 				}
424 	rrfreelist(rp);
425 	rp = nil;
426 
427 	/*
428 	 * try the cache for a canonical name. if found punt
429 	 * since we'll find it during the canonical name search
430 	 * in dnresolve().
431 	 */
432 	if(type != Tcname){
433 		rp = rrlookup(dp, Tcname, NOneg);
434 		rrfreelist(rp);
435 		if(rp)
436 			return nil;
437 	}
438 
439 	/*
440 	 * if the domain name is within an area of ours,
441 	 * we should have found its data in memory by now.
442 	 */
443 	area = inmyarea(dp->name);
444 	if (area || strncmp(dp->name, "local#", 6) == 0) {
445 //		char buf[32];
446 
447 //		dnslog("%s %s: no data in area %s", dp->name,
448 //			rrname(type, buf, sizeof buf), area->soarr->owner->name);
449 		return nil;
450 	}
451 
452 	qp = emalloc(sizeof *qp);
453 	queryinit(qp, dp, type, req);
454 	rp = issuequery(qp, rp, name, class, depth, recurse);
455 	querydestroy(qp);
456 	free(qp);
457 	if(rp)
458 		return rp;
459 
460 	/* settle for a non-authoritative answer */
461 	rp = rrlookup(dp, type, OKneg);
462 	if(rp)
463 		return rp;
464 
465 	/* noone answered.  try the database, we might have a chance. */
466 	return dblookup(name, class, type, 0, 0);
467 }
468 
469 /*
470  *  walk a domain name one element to the right.
471  *  return a pointer to that element.
472  *  in other words, return a pointer to the parent domain name.
473  */
474 char*
475 walkup(char *name)
476 {
477 	char *cp;
478 
479 	cp = strchr(name, '.');
480 	if(cp)
481 		return cp+1;
482 	else if(*name)
483 		return "";
484 	else
485 		return 0;
486 }
487 
488 /*
489  *  Get a udp port for sending requests and reading replies.  Put the port
490  *  into "headers" mode.
491  */
492 static char *hmsg = "headers";
493 
494 int
495 udpport(char *mtpt)
496 {
497 	int fd, ctl;
498 	char ds[64], adir[64];
499 
500 	/* get a udp port */
501 	snprint(ds, sizeof ds, "%s/udp!*!0", (mtpt? mtpt: "/net"));
502 	ctl = announce(ds, adir);
503 	if(ctl < 0){
504 		/* warning("can't get udp port"); */
505 		return -1;
506 	}
507 
508 	/* turn on header style interface */
509 	if(write(ctl, hmsg, strlen(hmsg)) , 0){
510 		close(ctl);
511 		warning(hmsg);
512 		return -1;
513 	}
514 
515 	/* grab the data file */
516 	snprint(ds, sizeof ds, "%s/data", adir);
517 	fd = open(ds, ORDWR);
518 	close(ctl);
519 	if(fd < 0)
520 		warning("can't open udp port %s: %r", ds);
521 	return fd;
522 }
523 
524 /* generate a DNS UDP query packet */
525 int
526 mkreq(DN *dp, int type, uchar *buf, int flags, ushort reqno)
527 {
528 	DNSmsg m;
529 	int len;
530 	Udphdr *uh = (Udphdr*)buf;
531 
532 	/* stuff port number into output buffer */
533 	memset(uh, 0, sizeof *uh);
534 	hnputs(uh->rport, 53);
535 
536 	/* make request and convert it to output format */
537 	memset(&m, 0, sizeof m);
538 	m.flags = flags;
539 	m.id = reqno;
540 	m.qd = rralloc(type);
541 	m.qd->owner = dp;
542 	m.qd->type = type;
543 	if (m.qd->type != type)
544 		dnslog("mkreq: bogus type %d", type);
545 	len = convDNS2M(&m, &buf[Udphdrsize], Maxudp);
546 	rrfree(m.qd);
547 	return len;
548 }
549 
550 /* for alarms in readreply */
551 static void
552 ding(void*, char *msg)
553 {
554 	if(strstr(msg, "alarm") != nil)
555 		noted(NCONT);		/* resume with system call error */
556 	else
557 		noted(NDFLT);		/* die */
558 }
559 
560 void
561 freeanswers(DNSmsg *mp)
562 {
563 	rrfreelist(mp->qd);
564 	rrfreelist(mp->an);
565 	rrfreelist(mp->ns);
566 	rrfreelist(mp->ar);
567 	mp->qd = mp->an = mp->ns = mp->ar = nil;
568 }
569 
570 /* sets srcip */
571 static int
572 readnet(Query *qp, int medium, uchar *ibuf, ulong endtime, uchar **replyp,
573 	uchar *srcip)
574 {
575 	int len, fd;
576 	long ms;
577 	vlong startns = nsec();
578 	uchar *reply;
579 	uchar lenbuf[2];
580 
581 	/* timed read of reply */
582 	ms = S2MS(endtime) - NS2MS(startns);
583 	if (ms < 2000)
584 		ms = 2000;	/* give the remote ns a fighting chance */
585 	reply = ibuf;
586 	len = -1;			/* pessimism */
587 	memset(srcip, 0, IPaddrlen);
588 	if (medium == Udp)
589 		if (qp->udpfd <= 0)
590 			dnslog("readnet: qp->udpfd closed");
591 		else {
592 			alarm(ms);
593 			len = read(qp->udpfd, ibuf, Udphdrsize+Maxudpin);
594 			alarm(0);
595 			notestats(startns, len < 0, qp->type);
596 			if (len >= IPaddrlen)
597 				memmove(srcip, ibuf, IPaddrlen);
598 			if (len >= Udphdrsize) {
599 				len   -= Udphdrsize;
600 				reply += Udphdrsize;
601 			}
602 		}
603 	else {
604 		if (!qp->tcpset)
605 			dnslog("readnet: tcp params not set");
606 		alarm(ms);
607 		fd = qp->tcpfd;
608 		if (fd <= 0)
609 			dnslog("readnet: %s: tcp fd unset for dest %I",
610 				qp->dp->name, qp->tcpip);
611 		else if (readn(fd, lenbuf, 2) != 2) {
612 			dnslog("readnet: short read of tcp size from %I",
613 				qp->tcpip);
614 			/* probably a time-out */
615 			notestats(startns, 1, qp->type);
616 		} else {
617 			len = lenbuf[0]<<8 | lenbuf[1];
618 			if (readn(fd, ibuf, len) != len) {
619 				dnslog("readnet: short read of tcp data from %I",
620 					qp->tcpip);
621 				/* probably a time-out */
622 				notestats(startns, 1, qp->type);
623 				len = -1;
624 			}
625 		}
626 		alarm(0);
627 		memmove(srcip, qp->tcpip, IPaddrlen);
628 	}
629 	*replyp = reply;
630 	return len;
631 }
632 
633 /*
634  *  read replies to a request and remember the rrs in the answer(s).
635  *  ignore any of the wrong type.
636  *  wait at most until endtime.
637  */
638 static int
639 readreply(Query *qp, int medium, ushort req, uchar *ibuf, DNSmsg *mp,
640 	ulong endtime)
641 {
642 	int len, rv;
643 	char *err;
644 	char tbuf[32];
645 	uchar *reply;
646 	uchar srcip[IPaddrlen];
647 	RR *rp;
648 
649 	notify(ding);
650 
651 	queryck(qp);
652 	rv = 0;
653 	memset(mp, 0, sizeof *mp);
654 	if (time(nil) >= endtime)
655 		return -1;		/* timed out before we started */
656 
657 	memset(srcip, 0, sizeof srcip);
658 	if (0)
659 		len = -1;
660 	for (; time(nil) < endtime &&
661 	    (len = readnet(qp, medium, ibuf, endtime, &reply, srcip)) >= 0;
662 	    freeanswers(mp)){
663 		/* convert into internal format  */
664 		memset(mp, 0, sizeof *mp);
665 		err = convM2DNS(reply, len, mp, nil);
666 		if (mp->flags & Ftrunc) {
667 			free(err);
668 			freeanswers(mp);
669 			/* notify our caller to retry the query via tcp. */
670 			return -1;
671 		} else if(err){
672 			dnslog("readreply: %s: input err, len %d: %s: %I",
673 				qp->dp->name, len, err, srcip);
674 			free(err);
675 			continue;
676 		}
677 		if(debug)
678 			logreply(qp->req->id, srcip, mp);
679 
680 		/* answering the right question? */
681 		if(mp->id != req)
682 			dnslog("%d: id %d instead of %d: %I", qp->req->id,
683 				mp->id, req, srcip);
684 		else if(mp->qd == 0)
685 			dnslog("%d: no question RR: %I", qp->req->id, srcip);
686 		else if(mp->qd->owner != qp->dp)
687 			dnslog("%d: owner %s instead of %s: %I", qp->req->id,
688 				mp->qd->owner->name, qp->dp->name, srcip);
689 		else if(mp->qd->type != qp->type)
690 			dnslog("%d: qp->type %d instead of %d: %I",
691 				qp->req->id, mp->qd->type, qp->type, srcip);
692 		else {
693 			/* remember what request this is in answer to */
694 			for(rp = mp->an; rp; rp = rp->next)
695 				rp->query = qp->type;
696 			return rv;
697 		}
698 	}
699 	if (time(nil) >= endtime) {
700 		;				/* query expired */
701 	} else if (0) {
702 		/* this happens routinely when a read times out */
703 		dnslog("readreply: %s type %s: ns %I read error or eof "
704 			"(returned %d): %r", qp->dp->name, rrname(qp->type,
705 			tbuf, sizeof tbuf), srcip, len);
706 		if (medium == Udp)
707 			for (rp = qp->nsrp; rp != nil; rp = rp->next)
708 				if (rp->type == Tns)
709 					dnslog("readreply: %s: query sent to "
710 						"ns %s", qp->dp->name,
711 						rp->host->name);
712 	}
713 	return -1;
714 }
715 
716 /*
717  *	return non-0 if first list includes second list
718  */
719 int
720 contains(RR *rp1, RR *rp2)
721 {
722 	RR *trp1, *trp2;
723 
724 	for(trp2 = rp2; trp2; trp2 = trp2->next){
725 		for(trp1 = rp1; trp1; trp1 = trp1->next)
726 			if(trp1->type == trp2->type)
727 			if(trp1->host == trp2->host)
728 			if(trp1->owner == trp2->owner)
729 				break;
730 		if(trp1 == nil)
731 			return 0;
732 	}
733 	return 1;
734 }
735 
736 
737 /*
738  *  return multicast version if any
739  */
740 int
741 ipisbm(uchar *ip)
742 {
743 	if(isv4(ip)){
744 		if (ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0 ||
745 		    ipcmp(ip, IPv4bcast) == 0)
746 			return 4;
747 	} else
748 		if(ip[0] == 0xff)
749 			return 6;
750 	return 0;
751 }
752 
753 /*
754  *  Get next server address
755  */
756 static int
757 serveraddrs(Query *qp, int nd, int depth)
758 {
759 	RR *rp, *arp, *trp;
760 	Dest *cur;
761 
762 	if(nd >= Maxdest)
763 		return 0;
764 
765 	/*
766 	 *  look for a server whose address we already know.
767 	 *  if we find one, mark it so we ignore this on
768 	 *  subsequent passes.
769 	 */
770 	arp = 0;
771 	for(rp = qp->nsrp; rp; rp = rp->next){
772 		assert(rp->magic == RRmagic);
773 		if(rp->marker)
774 			continue;
775 		arp = rrlookup(rp->host, Ta, NOneg);
776 		if(arp){
777 			rp->marker = 1;
778 			break;
779 		}
780 		arp = dblookup(rp->host->name, Cin, Ta, 0, 0);
781 		if(arp){
782 			rp->marker = 1;
783 			break;
784 		}
785 	}
786 
787 	/*
788 	 *  if the cache and database lookup didn't find any new
789 	 *  server addresses, try resolving one via the network.
790 	 *  Mark any we try to resolve so we don't try a second time.
791 	 */
792 	if(arp == 0)
793 		for(rp = qp->nsrp; rp; rp = rp->next){
794 			if(rp->marker)
795 				continue;
796 			rp->marker = 1;
797 
798 			/*
799 			 *  avoid loops looking up a server under itself
800 			 */
801 			if(subsume(rp->owner->name, rp->host->name))
802 				continue;
803 
804 			arp = dnresolve(rp->host->name, Cin, Ta, qp->req, 0,
805 				depth+1, Recurse, 1, 0);
806 			rrfreelist(rrremneg(&arp));
807 			if(arp)
808 				break;
809 		}
810 
811 	/* use any addresses that we found */
812 	for(trp = arp; trp && nd < Maxdest; trp = trp->next){
813 		cur = &qp->dest[nd];
814 		parseip(cur->a, trp->ip->name);
815 		/*
816 		 * straddling servers can reject all nameservers if they are all
817 		 * inside, so be sure to list at least one outside ns at
818 		 * the end of the ns list in /lib/ndb for `dom='.
819 		 */
820 		if (ipisbm(cur->a) ||
821 		    cfg.straddle && !insideaddr(qp->dp->name) && insidens(cur->a))
822 			continue;
823 		cur->nx = 0;
824 		cur->s = trp->owner;
825 		cur->code = Rtimeout;
826 		nd++;
827 	}
828 	rrfreelist(arp);
829 	return nd;
830 }
831 
832 /*
833  *  cache negative responses
834  */
835 static void
836 cacheneg(DN *dp, int type, int rcode, RR *soarr)
837 {
838 	RR *rp;
839 	DN *soaowner;
840 	ulong ttl;
841 
842 	stats.negcached++;
843 
844 	/* no cache time specified, don't make anything up */
845 	if(soarr != nil){
846 		if(soarr->next != nil){
847 			rrfreelist(soarr->next);
848 			soarr->next = nil;
849 		}
850 		soaowner = soarr->owner;
851 	} else
852 		soaowner = nil;
853 
854 	/* the attach can cause soarr to be freed so mine it now */
855 	if(soarr != nil && soarr->soa != nil)
856 		ttl = soarr->soa->minttl+now;
857 	else
858 		ttl = 5*Min;
859 
860 	/* add soa and negative RR to the database */
861 	rrattach(soarr, Authoritative);
862 
863 	rp = rralloc(type);
864 	rp->owner = dp;
865 	rp->negative = 1;
866 	rp->negsoaowner = soaowner;
867 	rp->negrcode = rcode;
868 	rp->ttl = ttl;
869 	rrattach(rp, Authoritative);
870 }
871 
872 static int
873 setdestoutns(Dest *p, int n)
874 {
875 	uchar *outns = outsidens(n);
876 
877 	destck(p);
878 	destinit(p);
879 	if (outns == nil) {
880 		if (n == 0)
881 			dnslog("[%d] no outside-ns in ndb", getpid());
882 		return -1;
883 	}
884 	memmove(p->a, outns, sizeof p->a);
885 	p->s = dnlookup("outside-ns-ips", Cin, 1);
886 	return 0;
887 }
888 
889 /*
890  * issue query via UDP or TCP as appropriate.
891  * for TCP, returns with qp->tcpip set from udppkt header.
892  */
893 static int
894 mydnsquery(Query *qp, int medium, uchar *udppkt, int len)
895 {
896 	int rv = -1, nfd;
897 	char *domain;
898 	char conndir[40];
899 	uchar belen[2];
900 	NetConnInfo *nci;
901 
902 	queryck(qp);
903 	domain = smprint("%I", udppkt);
904 	if (myaddr(domain)) {
905 		dnslog("mydnsquery: trying to send to myself (%s); bzzzt",
906 			domain);
907 		free(domain);
908 		return rv;
909 	}
910 
911 	switch (medium) {
912 	case Udp:
913 		free(domain);
914 		nfd = dup(qp->udpfd, -1);
915 		if (nfd < 0) {
916 			warning("mydnsquery: qp->udpfd %d: %r", qp->udpfd);
917 			close(qp->udpfd);	/* ensure it's closed */
918 			qp->udpfd = -1;		/* poison it */
919 			return rv;
920 		}
921 		close(nfd);
922 
923 		if (qp->udpfd <= 0)
924 			dnslog("mydnsquery: qp->udpfd %d closed", qp->udpfd);
925 		else {
926 			if (write(qp->udpfd, udppkt, len+Udphdrsize) !=
927 			    len+Udphdrsize)
928 				warning("sending udp msg: %r");
929 			else {
930 				stats.qsent++;
931 				rv = 0;
932 			}
933 		}
934 		break;
935 	case Tcp:
936 		/* send via TCP & keep fd around for reply */
937 		alarm(10*1000);
938 		qp->tcpfd = rv = dial(netmkaddr(domain, "tcp", "dns"), nil,
939 			conndir, &qp->tcpctlfd);
940 		alarm(0);
941 		if (qp->tcpfd < 0) {
942 			dnslog("can't dial tcp!%s!dns: %r", domain);
943 			free(domain);
944 			break;
945 		}
946 		free(domain);
947 		nci = getnetconninfo(conndir, qp->tcpfd);
948 		if (nci) {
949 			parseip(qp->tcpip, nci->rsys);
950 			freenetconninfo(nci);
951 		} else
952 			dnslog("mydnsquery: getnetconninfo failed");
953 		qp->tcpset = 1;
954 
955 		belen[0] = len >> 8;
956 		belen[1] = len;
957 		if (write(qp->tcpfd, belen, 2) != 2 ||
958 		    write(qp->tcpfd, udppkt + Udphdrsize, len) != len)
959 			warning("sending tcp msg: %r");
960 		break;
961 	default:
962 		sysfatal("mydnsquery: bad medium");
963 	}
964 	return rv;
965 }
966 
967 /*
968  * send query to all UDP destinations or one TCP destination,
969  * taken from obuf (udp packet) header
970  */
971 static int
972 xmitquery(Query *qp, int medium, int depth, uchar *obuf, int inns, int len)
973 {
974 	int j, n;
975 	char buf[32];
976 	Dest *p;
977 
978 	queryck(qp);
979 	if(time(nil) >= qp->req->aborttime)
980 		return -1;
981 
982 	/*
983 	 * get a nameserver address if we need one.
984 	 * serveraddrs populates qp->dest.
985 	 */
986 	p = qp->dest;
987 	destck(p);
988 	if (qp->ndest < 0 || qp->ndest > Maxdest)
989 		dnslog("qp->ndest %d out of range", qp->ndest);
990 	if (qp->ndest > qp->curdest - p)
991 		qp->curdest = &qp->dest[serveraddrs(qp, qp->curdest - p, depth)];
992 	destck(qp->curdest);
993 
994 	/* no servers, punt */
995 	if (qp->curdest == qp->dest)
996 		if (cfg.straddle && cfg.inside) {
997 			/* get ips of "outside-ns-ips" */
998 			p = qp->curdest = qp->dest;
999 			for(n = 0; n < Maxdest; n++, qp->curdest++)
1000 				if (setdestoutns(qp->curdest, n) < 0)
1001 					break;
1002 		} else {
1003 			/* it's probably just a bogus domain, don't log it */
1004 			// dnslog("xmitquery: %s: no nameservers", qp->dp->name);
1005 			return -1;
1006 		}
1007 
1008 	/* send to first 'qp->ndest' destinations */
1009 	j = 0;
1010 	if (medium == Tcp) {
1011 		j++;
1012 		queryck(qp);
1013 		assert(qp->dp);
1014 		procsetname("tcp %sside query for %s %s", (inns? "in": "out"),
1015 			qp->dp->name, rrname(qp->type, buf, sizeof buf));
1016 		mydnsquery(qp, medium, obuf, len); /* sets qp->tcpip from obuf */
1017 		if(debug)
1018 			logsend(qp->req->id, depth, qp->tcpip, "", qp->dp->name,
1019 				qp->type);
1020 	} else
1021 		for(; p < &qp->dest[qp->ndest] && p < qp->curdest; p++){
1022 			/* skip destinations we've finished with */
1023 			if(p->nx >= Maxtrans)
1024 				continue;
1025 
1026 			j++;
1027 
1028 			/* exponential backoff of requests */
1029 			if((1<<p->nx) > qp->ndest)
1030 				continue;
1031 
1032 			procsetname("udp %sside query to %I/%s %s %s",
1033 				(inns? "in": "out"), p->a, p->s->name,
1034 				qp->dp->name, rrname(qp->type, buf, sizeof buf));
1035 			if(debug)
1036 				logsend(qp->req->id, depth, p->a, p->s->name,
1037 					qp->dp->name, qp->type);
1038 
1039 			/* fill in UDP destination addr & send it */
1040 			memmove(obuf, p->a, sizeof p->a);
1041 			mydnsquery(qp, medium, obuf, len);
1042 			p->nx++;
1043 		}
1044 	if(j == 0) {
1045 		// dnslog("xmitquery: %s: no destinations left", qp->dp->name);
1046 		return -1;
1047 	}
1048 	return 0;
1049 }
1050 
1051 static int lckindex[Maxlcks] = {
1052 	0,			/* all others map here */
1053 	Ta,
1054 	Tns,
1055 	Tcname,
1056 	Tsoa,
1057 	Tptr,
1058 	Tmx,
1059 	Ttxt,
1060 	Taaaa,
1061 };
1062 
1063 static int
1064 qtype2lck(int qtype)		/* map query type to querylck index */
1065 {
1066 	int i;
1067 
1068 	for (i = 1; i < nelem(lckindex); i++)
1069 		if (lckindex[i] == qtype)
1070 			return i;
1071 	return 0;
1072 }
1073 
1074 /* is mp a cachable negative response (with Rname set)? */
1075 static int
1076 isnegrname(DNSmsg *mp)
1077 {
1078 	/* TODO: could add || cfg.justforw to RHS of && */
1079 	return mp->an == nil && (mp->flags & Rmask) == Rname;
1080 }
1081 
1082 static int
1083 procansw(Query *qp, DNSmsg *mp, uchar *srcip, int depth, Dest *p)
1084 {
1085 	int rv;
1086 //	int lcktype;
1087 	char buf[32];
1088 	DN *ndp;
1089 	Query *nqp;
1090 	RR *tp, *soarr;
1091 
1092 	if (mp->an == nil)
1093 		stats.negans++;
1094 
1095 	/* ignore any error replies */
1096 	if((mp->flags & Rmask) == Rserver){
1097 		stats.negserver++;
1098 		freeanswers(mp);
1099 		if(p != qp->curdest)
1100 			p->code = Rserver;
1101 		return -1;
1102 	}
1103 
1104 	/* ignore any bad delegations */
1105 	if(mp->ns && baddelegation(mp->ns, qp->nsrp, srcip)){
1106 		stats.negbaddeleg++;
1107 		if(mp->an == nil){
1108 			stats.negbdnoans++;
1109 			freeanswers(mp);
1110 			if(p != qp->curdest)
1111 				p->code = Rserver;
1112 			return -1;
1113 		}
1114 		rrfreelist(mp->ns);
1115 		mp->ns = nil;
1116 	}
1117 
1118 	/* remove any soa's from the authority section */
1119 	soarr = rrremtype(&mp->ns, Tsoa);
1120 
1121 	/* incorporate answers */
1122 	unique(mp->an);
1123 	unique(mp->ns);
1124 	unique(mp->ar);
1125 	if(mp->an)
1126 		rrattach(mp->an, (mp->flags & Fauth) != 0);
1127 	if(mp->ar)
1128 		rrattach(mp->ar, Notauthoritative);
1129 	if(mp->ns && !cfg.justforw){
1130 		ndp = mp->ns->owner;
1131 		rrattach(mp->ns, Notauthoritative);
1132 	} else {
1133 		ndp = nil;
1134 		rrfreelist(mp->ns);
1135 		mp->ns = nil;
1136 	}
1137 
1138 	/* free the question */
1139 	if(mp->qd) {
1140 		rrfreelist(mp->qd);
1141 		mp->qd = nil;
1142 	}
1143 
1144 	/*
1145 	 *  Any reply from an authoritative server,
1146 	 *  or a positive reply terminates the search.
1147 	 *  A negative response now also terminates the search.
1148 	 */
1149 	if(mp->an != nil || (mp->flags & Fauth)){
1150 		if(isnegrname(mp))
1151 			qp->dp->respcode = Rname;
1152 		else
1153 			qp->dp->respcode = 0;
1154 
1155 		/*
1156 		 *  cache any negative responses, free soarr.
1157 		 *  negative responses need not be authoritative:
1158 		 *  they can legitimately come from a cache.
1159 		 */
1160 		if( /* (mp->flags & Fauth) && */ mp->an == nil)
1161 			cacheneg(qp->dp, qp->type, (mp->flags & Rmask), soarr);
1162 		else
1163 			rrfreelist(soarr);
1164 		return 1;
1165 	} else if (isnegrname(mp)) {
1166 		qp->dp->respcode = Rname;
1167 		/*
1168 		 *  cache negative response.
1169 		 *  negative responses need not be authoritative:
1170 		 *  they can legitimately come from a cache.
1171 		 */
1172 		cacheneg(qp->dp, qp->type, (mp->flags & Rmask), soarr);
1173 		return 1;
1174 	}
1175 	stats.negnorname++;
1176 	rrfreelist(soarr);
1177 
1178 	/*
1179 	 *  if we've been given better name servers, recurse.
1180 	 *  if we're a pure resolver, don't recurse, we have
1181 	 *  to forward to a fixed set of named servers.
1182 	 */
1183 	if(!mp->ns || cfg.resolver && cfg.justforw)
1184 		return 0;
1185 	tp = rrlookup(ndp, Tns, NOneg);
1186 	if(contains(qp->nsrp, tp)){
1187 		rrfreelist(tp);
1188 		return 0;
1189 	}
1190 	procsetname("recursive query for %s %s", qp->dp->name,
1191 		rrname(qp->type, buf, sizeof buf));
1192 	/*
1193 	 *  we're called from udpquery, called from
1194 	 *  netquery, which current holds qp->dp->querylck,
1195 	 *  so release it now and acquire it upon return.
1196 	 */
1197 //	lcktype = qtype2lck(qp->type);
1198 //	qunlock(&qp->dp->querylck[lcktype]);
1199 
1200 	nqp = emalloc(sizeof *nqp);
1201 	queryinit(nqp, qp->dp, qp->type, qp->req);
1202 	nqp->nsrp = tp;
1203 	rv = netquery(nqp, depth+1);
1204 
1205 //	qlock(&qp->dp->querylck[lcktype]);
1206 	rrfreelist(nqp->nsrp);
1207 	querydestroy(nqp);
1208 	free(nqp);
1209 	return rv;
1210 }
1211 
1212 /*
1213  * send a query via tcp to a single address (from ibuf's udp header)
1214  * and read the answer(s) into mp->an.
1215  */
1216 static int
1217 tcpquery(Query *qp, DNSmsg *mp, int depth, uchar *ibuf, uchar *obuf, int len,
1218 	int waitsecs, int inns, ushort req)
1219 {
1220 	int rv = 0;
1221 	ulong endtime;
1222 
1223 	endtime = time(nil) + waitsecs;
1224 	if(endtime > qp->req->aborttime)
1225 		endtime = qp->req->aborttime;
1226 
1227 	if (0)
1228 		dnslog("%s: udp reply truncated; retrying query via tcp to %I",
1229 			qp->dp->name, qp->tcpip);
1230 
1231 	qlock(&qp->tcplock);
1232 	memmove(obuf, ibuf, IPaddrlen);		/* send back to respondent */
1233 	/* sets qp->tcpip from obuf's udp header */
1234 	if (xmitquery(qp, Tcp, depth, obuf, inns, len) < 0 ||
1235 	    readreply(qp, Tcp, req, ibuf, mp, endtime) < 0)
1236 		rv = -1;
1237 	if (qp->tcpfd > 0) {
1238 		hangup(qp->tcpctlfd);
1239 		close(qp->tcpctlfd);
1240 		close(qp->tcpfd);
1241 	}
1242 	qp->tcpfd = qp->tcpctlfd = -1;
1243 	qunlock(&qp->tcplock);
1244 	return rv;
1245 }
1246 
1247 /*
1248  *  query name servers.  If the name server returns a pointer to another
1249  *  name server, recurse.
1250  */
1251 static int
1252 queryns(Query *qp, int depth, uchar *ibuf, uchar *obuf, int waitsecs, int inns)
1253 {
1254 	int ndest, len, replywaits, rv;
1255 	ushort req;
1256 	ulong endtime;
1257 	char buf[12];
1258 	uchar srcip[IPaddrlen];
1259 	Dest *p, *np, *dest;
1260 //	Dest dest[Maxdest];
1261 
1262 	/* pack request into a udp message */
1263 	req = rand();
1264 	len = mkreq(qp->dp, qp->type, obuf, Frecurse|Oquery, req);
1265 
1266 	/* no server addresses yet */
1267 	queryck(qp);
1268 	dest = emalloc(Maxdest * sizeof *dest);	/* dest can't be on stack */
1269 	for (p = dest; p < dest + Maxdest; p++)
1270 		destinit(p);
1271 	/* this dest array is local to this call of queryns() */
1272 	free(qp->dest);
1273 	qp->curdest = qp->dest = dest;
1274 
1275 	/*
1276 	 *  transmit udp requests and wait for answers.
1277 	 *  at most Maxtrans attempts to each address.
1278 	 *  each cycle send one more message than the previous.
1279 	 *  retry a query via tcp if its response is truncated.
1280 	 */
1281 	for(ndest = 1; ndest < Maxdest; ndest++){
1282 		qp->ndest = ndest;
1283 		qp->tcpset = 0;
1284 		if (xmitquery(qp, Udp, depth, obuf, inns, len) < 0)
1285 			break;
1286 
1287 		endtime = time(nil) + waitsecs;
1288 		if(endtime > qp->req->aborttime)
1289 			endtime = qp->req->aborttime;
1290 
1291 		for(replywaits = 0; replywaits < ndest; replywaits++){
1292 			DNSmsg m;
1293 
1294 			procsetname("reading %sside reply from %I: %s %s from %s",
1295 				(inns? "in": "out"), obuf, qp->dp->name,
1296 				rrname(qp->type, buf, sizeof buf), qp->req->from);
1297 
1298 			/* read udp answer into m */
1299 			if (readreply(qp, Udp, req, ibuf, &m, endtime) >= 0)
1300 				memmove(srcip, ibuf, IPaddrlen);
1301 			else if (!(m.flags & Ftrunc)) {
1302 				freeanswers(&m);
1303 				break;		/* timed out on this dest */
1304 			} else {
1305 				/* whoops, it was truncated! ask again via tcp */
1306 				rv = tcpquery(qp, &m, depth, ibuf, obuf, len,
1307 					waitsecs, inns, req);  /* answer in m */
1308 				if (rv < 0) {
1309 					freeanswers(&m);
1310 					break;		/* failed via tcp too */
1311 				}
1312 				memmove(srcip, qp->tcpip, IPaddrlen);
1313 			}
1314 
1315 			/* find responder */
1316 			// dnslog("queryns got reply from %I", srcip);
1317 			for(p = qp->dest; p < qp->curdest; p++)
1318 				if(memcmp(p->a, srcip, sizeof p->a) == 0)
1319 					break;
1320 
1321 			/* remove all addrs of responding server from list */
1322 			for(np = qp->dest; np < qp->curdest; np++)
1323 				if(np->s == p->s)
1324 					p->nx = Maxtrans;
1325 
1326 			/* free or incorporate RRs in m */
1327 			rv = procansw(qp, &m, srcip, depth, p);
1328 			if (rv > 0) {
1329 				free(qp->dest);
1330 				qp->dest = qp->curdest = nil; /* prevent accidents */
1331 				return rv;
1332 			}
1333 		}
1334 	}
1335 
1336 	/* if all servers returned failure, propagate it */
1337 	qp->dp->respcode = Rserver;
1338 	for(p = dest; p < qp->curdest; p++) {
1339 		destck(p);
1340 		if(p->code != Rserver)
1341 			qp->dp->respcode = 0;
1342 		p->magic = 0;			/* prevent accidents */
1343 	}
1344 
1345 //	if (qp->dp->respcode)
1346 //		dnslog("queryns setting Rserver for %s", qp->dp->name);
1347 
1348 	free(qp->dest);
1349 	qp->dest = qp->curdest = nil;		/* prevent accidents */
1350 	return 0;
1351 }
1352 
1353 /*
1354  *  run a command with a supplied fd as standard input
1355  */
1356 char *
1357 system(int fd, char *cmd)
1358 {
1359 	int pid, p, i;
1360 	static Waitmsg msg;
1361 
1362 	if((pid = fork()) == -1)
1363 		sysfatal("fork failed: %r");
1364 	else if(pid == 0){
1365 		dup(fd, 0);
1366 		close(fd);
1367 		for (i = 3; i < 200; i++)
1368 			close(i);		/* don't leak fds */
1369 		execl("/bin/rc", "rc", "-c", cmd, nil);
1370 		sysfatal("exec rc: %r");
1371 	}
1372 	for(p = waitpid(); p >= 0; p = waitpid())
1373 		if(p == pid)
1374 			return msg.msg;
1375 	return "lost child";
1376 }
1377 
1378 /* compute wait, weighted by probability of success, with minimum */
1379 static ulong
1380 weight(ulong ms, unsigned pcntprob)
1381 {
1382 	ulong wait;
1383 
1384 	wait = (ms * pcntprob) / 100;
1385 	if (wait < 1500)
1386 		wait = 1500;
1387 	return wait;
1388 }
1389 
1390 /*
1391  * in principle we could use a single descriptor for a udp port
1392  * to send all queries and receive all the answers to them,
1393  * but we'd have to sort out the answers by dns-query id.
1394  */
1395 static int
1396 udpquery(Query *qp, char *mntpt, int depth, int patient, int inns)
1397 {
1398 	int fd, rv;
1399 	long now;
1400 	ulong pcntprob, wait, reqtm;
1401 	char *msg;
1402 	uchar *obuf, *ibuf;
1403 	static QLock mntlck;
1404 	static ulong lastmount;
1405 
1406 	/* use alloced buffers rather than ones from the stack */
1407 	// ibuf = emalloc(Maxudpin+Udphdrsize);
1408 	ibuf = emalloc(64*1024);		/* max. tcp reply size */
1409 	obuf = emalloc(Maxudp+Udphdrsize);
1410 
1411 	fd = udpport(mntpt);
1412 	while (fd < 0 && cfg.straddle && strcmp(mntpt, "/net.alt") == 0) {
1413 		/* HACK: remount /net.alt */
1414 		now = time(nil);
1415 		if (now < lastmount + Remntretry)
1416 			sleep((lastmount + Remntretry - now)*1000);
1417 		qlock(&mntlck);
1418 		fd = udpport(mntpt);	/* try again under lock */
1419 		if (fd < 0) {
1420 			dnslog("[%d] remounting /net.alt", getpid());
1421 			unmount(nil, "/net.alt");
1422 
1423 			msg = system(open("/dev/null", ORDWR), "outside");
1424 
1425 			lastmount = time(nil);
1426 			if (msg && *msg) {
1427 				dnslog("[%d] can't remount /net.alt: %s",
1428 					getpid(), msg);
1429 				sleep(10*1000);		/* don't spin wildly */
1430 			} else
1431 				fd = udpport(mntpt);
1432 		}
1433 		qunlock(&mntlck);
1434 	}
1435 	if (fd < 0) {
1436 		dnslog("can't get udpport for %s query of name %s: %r",
1437 			mntpt, qp->dp->name);
1438 		sysfatal("out of udp conversations");	/* we're buggered */
1439 	}
1440 
1441 	/*
1442 	 * Our QIP servers are busted, don't answer AAAA and
1443 	 * take forever to answer CNAME if there isn't one.
1444 	 * They rarely set Rname.
1445 	 * make time-to-wait proportional to estimated probability of an
1446 	 * RR of that type existing.
1447 	 */
1448 	if (qp->type >= nelem(likely))
1449 		pcntprob = 35;			/* unpopular query type */
1450 	else
1451 		pcntprob = likely[qp->type];
1452 	reqtm = (patient? 2*Maxreqtm: Maxreqtm);
1453 	/* time for a single outgoing udp query */
1454 	wait = weight(S2MS(reqtm)/3, pcntprob);
1455 	qp->req->aborttime = time(nil) + MS2S(3*wait); /* for all udp queries */
1456 
1457 	qp->udpfd = fd;
1458 	rv = queryns(qp, depth, ibuf, obuf, MS2S(wait), inns);
1459 	close(fd);
1460 	qp->udpfd = -1;
1461 
1462 	free(obuf);
1463 	free(ibuf);
1464 	return rv;
1465 }
1466 
1467 /* look up (qp->dp->name,qp->type) rr in dns, via *nsrp with results in *reqp */
1468 static int
1469 netquery(Query *qp, int depth)
1470 {
1471 	int lock, rv, triedin, inname, cnt;
1472 //	char buf[32];
1473 	RR *rp;
1474 	DN *dp;
1475 	Querylck *qlp;
1476 	static int whined;
1477 
1478 	rv = 0;				/* pessimism */
1479 	if(depth > 12)			/* in a recursive loop? */
1480 		return 0;
1481 
1482 	slave(qp->req);
1483 	/*
1484 	 * slave might have forked.  if so, the parent process longjmped to
1485 	 * req->mret; we're usually the child slave, but if there are too
1486 	 * many children already, we're still the same process.
1487 	 */
1488 
1489 	/*
1490 	 * don't lock before call to slave so only children can block.
1491 	 * just lock at top-level invocation.
1492 	 */
1493 	lock = depth <= 1 && qp->req->isslave;
1494 	dp = qp->dp;		/* ensure that it doesn't change underfoot */
1495 	qlp = nil;
1496 	if(lock) {
1497 //		procsetname("query lock wait: %s %s from %s", dp->name,
1498 //			rrname(qp->type, buf, sizeof buf), qp->req->from);
1499 		/*
1500 		 * don't make concurrent queries for this name.
1501 		 * dozens of processes blocking here probably indicates
1502 		 * an error in our dns data that causes us to not
1503 		 * recognise a zone (area) as one of our own, thus
1504 		 * causing us to query other nameservers.
1505 		 */
1506 		qlp = &dp->querylck[qtype2lck(qp->type)];
1507 		incref(qlp);
1508 		qlock(qlp);
1509 		cnt = qlp->Ref.ref;
1510 		qunlock(qlp);
1511 		if (cnt > 10) {
1512 			decref(qlp);
1513 			if (!whined) {
1514 				whined = 1;
1515 				dnslog("too many outstanding queries for %s; "
1516 					"dropping this one; "
1517 					"no further logging of drops",
1518 					dp->name);
1519 			}
1520 			return 0;
1521 		}
1522 	}
1523 	procsetname("netquery: %s", dp->name);
1524 
1525 	/* prepare server RR's for incremental lookup */
1526 	for(rp = qp->nsrp; rp; rp = rp->next)
1527 		rp->marker = 0;
1528 
1529 	triedin = 0;
1530 
1531 	/*
1532 	 * normal resolvers and servers will just use mntpt for all addresses,
1533 	 * even on the outside.  straddling servers will use mntpt (/net)
1534 	 * for inside addresses and /net.alt for outside addresses,
1535 	 * thus bypassing other inside nameservers.
1536 	 */
1537 	inname = insideaddr(dp->name);
1538 	if (!cfg.straddle || inname) {
1539 		rv = udpquery(qp, mntpt, depth, Hurry, (cfg.inside? Inns: Outns));
1540 		triedin = 1;
1541 	}
1542 
1543 	/*
1544 	 * if we're still looking, are inside, and have an outside domain,
1545 	 * try it on our outside interface, if any.
1546 	 */
1547 	if (rv == 0 && cfg.inside && !inname) {
1548 		if (triedin)
1549 			dnslog(
1550 	   "[%d] netquery: internal nameservers failed for %s; trying external",
1551 				getpid(), dp->name);
1552 
1553 		/* prepare server RR's for incremental lookup */
1554 		for(rp = qp->nsrp; rp; rp = rp->next)
1555 			rp->marker = 0;
1556 
1557 		rv = udpquery(qp, "/net.alt", depth, Patient, Outns);
1558 	}
1559 //	if (rv == 0)		/* could ask /net.alt/dns directly */
1560 //		askoutdns(dp, qp->type);
1561 
1562 	if(lock && qlp)
1563 		decref(qlp);
1564 	return rv;
1565 }
1566 
1567 int
1568 seerootns(void)
1569 {
1570 	int rv;
1571 	char root[] = "";
1572 	Request req;
1573 	Query *qp;
1574 
1575 	memset(&req, 0, sizeof req);
1576 	req.isslave = 1;
1577 	req.aborttime = now + Maxreqtm;
1578 	req.from = "internal";
1579 	qp = emalloc(sizeof *qp);
1580 	queryinit(qp, dnlookup(root, Cin, 1), Tns, &req);
1581 
1582 	qp->nsrp = dblookup(root, Cin, Tns, 0, 0);
1583 	rv = netquery(qp, 0);
1584 
1585 	rrfreelist(qp->nsrp);
1586 	querydestroy(qp);
1587 	free(qp);
1588 	return rv;
1589 }
1590