xref: /plan9-contrib/sys/src/cmd/ndb/dnresolve.c (revision d6d99297c773ad7750443c890a96b5a38335c6a9)
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 /* generate a DNS UDP query packet */
320 int
321 mkreq(DN *dp, int type, uchar *buf, int flags, ushort reqno)
322 {
323 	DNSmsg m;
324 	int len;
325 	OUdphdr *uh = (OUdphdr*)buf;
326 
327 	/* stuff port number into output buffer */
328 	memset(uh, 0, sizeof(*uh));
329 	hnputs(uh->rport, 53);
330 
331 	/* make request and convert it to output format */
332 	memset(&m, 0, sizeof(m));
333 	m.flags = flags;
334 	m.id = reqno;
335 	m.qd = rralloc(type);
336 	m.qd->owner = dp;
337 	m.qd->type = type;
338 	len = convDNS2M(&m, &buf[OUdphdrsize], Maxudp);
339 	rrfree(m.qd);
340 	return len;
341 }
342 
343 /* for alarms in readreply */
344 static void
345 ding(void *x, char *msg)
346 {
347 	USED(x);
348 	if(strcmp(msg, "alarm") == 0)
349 		noted(NCONT);
350 	else
351 		noted(NDFLT);
352 }
353 
354 static void
355 freeanswers(DNSmsg *mp)
356 {
357 	rrfreelist(mp->qd);
358 	rrfreelist(mp->an);
359 	rrfreelist(mp->ns);
360 	rrfreelist(mp->ar);
361 	mp->qd = mp->an = mp->ns = mp->ar = nil;
362 }
363 
364 /*
365  *  read replies to a request.  ignore any of the wrong type.
366  *  wait at most until endtime.
367  */
368 static int
369 readreply(int fd, DN *dp, int type, ushort req, uchar *ibuf, DNSmsg *mp,
370 	ulong endtime, Request *reqp)
371 {
372 	char *err;
373 	int len;
374 	ulong now;
375 	RR *rp;
376 
377 	notify(ding);
378 
379 	for(; ; freeanswers(mp)){
380 		now = time(nil);
381 		if(now >= endtime)
382 			return -1;	/* timed out */
383 
384 		/* timed read of UDP reply */
385 		alarm((endtime - now) * 1000);
386 		len = read(fd, ibuf, OUdphdrsize+Maxudpin);
387 		alarm(0);
388 		len -= OUdphdrsize;
389 		if(len < 0)
390 			return -1;	/* timed out */
391 
392 		/* convert into internal format  */
393 		memset(mp, 0, sizeof(*mp));
394 		err = convM2DNS(&ibuf[OUdphdrsize], len, mp, nil);
395 		if (mp->flags & Ftrunc) {
396 			dnslog("truncated reply, len %d from %I", len, ibuf);
397 			/* TODO: reissue query via tcp, process answer */
398 			/* for now, salvage what we can */
399 		}
400 		if(err){
401 			dnslog("input err, len %d: %s: %I", len, err, ibuf);
402 			continue;
403 		}
404 		if(debug)
405 			logreply(reqp->id, ibuf, mp);
406 
407 		/* answering the right question? */
408 		if(mp->id != req){
409 			dnslog("%d: id %d instead of %d: %I", reqp->id,
410 					mp->id, req, ibuf);
411 			continue;
412 		}
413 		if(mp->qd == 0){
414 			dnslog("%d: no question RR: %I", reqp->id, ibuf);
415 			continue;
416 		}
417 		if(mp->qd->owner != dp){
418 			dnslog("%d: owner %s instead of %s: %I",
419 				reqp->id, mp->qd->owner->name, dp->name, ibuf);
420 			continue;
421 		}
422 		if(mp->qd->type != type){
423 			dnslog("%d: type %d instead of %d: %I",
424 				reqp->id, mp->qd->type, type, ibuf);
425 			continue;
426 		}
427 
428 		/* remember what request this is in answer to */
429 		for(rp = mp->an; rp; rp = rp->next)
430 			rp->query = type;
431 
432 		return 0;
433 	}
434 }
435 
436 /*
437  *	return non-0 if first list includes second list
438  */
439 int
440 contains(RR *rp1, RR *rp2)
441 {
442 	RR *trp1, *trp2;
443 
444 	for(trp2 = rp2; trp2; trp2 = trp2->next){
445 		for(trp1 = rp1; trp1; trp1 = trp1->next){
446 			if(trp1->type == trp2->type)
447 			if(trp1->host == trp2->host)
448 			if(trp1->owner == trp2->owner)
449 				break;
450 		}
451 		if(trp1 == nil)
452 			return 0;
453 	}
454 	return 1;
455 }
456 
457 
458 typedef struct Dest	Dest;
459 struct Dest
460 {
461 	uchar	a[IPaddrlen];	/* ip address */
462 	DN	*s;		/* name server */
463 	int	nx;		/* number of transmissions */
464 	int	code;		/* response code; used to clear dp->respcode */
465 };
466 
467 
468 /*
469  *  return multicast version if any
470  */
471 int
472 ipisbm(uchar *ip)
473 {
474 	if(isv4(ip)){
475 		if (ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0 ||
476 		    ipcmp(ip, IPv4bcast) == 0)
477 			return 4;
478 	} else
479 		if(ip[0] == 0xff)
480 			return 6;
481 	return 0;
482 }
483 
484 /*
485  *  Get next server address
486  */
487 static int
488 serveraddrs(DN *dp, RR *nsrp, Dest *dest, int nd, int depth, Request *reqp)
489 {
490 	RR *rp, *arp, *trp;
491 	Dest *cur;
492 
493 	if(nd >= Maxdest)
494 		return 0;
495 
496 	/*
497 	 *  look for a server whose address we already know.
498 	 *  if we find one, mark it so we ignore this on
499 	 *  subsequent passes.
500 	 */
501 	arp = 0;
502 	for(rp = nsrp; rp; rp = rp->next){
503 		assert(rp->magic == RRmagic);
504 		if(rp->marker)
505 			continue;
506 		arp = rrlookup(rp->host, Ta, NOneg);
507 		if(arp){
508 			rp->marker = 1;
509 			break;
510 		}
511 		arp = dblookup(rp->host->name, Cin, Ta, 0, 0);
512 		if(arp){
513 			rp->marker = 1;
514 			break;
515 		}
516 	}
517 
518 	/*
519 	 *  if the cache and database lookup didn't find any new
520 	 *  server addresses, try resolving one via the network.
521 	 *  Mark any we try to resolve so we don't try a second time.
522 	 */
523 	if(arp == 0)
524 		for(rp = nsrp; rp; rp = rp->next){
525 			if(rp->marker)
526 				continue;
527 			rp->marker = 1;
528 
529 			/*
530 			 *  avoid loops looking up a server under itself
531 			 */
532 			if(subsume(rp->owner->name, rp->host->name))
533 				continue;
534 
535 			arp = dnresolve(rp->host->name, Cin, Ta, reqp, 0,
536 				depth+1, Recurse, 1, 0);
537 			rrfreelist(rrremneg(&arp));
538 			if(arp)
539 				break;
540 		}
541 
542 	/* use any addresses that we found */
543 	for(trp = arp; trp; trp = trp->next){
544 		if(nd >= Maxdest)
545 			break;
546 		cur = &dest[nd];
547 		parseip(cur->a, trp->ip->name);
548 		/*
549 		 * straddling servers can reject all nameservers if they are all
550 		 * inside, so be sure to list at least one outside ns at
551 		 * the end of the ns list in /lib/ndb for `dom='.
552 		 */
553 		if (ipisbm(cur->a) ||
554 		    cfg.straddle && !insideaddr(dp->name) && insidens(cur->a))
555 			continue;
556 		cur->nx = 0;
557 		cur->s = trp->owner;
558 		cur->code = Rtimeout;
559 		nd++;
560 	}
561 	rrfreelist(arp);
562 	return nd;
563 }
564 
565 /*
566  *  cache negative responses
567  */
568 static void
569 cacheneg(DN *dp, int type, int rcode, RR *soarr)
570 {
571 	RR *rp;
572 	DN *soaowner;
573 	ulong ttl;
574 
575 	/* no cache time specified, don't make anything up */
576 	if(soarr != nil){
577 		if(soarr->next != nil){
578 			rrfreelist(soarr->next);
579 			soarr->next = nil;
580 		}
581 		soaowner = soarr->owner;
582 	} else
583 		soaowner = nil;
584 
585 	/* the attach can cause soarr to be freed so mine it now */
586 	if(soarr != nil && soarr->soa != nil)
587 		ttl = soarr->soa->minttl+now;
588 	else
589 		ttl = 5*Min;
590 
591 	/* add soa and negative RR to the database */
592 	rrattach(soarr, 1);
593 
594 	rp = rralloc(type);
595 	rp->owner = dp;
596 	rp->negative = 1;
597 	rp->negsoaowner = soaowner;
598 	rp->negrcode = rcode;
599 	rp->ttl = ttl;
600 	rrattach(rp, 1);
601 }
602 
603 static int
604 setdestoutns(Dest *p, int n)
605 {
606 	uchar *outns = outsidens(n);
607 
608 	memset(p, 0, sizeof *p);
609 	if (outns == nil) {
610 		if (n == 0)
611 			dnslog("[%d] no outside-ns in ndb", getpid());
612 		return -1;
613 	}
614 	memmove(p->a, outns, sizeof p->a);
615 	p->s = dnlookup("outside-ns-ips", Cin, 1);
616 	return 0;
617 }
618 
619 /*
620  *  query name servers.  If the name server returns a pointer to another
621  *  name server, recurse.
622  *  BUG: UDP only currently.
623  */
624 static int
625 netquery1(int fd, DN *dp, int type, RR *nsrp, Request *reqp, int depth,
626 	uchar *ibuf, uchar *obuf, int waitsecs, int inns)
627 {
628 	int ndest, j, len, replywaits, rv, n;
629 	ushort req;
630 	ulong endtime;
631 	char buf[12];
632 	DN *ndp;
633 	DNSmsg m;
634 	Dest *p, *l, *np;
635 	Dest dest[Maxdest];
636 	RR *tp, *soarr;
637 //	char fdbuf[1024];
638 
639 //	fd2path(fd, fdbuf, sizeof fdbuf);
640 //	dnslog("netquery: on %s for %s %s ns", fdbuf, dp->name,
641 //		(inns? "inside": "outside"));
642 
643 	/* pack request into a message */
644 	req = rand();
645 	/* 1. generate UDP DNS query packet */
646 	len = mkreq(dp, type, obuf, Frecurse|Oquery, req);
647 	/* TODO: convert back and see if m.flags&Ftrunc is set */
648 
649 	/* no server addresses yet */
650 	l = dest;
651 
652 	/*
653 	 *  transmit requests and wait for answers.
654 	 *  at most Maxtrans attempts to each address.
655 	 *  each cycle send one more message than the previous.
656 	 */
657 	for(ndest = 1; ndest < Maxdest; ndest++){
658 //		dnslog("netquery1 xmit loop: now %ld aborttime %ld", time(nil),
659 //			reqp->aborttime);
660 		if(time(nil) >= reqp->aborttime)
661 			break;
662 
663 		/* get a server address if we need one */
664 		p = dest;
665 		if(ndest > l - p){
666 			j = serveraddrs(dp, nsrp, dest, l - p, depth, reqp);
667 			l = &dest[j];
668 		}
669 
670 		/* no servers, punt */
671 		if(l == dest)
672 			if (cfg.straddle && cfg.inside) {
673 				p = l = dest;
674 				for(n = 0; n < Maxdest; n++, l++)
675 					if (setdestoutns(l, n) < 0)
676 						break;
677 			} else {
678 //				dnslog("netquery1: %s: no servers", dp->name);
679 				break;
680 			}
681 
682 		/* send to first 'ndest' destinations */
683 		j = 0;
684 		for(; p < &dest[ndest] && p < l; p++){
685 			/* skip destinations we've finished with */
686 			if(p->nx >= Maxtrans)
687 				continue;
688 
689 			j++;
690 
691 			/* exponential backoff of requests */
692 			if((1<<p->nx) > ndest)
693 				continue;
694 
695 			memmove(obuf, p->a, sizeof p->a);
696 			procsetname("req slave: %sside query to %I/%s %s %s",
697 				(inns? "in": "out"), obuf, p->s->name, dp->name,
698 				rrname(type, buf, sizeof buf));
699 			if(debug)
700 				logsend(reqp->id, depth, obuf, p->s->name,
701 					dp->name, type);
702 
703 			/* 2. actually send the UDP packet */
704 // TODO or send via TCP & keep fd around for reply */
705 			if(write(fd, obuf, len + OUdphdrsize) < 0)
706 				warning("sending udp msg %r");
707 			p->nx++;
708 		}
709 		if(j == 0)
710 			break;		/* no destinations left */
711 
712 		endtime = time(nil) + waitsecs;
713 		if(endtime > reqp->aborttime)
714 			endtime = reqp->aborttime;
715 
716 //		dnslog(
717 //		    "netquery1 reply wait: now %ld aborttime %ld endtime %ld",
718 //			time(nil), reqp->aborttime, endtime);
719 		for(replywaits = 0; replywaits < ndest; replywaits++){
720 			procsetname(
721 			    "req slave: reading %sside reply from %I for %s %s",
722 				(inns? "in": "out"), obuf, dp->name,
723 				rrname(type, buf, sizeof buf));
724 			memset(&m, 0, sizeof m);
725 			/* 3. get UDP reply packet */
726 			if(readreply(fd, dp, type, req, ibuf, &m, endtime, reqp)
727 			    < 0)
728 				break;		/* timed out */
729 
730 //			dnslog("netquery1 got reply from %I", ibuf);
731 			/* find responder */
732 			for(p = dest; p < l; p++)
733 				if(memcmp(p->a, ibuf, sizeof p->a) == 0)
734 					break;
735 
736 			/* remove all addrs of responding server from list */
737 			for(np = dest; np < l; np++)
738 				if(np->s == p->s)
739 					p->nx = Maxtrans;
740 
741 			/* ignore any error replies */
742 			if((m.flags & Rmask) == Rserver){
743 //				dnslog(
744 //				 "netquery1 got Rserver for dest %s of name %s",
745 //					p->s->name, dp->name);
746 				rrfreelist(m.qd);
747 				rrfreelist(m.an);
748 				rrfreelist(m.ar);
749 				rrfreelist(m.ns);
750 				if(p != l) {
751 //					dnslog(
752 //	"netquery1 setting Rserver for dest %s of name %s due to Rserver reply",
753 //						p->s->name, dp->name);
754 					p->code = Rserver;
755 				}
756 				continue;
757 			}
758 
759 			/* ignore any bad delegations */
760 			if(m.ns && baddelegation(m.ns, nsrp, ibuf)){
761 //				dnslog("netquery1 got a bad delegation from %s",
762 //					p->s->name);
763 				rrfreelist(m.ns);
764 				m.ns = nil;
765 				if(m.an == nil){
766 					rrfreelist(m.qd);
767 					rrfreelist(m.ar);
768 					if(p != l) {
769 //						dnslog(
770 //"netquery1 setting Rserver for dest %s of name %s due to bad delegation",
771 //							p->s->name, dp->name);
772 						p->code = Rserver;
773 					}
774 					continue;
775 				}
776 			}
777 
778 			/* remove any soa's from the authority section */
779 			soarr = rrremtype(&m.ns, Tsoa);
780 
781 			/* incorporate answers */
782 			if(m.an)
783 				rrattach(m.an, (m.flags & Fauth) != 0);
784 			if(m.ar)
785 				rrattach(m.ar, 0);
786 			if(m.ns){
787 				ndp = m.ns->owner;
788 				rrattach(m.ns, 0);
789 			} else
790 				ndp = nil;
791 
792 			/* free the question */
793 			if(m.qd)
794 				rrfreelist(m.qd);
795 
796 			/*
797 			 *  Any reply from an authoritative server,
798 			 *  or a positive reply terminates the search
799 			 */
800 			if(m.an != nil || (m.flags & Fauth)){
801 				if(m.an == nil && (m.flags & Rmask) == Rname)
802 					dp->respcode = Rname;
803 				else
804 					dp->respcode = 0;
805 
806 				/*
807 				 *  cache any negative responses, free soarr
808 				 */
809 				if((m.flags & Fauth) && m.an == nil)
810 					cacheneg(dp, type, (m.flags & Rmask),
811 						soarr);
812 				else
813 					rrfreelist(soarr);
814 				return 1;
815 			}
816 			rrfreelist(soarr);
817 
818 			/*
819 			 *  if we've been given better name servers,
820 			 *  recurse.  we're called from udpquery, called from
821 			 *  netquery, which current holds dp->querylck,
822 			 *  so release it now and acquire it upon return.
823 			 */
824 			if(m.ns){
825 				tp = rrlookup(ndp, Tns, NOneg);
826 				if(!contains(nsrp, tp)){
827 					procsetname(
828 					 "req slave: recursive query for %s %s",
829 						dp->name,
830 						rrname(type, buf, sizeof buf));
831 					qunlock(&dp->querylck);
832 
833 					rv = netquery(dp, type, tp, reqp,
834 						depth+1);
835 
836 					qlock(&dp->querylck);
837 					rrfreelist(tp);
838 					return rv;
839 				} else
840 					rrfreelist(tp);
841 			}
842 		}
843 	}
844 
845 	/* if all servers returned failure, propagate it */
846 	dp->respcode = Rserver;
847 	for(p = dest; p < l; p++)
848 		if(p->code != Rserver)
849 			dp->respcode = 0;
850 
851 //	if (dp->respcode)
852 //		dnslog("netquery1 setting Rserver for %s", dp->name);
853 
854 	return 0;
855 }
856 
857 /*
858  *  run a command with a supplied fd as standard input
859  */
860 char *
861 system(int fd, char *cmd)
862 {
863 	int pid, p, i;
864 	static Waitmsg msg;
865 
866 	if((pid = fork()) == -1)
867 		sysfatal("fork failed: %r");
868 	else if(pid == 0){
869 		dup(fd, 0);
870 		close(fd);
871 		for (i = 3; i < 200; i++)
872 			close(i);		/* don't leak fds */
873 		execl("/bin/rc", "rc", "-c", cmd, nil);
874 		sysfatal("exec rc: %r");
875 	}
876 	for(p = waitpid(); p >= 0; p = waitpid())
877 		if(p == pid)
878 			return msg.msg;
879 	return "lost child";
880 }
881 
882 enum { Hurry, Patient, };
883 enum { Outns, Inns, };
884 enum { Remntretry = 15, };	/* min. sec.s between remount attempts */
885 
886 static int
887 udpquery(char *mntpt, DN *dp, int type, RR *nsrp, Request *reqp, int depth,
888 	int patient, int inns)
889 {
890 	int fd, rv = 0;
891 	long now;
892 	char *msg;
893 	uchar *obuf, *ibuf;
894 	static QLock mntlck;
895 	static ulong lastmount;
896 
897 	/* use alloced buffers rather than ones from the stack */
898 	ibuf = emalloc(Maxudpin+OUdphdrsize);
899 	obuf = emalloc(Maxudp+OUdphdrsize);
900 
901 	fd = udpport(mntpt);
902 	while (fd < 0 && cfg.straddle && strcmp(mntpt, "/net.alt") == 0) {
903 		/* HACK: remount /net.alt */
904 		now = time(nil);
905 		if (now < lastmount + Remntretry)
906 			sleep((lastmount + Remntretry - now)*1000);
907 		qlock(&mntlck);
908 		fd = udpport(mntpt);	/* try again under lock */
909 		if (fd < 0) {
910 			dnslog("[%d] remounting /net.alt", getpid());
911 			unmount(nil, "/net.alt");
912 
913 			msg = system(open("/dev/null", ORDWR), "outside");
914 
915 			lastmount = time(nil);
916 			if (msg && *msg) {
917 				dnslog("[%d] can't remount /net.alt: %s",
918 					getpid(), msg);
919 				sleep(10*1000);		/* don't spin wildly */
920 			} else
921 				fd = udpport(mntpt);
922 		}
923 		qunlock(&mntlck);
924 	}
925 	if(fd >= 0) {
926 		reqp->aborttime = time(nil) + (patient? Maxreqtm: Maxreqtm/2);
927 //		dnslog("udpquery: %s/udp for %s with %s ns", mntpt, dp->name,
928 //			(inns? "inside": "outside"));
929 		/* tune; was (patient? 15: 10) */
930 		rv = netquery1(fd, dp, type, nsrp, reqp, depth,
931 			ibuf, obuf, (patient? 10: 5), inns);
932 		close(fd);
933 	} else
934 		dnslog("can't get udpport for %s query of name %s: %r",
935 			mntpt, dp->name);
936 
937 	free(obuf);
938 	free(ibuf);
939 	return rv;
940 }
941 
942 static int
943 dnssetup(int domount, char *dns, char *srv, char *mtpt)
944 {
945 	int fd;
946 
947 	fd = open(dns, ORDWR);
948 	if(fd < 0){
949 		if(domount == 0){
950 			werrstr("can't open %s: %r", mtpt);
951 			return -1;
952 		}
953 		fd = open(srv, ORDWR);
954 		if(fd < 0){
955 			werrstr("can't open %s: %r", srv);
956 			return -1;
957 		}
958 		if(mount(fd, -1, mtpt, MBEFORE, "") < 0){
959 			werrstr("can't mount(%s, %s): %r", srv, mtpt);
960 			return -1;
961 		}
962 		fd = open(mtpt, ORDWR);
963 		if(fd < 0)
964 			werrstr("can't open %s: %r", mtpt);
965 	}
966 	return fd;
967 }
968 
969 /* this is unfinished */
970 static RR *
971 rrparse(char *lines)
972 {
973 	int nl, nf, ln, type;
974 	char *line[100];
975 	char *field[32];
976 	RR *rp, *rplist;
977 //	Server *s;
978 	SOA *soa;
979 	Srv *srv;
980 //	Txt *t;
981 
982 	rplist = nil;
983 	nl = tokenize(lines, line, nelem(line));
984 	for (ln = 0; ln < nl; ln++) {
985 		if (*line[ln] == '!' || *line[ln] == '?')
986 			continue;		/* error */
987 		nf = tokenize(line[ln], field, nelem(field));
988 		if (nf < 2)
989 			continue;		/* mal-formed */
990 		type = rrtype(field[1]);
991 		rp = rralloc(type);
992 		rp->owner = dnlookup(field[0], Cin, 1);
993 		rp->next = rplist;
994 		rplist = rp;
995 		switch (type) {		/* TODO: copy fields to *rp */
996 		case Thinfo:
997 			// "\t%s %s", dnname(rp->cpu), dnname(rp->os));
998 			break;
999 		case Tcname:
1000 		case Tmb:
1001 		case Tmd:
1002 		case Tmf:
1003 		case Tns:
1004 			// "\t%s", dnname(rp->host));
1005 			break;
1006 		case Tmg:
1007 		case Tmr:
1008 			// "\t%s", dnname(rp->mb));
1009 			break;
1010 		case Tminfo:
1011 			// "\t%s %s", dnname(rp->mb), dnname(rp->rmb));
1012 			break;
1013 		case Tmx:
1014 			// "\t%lud %s", rp->pref, dnname(rp->host));
1015 			break;
1016 		case Ta:
1017 		case Taaaa:
1018 			// "\t%s", dnname(rp->ip));	// TODO: parseip
1019 			break;
1020 		case Tptr:
1021 			// "\t%s", dnname(rp->ptr));
1022 			break;
1023 		case Tsoa:
1024 			soa = rp->soa;
1025 			USED(soa);
1026 			// "\t%s %s %lud %lud %lud %lud %lud",
1027 			//	dnname(rp->host), dnname(rp->rmb),
1028 			//	(soa? soa->serial: 0),
1029 			//	(soa? soa->refresh: 0), (soa? soa->retry: 0),
1030 			//	(soa? soa->expire: 0), (soa? soa->minttl: 0));
1031 			break;
1032 		case Tsrv:
1033 			srv = rp->srv;
1034 			USED(srv);
1035 			break;
1036 		case Tnull:
1037 			// "\t%.*H", rp->null->dlen, rp->null->data);
1038 			break;
1039 		case Ttxt:
1040 			// for(t = rp->txt; t != nil; t = t->next)
1041 			//	"%s", t->p);
1042 			break;
1043 		case Trp:
1044 			// "\t%s %s", dnname(rp->rmb), dnname(rp->rp));
1045 			break;
1046 		case Tkey:
1047 			// "\t%d %d %d", rp->key->flags, rp->key->proto, rp->key->alg);
1048 			break;
1049 		case Tsig:
1050 			// "\t%d %d %d %lud %lud %lud %d %s",
1051 			//	rp->sig->type, rp->sig->alg, rp->sig->labels,
1052 			//	rp->sig->ttl, rp->sig->exp, rp->sig->incep,
1053 			//	rp->sig->tag, dnname(rp->sig->signer));
1054 			break;
1055 		case Tcert:
1056 			// "\t%d %d %d", rp->cert->type, rp->cert->tag, rp->cert->alg);
1057 			break;
1058 		}
1059 	}
1060 	return nil;
1061 }
1062 
1063 static int
1064 querydns(int fd, char *line, int n)
1065 {
1066 	int rv = 0;
1067 	char buf[1024];
1068 
1069 	seek(fd, 0, 0);
1070 	if(write(fd, line, n) != n)
1071 		return rv;
1072 
1073 	seek(fd, 0, 0);
1074 	buf[0] = '\0';
1075 	while((n = read(fd, buf, sizeof buf - 1)) > 0) {
1076 		buf[n] = 0;
1077 		rrattach(rrparse(buf), 1);	/* incorporate answers */
1078 		rv = 1;
1079 		buf[0] = '\0';
1080 	}
1081 	return rv;
1082 }
1083 
1084 static void
1085 askoutdns(DN *dp, int type)		/* ask /net.alt/dns directly */
1086 {
1087 	int len;
1088 	char buf[32];
1089 	char *query;
1090 	char *mtpt = "/net.alt";
1091 	char *dns =  "/net.alt/dns";
1092 	char *srv =  "/srv/dns_net.alt";
1093 	static int fd = -1;
1094 
1095 	if (fd < 0)
1096 		fd = dnssetup(1, dns, srv, mtpt);
1097 
1098 	query = smprint("%s %s\n", dp->name, rrname(type, buf, sizeof buf));
1099 	len = strlen(query);
1100 	if (!querydns(fd, query, len)) {
1101 		close(fd);
1102 		/* could run outside here */
1103 		fd = dnssetup(1, dns, srv, mtpt);
1104 		querydns(fd, query, len);
1105 	}
1106 	free(query);
1107 }
1108 
1109 /* look up (dp->name,type) via *nsrp with results in *reqp */
1110 static int
1111 netquery(DN *dp, int type, RR *nsrp, Request *reqp, int depth)
1112 {
1113 	int lock, rv, triedin, inname;
1114 	RR *rp;
1115 
1116 	if(depth > 12)			/* in a recursive loop? */
1117 		return 0;
1118 
1119 	slave(reqp);
1120 	/*
1121 	 * slave might have forked.  if so, the parent process longjmped to
1122 	 * req->mret; we're usually the child slave, but if there are too
1123 	 * many children already, we're still the same process.
1124 	 */
1125 
1126 	/* don't lock before call to slave so only children can block */
1127 	lock = reqp->isslave != 0;
1128 	if(lock) {
1129 		procsetname("waiting for query lock on %s", dp->name);
1130 		/* don't make concurrent queries for this name */
1131 		qlock(&dp->querylck);
1132 		procsetname("netquery: %s", dp->name);
1133 	}
1134 
1135 	/* prepare server RR's for incremental lookup */
1136 	for(rp = nsrp; rp; rp = rp->next)
1137 		rp->marker = 0;
1138 
1139 	rv = 0;				/* pessimism */
1140 	triedin = 0;
1141 	/*
1142 	 * normal resolvers and servers will just use mntpt for all addresses,
1143 	 * even on the outside.  straddling servers will use mntpt (/net)
1144 	 * for inside addresses and /net.alt for outside addresses,
1145 	 * thus bypassing other inside nameservers.
1146 	 */
1147 	inname = insideaddr(dp->name);
1148 	if (!cfg.straddle || inname) {
1149 		rv = udpquery(mntpt, dp, type, nsrp, reqp, depth, Hurry,
1150 			(cfg.inside? Inns: Outns));
1151 		triedin = 1;
1152 	}
1153 
1154 	/*
1155 	 * if we're still looking, are inside, and have an outside domain,
1156 	 * try it on our outside interface, if any.
1157 	 */
1158 	if (rv == 0 && cfg.inside && !inname) {
1159 		if (triedin)
1160 			dnslog(
1161 	   "[%d] netquery: internal nameservers failed for %s; trying external",
1162 				getpid(), dp->name);
1163 
1164 		/* prepare server RR's for incremental lookup */
1165 		for(rp = nsrp; rp; rp = rp->next)
1166 			rp->marker = 0;
1167 
1168 		rv = udpquery("/net.alt", dp, type, nsrp, reqp, depth, Patient,
1169 			Outns);
1170 	}
1171 	if (0 && rv == 0)		/* could ask /net.alt/dns directly */
1172 		askoutdns(dp, type);
1173 
1174 	if(lock)
1175 		qunlock(&dp->querylck);
1176 
1177 	return rv;
1178 }
1179 
1180 int
1181 seerootns(void)
1182 {
1183 	char root[] = "";
1184 	Request req;
1185 
1186 	memset(&req, 0, sizeof req);
1187 	req.isslave = 1;
1188 	req.aborttime = now + Maxreqtm*2;	/* be patient */
1189 	return netquery(dnlookup(root, Cin, 1), Tns,
1190 		dblookup(root, Cin, Tns, 0, 0), &req, 0);
1191 }
1192