xref: /plan9/sys/src/cmd/ndb/dnresolve.c (revision d3907fe5a68251e8b016f54f72acf8767ba044bb)
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 /*
18  * reading /proc/pid/args yields either "name" or "name [display args]",
19  * so return only display args, if any.
20  */
21 static char *
22 procgetname(void)
23 {
24 	int fd, n;
25 	char *lp, *rp;
26 	char buf[256];
27 
28 	snprint(buf, sizeof buf, "#p/%d/args", getpid());
29 	if((fd = open(buf, OREAD)) < 0)
30 		return strdup("");
31 	*buf = '\0';
32 	n = read(fd, buf, sizeof buf-1);
33 	close(fd);
34 	if (n >= 0)
35 		buf[n] = '\0';
36 	if ((lp = strchr(buf, '[')) == nil ||
37 	    (rp = strrchr(buf, ']')) == nil)
38 		return strdup("");
39 	*rp = '\0';
40 	return strdup(lp+1);
41 }
42 
43 /*
44  *  lookup 'type' info for domain name 'name'.  If it doesn't exist, try
45  *  looking it up as a canonical name.
46  */
47 RR*
48 dnresolve(char *name, int class, int type, Request *req, RR **cn, int depth,
49 	int recurse, int rooted, int *status)
50 {
51 	RR *rp, *nrp, *drp;
52 	DN *dp;
53 	int loops;
54 	char *procname;
55 	char nname[Domlen];
56 
57 	if(status)
58 		*status = 0;
59 
60 	procname = procgetname();
61 	/*
62 	 *  hack for systems that don't have resolve search
63 	 *  lists.  Just look up the simple name in the database.
64 	 */
65 	if(!rooted && strchr(name, '.') == 0){
66 		rp = nil;
67 		drp = domainlist(class);
68 		for(nrp = drp; nrp != nil; nrp = nrp->next){
69 			snprint(nname, sizeof nname, "%s.%s", name,
70 				nrp->ptr->name);
71 			rp = dnresolve(nname, class, type, req, cn, depth,
72 				recurse, rooted, status);
73 			rrfreelist(rrremneg(&rp));
74 			if(rp != nil)
75 				break;
76 		}
77 		if(drp != nil)
78 			rrfree(drp);
79 		procsetname(procname);
80 		free(procname);
81 		return rp;
82 	}
83 
84 	/*
85 	 *  try the name directly
86 	 */
87 	rp = dnresolve1(name, class, type, req, depth, recurse);
88 	if(rp) {
89 		procsetname(procname);
90 		free(procname);
91 		return randomize(rp);
92 	}
93 
94 	/* try it as a canonical name if we weren't told the name didn't exist */
95 	dp = dnlookup(name, class, 0);
96 	if(type != Tptr && dp->respcode != Rname)
97 		for(loops = 0; rp == nil && loops < 32; loops++){
98 			rp = dnresolve1(name, class, Tcname, req, depth, recurse);
99 			if(rp == nil)
100 				break;
101 
102 			if(rp->negative){
103 				rrfreelist(rp);
104 				rp = nil;
105 				break;
106 			}
107 
108 			name = rp->host->name;
109 			if(cn)
110 				rrcat(cn, rp);
111 			else
112 				rrfreelist(rp);
113 
114 			rp = dnresolve1(name, class, type, req, depth, recurse);
115 		}
116 
117 	/* distinction between not found and not good */
118 	if(rp == nil && status != nil && dp->respcode != 0)
119 		*status = dp->respcode;
120 
121 	procsetname(procname);
122 	free(procname);
123 	return randomize(rp);
124 }
125 
126 static RR*
127 dnresolve1(char *name, int class, int type, Request *req, int depth,
128 	int recurse)
129 {
130 	DN *dp, *nsdp;
131 	RR *rp, *nsrp, *dbnsrp;
132 	char *cp;
133 
134 	if(debug)
135 		dnslog("[%d] dnresolve1 %s %d %d", getpid(), name, type, class);
136 
137 	/* only class Cin implemented so far */
138 	if(class != Cin)
139 		return nil;
140 
141 	dp = dnlookup(name, class, 1);
142 
143 	/*
144 	 *  Try the cache first
145 	 */
146 	rp = rrlookup(dp, type, OKneg);
147 	if(rp)
148 		if(rp->db){
149 			/* unauthoritative db entries are hints */
150 			if(rp->auth)
151 				return rp;
152 		} else
153 			/* cached entry must still be valid */
154 			if(rp->ttl > now)
155 				/* but Tall entries are special */
156 				if(type != Tall || rp->query == Tall)
157 					return rp;
158 
159 	rrfreelist(rp);
160 
161 	/*
162 	 * try the cache for a canonical name. if found punt
163 	 * since we'll find it during the canonical name search
164 	 * in dnresolve().
165 	 */
166 	if(type != Tcname){
167 		rp = rrlookup(dp, Tcname, NOneg);
168 		rrfreelist(rp);
169 		if(rp)
170 			return nil;
171 	}
172 
173 	/*
174 	 *  if we're running as just a resolver, query our
175 	 *  designated name servers
176 	 */
177 	if(cfg.resolver){
178 		nsrp = randomize(getdnsservers(class));
179 		if(nsrp != nil) {
180 			if(netquery(dp, type, nsrp, req, depth+1)){
181 				rrfreelist(nsrp);
182 				return rrlookup(dp, type, OKneg);
183 			}
184 			rrfreelist(nsrp);
185 		}
186 	}
187 
188 	/*
189  	 *  walk up the domain name looking for
190 	 *  a name server for the domain.
191 	 */
192 	for(cp = name; cp; cp = walkup(cp)){
193 		/*
194 		 *  if this is a local (served by us) domain,
195 		 *  return answer
196 		 */
197 		dbnsrp = randomize(dblookup(cp, class, Tns, 0, 0));
198 		if(dbnsrp && dbnsrp->local){
199 			rp = dblookup(name, class, type, 1, dbnsrp->ttl);
200 //			dnslog("dnresolve1: local domain %s -> %#p", name, rp);
201 			rrfreelist(dbnsrp);
202 			return rp;
203 		}
204 
205 		/*
206 		 *  if recursion isn't set, just accept local
207 		 *  entries
208 		 */
209 		if(recurse == Dontrecurse){
210 			if(dbnsrp)
211 				rrfreelist(dbnsrp);
212 			continue;
213 		}
214 
215 		/* look for ns in cache */
216 		nsdp = dnlookup(cp, class, 0);
217 		nsrp = nil;
218 		if(nsdp)
219 			nsrp = randomize(rrlookup(nsdp, Tns, NOneg));
220 
221 		/* if the entry timed out, ignore it */
222 		if(nsrp && nsrp->ttl < now){
223 			rrfreelist(nsrp);
224 			nsrp = nil;
225 		}
226 
227 		if(nsrp){
228 			rrfreelist(dbnsrp);
229 
230 			/* query the name servers found in cache */
231 //			dnslog("dnresolve1: %s: trying ns in cache", dp->name);
232 			if(netquery(dp, type, nsrp, req, depth+1)){
233 				rrfreelist(nsrp);
234 				return rrlookup(dp, type, OKneg);
235 			}
236 			rrfreelist(nsrp);
237 			continue;
238 		}
239 
240 		/* use ns from db */
241 		if(dbnsrp){
242 			/* try the name servers found in db */
243 //			dnslog("dnresolve1: %s: trying ns in db", dp->name);
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 until endtime.
368  */
369 static int
370 readreply(int fd, DN *dp, int type, ushort req, uchar *ibuf, DNSmsg *mp,
371 	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(nil);
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 			dnslog("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 			dnslog("%d: id %d instead of %d: %I", reqp->id,
406 					mp->id, req, ibuf);
407 			continue;
408 		}
409 		if(mp->qd == 0){
410 			dnslog("%d: no question RR: %I", reqp->id, ibuf);
411 			continue;
412 		}
413 		if(mp->qd->owner != dp){
414 			dnslog("%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 			dnslog("%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;		/* response code; used to clear dp->respcode */
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 /*
481  *  Get next server address
482  */
483 static int
484 serveraddrs(DN *dp, RR *nsrp, Dest *dest, int nd, int depth, Request *reqp)
485 {
486 	RR *rp, *arp, *trp;
487 	Dest *cur;
488 
489 	if(nd >= Maxdest)
490 		return 0;
491 
492 	/*
493 	 *  look for a server whose address we already know.
494 	 *  if we find one, mark it so we ignore this on
495 	 *  subsequent passes.
496 	 */
497 	arp = 0;
498 	for(rp = nsrp; rp; rp = rp->next){
499 		assert(rp->magic == RRmagic);
500 		if(rp->marker)
501 			continue;
502 		arp = rrlookup(rp->host, Ta, NOneg);
503 		if(arp){
504 			rp->marker = 1;
505 			break;
506 		}
507 		arp = dblookup(rp->host->name, Cin, Ta, 0, 0);
508 		if(arp){
509 			rp->marker = 1;
510 			break;
511 		}
512 	}
513 
514 	/*
515 	 *  if the cache and database lookup didn't find any new
516 	 *  server addresses, try resolving one via the network.
517 	 *  Mark any we try to resolve so we don't try a second time.
518 	 */
519 	if(arp == 0)
520 		for(rp = nsrp; rp; rp = rp->next){
521 			if(rp->marker)
522 				continue;
523 			rp->marker = 1;
524 
525 			/*
526 			 *  avoid loops looking up a server under itself
527 			 */
528 			if(subsume(rp->owner->name, rp->host->name))
529 				continue;
530 
531 			arp = dnresolve(rp->host->name, Cin, Ta, reqp, 0,
532 				depth+1, Recurse, 1, 0);
533 			rrfreelist(rrremneg(&arp));
534 			if(arp)
535 				break;
536 		}
537 
538 	/* use any addresses that we found */
539 	for(trp = arp; trp; trp = trp->next){
540 		if(nd >= Maxdest)
541 			break;
542 		cur = &dest[nd];
543 		parseip(cur->a, trp->ip->name);
544 		/*
545 		 * straddling servers can reject all nameservers if they are all
546 		 * inside, so be sure to list at least one outside ns at
547 		 * the end of the ns list in /lib/ndb for `dom='.
548 		 */
549 		if (ipisbm(cur->a) ||
550 		    cfg.straddle && !insideaddr(dp->name) && insidens(cur->a))
551 			continue;
552 		cur->nx = 0;
553 		cur->s = trp->owner;
554 		cur->code = Rtimeout;
555 		nd++;
556 	}
557 	rrfreelist(arp);
558 	return nd;
559 }
560 
561 /*
562  *  cache negative responses
563  */
564 static void
565 cacheneg(DN *dp, int type, int rcode, RR *soarr)
566 {
567 	RR *rp;
568 	DN *soaowner;
569 	ulong ttl;
570 
571 	/* no cache time specified, don't make anything up */
572 	if(soarr != nil){
573 		if(soarr->next != nil){
574 			rrfreelist(soarr->next);
575 			soarr->next = nil;
576 		}
577 		soaowner = soarr->owner;
578 	} else
579 		soaowner = nil;
580 
581 	/* the attach can cause soarr to be freed so mine it now */
582 	if(soarr != nil && soarr->soa != nil)
583 		ttl = soarr->soa->minttl+now;
584 	else
585 		ttl = 5*Min;
586 
587 	/* add soa and negative RR to the database */
588 	rrattach(soarr, 1);
589 
590 	rp = rralloc(type);
591 	rp->owner = dp;
592 	rp->negative = 1;
593 	rp->negsoaowner = soaowner;
594 	rp->negrcode = rcode;
595 	rp->ttl = ttl;
596 	rrattach(rp, 1);
597 }
598 
599 static int
600 setdestoutns(Dest *p, int n)
601 {
602 	uchar *outns = outsidens(n);
603 
604 	memset(p, 0, sizeof *p);
605 	if (outns == nil) {
606 		if (n == 0)
607 			dnslog("[%d] no outside-ns in ndb", getpid());
608 		return -1;
609 	}
610 	memmove(p->a, outns, sizeof p->a);
611 	p->s = dnlookup("outside-ns-ips", Cin, 1);
612 	return 0;
613 }
614 
615 /*
616  *  query name servers.  If the name server returns a pointer to another
617  *  name server, recurse.
618  */
619 static int
620 netquery1(int fd, DN *dp, int type, RR *nsrp, Request *reqp, int depth,
621 	uchar *ibuf, uchar *obuf, int waitsecs, int inns)
622 {
623 	int ndest, j, len, replywaits, rv, n;
624 	ushort req;
625 	ulong endtime;
626 	char buf[12];
627 	DN *ndp;
628 	DNSmsg m;
629 	Dest *p, *l, *np;
630 	Dest dest[Maxdest];
631 	RR *tp, *soarr;
632 //	char fdbuf[1024];
633 
634 //	fd2path(fd, fdbuf, sizeof fdbuf);
635 //	dnslog("netquery: on %s for %s %s ns", fdbuf, dp->name,
636 //		(inns? "inside": "outside"));
637 
638 	/* pack request into a message */
639 	req = rand();
640 	len = mkreq(dp, type, obuf, Frecurse|Oquery, req);
641 
642 	/* no server addresses yet */
643 	l = dest;
644 
645 	/*
646 	 *  transmit requests and wait for answers.
647 	 *  at most Maxtrans attempts to each address.
648 	 *  each cycle send one more message than the previous.
649 	 */
650 	for(ndest = 1; ndest < Maxdest; ndest++){
651 //		dnslog("netquery1 xmit loop: now %ld aborttime %ld", time(nil),
652 //			reqp->aborttime);
653 		if(time(nil) >= reqp->aborttime)
654 			break;
655 
656 		/* get a server address if we need one */
657 		p = dest;
658 		if(ndest > l - p){
659 			j = serveraddrs(dp, nsrp, dest, l - p, depth, reqp);
660 			l = &dest[j];
661 		}
662 
663 		/* no servers, punt */
664 		if(l == dest)
665 			if (cfg.straddle && cfg.inside) {
666 				p = l = dest;
667 				for(n = 0; n < Maxdest; n++, l++)
668 					if (setdestoutns(l, n) < 0)
669 						break;
670 			} else {
671 //				dnslog("netquery1: %s: no servers", dp->name);
672 				break;
673 			}
674 
675 		/* send to first 'ndest' destinations */
676 		j = 0;
677 		for(; p < &dest[ndest] && p < l; p++){
678 			/* skip destinations we've finished with */
679 			if(p->nx >= Maxtrans)
680 				continue;
681 
682 			j++;
683 
684 			/* exponential backoff of requests */
685 			if((1<<p->nx) > ndest)
686 				continue;
687 
688 			memmove(obuf, p->a, sizeof p->a);
689 			procsetname("req slave: %sside query to %I/%s %s %s",
690 				(inns? "in": "out"), obuf, p->s->name, dp->name,
691 				rrname(type, buf, sizeof buf));
692 			if(debug)
693 				logsend(reqp->id, depth, obuf, p->s->name,
694 					dp->name, type);
695 
696 			/* actually send the UDP packet */
697 			if(write(fd, obuf, len + OUdphdrsize) < 0)
698 				warning("sending udp msg %r");
699 			p->nx++;
700 		}
701 		if(j == 0)
702 			break;		/* no destinations left */
703 
704 		endtime = time(nil) + waitsecs;
705 		if(endtime > reqp->aborttime)
706 			endtime = reqp->aborttime;
707 
708 //		dnslog(
709 //		    "netquery1 reply wait: now %ld aborttime %ld endtime %ld",
710 //			time(nil), reqp->aborttime, endtime);
711 		for(replywaits = 0; replywaits < ndest; replywaits++){
712 			procsetname(
713 			    "req slave: reading %sside reply from %I for %s %s",
714 				(inns? "in": "out"), obuf, dp->name,
715 				rrname(type, buf, sizeof buf));
716 			memset(&m, 0, sizeof m);
717 			if(readreply(fd, dp, type, req, ibuf, &m, endtime, reqp)
718 			    < 0)
719 				break;		/* timed out */
720 
721 //			dnslog("netquery1 got reply from %I", ibuf);
722 			/* find responder */
723 			for(p = dest; p < l; p++)
724 				if(memcmp(p->a, ibuf, sizeof p->a) == 0)
725 					break;
726 
727 			/* remove all addrs of responding server from list */
728 			for(np = dest; np < l; np++)
729 				if(np->s == p->s)
730 					p->nx = Maxtrans;
731 
732 			/* ignore any error replies */
733 			if((m.flags & Rmask) == Rserver){
734 //				dnslog(
735 //				 "netquery1 got Rserver for dest %s of name %s",
736 //					p->s->name, dp->name);
737 				rrfreelist(m.qd);
738 				rrfreelist(m.an);
739 				rrfreelist(m.ar);
740 				rrfreelist(m.ns);
741 				if(p != l) {
742 //					dnslog(
743 //	"netquery1 setting Rserver for dest %s of name %s due to Rserver reply",
744 //						p->s->name, dp->name);
745 					p->code = Rserver;
746 				}
747 				continue;
748 			}
749 
750 			/* ignore any bad delegations */
751 			if(m.ns && baddelegation(m.ns, nsrp, ibuf)){
752 //				dnslog("netquery1 got a bad delegation from %s",
753 //					p->s->name);
754 				rrfreelist(m.ns);
755 				m.ns = nil;
756 				if(m.an == nil){
757 					rrfreelist(m.qd);
758 					rrfreelist(m.ar);
759 					if(p != l) {
760 //						dnslog(
761 //"netquery1 setting Rserver for dest %s of name %s due to bad delegation",
762 //							p->s->name, dp->name);
763 						p->code = Rserver;
764 					}
765 					continue;
766 				}
767 			}
768 
769 			/* remove any soa's from the authority section */
770 			soarr = rrremtype(&m.ns, Tsoa);
771 
772 			/* incorporate answers */
773 			if(m.an)
774 				rrattach(m.an, (m.flags & Fauth) != 0);
775 			if(m.ar)
776 				rrattach(m.ar, 0);
777 			if(m.ns){
778 				ndp = m.ns->owner;
779 				rrattach(m.ns, 0);
780 			} else
781 				ndp = nil;
782 
783 			/* free the question */
784 			if(m.qd)
785 				rrfreelist(m.qd);
786 
787 			/*
788 			 *  Any reply from an authoritative server,
789 			 *  or a positive reply terminates the search
790 			 */
791 			if(m.an != nil || (m.flags & Fauth)){
792 				if(m.an == nil && (m.flags & Rmask) == Rname)
793 					dp->respcode = Rname;
794 				else
795 					dp->respcode = 0;
796 
797 				/*
798 				 *  cache any negative responses, free soarr
799 				 */
800 				if((m.flags & Fauth) && m.an == nil)
801 					cacheneg(dp, type, (m.flags & Rmask),
802 						soarr);
803 				else
804 					rrfreelist(soarr);
805 				return 1;
806 			}
807 			rrfreelist(soarr);
808 
809 			/*
810 			 *  if we've been given better name servers,
811 			 *  recurse.  we're called from udpquery, called from
812 			 *  netquery, which current holds dp->querylck,
813 			 *  so release it now and acquire it upon return.
814 			 */
815 			if(m.ns){
816 				tp = rrlookup(ndp, Tns, NOneg);
817 				if(!contains(nsrp, tp)){
818 					procsetname(
819 					 "req slave: recursive query for %s %s",
820 						dp->name,
821 						rrname(type, buf, sizeof buf));
822 					qunlock(&dp->querylck);
823 
824 					rv = netquery(dp, type, tp, reqp,
825 						depth+1);
826 
827 					qlock(&dp->querylck);
828 					rrfreelist(tp);
829 					return rv;
830 				} else
831 					rrfreelist(tp);
832 			}
833 		}
834 	}
835 
836 	/* if all servers returned failure, propagate it */
837 	dp->respcode = Rserver;
838 	for(p = dest; p < l; p++)
839 		if(p->code != Rserver)
840 			dp->respcode = 0;
841 
842 //	if (dp->respcode)
843 //		dnslog("netquery1 setting Rserver for %s", dp->name);
844 
845 	return 0;
846 }
847 
848 /*
849  *  run a command with a supplied fd as standard input
850  */
851 char *
852 system(int fd, char *cmd)
853 {
854 	int pid, p, i;
855 	static Waitmsg msg;
856 
857 	if((pid = fork()) == -1)
858 		sysfatal("fork failed: %r");
859 	else if(pid == 0){
860 		dup(fd, 0);
861 		close(fd);
862 		for (i = 3; i < 200; i++)
863 			close(i);		/* don't leak fds */
864 		execl("/bin/rc", "rc", "-c", cmd, nil);
865 		sysfatal("exec rc: %r");
866 	}
867 	for(p = waitpid(); p >= 0; p = waitpid())
868 		if(p == pid)
869 			return msg.msg;
870 	return "lost child";
871 }
872 
873 enum { Hurry, Patient, };
874 enum { Outns, Inns, };
875 enum { Remntretry = 15, };	/* min. sec.s between remount attempts */
876 
877 static int
878 udpquery(char *mntpt, DN *dp, int type, RR *nsrp, Request *reqp, int depth,
879 	int patient, int inns)
880 {
881 	int fd, rv = 0;
882 	long now;
883 	char *msg;
884 	uchar *obuf, *ibuf;
885 	static QLock mntlck;
886 	static ulong lastmount;
887 
888 	/* use alloced buffers rather than ones from the stack */
889 	ibuf = emalloc(Maxudpin+OUdphdrsize);
890 	obuf = emalloc(Maxudp+OUdphdrsize);
891 
892 	fd = udpport(mntpt);
893 	while (fd < 0 && cfg.straddle && strcmp(mntpt, "/net.alt") == 0) {
894 		/* HACK: remount /net.alt */
895 		now = time(nil);
896 		if (now < lastmount + Remntretry)
897 			sleep((lastmount + Remntretry - now)*1000);
898 		qlock(&mntlck);
899 		fd = udpport(mntpt);	/* try again under lock */
900 		if (fd < 0) {
901 			dnslog("[%d] remounting /net.alt", getpid());
902 			unmount(nil, "/net.alt");
903 
904 			msg = system(open("/dev/null", ORDWR), "outside");
905 
906 			lastmount = time(nil);
907 			if (msg && *msg) {
908 				dnslog("[%d] can't remount /net.alt: %s",
909 					getpid(), msg);
910 				sleep(10*1000);		/* don't spin wildly */
911 			} else
912 				fd = udpport(mntpt);
913 		}
914 		qunlock(&mntlck);
915 	}
916 	if(fd >= 0) {
917 		reqp->aborttime = time(nil) + (patient? Maxreqtm: Maxreqtm/2);
918 //		dnslog("udpquery: %s/udp for %s with %s ns", mntpt, dp->name,
919 //			(inns? "inside": "outside"));
920 		rv = netquery1(fd, dp, type, nsrp, reqp, depth,
921 			ibuf, obuf, (patient? 15: 10), inns);
922 		close(fd);
923 	} else
924 		dnslog("can't get udpport for %s query of name %s: %r",
925 			mntpt, dp->name);
926 
927 	free(obuf);
928 	free(ibuf);
929 	return rv;
930 }
931 
932 static int
933 dnssetup(int domount, char *dns, char *srv, char *mtpt)
934 {
935 	int fd;
936 
937 	fd = open(dns, ORDWR);
938 	if(fd < 0){
939 		if(domount == 0){
940 			werrstr("can't open %s: %r", mtpt);
941 			return -1;
942 		}
943 		fd = open(srv, ORDWR);
944 		if(fd < 0){
945 			werrstr("can't open %s: %r", srv);
946 			return -1;
947 		}
948 		if(mount(fd, -1, mtpt, MBEFORE, "") < 0){
949 			werrstr("can't mount(%s, %s): %r", srv, mtpt);
950 			return -1;
951 		}
952 		fd = open(mtpt, ORDWR);
953 		if(fd < 0)
954 			werrstr("can't open %s: %r", mtpt);
955 	}
956 	return fd;
957 }
958 
959 static RR *
960 rrparse(char *lines)
961 {
962 	int nl, nf, ln, type;
963 	char *line[100];
964 	char *field[32];
965 	RR *rp, *rplist;
966 //	Server *s;
967 	SOA *soa;
968 	Srv *srv;
969 //	Txt *t;
970 
971 	rplist = nil;
972 	nl = tokenize(lines, line, nelem(line));
973 	for (ln = 0; ln < nl; ln++) {
974 		if (*line[ln] == '!' || *line[ln] == '?')
975 			continue;		/* error */
976 		nf = tokenize(line[ln], field, nelem(field));
977 		if (nf < 2)
978 			continue;		/* mal-formed */
979 		type = rrtype(field[1]);
980 		rp = rralloc(type);
981 		rp->owner = dnlookup(field[0], Cin, 1);
982 		rp->next = rplist;
983 		rplist = rp;
984 		switch (type) {		/* TODO: copy fields to *rp */
985 		case Thinfo:
986 			// "\t%s %s", dnname(rp->cpu), dnname(rp->os));
987 			break;
988 		case Tcname:
989 		case Tmb:
990 		case Tmd:
991 		case Tmf:
992 		case Tns:
993 			// "\t%s", dnname(rp->host));
994 			break;
995 		case Tmg:
996 		case Tmr:
997 			// "\t%s", dnname(rp->mb));
998 			break;
999 		case Tminfo:
1000 			// "\t%s %s", dnname(rp->mb), dnname(rp->rmb));
1001 			break;
1002 		case Tmx:
1003 			// "\t%lud %s", rp->pref, dnname(rp->host));
1004 			break;
1005 		case Ta:
1006 		case Taaaa:
1007 			// "\t%s", dnname(rp->ip));	// TODO parseip
1008 			break;
1009 		case Tptr:
1010 			// "\t%s", dnname(rp->ptr));
1011 			break;
1012 		case Tsoa:
1013 			soa = rp->soa;
1014 			USED(soa);
1015 			// "\t%s %s %lud %lud %lud %lud %lud",
1016 			//	dnname(rp->host), dnname(rp->rmb),
1017 			//	(soa? soa->serial: 0),
1018 			//	(soa? soa->refresh: 0), (soa? soa->retry: 0),
1019 			//	(soa? soa->expire: 0), (soa? soa->minttl: 0));
1020 			break;
1021 		case Tsrv:
1022 			srv = rp->srv;
1023 			USED(srv);
1024 			break;
1025 		case Tnull:
1026 			// "\t%.*H", rp->null->dlen, rp->null->data);
1027 			break;
1028 		case Ttxt:
1029 			// for(t = rp->txt; t != nil; t = t->next)
1030 			//	"%s", t->p);
1031 			break;
1032 		case Trp:
1033 			// "\t%s %s", dnname(rp->rmb), dnname(rp->rp));
1034 			break;
1035 		case Tkey:
1036 			// "\t%d %d %d", rp->key->flags, rp->key->proto, rp->key->alg);
1037 			break;
1038 		case Tsig:
1039 			// "\t%d %d %d %lud %lud %lud %d %s",
1040 			//	rp->sig->type, rp->sig->alg, rp->sig->labels,
1041 			//	rp->sig->ttl, rp->sig->exp, rp->sig->incep,
1042 			//	rp->sig->tag, dnname(rp->sig->signer));
1043 			break;
1044 		case Tcert:
1045 			// "\t%d %d %d", rp->cert->type, rp->cert->tag, rp->cert->alg);
1046 			break;
1047 		}
1048 	}
1049 	return nil;
1050 }
1051 
1052 static int
1053 querydns(int fd, char *line, int n)
1054 {
1055 	int rv = 0;
1056 	char buf[1024];
1057 
1058 	seek(fd, 0, 0);
1059 	if(write(fd, line, n) != n)
1060 		return rv;
1061 
1062 	seek(fd, 0, 0);
1063 	buf[0] = '\0';
1064 	while((n = read(fd, buf, sizeof buf - 1)) > 0) {
1065 		buf[n] = 0;
1066 		rrattach(rrparse(buf), 1);	/* incorporate answers */
1067 		rv = 1;
1068 		buf[0] = '\0';
1069 	}
1070 	return rv;
1071 }
1072 
1073 static void
1074 askoutdns(DN *dp, int type)		/* ask /net.alt/dns directly */
1075 {
1076 	int len;
1077 	char buf[32];
1078 	char *query;
1079 	char *mtpt = "/net.alt";
1080 	char *dns =  "/net.alt/dns";
1081 	char *srv =  "/srv/dns_net.alt";
1082 	static int fd = -1;
1083 
1084 	if (fd < 0)
1085 		fd = dnssetup(1, dns, srv, mtpt);
1086 
1087 	query = smprint("%s %s\n", dp->name, rrname(type, buf, sizeof buf));
1088 	len = strlen(query);
1089 	if (!querydns(fd, query, len)) {
1090 		close(fd);
1091 		/* could run outside here */
1092 		fd = dnssetup(1, dns, srv, mtpt);
1093 		querydns(fd, query, len);
1094 	}
1095 	free(query);
1096 }
1097 
1098 /* look up (dp->name,type) via *nsrp with results in *reqp */
1099 static int
1100 netquery(DN *dp, int type, RR *nsrp, Request *reqp, int depth)
1101 {
1102 	int lock, rv, triedin, inname;
1103 	RR *rp;
1104 
1105 	if(depth > 12)			/* in a recursive loop? */
1106 		return 0;
1107 
1108 	slave(reqp);
1109 	/*
1110 	 * slave might have forked.  if so, the parent process longjmped to
1111 	 * req->mret; we're usually the child slave, but if there are too
1112 	 * many children already, we're still the same process.
1113 	 */
1114 
1115 	/* don't lock before call to slave so only children can block */
1116 	lock = reqp->isslave != 0;
1117 	if(lock) {
1118 		procsetname("waiting for query lock on %s", dp->name);
1119 		/* don't make concurrent queries for this name */
1120 		qlock(&dp->querylck);
1121 		procsetname("netquery: %s", dp->name);
1122 	}
1123 
1124 	/* prepare server RR's for incremental lookup */
1125 	for(rp = nsrp; rp; rp = rp->next)
1126 		rp->marker = 0;
1127 
1128 	rv = 0;				/* pessimism */
1129 	triedin = 0;
1130 	/*
1131 	 * normal resolvers and servers will just use mntpt for all addresses,
1132 	 * even on the outside.  straddling servers will use mntpt (/net)
1133 	 * for inside addresses and /net.alt for outside addresses,
1134 	 * thus bypassing other inside nameservers.
1135 	 */
1136 	inname = insideaddr(dp->name);
1137 	if (!cfg.straddle || inname) {
1138 		rv = udpquery(mntpt, dp, type, nsrp, reqp, depth, Hurry,
1139 			(cfg.inside? Inns: Outns));
1140 		triedin = 1;
1141 	}
1142 
1143 	/*
1144 	 * if we're still looking, are inside, and have an outside domain,
1145 	 * try it on our outside interface, if any.
1146 	 */
1147 	if (rv == 0 && cfg.inside && !inname) {
1148 		if (triedin)
1149 			dnslog(
1150 	   "[%d] netquery: internal nameservers failed for %s; trying external",
1151 				getpid(), dp->name);
1152 
1153 		/* prepare server RR's for incremental lookup */
1154 		for(rp = nsrp; rp; rp = rp->next)
1155 			rp->marker = 0;
1156 
1157 		rv = udpquery("/net.alt", dp, type, nsrp, reqp, depth, Patient,
1158 			Outns);
1159 	}
1160 	if (0 && rv == 0)		/* TODO: ask /net.alt/dns directly */
1161 		askoutdns(dp, type);
1162 
1163 	if(lock)
1164 		qunlock(&dp->querylck);
1165 
1166 	return rv;
1167 }
1168 
1169 int
1170 seerootns(void)
1171 {
1172 	char root[] = "";
1173 	Request req;
1174 
1175 	memset(&req, 0, sizeof req);
1176 	req.isslave = 1;
1177 	req.aborttime = now + Maxreqtm*2;	/* be patient */
1178 	return netquery(dnlookup(root, Cin, 1), Tns,
1179 		dblookup(root, Cin, Tns, 0, 0), &req, 0);
1180 }
1181