xref: /plan9/sys/src/cmd/ndb/dnresolve.c (revision 7ad596ff0f87e7b1bf4052ceeca38bcec4a5e832)
1 #include <u.h>
2 #include <libc.h>
3 #include <ip.h>
4 #include <bio.h>
5 #include <ndb.h>
6 #include "dns.h"
7 
8 enum
9 {
10 	Maxdest=	24,	/* maximum destinations for a request message */
11 	Maxtrans=	3,	/* maximum transmissions to a server */
12 };
13 
14 int inside, straddle, serve;
15 
16 static int	netquery(DN*, int, RR*, Request*, int);
17 static RR*	dnresolve1(char*, int, int, Request*, int, int);
18 
19 char *LOG = "dns";
20 
21 /*
22  * reading /proc/pid/args yields either "name" or "name [display args]",
23  * so return only display args, if any.
24  */
25 static char *
26 procgetname(void)
27 {
28 	int fd, n;
29 	char *lp, *rp;
30 	char buf[256];
31 
32 	snprint(buf, sizeof buf, "#p/%d/args", getpid());
33 	if((fd = open(buf, OREAD)) < 0)
34 		return strdup("");
35 	*buf = '\0';
36 	n = read(fd, buf, sizeof buf-1);
37 	close(fd);
38 	if (n >= 0)
39 		buf[n] = '\0';
40 	if ((lp = strchr(buf, '[')) == nil ||
41 	    (rp = strrchr(buf, ']')) == nil)
42 		return strdup("");
43 	*rp = '\0';
44 	return strdup(lp+1);
45 }
46 
47 /*
48  *  lookup 'type' info for domain name 'name'.  If it doesn't exist, try
49  *  looking it up as a canonical name.
50  */
51 RR*
52 dnresolve(char *name, int class, int type, Request *req, RR **cn, int depth,
53 	int recurse, int rooted, int *status)
54 {
55 	RR *rp, *nrp, *drp;
56 	DN *dp;
57 	int loops;
58 	char *procname;
59 	char nname[Domlen];
60 
61 	if(status)
62 		*status = 0;
63 
64 	procname = procgetname();
65 	/*
66 	 *  hack for systems that don't have resolve search
67 	 *  lists.  Just look up the simple name in the database.
68 	 */
69 	if(!rooted && strchr(name, '.') == 0){
70 		rp = nil;
71 		drp = domainlist(class);
72 		for(nrp = drp; nrp != nil; nrp = nrp->next){
73 			snprint(nname, sizeof nname, "%s.%s", name,
74 				nrp->ptr->name);
75 			rp = dnresolve(nname, class, type, req, cn, depth,
76 				recurse, rooted, status);
77 			rrfreelist(rrremneg(&rp));
78 			if(rp != nil)
79 				break;
80 		}
81 		if(drp != nil)
82 			rrfree(drp);
83 		procsetname(procname);
84 		free(procname);
85 		return rp;
86 	}
87 
88 	/*
89 	 *  try the name directly
90 	 */
91 	rp = dnresolve1(name, class, type, req, depth, recurse);
92 	if(rp) {
93 		procsetname(procname);
94 		free(procname);
95 		return randomize(rp);
96 	}
97 
98 	/* try it as a canonical name if we weren't told the name didn't exist */
99 	dp = dnlookup(name, class, 0);
100 	if(type != Tptr && dp->respcode != Rname)
101 		for(loops = 0; rp == nil && loops < 32; loops++){
102 			rp = dnresolve1(name, class, Tcname, req, depth, recurse);
103 			if(rp == nil)
104 				break;
105 
106 			if(rp->negative){
107 				rrfreelist(rp);
108 				rp = nil;
109 				break;
110 			}
111 
112 			name = rp->host->name;
113 			if(cn)
114 				rrcat(cn, rp);
115 			else
116 				rrfreelist(rp);
117 
118 			rp = dnresolve1(name, class, type, req, depth, recurse);
119 		}
120 
121 	/* distinction between not found and not good */
122 	if(rp == nil && status != nil && dp->respcode != 0)
123 		*status = dp->respcode;
124 
125 	procsetname(procname);
126 	free(procname);
127 	return randomize(rp);
128 }
129 
130 static RR*
131 dnresolve1(char *name, int class, int type, Request *req, int depth,
132 	int recurse)
133 {
134 	DN *dp, *nsdp;
135 	RR *rp, *nsrp, *dbnsrp;
136 	char *cp;
137 
138 	if(debug)
139 		syslog(0, LOG, "[%d] dnresolve1 %s %d %d",
140 			getpid(), name, type, class);
141 
142 	/* only class Cin implemented so far */
143 	if(class != Cin)
144 		return nil;
145 
146 	dp = dnlookup(name, class, 1);
147 
148 	/*
149 	 *  Try the cache first
150 	 */
151 	rp = rrlookup(dp, type, OKneg);
152 	if(rp)
153 		if(rp->db){
154 			/* unauthenticated db entries are hints */
155 			if(rp->auth)
156 				return rp;
157 		} else
158 			/* cached entry must still be valid */
159 			if(rp->ttl > now)
160 				/* but Tall entries are special */
161 				if(type != Tall || rp->query == Tall)
162 					return rp;
163 
164 	rrfreelist(rp);
165 
166 	/*
167 	 * try the cache for a canonical name. if found punt
168 	 * since we'll find it during the canonical name search
169 	 * in dnresolve().
170 	 */
171 	if(type != Tcname){
172 		rp = rrlookup(dp, Tcname, NOneg);
173 		rrfreelist(rp);
174 		if(rp)
175 			return nil;
176 	}
177 
178 	/*
179 	 *  if we're running as just a resolver, go to our
180 	 *  designated name servers
181 	 */
182 	if(resolver){
183 		nsrp = randomize(getdnsservers(class));
184 		if(nsrp != nil) {
185 			if(netquery(dp, type, nsrp, req, depth+1)){
186 				rrfreelist(nsrp);
187 				return rrlookup(dp, type, OKneg);
188 			}
189 			rrfreelist(nsrp);
190 		}
191 	}
192 
193 	/*
194  	 *  walk up the domain name looking for
195 	 *  a name server for the domain.
196 	 */
197 	for(cp = name; cp; cp = walkup(cp)){
198 		/*
199 		 *  if this is a local (served by us) domain,
200 		 *  return answer
201 		 */
202 		dbnsrp = randomize(dblookup(cp, class, Tns, 0, 0));
203 		if(dbnsrp && dbnsrp->local){
204 			rp = dblookup(name, class, type, 1, dbnsrp->ttl);
205 			rrfreelist(dbnsrp);
206 			return rp;
207 		}
208 
209 		/*
210 		 *  if recursion isn't set, just accept local
211 		 *  entries
212 		 */
213 		if(recurse == Dontrecurse){
214 			if(dbnsrp)
215 				rrfreelist(dbnsrp);
216 			continue;
217 		}
218 
219 		/* look for ns in cache */
220 		nsdp = dnlookup(cp, class, 0);
221 		nsrp = nil;
222 		if(nsdp)
223 			nsrp = randomize(rrlookup(nsdp, Tns, NOneg));
224 
225 		/* if the entry timed out, ignore it */
226 		if(nsrp && nsrp->ttl < now){
227 			rrfreelist(nsrp);
228 			nsrp = nil;
229 		}
230 
231 		if(nsrp){
232 			rrfreelist(dbnsrp);
233 
234 			/* try the name servers found in cache */
235 			if(netquery(dp, type, nsrp, req, depth+1)){
236 				rrfreelist(nsrp);
237 				return rrlookup(dp, type, OKneg);
238 			}
239 			rrfreelist(nsrp);
240 			continue;
241 		}
242 
243 		/* use ns from db */
244 		if(dbnsrp){
245 			/* try the name servers found in db */
246 			if(netquery(dp, type, dbnsrp, req, depth+1)){
247 				/* we got an answer */
248 				rrfreelist(dbnsrp);
249 				return rrlookup(dp, type, NOneg);
250 			}
251 			rrfreelist(dbnsrp);
252 		}
253 	}
254 
255 	/* settle for a non-authoritative answer */
256 	rp = rrlookup(dp, type, OKneg);
257 	if(rp)
258 		return rp;
259 
260 	/* noone answered.  try the database, we might have a chance. */
261 	return dblookup(name, class, type, 0, 0);
262 }
263 
264 /*
265  *  walk a domain name one element to the right.
266  *  return a pointer to that element.
267  *  in other words, return a pointer to the parent domain name.
268  */
269 char*
270 walkup(char *name)
271 {
272 	char *cp;
273 
274 	cp = strchr(name, '.');
275 	if(cp)
276 		return cp+1;
277 	else if(*name)
278 		return "";
279 	else
280 		return 0;
281 }
282 
283 /*
284  *  Get a udpport for requests and replies.  Put the port
285  *  into "headers" mode.
286  */
287 static char *hmsg = "headers";
288 static char *ohmsg = "oldheaders";
289 
290 int
291 udpport(char *mtpt)
292 {
293 	int fd, ctl;
294 	char ds[64], adir[64];
295 
296 	/* get a udp port */
297 	snprint(ds, sizeof ds, "%s/udp!*!0", (mtpt? mtpt: "/net"));
298 	ctl = announce(ds, adir);
299 	if(ctl < 0){
300 		/* warning("can't get udp port"); */
301 		return -1;
302 	}
303 
304 	/* turn on header style interface */
305 	if(write(ctl, hmsg, strlen(hmsg)) , 0){
306 		close(ctl);
307 		warning(hmsg);
308 		return -1;
309 	}
310 	write(ctl, ohmsg, strlen(ohmsg));
311 
312 	/* grab the data file */
313 	snprint(ds, sizeof ds, "%s/data", adir);
314 	fd = open(ds, ORDWR);
315 	close(ctl);
316 	if(fd < 0)
317 		warning("can't open udp port %s: %r", ds);
318 	return fd;
319 }
320 
321 int
322 mkreq(DN *dp, int type, uchar *buf, int flags, ushort reqno)
323 {
324 	DNSmsg m;
325 	int len;
326 	OUdphdr *uh = (OUdphdr*)buf;
327 
328 	/* stuff port number into output buffer */
329 	memset(uh, 0, sizeof(*uh));
330 	hnputs(uh->rport, 53);
331 
332 	/* make request and convert it to output format */
333 	memset(&m, 0, sizeof(m));
334 	m.flags = flags;
335 	m.id = reqno;
336 	m.qd = rralloc(type);
337 	m.qd->owner = dp;
338 	m.qd->type = type;
339 	len = convDNS2M(&m, &buf[OUdphdrsize], Maxudp);
340 	if(len < 0)
341 		abort();	/* "can't convert" */
342 	rrfree(m.qd);
343 	return len;
344 }
345 
346 /* for alarms in readreply */
347 static void
348 ding(void *x, char *msg)
349 {
350 	USED(x);
351 	if(strcmp(msg, "alarm") == 0)
352 		noted(NCONT);
353 	else
354 		noted(NDFLT);
355 }
356 
357 static void
358 freeanswers(DNSmsg *mp)
359 {
360 	rrfreelist(mp->qd);
361 	rrfreelist(mp->an);
362 	rrfreelist(mp->ns);
363 	rrfreelist(mp->ar);
364 	mp->qd = mp->an = mp->ns = mp->ar = nil;
365 }
366 
367 /*
368  *  read replies to a request.  ignore any of the wrong type.
369  *  wait at most 5 seconds.
370  */
371 static int
372 readreply(int fd, DN *dp, int type, ushort req,
373 	  uchar *ibuf, DNSmsg *mp, ulong endtime, Request *reqp)
374 {
375 	char *err;
376 	int len;
377 	ulong now;
378 	RR *rp;
379 
380 	notify(ding);
381 
382 	for(; ; freeanswers(mp)){
383 		now = time(0);
384 		if(now >= endtime)
385 			return -1;	/* timed out */
386 
387 		/* timed read */
388 		alarm((endtime - now) * 1000);
389 		len = read(fd, ibuf, OUdphdrsize+Maxudpin);
390 		alarm(0);
391 		len -= OUdphdrsize;
392 		if(len < 0)
393 			return -1;	/* timed out */
394 
395 		/* convert into internal format  */
396 		memset(mp, 0, sizeof(*mp));
397 		err = convM2DNS(&ibuf[OUdphdrsize], len, mp, nil);
398 		if(err){
399 			syslog(0, LOG, "input err: %s: %I", err, ibuf);
400 			continue;
401 		}
402 		if(debug)
403 			logreply(reqp->id, ibuf, mp);
404 
405 		/* answering the right question? */
406 		if(mp->id != req){
407 			syslog(0, LOG, "%d: id %d instead of %d: %I", reqp->id,
408 					mp->id, req, ibuf);
409 			continue;
410 		}
411 		if(mp->qd == 0){
412 			syslog(0, LOG, "%d: no question RR: %I", reqp->id, ibuf);
413 			continue;
414 		}
415 		if(mp->qd->owner != dp){
416 			syslog(0, LOG, "%d: owner %s instead of %s: %I",
417 				reqp->id, mp->qd->owner->name, dp->name, ibuf);
418 			continue;
419 		}
420 		if(mp->qd->type != type){
421 			syslog(0, LOG, "%d: type %d instead of %d: %I",
422 				reqp->id, mp->qd->type, type, ibuf);
423 			continue;
424 		}
425 
426 		/* remember what request this is in answer to */
427 		for(rp = mp->an; rp; rp = rp->next)
428 			rp->query = type;
429 
430 		return 0;
431 	}
432 }
433 
434 /*
435  *	return non-0 if first list includes second list
436  */
437 int
438 contains(RR *rp1, RR *rp2)
439 {
440 	RR *trp1, *trp2;
441 
442 	for(trp2 = rp2; trp2; trp2 = trp2->next){
443 		for(trp1 = rp1; trp1; trp1 = trp1->next){
444 			if(trp1->type == trp2->type)
445 			if(trp1->host == trp2->host)
446 			if(trp1->owner == trp2->owner)
447 				break;
448 		}
449 		if(trp1 == nil)
450 			return 0;
451 	}
452 	return 1;
453 }
454 
455 
456 typedef struct Dest	Dest;
457 struct Dest
458 {
459 	uchar	a[IPaddrlen];	/* ip address */
460 	DN	*s;		/* name server */
461 	int	nx;		/* number of transmissions */
462 	int	code;		/* response code; used to clear dp->respcode */
463 };
464 
465 
466 /*
467  *  return multicast version if any
468  */
469 int
470 ipisbm(uchar *ip)
471 {
472 	if(isv4(ip)){
473 		if (ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0 ||
474 		    ipcmp(ip, IPv4bcast) == 0)
475 			return 4;
476 	} else
477 		if(ip[0] == 0xff)
478 			return 6;
479 	return 0;
480 }
481 
482 static Ndb *db;
483 static Ndbtuple *indoms, *innmsrvs, *outnmsrvs;
484 static QLock ndblock;
485 
486 static void
487 loaddomsrvs(Ndb *db)
488 {
489 	Ndbs s;
490 
491 	if (indoms == nil) {
492 		free(ndbgetvalue(db, &s, "sys", "inside-dom", "dom", &indoms));
493 		free(ndbgetvalue(db, &s, "sys", "inside-ns", "ip", &innmsrvs));
494 		free(ndbgetvalue(db, &s, "sys", "outside-ns", "ip", &outnmsrvs));
495 		syslog(0, LOG, "reloaded inside-dom, inside-ns, outside-ns");
496 	}
497 }
498 
499 /*
500  * is this domain (or DOMAIN or Domain or dOMAIN)
501  * internal to our organisation (behind our firewall)?
502  * only inside straddling servers care, everybody else gets told `yes',
503  * so they'll use mntpt for their queries.
504  */
505 static int
506 insideaddr(char *dom)
507 {
508 	int domlen, vallen, rv;
509 	Ndbtuple *t;
510 
511 	if (!inside || !straddle || !serve)
512 		return 1;
513 
514 	qlock(&ndblock);
515 	if (indoms == nil) {
516 		db = ndbopen(nil);
517 		if (db != nil)
518 			loaddomsrvs(db);
519 		/* leave db open so we can quickly test for changes */
520 	} else if (ndbchanged(db)) {
521 		ndbfree(indoms);
522 		ndbfree(innmsrvs);
523 		ndbfree(outnmsrvs);
524 		indoms = innmsrvs = outnmsrvs = nil;
525 		ndbreopen(db);
526 		loaddomsrvs(db);
527 	}
528 
529 	if (indoms == nil) {
530 		qunlock(&ndblock);
531 		return 1;	/* no "inside" sys, try inside nameservers */
532 	}
533 
534 	rv = 0;
535 	domlen = strlen(dom);
536 	for (t = indoms; t != nil; t = t->entry) {
537 		if (strcmp(t->attr, "dom") != 0)
538 			continue;
539 		vallen = strlen(t->val);
540 		if (cistrcmp(dom, t->val) == 0 ||
541 		    domlen > vallen &&
542 		     cistrcmp(dom + domlen - vallen, t->val) == 0 &&
543 		     dom[domlen - vallen - 1] == '.') {
544 			rv = 1;
545 			break;
546 		}
547 	}
548 	qunlock(&ndblock);
549 	return rv;
550 }
551 
552 static int
553 insidens(uchar *ip)
554 {
555 	uchar ipa[IPaddrlen];
556 	Ndbtuple *t;
557 
558 	for (t = innmsrvs; t != nil; t = t->entry)
559 		if (strcmp(t->attr, "ip") == 0) {
560 			parseip(ipa, t->val);
561 			if (memcmp(ipa, ip, sizeof ipa) == 0)
562 				return 1;
563 		}
564 	return 0;
565 }
566 
567 static uchar *
568 outsidens(void)
569 {
570 	Ndbtuple *t;
571 	static uchar ipa[IPaddrlen];
572 
573 	for (t = outnmsrvs; t != nil; t = t->entry)
574 		if (strcmp(t->attr, "ip") == 0) {
575 			parseip(ipa, t->val);
576 			return ipa;
577 		}
578 	return nil;
579 }
580 
581 /*
582  *  Get next server address
583  */
584 static int
585 serveraddrs(DN *dp, RR *nsrp, Dest *dest, int nd, int depth, Request *reqp)
586 {
587 	RR *rp, *arp, *trp;
588 	Dest *cur;
589 
590 	if(nd >= Maxdest)
591 		return 0;
592 
593 	/*
594 	 *  look for a server whose address we already know.
595 	 *  if we find one, mark it so we ignore this on
596 	 *  subsequent passes.
597 	 */
598 	arp = 0;
599 	for(rp = nsrp; rp; rp = rp->next){
600 		assert(rp->magic == RRmagic);
601 		if(rp->marker)
602 			continue;
603 		arp = rrlookup(rp->host, Ta, NOneg);
604 		if(arp){
605 			rp->marker = 1;
606 			break;
607 		}
608 		arp = dblookup(rp->host->name, Cin, Ta, 0, 0);
609 		if(arp){
610 			rp->marker = 1;
611 			break;
612 		}
613 	}
614 
615 	/*
616 	 *  if the cache and database lookup didn't find any new
617 	 *  server addresses, try resolving one via the network.
618 	 *  Mark any we try to resolve so we don't try a second time.
619 	 */
620 	if(arp == 0)
621 		for(rp = nsrp; rp; rp = rp->next){
622 			if(rp->marker)
623 				continue;
624 			rp->marker = 1;
625 
626 			/*
627 			 *  avoid loops looking up a server under itself
628 			 */
629 			if(subsume(rp->owner->name, rp->host->name))
630 				continue;
631 
632 			arp = dnresolve(rp->host->name, Cin, Ta, reqp, 0,
633 				depth+1, Recurse, 1, 0);
634 			rrfreelist(rrremneg(&arp));
635 			if(arp)
636 				break;
637 		}
638 
639 	/* use any addresses that we found */
640 	for(trp = arp; trp; trp = trp->next){
641 		if(nd >= Maxdest)
642 			break;
643 		cur = &dest[nd];
644 		parseip(cur->a, trp->ip->name);
645 		if (ipisbm(cur->a) ||
646 		    !insideaddr(dp->name) && insidens(cur->a))
647 			continue;
648 		cur->nx = 0;
649 		cur->s = trp->owner;
650 		cur->code = Rtimeout;
651 		nd++;
652 	}
653 	rrfreelist(arp);
654 	return nd;
655 }
656 
657 /*
658  *  cache negative responses
659  */
660 static void
661 cacheneg(DN *dp, int type, int rcode, RR *soarr)
662 {
663 	RR *rp;
664 	DN *soaowner;
665 	ulong ttl;
666 
667 	/* no cache time specified, don't make anything up */
668 	if(soarr != nil){
669 		if(soarr->next != nil){
670 			rrfreelist(soarr->next);
671 			soarr->next = nil;
672 		}
673 		soaowner = soarr->owner;
674 	} else
675 		soaowner = nil;
676 
677 	/* the attach can cause soarr to be freed so mine it now */
678 	if(soarr != nil && soarr->soa != nil)
679 		ttl = soarr->soa->minttl+now;
680 	else
681 		ttl = 5*Min;
682 
683 	/* add soa and negative RR to the database */
684 	rrattach(soarr, 1);
685 
686 	rp = rralloc(type);
687 	rp->owner = dp;
688 	rp->negative = 1;
689 	rp->negsoaowner = soaowner;
690 	rp->negrcode = rcode;
691 	rp->ttl = ttl;
692 	rrattach(rp, 1);
693 }
694 
695 /*
696  *  query name servers.  If the name server returns a pointer to another
697  *  name server, recurse.
698  */
699 static int
700 netquery1(int fd, DN *dp, int type, RR *nsrp, Request *reqp, int depth,
701 	uchar *ibuf, uchar *obuf, int waitsecs, int inns)
702 {
703 	int ndest, j, len, replywaits, rv;
704 	ulong endtime;
705 	ushort req;
706 	char buf[12];
707 	DN *ndp;
708 	DNSmsg m;
709 	Dest *p, *l, *np;
710 	Dest dest[Maxdest];
711 	RR *tp, *soarr;
712 
713 	/* pack request into a message */
714 	req = rand();
715 	len = mkreq(dp, type, obuf, Frecurse|Oquery, req);
716 
717 	/* no server addresses yet */
718 	l = dest;
719 
720 	/*
721 	 *  transmit requests and wait for answers.
722 	 *  at most Maxtrans attempts to each address.
723 	 *  each cycle send one more message than the previous.
724 	 */
725 	for(ndest = 1; ndest < Maxdest; ndest++){
726 		p = dest;
727 
728 		endtime = time(0);
729 		if(endtime >= reqp->aborttime)
730 			break;
731 
732 		/* get a server address if we need one */
733 		if(ndest > l - p){
734 			j = serveraddrs(dp, nsrp, dest, l - p, depth, reqp);
735 			l = &dest[j];
736 		}
737 
738 		/* no servers, punt */
739 		if(l == dest)
740 			if (straddle && inside) {
741 				/* HACK: use sys=outside ips */
742 				if (outsidens() == nil)
743 					sysfatal("no outside-ns in ndb");
744 				p = dest;
745 				memmove(p->a, outsidens(), sizeof p->a);
746 				p->s = dnlookup("outside", Cin, 1);
747 				p->nx = p->code = 0;
748 				l = p + 1;
749 			} else {
750 syslog(0, LOG, "netquery1: no servers for %s", dp->name);	// DEBUG
751 				break;
752 			}
753 
754 		/* send to first 'ndest' destinations */
755 		j = 0;
756 		for(; p < &dest[ndest] && p < l; p++){
757 			/* skip destinations we've finished with */
758 			if(p->nx >= Maxtrans)
759 				continue;
760 
761 			j++;
762 
763 			/* exponential backoff of requests */
764 			if((1<<p->nx) > ndest)
765 				continue;
766 
767 			memmove(obuf, p->a, sizeof p->a);
768 			procsetname("req slave: %sside query to %I/%s %s %s",
769 				(inns? "in": "out"), obuf, p->s->name, dp->name,
770 				rrname(type, buf, sizeof buf));
771 			if(debug)
772 				logsend(reqp->id, depth, obuf, p->s->name,
773 					dp->name, type);
774 
775 			/* actually send the UDP packet */
776 			if(write(fd, obuf, len + OUdphdrsize) < 0)
777 				warning("sending udp msg %r");
778 			p->nx++;
779 		}
780 		if(j == 0)
781 			break;		/* no destinations left */
782 
783 		endtime = time(0) + waitsecs;
784 		if(endtime > reqp->aborttime)
785 			endtime = reqp->aborttime;
786 
787 		for(replywaits = 0; replywaits < ndest; replywaits++){
788 			procsetname(
789 			    "req slave: reading %sside reply from %I for %s %s",
790 				(inns? "in": "out"), obuf, dp->name,
791 				rrname(type, buf, sizeof buf));
792 			memset(&m, 0, sizeof m);
793 			if(readreply(fd, dp, type, req, ibuf, &m, endtime, reqp) < 0)
794 				break;		/* timed out */
795 
796 			/* find responder */
797 			for(p = dest; p < l; p++)
798 				if(memcmp(p->a, ibuf, sizeof(p->a)) == 0)
799 					break;
800 
801 			/* remove all addrs of responding server from list */
802 			for(np = dest; np < l; np++)
803 				if(np->s == p->s)
804 					p->nx = Maxtrans;
805 
806 			/* ignore any error replies */
807 			if((m.flags & Rmask) == Rserver){
808 				rrfreelist(m.qd);
809 				rrfreelist(m.an);
810 				rrfreelist(m.ar);
811 				rrfreelist(m.ns);
812 				if(p != l)
813 					p->code = Rserver;
814 				continue;
815 			}
816 
817 			/* ignore any bad delegations */
818 			if(m.ns && baddelegation(m.ns, nsrp, ibuf)){
819 				rrfreelist(m.ns);
820 				m.ns = nil;
821 				if(m.an == nil){
822 					rrfreelist(m.qd);
823 					rrfreelist(m.ar);
824 					if(p != l)
825 						p->code = Rserver;
826 					continue;
827 				}
828 			}
829 
830 
831 			/* remove any soa's from the authority section */
832 			soarr = rrremtype(&m.ns, Tsoa);
833 
834 			/* incorporate answers */
835 			if(m.an)
836 				rrattach(m.an, (m.flags & Fauth) != 0);
837 			if(m.ar)
838 				rrattach(m.ar, 0);
839 			if(m.ns){
840 				ndp = m.ns->owner;
841 				rrattach(m.ns, 0);
842 			} else
843 				ndp = nil;
844 
845 			/* free the question */
846 			if(m.qd)
847 				rrfreelist(m.qd);
848 
849 			/*
850 			 *  Any reply from an authoritative server,
851 			 *  or a positive reply terminates the search
852 			 */
853 			if(m.an != nil || (m.flags & Fauth)){
854 				if(m.an == nil && (m.flags & Rmask) == Rname)
855 					dp->respcode = Rname;
856 				else
857 					dp->respcode = 0;
858 
859 				/*
860 				 *  cache any negative responses, free soarr
861 				 */
862 				if((m.flags & Fauth) && m.an == nil)
863 					cacheneg(dp, type, (m.flags & Rmask),
864 						soarr);
865 				else
866 					rrfreelist(soarr);
867 				return 1;
868 			}
869 			rrfreelist(soarr);
870 
871 			/*
872 			 *  if we've been given better name servers,
873 			 *  recurse.  we're called from udpquery, called from
874 			 *  netquery, which current holds dp->querylck,
875 			 *  so release it now and acquire it upon return.
876 			 */
877 			if(m.ns){
878 				tp = rrlookup(ndp, Tns, NOneg);
879 				if(!contains(nsrp, tp)){
880 					procsetname(
881 					 "req slave: recursive query for %s %s",
882 						dp->name,
883 						rrname(type, buf, sizeof buf));
884 					qunlock(&dp->querylck);
885 					rv = netquery(dp, type, tp, reqp,
886 						depth + 1);
887 					qlock(&dp->querylck);
888 					rrfreelist(tp);
889 					return rv;
890 				} else
891 					rrfreelist(tp);
892 			}
893 		}
894 	}
895 
896 	/* if all servers returned failure, propagate it */
897 	dp->respcode = Rserver;
898 	for(p = dest; p < l; p++)
899 		if(p->code != Rserver)
900 			dp->respcode = 0;
901 
902 	return 0;
903 }
904 
905 /*
906  *  run a command with a supplied fd as standard input
907  */
908 char *
909 system(int fd, char *cmd)
910 {
911 	int pid, p, i;
912 	static Waitmsg msg;
913 
914 	if((pid = fork()) == -1)
915 		sysfatal("fork failed: %r");
916 	else if(pid == 0){
917 		dup(fd, 0);
918 		close(fd);
919 		for (i = 3; i < 200; i++)
920 			close(i);		/* don't leak fds */
921 		execl("/bin/rc", "rc", "-c", cmd, nil);
922 		sysfatal("exec rc: %r");
923 	}
924 	for(p = waitpid(); p >= 0; p = waitpid())
925 		if(p == pid)
926 			return msg.msg;
927 	return "lost child";
928 }
929 
930 enum { Hurry, Patient, };
931 enum { Outns, Inns, };
932 
933 static int
934 udpquery(char *mntpt, DN *dp, int type, RR *nsrp, Request *reqp, int depth,
935 	int patient, int inns)
936 {
937 	int fd, rv = 0;
938 	char *msg;
939 	uchar *obuf, *ibuf;
940 
941 	/* use alloced buffers rather than ones from the stack */
942 	ibuf = emalloc(Maxudpin+OUdphdrsize);
943 	obuf = emalloc(Maxudp+OUdphdrsize);
944 
945 	fd = udpport(mntpt);
946 	if (fd < 0 && straddle && strcmp(mntpt, "/net.alt") == 0) {
947 		/* HACK: remount /net.alt */
948 		syslog(0, LOG, "remounting /net.alt");
949 		unmount(nil, "/net.alt");
950 		msg = system(open("/dev/null", ORDWR), "outside");
951 		if (msg && *msg) {
952 			syslog(0, LOG, "can't remount /net.alt: %s", msg);
953 			sleep(2000);		/* don't spin wildly */
954 		} else
955 			fd = udpport(mntpt);
956 	}
957 	if(fd >= 0) {
958 		reqp->aborttime = time(0) + (patient? Maxreqtm: Maxreqtm/2);
959 		rv = netquery1(fd, dp, type, nsrp, reqp, depth,
960 			ibuf, obuf, (patient? 15: 10), inns);
961 		close(fd);
962 	}
963 
964 	free(obuf);
965 	free(ibuf);
966 	return rv;
967 }
968 
969 /* look up (dp->name,type) via *nsrp with results in *reqp */
970 static int
971 netquery(DN *dp, int type, RR *nsrp, Request *reqp, int depth)
972 {
973 	int lock, rv, triedin;
974 	RR *rp;
975 
976 	if(depth > 12)			/* in a recursive loop? */
977 		return 0;
978 
979 	slave(reqp);			/* might fork */
980 	/* if so, parent process longjmped to req->mret; we're child slave */
981 	if (!reqp->isslave)
982 		syslog(0, LOG,
983 			"[%d] netquery: slave returned with reqp->isslave==0",
984 			getpid());
985 
986 	/* don't lock before call to slave so only children can block */
987 	lock = reqp->isslave != 0;
988 	if(lock) {
989 		procsetname("waiting for query lock on %s", dp->name);
990 		/* don't make concurrent queries for this name */
991 		qlock(&dp->querylck);
992 		procsetname("netquery: %s", dp->name);
993 	}
994 
995 	/* prepare server RR's for incremental lookup */
996 	for(rp = nsrp; rp; rp = rp->next)
997 		rp->marker = 0;
998 
999 	rv = 0;				/* pessimism */
1000 	triedin = 0;
1001 	/*
1002 	 * normal resolvers and servers will just use mntpt for all addresses,
1003 	 * even on the outside.  straddling servers will use mntpt (/net)
1004 	 * for inside addresses and /net.alt for outside addresses,
1005 	 * thus bypassing other inside nameservers.
1006 	 */
1007 	if (!straddle || insideaddr(dp->name)) {
1008 		rv = udpquery(mntpt, dp, type, nsrp, reqp, depth, Hurry,
1009 			(inside? Inns: Outns));
1010 		triedin = 1;
1011 	}
1012 
1013 	/*
1014 	 * if we're still looking, are inside, and have an outside domain,
1015 	 * try it on our outside interface, if any.
1016 	 */
1017 	if (rv == 0 && inside && !insideaddr(dp->name)) {
1018 		if (triedin)
1019 			syslog(0, LOG,
1020 	   "[%d] netquery: internal nameservers failed for %s; trying external",
1021 				getpid(), dp->name);
1022 
1023 		/* prepare server RR's for incremental lookup */
1024 		for(rp = nsrp; rp; rp = rp->next)
1025 			rp->marker = 0;
1026 
1027 		rv = udpquery("/net.alt", dp, type, nsrp, reqp, depth, Patient,
1028 			Outns);
1029 		if (rv == 0)
1030 			syslog(0, LOG, "[%d] netquery: no luck for %s",
1031 				getpid(), dp->name);
1032 	}
1033 
1034 	if(lock)
1035 		qunlock(&dp->querylck);
1036 
1037 	return rv;
1038 }
1039