xref: /plan9-contrib/sys/src/cmd/ndb/dnresolve.c (revision 254fe3d33c382063ab759e3351c8169ee4bc264e)
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);
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, char *name, int class, int depth, int recurse)
300 {
301 	char *cp;
302 	DN *nsdp;
303 	RR *rp, *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 nil;
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;		/* accident prevention */
426 	USED(rp);
427 
428 	/*
429 	 * try the cache for a canonical name. if found punt
430 	 * since we'll find it during the canonical name search
431 	 * in dnresolve().
432 	 */
433 	if(type != Tcname){
434 		rp = rrlookup(dp, Tcname, NOneg);
435 		rrfreelist(rp);
436 		if(rp)
437 			return nil;
438 	}
439 
440 	/*
441 	 * if the domain name is within an area of ours,
442 	 * we should have found its data in memory by now.
443 	 */
444 	area = inmyarea(dp->name);
445 	if (area || strncmp(dp->name, "local#", 6) == 0) {
446 //		char buf[32];
447 
448 //		dnslog("%s %s: no data in area %s", dp->name,
449 //			rrname(type, buf, sizeof buf), area->soarr->owner->name);
450 		return nil;
451 	}
452 
453 	qp = emalloc(sizeof *qp);
454 	queryinit(qp, dp, type, req);
455 	rp = issuequery(qp, name, class, depth, recurse);
456 	querydestroy(qp);
457 	free(qp);
458 	if(rp)
459 		return rp;
460 
461 	/* settle for a non-authoritative answer */
462 	rp = rrlookup(dp, type, OKneg);
463 	if(rp)
464 		return rp;
465 
466 	/* noone answered.  try the database, we might have a chance. */
467 	return dblookup(name, class, type, 0, 0);
468 }
469 
470 /*
471  *  walk a domain name one element to the right.
472  *  return a pointer to that element.
473  *  in other words, return a pointer to the parent domain name.
474  */
475 char*
476 walkup(char *name)
477 {
478 	char *cp;
479 
480 	cp = strchr(name, '.');
481 	if(cp)
482 		return cp+1;
483 	else if(*name)
484 		return "";
485 	else
486 		return 0;
487 }
488 
489 /*
490  *  Get a udp port for sending requests and reading replies.  Put the port
491  *  into "headers" mode.
492  */
493 static char *hmsg = "headers";
494 
495 int
496 udpport(char *mtpt)
497 {
498 	int fd, ctl;
499 	char ds[64], adir[64];
500 
501 	/* get a udp port */
502 	snprint(ds, sizeof ds, "%s/udp!*!0", (mtpt? mtpt: "/net"));
503 	ctl = announce(ds, adir);
504 	if(ctl < 0){
505 		/* warning("can't get udp port"); */
506 		return -1;
507 	}
508 
509 	/* turn on header style interface */
510 	if(write(ctl, hmsg, strlen(hmsg)) , 0){
511 		close(ctl);
512 		warning(hmsg);
513 		return -1;
514 	}
515 
516 	/* grab the data file */
517 	snprint(ds, sizeof ds, "%s/data", adir);
518 	fd = open(ds, ORDWR);
519 	close(ctl);
520 	if(fd < 0)
521 		warning("can't open udp port %s: %r", ds);
522 	return fd;
523 }
524 
525 /* generate a DNS UDP query packet */
526 int
527 mkreq(DN *dp, int type, uchar *buf, int flags, ushort reqno)
528 {
529 	DNSmsg m;
530 	int len;
531 	Udphdr *uh = (Udphdr*)buf;
532 
533 	/* stuff port number into output buffer */
534 	memset(uh, 0, sizeof *uh);
535 	hnputs(uh->rport, 53);
536 
537 	/* make request and convert it to output format */
538 	memset(&m, 0, sizeof m);
539 	m.flags = flags;
540 	m.id = reqno;
541 	m.qd = rralloc(type);
542 	m.qd->owner = dp;
543 	m.qd->type = type;
544 	if (m.qd->type != type)
545 		dnslog("mkreq: bogus type %d", type);
546 	len = convDNS2M(&m, &buf[Udphdrsize], Maxudp);
547 	rrfree(m.qd);
548 	memset(&m, 0, sizeof m);		/* cause trouble */
549 	return len;
550 }
551 
552 void
553 freeanswers(DNSmsg *mp)
554 {
555 	rrfreelist(mp->qd);
556 	rrfreelist(mp->an);
557 	rrfreelist(mp->ns);
558 	rrfreelist(mp->ar);
559 	mp->qd = mp->an = mp->ns = mp->ar = nil;
560 }
561 
562 /* sets srcip */
563 static int
564 readnet(Query *qp, int medium, uchar *ibuf, ulong endtime, uchar **replyp,
565 	uchar *srcip)
566 {
567 	int len, fd;
568 	long ms;
569 	vlong startns = nsec();
570 	uchar *reply;
571 	uchar lenbuf[2];
572 
573 	/* timed read of reply */
574 	ms = S2MS(endtime) - NS2MS(startns);
575 	if (ms < 2000)
576 		ms = 2000;	/* give the remote ns a fighting chance */
577 	reply = ibuf;
578 	len = -1;			/* pessimism */
579 	memset(srcip, 0, IPaddrlen);
580 	if (medium == Udp)
581 		if (qp->udpfd <= 0)
582 			dnslog("readnet: qp->udpfd closed");
583 		else {
584 			alarm(ms);
585 			len = read(qp->udpfd, ibuf, Udphdrsize+Maxudpin);
586 			alarm(0);
587 			notestats(startns, len < 0, qp->type);
588 			if (len >= IPaddrlen)
589 				memmove(srcip, ibuf, IPaddrlen);
590 			if (len >= Udphdrsize) {
591 				len   -= Udphdrsize;
592 				reply += Udphdrsize;
593 			}
594 		}
595 	else {
596 		if (!qp->tcpset)
597 			dnslog("readnet: tcp params not set");
598 		alarm(ms);
599 		fd = qp->tcpfd;
600 		if (fd <= 0)
601 			dnslog("readnet: %s: tcp fd unset for dest %I",
602 				qp->dp->name, qp->tcpip);
603 		else if (readn(fd, lenbuf, 2) != 2) {
604 			dnslog("readnet: short read of tcp size from %I",
605 				qp->tcpip);
606 			/* probably a time-out */
607 			notestats(startns, 1, qp->type);
608 		} else {
609 			len = lenbuf[0]<<8 | lenbuf[1];
610 			if (readn(fd, ibuf, len) != len) {
611 				dnslog("readnet: short read of tcp data from %I",
612 					qp->tcpip);
613 				/* probably a time-out */
614 				notestats(startns, 1, qp->type);
615 				len = -1;
616 			}
617 		}
618 		alarm(0);
619 		memmove(srcip, qp->tcpip, IPaddrlen);
620 	}
621 	*replyp = reply;
622 	return len;
623 }
624 
625 /*
626  *  read replies to a request and remember the rrs in the answer(s).
627  *  ignore any of the wrong type.
628  *  wait at most until endtime.
629  */
630 static int
631 readreply(Query *qp, int medium, ushort req, uchar *ibuf, DNSmsg *mp,
632 	ulong endtime)
633 {
634 	int len, rv;
635 	char *err;
636 	char tbuf[32];
637 	uchar *reply;
638 	uchar srcip[IPaddrlen];
639 	RR *rp;
640 
641 	queryck(qp);
642 	rv = 0;
643 	memset(mp, 0, sizeof *mp);
644 	if (time(nil) >= endtime)
645 		return -1;		/* timed out before we started */
646 
647 	memset(srcip, 0, sizeof srcip);
648 	if (0)
649 		len = -1;
650 	for (; time(nil) < endtime &&
651 	    (len = readnet(qp, medium, ibuf, endtime, &reply, srcip)) >= 0;
652 	    freeanswers(mp)){
653 		/* convert into internal format  */
654 		memset(mp, 0, sizeof *mp);
655 		err = convM2DNS(reply, len, mp, nil);
656 		if (mp->flags & Ftrunc) {
657 			free(err);
658 			freeanswers(mp);
659 			/* notify our caller to retry the query via tcp. */
660 			return -1;
661 		} else if(err){
662 			dnslog("readreply: %s: input err, len %d: %s: %I",
663 				qp->dp->name, len, err, srcip);
664 			free(err);
665 			continue;
666 		}
667 		if(debug)
668 			logreply(qp->req->id, srcip, mp);
669 
670 		/* answering the right question? */
671 		if(mp->id != req)
672 			dnslog("%d: id %d instead of %d: %I", qp->req->id,
673 				mp->id, req, srcip);
674 		else if(mp->qd == 0)
675 			dnslog("%d: no question RR: %I", qp->req->id, srcip);
676 		else if(mp->qd->owner != qp->dp)
677 			dnslog("%d: owner %s instead of %s: %I", qp->req->id,
678 				mp->qd->owner->name, qp->dp->name, srcip);
679 		else if(mp->qd->type != qp->type)
680 			dnslog("%d: qp->type %d instead of %d: %I",
681 				qp->req->id, mp->qd->type, qp->type, srcip);
682 		else {
683 			/* remember what request this is in answer to */
684 			for(rp = mp->an; rp; rp = rp->next)
685 				rp->query = qp->type;
686 			return rv;
687 		}
688 	}
689 	if (time(nil) >= endtime) {
690 		;				/* query expired */
691 	} else if (0) {
692 		/* this happens routinely when a read times out */
693 		dnslog("readreply: %s type %s: ns %I read error or eof "
694 			"(returned %d): %r", qp->dp->name, rrname(qp->type,
695 			tbuf, sizeof tbuf), srcip, len);
696 		if (medium == Udp)
697 			for (rp = qp->nsrp; rp != nil; rp = rp->next)
698 				if (rp->type == Tns)
699 					dnslog("readreply: %s: query sent to "
700 						"ns %s", qp->dp->name,
701 						rp->host->name);
702 	}
703 	return -1;
704 }
705 
706 /*
707  *	return non-0 if first list includes second list
708  */
709 int
710 contains(RR *rp1, RR *rp2)
711 {
712 	RR *trp1, *trp2;
713 
714 	for(trp2 = rp2; trp2; trp2 = trp2->next){
715 		for(trp1 = rp1; trp1; trp1 = trp1->next)
716 			if(trp1->type == trp2->type)
717 			if(trp1->host == trp2->host)
718 			if(trp1->owner == trp2->owner)
719 				break;
720 		if(trp1 == nil)
721 			return 0;
722 	}
723 	return 1;
724 }
725 
726 
727 /*
728  *  return multicast version if any
729  */
730 int
731 ipisbm(uchar *ip)
732 {
733 	if(isv4(ip)){
734 		if (ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0 ||
735 		    ipcmp(ip, IPv4bcast) == 0)
736 			return 4;
737 	} else
738 		if(ip[0] == 0xff)
739 			return 6;
740 	return 0;
741 }
742 
743 /*
744  *  Get next server address
745  */
746 static int
747 serveraddrs(Query *qp, int nd, int depth)
748 {
749 	RR *rp, *arp, *trp;
750 	Dest *cur;
751 
752 	if(nd >= Maxdest)
753 		return 0;
754 
755 	/*
756 	 *  look for a server whose address we already know.
757 	 *  if we find one, mark it so we ignore this on
758 	 *  subsequent passes.
759 	 */
760 	arp = 0;
761 	for(rp = qp->nsrp; rp; rp = rp->next){
762 		assert(rp->magic == RRmagic);
763 		if(rp->marker)
764 			continue;
765 		arp = rrlookup(rp->host, Ta, NOneg);
766 		if(arp){
767 			rp->marker = 1;
768 			break;
769 		}
770 		arp = dblookup(rp->host->name, Cin, Ta, 0, 0);
771 		if(arp){
772 			rp->marker = 1;
773 			break;
774 		}
775 	}
776 
777 	/*
778 	 *  if the cache and database lookup didn't find any new
779 	 *  server addresses, try resolving one via the network.
780 	 *  Mark any we try to resolve so we don't try a second time.
781 	 */
782 	if(arp == 0)
783 		for(rp = qp->nsrp; rp; rp = rp->next){
784 			if(rp->marker)
785 				continue;
786 			rp->marker = 1;
787 
788 			/*
789 			 *  avoid loops looking up a server under itself
790 			 */
791 			if(subsume(rp->owner->name, rp->host->name))
792 				continue;
793 
794 			arp = dnresolve(rp->host->name, Cin, Ta, qp->req, 0,
795 				depth+1, Recurse, 1, 0);
796 			rrfreelist(rrremneg(&arp));
797 			if(arp)
798 				break;
799 		}
800 
801 	/* use any addresses that we found */
802 	for(trp = arp; trp && nd < Maxdest; trp = trp->next){
803 		cur = &qp->dest[nd];
804 		parseip(cur->a, trp->ip->name);
805 		/*
806 		 * straddling servers can reject all nameservers if they are all
807 		 * inside, so be sure to list at least one outside ns at
808 		 * the end of the ns list in /lib/ndb for `dom='.
809 		 */
810 		if (ipisbm(cur->a) ||
811 		    cfg.straddle && !insideaddr(qp->dp->name) && insidens(cur->a))
812 			continue;
813 		cur->nx = 0;
814 		cur->s = trp->owner;
815 		cur->code = Rtimeout;
816 		nd++;
817 	}
818 	rrfreelist(arp);
819 	return nd;
820 }
821 
822 /*
823  *  cache negative responses
824  */
825 static void
826 cacheneg(DN *dp, int type, int rcode, RR *soarr)
827 {
828 	RR *rp;
829 	DN *soaowner;
830 	ulong ttl;
831 
832 	stats.negcached++;
833 
834 	/* no cache time specified, don't make anything up */
835 	if(soarr != nil){
836 		if(soarr->next != nil){
837 			rrfreelist(soarr->next);
838 			soarr->next = nil;
839 		}
840 		soaowner = soarr->owner;
841 	} else
842 		soaowner = nil;
843 
844 	/* the attach can cause soarr to be freed so mine it now */
845 	if(soarr != nil && soarr->soa != nil)
846 		ttl = soarr->soa->minttl+now;
847 	else
848 		ttl = 5*Min;
849 
850 	/* add soa and negative RR to the database */
851 	rrattach(soarr, Authoritative);
852 
853 	rp = rralloc(type);
854 	rp->owner = dp;
855 	rp->negative = 1;
856 	rp->negsoaowner = soaowner;
857 	rp->negrcode = rcode;
858 	rp->ttl = ttl;
859 	rrattach(rp, Authoritative);
860 }
861 
862 static int
863 setdestoutns(Dest *p, int n)
864 {
865 	uchar *outns = outsidens(n);
866 
867 	destck(p);
868 	destinit(p);
869 	if (outns == nil) {
870 		if (n == 0)
871 			dnslog("[%d] no outside-ns in ndb", getpid());
872 		return -1;
873 	}
874 	memmove(p->a, outns, sizeof p->a);
875 	p->s = dnlookup("outside-ns-ips", Cin, 1);
876 	return 0;
877 }
878 
879 /*
880  * issue query via UDP or TCP as appropriate.
881  * for TCP, returns with qp->tcpip set from udppkt header.
882  */
883 static int
884 mydnsquery(Query *qp, int medium, uchar *udppkt, int len)
885 {
886 	int rv = -1, nfd;
887 	char *domain;
888 	char conndir[40];
889 	uchar belen[2];
890 	NetConnInfo *nci;
891 
892 	queryck(qp);
893 	domain = smprint("%I", udppkt);
894 	if (myaddr(domain)) {
895 		dnslog("mydnsquery: trying to send to myself (%s); bzzzt",
896 			domain);
897 		free(domain);
898 		return rv;
899 	}
900 
901 	switch (medium) {
902 	case Udp:
903 		free(domain);
904 		nfd = dup(qp->udpfd, -1);
905 		if (nfd < 0) {
906 			warning("mydnsquery: qp->udpfd %d: %r", qp->udpfd);
907 			close(qp->udpfd);	/* ensure it's closed */
908 			qp->udpfd = -1;		/* poison it */
909 			return rv;
910 		}
911 		close(nfd);
912 
913 		if (qp->udpfd <= 0)
914 			dnslog("mydnsquery: qp->udpfd %d closed", qp->udpfd);
915 		else {
916 			if (write(qp->udpfd, udppkt, len+Udphdrsize) !=
917 			    len+Udphdrsize)
918 				warning("sending udp msg: %r");
919 			else {
920 				stats.qsent++;
921 				rv = 0;
922 			}
923 		}
924 		break;
925 	case Tcp:
926 		/* send via TCP & keep fd around for reply */
927 		alarm(10*1000);
928 		qp->tcpfd = rv = dial(netmkaddr(domain, "tcp", "dns"), nil,
929 			conndir, &qp->tcpctlfd);
930 		alarm(0);
931 		if (qp->tcpfd < 0) {
932 			dnslog("can't dial tcp!%s!dns: %r", domain);
933 			free(domain);
934 			break;
935 		}
936 		free(domain);
937 		nci = getnetconninfo(conndir, qp->tcpfd);
938 		if (nci) {
939 			parseip(qp->tcpip, nci->rsys);
940 			freenetconninfo(nci);
941 		} else
942 			dnslog("mydnsquery: getnetconninfo failed");
943 		qp->tcpset = 1;
944 
945 		belen[0] = len >> 8;
946 		belen[1] = len;
947 		if (write(qp->tcpfd, belen, 2) != 2 ||
948 		    write(qp->tcpfd, udppkt + Udphdrsize, len) != len)
949 			warning("sending tcp msg: %r");
950 		break;
951 	default:
952 		sysfatal("mydnsquery: bad medium");
953 	}
954 	return rv;
955 }
956 
957 /*
958  * send query to all UDP destinations or one TCP destination,
959  * taken from obuf (udp packet) header
960  */
961 static int
962 xmitquery(Query *qp, int medium, int depth, uchar *obuf, int inns, int len)
963 {
964 	int j, n;
965 	char buf[32];
966 	Dest *p;
967 
968 	queryck(qp);
969 	if(time(nil) >= qp->req->aborttime)
970 		return -1;
971 
972 	/*
973 	 * get a nameserver address if we need one.
974 	 * serveraddrs populates qp->dest.
975 	 */
976 	p = qp->dest;
977 	destck(p);
978 	if (qp->ndest < 0 || qp->ndest > Maxdest)
979 		dnslog("qp->ndest %d out of range", qp->ndest);
980 	if (qp->ndest > qp->curdest - p)
981 		qp->curdest = &qp->dest[serveraddrs(qp, qp->curdest - p, depth)];
982 	destck(qp->curdest);
983 
984 	/* no servers, punt */
985 	if (qp->curdest == qp->dest)
986 		if (cfg.straddle && cfg.inside) {
987 			/* get ips of "outside-ns-ips" */
988 			p = qp->curdest = qp->dest;
989 			for(n = 0; n < Maxdest; n++, qp->curdest++)
990 				if (setdestoutns(qp->curdest, n) < 0)
991 					break;
992 		} else {
993 			/* it's probably just a bogus domain, don't log it */
994 			// dnslog("xmitquery: %s: no nameservers", qp->dp->name);
995 			return -1;
996 		}
997 
998 	/* send to first 'qp->ndest' destinations */
999 	j = 0;
1000 	if (medium == Tcp) {
1001 		j++;
1002 		queryck(qp);
1003 		assert(qp->dp);
1004 		procsetname("tcp %sside query for %s %s", (inns? "in": "out"),
1005 			qp->dp->name, rrname(qp->type, buf, sizeof buf));
1006 		mydnsquery(qp, medium, obuf, len); /* sets qp->tcpip from obuf */
1007 		if(debug)
1008 			logsend(qp->req->id, depth, qp->tcpip, "", qp->dp->name,
1009 				qp->type);
1010 	} else
1011 		for(; p < &qp->dest[qp->ndest] && p < qp->curdest; p++){
1012 			/* skip destinations we've finished with */
1013 			if(p->nx >= Maxtrans)
1014 				continue;
1015 
1016 			j++;
1017 
1018 			/* exponential backoff of requests */
1019 			if((1<<p->nx) > qp->ndest)
1020 				continue;
1021 
1022 			procsetname("udp %sside query to %I/%s %s %s",
1023 				(inns? "in": "out"), p->a, p->s->name,
1024 				qp->dp->name, rrname(qp->type, buf, sizeof buf));
1025 			if(debug)
1026 				logsend(qp->req->id, depth, p->a, p->s->name,
1027 					qp->dp->name, qp->type);
1028 
1029 			/* fill in UDP destination addr & send it */
1030 			memmove(obuf, p->a, sizeof p->a);
1031 			mydnsquery(qp, medium, obuf, len);
1032 			p->nx++;
1033 		}
1034 	if(j == 0) {
1035 		// dnslog("xmitquery: %s: no destinations left", qp->dp->name);
1036 		return -1;
1037 	}
1038 	return 0;
1039 }
1040 
1041 static int lckindex[Maxlcks] = {
1042 	0,			/* all others map here */
1043 	Ta,
1044 	Tns,
1045 	Tcname,
1046 	Tsoa,
1047 	Tptr,
1048 	Tmx,
1049 	Ttxt,
1050 	Taaaa,
1051 };
1052 
1053 static int
1054 qtype2lck(int qtype)		/* map query type to querylck index */
1055 {
1056 	int i;
1057 
1058 	for (i = 1; i < nelem(lckindex); i++)
1059 		if (lckindex[i] == qtype)
1060 			return i;
1061 	return 0;
1062 }
1063 
1064 /* is mp a cachable negative response (with Rname set)? */
1065 static int
1066 isnegrname(DNSmsg *mp)
1067 {
1068 	/* TODO: could add || cfg.justforw to RHS of && */
1069 	return mp->an == nil && (mp->flags & Rmask) == Rname;
1070 }
1071 
1072 static int
1073 procansw(Query *qp, DNSmsg *mp, uchar *srcip, int depth, Dest *p)
1074 {
1075 	int rv;
1076 //	int lcktype;
1077 	char buf[32];
1078 	DN *ndp;
1079 	Query *nqp;
1080 	RR *tp, *soarr;
1081 
1082 	if (mp->an == nil)
1083 		stats.negans++;
1084 
1085 	/* ignore any error replies */
1086 	if((mp->flags & Rmask) == Rserver){
1087 		stats.negserver++;
1088 		freeanswers(mp);
1089 		if(p != qp->curdest)
1090 			p->code = Rserver;
1091 		return -1;
1092 	}
1093 
1094 	/* ignore any bad delegations */
1095 	if(mp->ns && baddelegation(mp->ns, qp->nsrp, srcip)){
1096 		stats.negbaddeleg++;
1097 		if(mp->an == nil){
1098 			stats.negbdnoans++;
1099 			freeanswers(mp);
1100 			if(p != qp->curdest)
1101 				p->code = Rserver;
1102 			return -1;
1103 		}
1104 		rrfreelist(mp->ns);
1105 		mp->ns = nil;
1106 	}
1107 
1108 	/* remove any soa's from the authority section */
1109 	soarr = rrremtype(&mp->ns, Tsoa);
1110 
1111 	/* incorporate answers */
1112 	unique(mp->an);
1113 	unique(mp->ns);
1114 	unique(mp->ar);
1115 	if(mp->an)
1116 		rrattach(mp->an, (mp->flags & Fauth) != 0);
1117 	if(mp->ar)
1118 		rrattach(mp->ar, Notauthoritative);
1119 	if(mp->ns && !cfg.justforw){
1120 		ndp = mp->ns->owner;
1121 		rrattach(mp->ns, Notauthoritative);
1122 	} else {
1123 		ndp = nil;
1124 		rrfreelist(mp->ns);
1125 		mp->ns = nil;
1126 	}
1127 
1128 	/* free the question */
1129 	if(mp->qd) {
1130 		rrfreelist(mp->qd);
1131 		mp->qd = nil;
1132 	}
1133 
1134 	/*
1135 	 *  Any reply from an authoritative server,
1136 	 *  or a positive reply terminates the search.
1137 	 *  A negative response now also terminates the search.
1138 	 */
1139 	if(mp->an != nil || (mp->flags & Fauth)){
1140 		if(isnegrname(mp))
1141 			qp->dp->respcode = Rname;
1142 		else
1143 			qp->dp->respcode = 0;
1144 
1145 		/*
1146 		 *  cache any negative responses, free soarr.
1147 		 *  negative responses need not be authoritative:
1148 		 *  they can legitimately come from a cache.
1149 		 */
1150 		if( /* (mp->flags & Fauth) && */ mp->an == nil)
1151 			cacheneg(qp->dp, qp->type, (mp->flags & Rmask), soarr);
1152 		else
1153 			rrfreelist(soarr);
1154 		return 1;
1155 	} else if (isnegrname(mp)) {
1156 		qp->dp->respcode = Rname;
1157 		/*
1158 		 *  cache negative response.
1159 		 *  negative responses need not be authoritative:
1160 		 *  they can legitimately come from a cache.
1161 		 */
1162 		cacheneg(qp->dp, qp->type, (mp->flags & Rmask), soarr);
1163 		return 1;
1164 	}
1165 	stats.negnorname++;
1166 	rrfreelist(soarr);
1167 
1168 	/*
1169 	 *  if we've been given better name servers, recurse.
1170 	 *  if we're a pure resolver, don't recurse, we have
1171 	 *  to forward to a fixed set of named servers.
1172 	 */
1173 	if(!mp->ns || cfg.resolver && cfg.justforw)
1174 		return 0;
1175 	tp = rrlookup(ndp, Tns, NOneg);
1176 	if(contains(qp->nsrp, tp)){
1177 		rrfreelist(tp);
1178 		return 0;
1179 	}
1180 	procsetname("recursive query for %s %s", qp->dp->name,
1181 		rrname(qp->type, buf, sizeof buf));
1182 	/*
1183 	 *  we're called from udpquery, called from
1184 	 *  netquery, which current holds qp->dp->querylck,
1185 	 *  so release it now and acquire it upon return.
1186 	 */
1187 //	lcktype = qtype2lck(qp->type);
1188 //	qunlock(&qp->dp->querylck[lcktype]);
1189 
1190 	nqp = emalloc(sizeof *nqp);
1191 	queryinit(nqp, qp->dp, qp->type, qp->req);
1192 	nqp->nsrp = tp;
1193 	rv = netquery(nqp, depth+1);
1194 
1195 //	qlock(&qp->dp->querylck[lcktype]);
1196 	rrfreelist(nqp->nsrp);
1197 	querydestroy(nqp);
1198 	free(nqp);
1199 	return rv;
1200 }
1201 
1202 /*
1203  * send a query via tcp to a single address (from ibuf's udp header)
1204  * and read the answer(s) into mp->an.
1205  */
1206 static int
1207 tcpquery(Query *qp, DNSmsg *mp, int depth, uchar *ibuf, uchar *obuf, int len,
1208 	int waitsecs, int inns, ushort req)
1209 {
1210 	int rv = 0;
1211 	ulong endtime;
1212 
1213 	endtime = time(nil) + waitsecs;
1214 	if(endtime > qp->req->aborttime)
1215 		endtime = qp->req->aborttime;
1216 
1217 	if (0)
1218 		dnslog("%s: udp reply truncated; retrying query via tcp to %I",
1219 			qp->dp->name, qp->tcpip);
1220 
1221 	qlock(&qp->tcplock);
1222 	memmove(obuf, ibuf, IPaddrlen);		/* send back to respondent */
1223 	/* sets qp->tcpip from obuf's udp header */
1224 	if (xmitquery(qp, Tcp, depth, obuf, inns, len) < 0 ||
1225 	    readreply(qp, Tcp, req, ibuf, mp, endtime) < 0)
1226 		rv = -1;
1227 	if (qp->tcpfd > 0) {
1228 		hangup(qp->tcpctlfd);
1229 		close(qp->tcpctlfd);
1230 		close(qp->tcpfd);
1231 	}
1232 	qp->tcpfd = qp->tcpctlfd = -1;
1233 	qunlock(&qp->tcplock);
1234 	return rv;
1235 }
1236 
1237 /*
1238  *  query name servers.  If the name server returns a pointer to another
1239  *  name server, recurse.
1240  */
1241 static int
1242 queryns(Query *qp, int depth, uchar *ibuf, uchar *obuf, int waitsecs, int inns)
1243 {
1244 	int ndest, len, replywaits, rv;
1245 	ushort req;
1246 	ulong endtime;
1247 	char buf[12];
1248 	uchar srcip[IPaddrlen];
1249 	Dest *p, *np, *dest;
1250 //	Dest dest[Maxdest];
1251 
1252 	/* pack request into a udp message */
1253 	req = rand();
1254 	len = mkreq(qp->dp, qp->type, obuf, Frecurse|Oquery, req);
1255 
1256 	/* no server addresses yet */
1257 	queryck(qp);
1258 	dest = emalloc(Maxdest * sizeof *dest);	/* dest can't be on stack */
1259 	for (p = dest; p < dest + Maxdest; p++)
1260 		destinit(p);
1261 	/* this dest array is local to this call of queryns() */
1262 	free(qp->dest);
1263 	qp->curdest = qp->dest = dest;
1264 
1265 	/*
1266 	 *  transmit udp requests and wait for answers.
1267 	 *  at most Maxtrans attempts to each address.
1268 	 *  each cycle send one more message than the previous.
1269 	 *  retry a query via tcp if its response is truncated.
1270 	 */
1271 	for(ndest = 1; ndest < Maxdest; ndest++){
1272 		qp->ndest = ndest;
1273 		qp->tcpset = 0;
1274 		if (xmitquery(qp, Udp, depth, obuf, inns, len) < 0)
1275 			break;
1276 
1277 		endtime = time(nil) + waitsecs;
1278 		if(endtime > qp->req->aborttime)
1279 			endtime = qp->req->aborttime;
1280 
1281 		for(replywaits = 0; replywaits < ndest; replywaits++){
1282 			DNSmsg m;
1283 
1284 			procsetname("reading %sside reply from %I: %s %s from %s",
1285 				(inns? "in": "out"), obuf, qp->dp->name,
1286 				rrname(qp->type, buf, sizeof buf), qp->req->from);
1287 
1288 			/* read udp answer into m */
1289 			if (readreply(qp, Udp, req, ibuf, &m, endtime) >= 0)
1290 				memmove(srcip, ibuf, IPaddrlen);
1291 			else if (!(m.flags & Ftrunc)) {
1292 				freeanswers(&m);
1293 				break;		/* timed out on this dest */
1294 			} else {
1295 				/* whoops, it was truncated! ask again via tcp */
1296 				freeanswers(&m);
1297 				rv = tcpquery(qp, &m, depth, ibuf, obuf, len,
1298 					waitsecs, inns, req);  /* answer in m */
1299 				if (rv < 0) {
1300 					freeanswers(&m);
1301 					break;		/* failed via tcp too */
1302 				}
1303 				memmove(srcip, qp->tcpip, IPaddrlen);
1304 			}
1305 
1306 			/* find responder */
1307 			// dnslog("queryns got reply from %I", srcip);
1308 			for(p = qp->dest; p < qp->curdest; p++)
1309 				if(memcmp(p->a, srcip, sizeof p->a) == 0)
1310 					break;
1311 
1312 			/* remove all addrs of responding server from list */
1313 			for(np = qp->dest; np < qp->curdest; np++)
1314 				if(np->s == p->s)
1315 					p->nx = Maxtrans;
1316 
1317 			/* free or incorporate RRs in m */
1318 			rv = procansw(qp, &m, srcip, depth, p);
1319 			if (rv > 0) {
1320 				free(qp->dest);
1321 				qp->dest = qp->curdest = nil; /* prevent accidents */
1322 				return rv;
1323 			}
1324 		}
1325 	}
1326 
1327 	/* if all servers returned failure, propagate it */
1328 	qp->dp->respcode = Rserver;
1329 	for(p = dest; p < qp->curdest; p++) {
1330 		destck(p);
1331 		if(p->code != Rserver)
1332 			qp->dp->respcode = 0;
1333 		p->magic = 0;			/* prevent accidents */
1334 	}
1335 
1336 //	if (qp->dp->respcode)
1337 //		dnslog("queryns setting Rserver for %s", qp->dp->name);
1338 
1339 	free(qp->dest);
1340 	qp->dest = qp->curdest = nil;		/* prevent accidents */
1341 	return 0;
1342 }
1343 
1344 /*
1345  *  run a command with a supplied fd as standard input
1346  */
1347 char *
1348 system(int fd, char *cmd)
1349 {
1350 	int pid, p, i;
1351 	static Waitmsg msg;
1352 
1353 	if((pid = fork()) == -1)
1354 		sysfatal("fork failed: %r");
1355 	else if(pid == 0){
1356 		dup(fd, 0);
1357 		close(fd);
1358 		for (i = 3; i < 200; i++)
1359 			close(i);		/* don't leak fds */
1360 		execl("/bin/rc", "rc", "-c", cmd, nil);
1361 		sysfatal("exec rc: %r");
1362 	}
1363 	for(p = waitpid(); p >= 0; p = waitpid())
1364 		if(p == pid)
1365 			return msg.msg;
1366 	return "lost child";
1367 }
1368 
1369 /* compute wait, weighted by probability of success, with minimum */
1370 static ulong
1371 weight(ulong ms, unsigned pcntprob)
1372 {
1373 	ulong wait;
1374 
1375 	wait = (ms * pcntprob) / 100;
1376 	if (wait < 1500)
1377 		wait = 1500;
1378 	return wait;
1379 }
1380 
1381 /*
1382  * in principle we could use a single descriptor for a udp port
1383  * to send all queries and receive all the answers to them,
1384  * but we'd have to sort out the answers by dns-query id.
1385  */
1386 static int
1387 udpquery(Query *qp, char *mntpt, int depth, int patient, int inns)
1388 {
1389 	int fd, rv;
1390 	long now;
1391 	ulong pcntprob, wait, reqtm;
1392 	char *msg;
1393 	uchar *obuf, *ibuf;
1394 	static QLock mntlck;
1395 	static ulong lastmount;
1396 
1397 	/* use alloced buffers rather than ones from the stack */
1398 	// ibuf = emalloc(Maxudpin+Udphdrsize);
1399 	ibuf = emalloc(64*1024);		/* max. tcp reply size */
1400 	obuf = emalloc(Maxudp+Udphdrsize);
1401 
1402 	fd = udpport(mntpt);
1403 	while (fd < 0 && cfg.straddle && strcmp(mntpt, "/net.alt") == 0) {
1404 		/* HACK: remount /net.alt */
1405 		now = time(nil);
1406 		if (now < lastmount + Remntretry)
1407 			sleep((lastmount + Remntretry - now)*1000);
1408 		qlock(&mntlck);
1409 		fd = udpport(mntpt);	/* try again under lock */
1410 		if (fd < 0) {
1411 			dnslog("[%d] remounting /net.alt", getpid());
1412 			unmount(nil, "/net.alt");
1413 
1414 			msg = system(open("/dev/null", ORDWR), "outside");
1415 
1416 			lastmount = time(nil);
1417 			if (msg && *msg) {
1418 				dnslog("[%d] can't remount /net.alt: %s",
1419 					getpid(), msg);
1420 				sleep(10*1000);		/* don't spin wildly */
1421 			} else
1422 				fd = udpport(mntpt);
1423 		}
1424 		qunlock(&mntlck);
1425 	}
1426 	if (fd < 0) {
1427 		dnslog("can't get udpport for %s query of name %s: %r",
1428 			mntpt, qp->dp->name);
1429 		sysfatal("out of udp conversations");	/* we're buggered */
1430 	}
1431 
1432 	/*
1433 	 * Our QIP servers are busted, don't answer AAAA and
1434 	 * take forever to answer CNAME if there isn't one.
1435 	 * They rarely set Rname.
1436 	 * make time-to-wait proportional to estimated probability of an
1437 	 * RR of that type existing.
1438 	 */
1439 	if (qp->type >= nelem(likely))
1440 		pcntprob = 35;			/* unpopular query type */
1441 	else
1442 		pcntprob = likely[qp->type];
1443 	reqtm = (patient? 2*Maxreqtm: Maxreqtm);
1444 	/* time for a single outgoing udp query */
1445 	wait = weight(S2MS(reqtm)/3, pcntprob);
1446 	qp->req->aborttime = time(nil) + MS2S(3*wait); /* for all udp queries */
1447 
1448 	qp->udpfd = fd;
1449 	rv = queryns(qp, depth, ibuf, obuf, MS2S(wait), inns);
1450 	close(fd);
1451 	qp->udpfd = -1;
1452 
1453 	free(obuf);
1454 	free(ibuf);
1455 	return rv;
1456 }
1457 
1458 /* look up (qp->dp->name,qp->type) rr in dns, via *nsrp with results in *reqp */
1459 static int
1460 netquery(Query *qp, int depth)
1461 {
1462 	int lock, rv, triedin, inname, cnt;
1463 //	char buf[32];
1464 	RR *rp;
1465 	DN *dp;
1466 	Querylck *qlp;
1467 	static int whined;
1468 
1469 	rv = 0;				/* pessimism */
1470 	if(depth > 12)			/* in a recursive loop? */
1471 		return 0;
1472 
1473 	slave(qp->req);
1474 	/*
1475 	 * slave might have forked.  if so, the parent process longjmped to
1476 	 * req->mret; we're usually the child slave, but if there are too
1477 	 * many children already, we're still the same process.
1478 	 */
1479 
1480 	/*
1481 	 * don't lock before call to slave so only children can block.
1482 	 * just lock at top-level invocation.
1483 	 */
1484 	lock = depth <= 1 && qp->req->isslave;
1485 	dp = qp->dp;		/* ensure that it doesn't change underfoot */
1486 	qlp = nil;
1487 	if(lock) {
1488 //		procsetname("query lock wait: %s %s from %s", dp->name,
1489 //			rrname(qp->type, buf, sizeof buf), qp->req->from);
1490 		/*
1491 		 * don't make concurrent queries for this name.
1492 		 * dozens of processes blocking here probably indicates
1493 		 * an error in our dns data that causes us to not
1494 		 * recognise a zone (area) as one of our own, thus
1495 		 * causing us to query other nameservers.
1496 		 */
1497 		qlp = &dp->querylck[qtype2lck(qp->type)];
1498 		incref(qlp);
1499 		qlock(qlp);
1500 		cnt = qlp->Ref.ref;
1501 		qunlock(qlp);
1502 		if (cnt > 10) {
1503 			decref(qlp);
1504 			if (!whined) {
1505 				whined = 1;
1506 				dnslog("too many outstanding queries for %s; "
1507 					"dropping this one; "
1508 					"no further logging of drops",
1509 					dp->name);
1510 			}
1511 			return 0;
1512 		}
1513 	}
1514 	procsetname("netquery: %s", dp->name);
1515 
1516 	/* prepare server RR's for incremental lookup */
1517 	for(rp = qp->nsrp; rp; rp = rp->next)
1518 		rp->marker = 0;
1519 
1520 	triedin = 0;
1521 
1522 	/*
1523 	 * normal resolvers and servers will just use mntpt for all addresses,
1524 	 * even on the outside.  straddling servers will use mntpt (/net)
1525 	 * for inside addresses and /net.alt for outside addresses,
1526 	 * thus bypassing other inside nameservers.
1527 	 */
1528 	inname = insideaddr(dp->name);
1529 	if (!cfg.straddle || inname) {
1530 		rv = udpquery(qp, mntpt, depth, Hurry, (cfg.inside? Inns: Outns));
1531 		triedin = 1;
1532 	}
1533 
1534 	/*
1535 	 * if we're still looking, are inside, and have an outside domain,
1536 	 * try it on our outside interface, if any.
1537 	 */
1538 	if (rv == 0 && cfg.inside && !inname) {
1539 		if (triedin)
1540 			dnslog(
1541 	   "[%d] netquery: internal nameservers failed for %s; trying external",
1542 				getpid(), dp->name);
1543 
1544 		/* prepare server RR's for incremental lookup */
1545 		for(rp = qp->nsrp; rp; rp = rp->next)
1546 			rp->marker = 0;
1547 
1548 		rv = udpquery(qp, "/net.alt", depth, Patient, Outns);
1549 	}
1550 //	if (rv == 0)		/* could ask /net.alt/dns directly */
1551 //		askoutdns(dp, qp->type);
1552 
1553 	if(lock && qlp)
1554 		decref(qlp);
1555 	return rv;
1556 }
1557 
1558 int
1559 seerootns(void)
1560 {
1561 	int rv;
1562 	char root[] = "";
1563 	Request req;
1564 	Query *qp;
1565 
1566 	memset(&req, 0, sizeof req);
1567 	req.isslave = 1;
1568 	req.aborttime = now + Maxreqtm;
1569 	req.from = "internal";
1570 	qp = emalloc(sizeof *qp);
1571 	queryinit(qp, dnlookup(root, Cin, 1), Tns, &req);
1572 
1573 	qp->nsrp = dblookup(root, Cin, Tns, 0, 0);
1574 	rv = netquery(qp, 0);
1575 
1576 	rrfreelist(qp->nsrp);
1577 	querydestroy(qp);
1578 	free(qp);
1579 	return rv;
1580 }
1581