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