xref: /plan9/sys/src/cmd/ndb/dnresolve.c (revision 59f7772c483e72129b15add5aa078393f82b7d66)
13cbadd90SDavid du Colombier /*
23cbadd90SDavid du Colombier  * domain name resolvers, see rfcs 1035 and 1123
33cbadd90SDavid du Colombier  */
43e12c5d1SDavid du Colombier #include <u.h>
53e12c5d1SDavid du Colombier #include <libc.h>
64f8f669cSDavid du Colombier #include <ip.h>
74f8f669cSDavid du Colombier #include <bio.h>
84f8f669cSDavid du Colombier #include <ndb.h>
93e12c5d1SDavid du Colombier #include "dns.h"
103e12c5d1SDavid du Colombier 
113cbadd90SDavid du Colombier typedef struct Dest Dest;
123cbadd90SDavid du Colombier typedef struct Ipaddr Ipaddr;
133cbadd90SDavid du Colombier typedef struct Query Query;
143cbadd90SDavid du Colombier 
153e12c5d1SDavid du Colombier enum
163e12c5d1SDavid du Colombier {
173cbadd90SDavid du Colombier 	Udp, Tcp,
18b8b25780SDavid du Colombier 
197dd7cddfSDavid du Colombier 	Maxdest=	24,	/* maximum destinations for a request message */
20b8b25780SDavid du Colombier 	Maxoutstanding=	15,	/* max. outstanding queries per domain name */
21e6d9d902SDavid du Colombier 	Remntretry=	15,	/* min. sec.s between /net.alt remount tries */
22e6d9d902SDavid du Colombier 
23e6d9d902SDavid du Colombier 	/*
24e6d9d902SDavid du Colombier 	 * these are the old values; we're trying longer timeouts now
25e6d9d902SDavid du Colombier 	 * primarily for the benefit of remote nameservers querying us
26e6d9d902SDavid du Colombier 	 * during times of bad connectivity.
27e6d9d902SDavid du Colombier 	 */
28e6d9d902SDavid du Colombier //	Maxtrans=	3,	/* maximum transmissions to a server */
29e6d9d902SDavid du Colombier //	Maxretries=	3, /* cname+actual resends: was 32; have pity on user */
30e6d9d902SDavid du Colombier //	Maxwaitms=	1000,	/* wait no longer for a remote dns query */
31e6d9d902SDavid du Colombier //	Minwaitms=	100,	/* willing to wait for a remote dns query */
32e6d9d902SDavid du Colombier 
33e6d9d902SDavid du Colombier 	Maxtrans=	5,	/* maximum transmissions to a server */
34e6d9d902SDavid du Colombier 	Maxretries=	5, /* cname+actual resends: was 32; have pity on user */
35e6d9d902SDavid du Colombier 	Maxwaitms=	5000,	/* wait no longer for a remote dns query */
36e6d9d902SDavid du Colombier 	Minwaitms=	500,	/* willing to wait for a remote dns query */
37b8b25780SDavid du Colombier 
383cbadd90SDavid du Colombier 	Destmagic=	0xcafebabe,
393cbadd90SDavid du Colombier 	Querymagic=	0xdeadbeef,
403e12c5d1SDavid du Colombier };
41a41547ffSDavid du Colombier enum { Hurry, Patient, };
42a41547ffSDavid du Colombier enum { Outns, Inns, };
433e12c5d1SDavid du Colombier 
443cbadd90SDavid du Colombier struct Ipaddr {
453cbadd90SDavid du Colombier 	Ipaddr *next;
463cbadd90SDavid du Colombier 	uchar	ip[IPaddrlen];
473cbadd90SDavid du Colombier };
483cbadd90SDavid du Colombier 
493cbadd90SDavid du Colombier struct Dest
503cbadd90SDavid du Colombier {
513cbadd90SDavid du Colombier 	uchar	a[IPaddrlen];	/* ip address */
523cbadd90SDavid du Colombier 	DN	*s;		/* name server */
533cbadd90SDavid du Colombier 	int	nx;		/* number of transmissions */
543cbadd90SDavid du Colombier 	int	code;		/* response code; used to clear dp->respcode */
553cbadd90SDavid du Colombier 
563cbadd90SDavid du Colombier 	ulong	magic;
573cbadd90SDavid du Colombier };
583cbadd90SDavid du Colombier 
59c73252aeSDavid du Colombier /*
60c73252aeSDavid du Colombier  * Query has a QLock in it, thus it can't be an automatic
61c73252aeSDavid du Colombier  * variable, since each process would see a separate copy
62c73252aeSDavid du Colombier  * of the lock on its stack.
63c73252aeSDavid du Colombier  */
643cbadd90SDavid du Colombier struct Query {
653cbadd90SDavid du Colombier 	DN	*dp;		/* domain */
66adb31a62SDavid du Colombier 	ushort	type;		/* and type to look up */
673cbadd90SDavid du Colombier 	Request *req;
683cbadd90SDavid du Colombier 	RR	*nsrp;		/* name servers to consult */
693cbadd90SDavid du Colombier 
70a41547ffSDavid du Colombier 	/* dest must not be on the stack due to forking in slave() */
713cbadd90SDavid du Colombier 	Dest	*dest;		/* array of destinations */
72*59f7772cSDavid du Colombier 	Dest	*curdest;	/* pointer to next to fill */
73*59f7772cSDavid du Colombier 	int	ndest;		/* transmit to this many on this round */
743cbadd90SDavid du Colombier 
75f46c709fSDavid du Colombier 	int	udpfd;
763cbadd90SDavid du Colombier 
773cbadd90SDavid du Colombier 	QLock	tcplock;	/* only one tcp call at a time per query */
783cbadd90SDavid du Colombier 	int	tcpset;
793cbadd90SDavid du Colombier 	int	tcpfd;		/* if Tcp, read replies from here */
803cbadd90SDavid du Colombier 	int	tcpctlfd;
813cbadd90SDavid du Colombier 	uchar	tcpip[IPaddrlen];
823cbadd90SDavid du Colombier 
833cbadd90SDavid du Colombier 	ulong	magic;
843cbadd90SDavid du Colombier };
853cbadd90SDavid du Colombier 
86a41547ffSDavid du Colombier /* estimated % probability of such a record existing at all */
87a41547ffSDavid du Colombier int likely[] = {
88a41547ffSDavid du Colombier 	[Ta]		95,
89a41547ffSDavid du Colombier 	[Taaaa]		10,
90a41547ffSDavid du Colombier 	[Tcname]	15,
91a41547ffSDavid du Colombier 	[Tmx]		60,
92a41547ffSDavid du Colombier 	[Tns]		90,
93a41547ffSDavid du Colombier 	[Tnull]		5,
94a41547ffSDavid du Colombier 	[Tptr]		35,
95a41547ffSDavid du Colombier 	[Tsoa]		90,
96a41547ffSDavid du Colombier 	[Tsrv]		60,
97a41547ffSDavid du Colombier 	[Ttxt]		15,
98a41547ffSDavid du Colombier 	[Tall]		95,
993cbadd90SDavid du Colombier };
1003cbadd90SDavid du Colombier 
1017dd7cddfSDavid du Colombier static RR*	dnresolve1(char*, int, int, Request*, int, int);
1023cbadd90SDavid du Colombier static int	netquery(Query *, int);
1033cbadd90SDavid du Colombier 
1044f8f669cSDavid du Colombier /*
105530fef66SDavid du Colombier  * reading /proc/pid/args yields either "name args" or "name [display args]",
1064f8f669cSDavid du Colombier  * so return only display args, if any.
1074f8f669cSDavid du Colombier  */
1084f8f669cSDavid du Colombier static char *
1094f8f669cSDavid du Colombier procgetname(void)
1104f8f669cSDavid du Colombier {
1114f8f669cSDavid du Colombier 	int fd, n;
1124f8f669cSDavid du Colombier 	char *lp, *rp;
1134f8f669cSDavid du Colombier 	char buf[256];
1144f8f669cSDavid du Colombier 
1154f8f669cSDavid du Colombier 	snprint(buf, sizeof buf, "#p/%d/args", getpid());
1164f8f669cSDavid du Colombier 	if((fd = open(buf, OREAD)) < 0)
1174f8f669cSDavid du Colombier 		return strdup("");
1184f8f669cSDavid du Colombier 	*buf = '\0';
1194f8f669cSDavid du Colombier 	n = read(fd, buf, sizeof buf-1);
1204f8f669cSDavid du Colombier 	close(fd);
1214f8f669cSDavid du Colombier 	if (n >= 0)
1224f8f669cSDavid du Colombier 		buf[n] = '\0';
1234f8f669cSDavid du Colombier 	if ((lp = strchr(buf, '[')) == nil ||
1244f8f669cSDavid du Colombier 	    (rp = strrchr(buf, ']')) == nil)
1254f8f669cSDavid du Colombier 		return strdup("");
1264f8f669cSDavid du Colombier 	*rp = '\0';
1274f8f669cSDavid du Colombier 	return strdup(lp+1);
1284f8f669cSDavid du Colombier }
1293e12c5d1SDavid du Colombier 
130e6d9d902SDavid du Colombier void
131e6d9d902SDavid du Colombier rrfreelistptr(RR **rpp)
132e6d9d902SDavid du Colombier {
133e6d9d902SDavid du Colombier 	RR *rp;
134e6d9d902SDavid du Colombier 
135e6d9d902SDavid du Colombier 	if (rpp == nil || *rpp == nil)
136e6d9d902SDavid du Colombier 		return;
137e6d9d902SDavid du Colombier 	rp = *rpp;
138e6d9d902SDavid du Colombier 	*rpp = nil;		/* update pointer in memory before freeing */
139e6d9d902SDavid du Colombier 	rrfreelist(rp);
140e6d9d902SDavid du Colombier }
141e6d9d902SDavid du Colombier 
1423e12c5d1SDavid du Colombier /*
1433e12c5d1SDavid du Colombier  *  lookup 'type' info for domain name 'name'.  If it doesn't exist, try
1443e12c5d1SDavid du Colombier  *  looking it up as a canonical name.
145b8b25780SDavid du Colombier  *
146b8b25780SDavid du Colombier  *  this process can be quite slow if time-outs are set too high when querying
147b8b25780SDavid du Colombier  *  nameservers that just don't respond to certain query types.  in that case,
148b8b25780SDavid du Colombier  *  there will be multiple udp retries, multiple nameservers will be queried,
149b8b25780SDavid du Colombier  *  and this will be repeated for a cname query.  the whole thing will be
150b8b25780SDavid du Colombier  *  retried several times until we get an answer or a time-out.
1513e12c5d1SDavid du Colombier  */
1523e12c5d1SDavid du Colombier RR*
1534f8f669cSDavid du Colombier dnresolve(char *name, int class, int type, Request *req, RR **cn, int depth,
1544f8f669cSDavid du Colombier 	int recurse, int rooted, int *status)
1553e12c5d1SDavid du Colombier {
1567dd7cddfSDavid du Colombier 	RR *rp, *nrp, *drp;
1573e12c5d1SDavid du Colombier 	DN *dp;
1587dd7cddfSDavid du Colombier 	int loops;
1594f8f669cSDavid du Colombier 	char *procname;
1607dd7cddfSDavid du Colombier 	char nname[Domlen];
1613e12c5d1SDavid du Colombier 
1627dd7cddfSDavid du Colombier 	if(status)
1637dd7cddfSDavid du Colombier 		*status = 0;
1647dd7cddfSDavid du Colombier 
165a41547ffSDavid du Colombier 	if(depth > 12)			/* in a recursive loop? */
166a41547ffSDavid du Colombier 		return nil;
167a41547ffSDavid du Colombier 
1684f8f669cSDavid du Colombier 	procname = procgetname();
1697dd7cddfSDavid du Colombier 	/*
1707dd7cddfSDavid du Colombier 	 *  hack for systems that don't have resolve search
1717dd7cddfSDavid du Colombier 	 *  lists.  Just look up the simple name in the database.
1727dd7cddfSDavid du Colombier 	 */
1736dc4800dSDavid du Colombier 	if(!rooted && strchr(name, '.') == nil){
1747dd7cddfSDavid du Colombier 		rp = nil;
1757dd7cddfSDavid du Colombier 		drp = domainlist(class);
176a41547ffSDavid du Colombier 		for(nrp = drp; rp == nil && nrp != nil; nrp = nrp->next){
1774f8f669cSDavid du Colombier 			snprint(nname, sizeof nname, "%s.%s", name,
1784f8f669cSDavid du Colombier 				nrp->ptr->name);
179a41547ffSDavid du Colombier 			rp = dnresolve(nname, class, type, req, cn, depth+1,
1804f8f669cSDavid du Colombier 				recurse, rooted, status);
181530fef66SDavid du Colombier 			lock(&dnlock);
182271b8d73SDavid du Colombier 			rrfreelist(rrremneg(&rp));
183530fef66SDavid du Colombier 			unlock(&dnlock);
1847dd7cddfSDavid du Colombier 		}
1857dd7cddfSDavid du Colombier 		if(drp != nil)
1866aaebd7dSDavid du Colombier 			rrfreelist(drp);
1874f8f669cSDavid du Colombier 		procsetname(procname);
1884f8f669cSDavid du Colombier 		free(procname);
1893e12c5d1SDavid du Colombier 		return rp;
1907dd7cddfSDavid du Colombier 	}
1913e12c5d1SDavid du Colombier 
1927dd7cddfSDavid du Colombier 	/*
1937dd7cddfSDavid du Colombier 	 *  try the name directly
1947dd7cddfSDavid du Colombier 	 */
1957dd7cddfSDavid du Colombier 	rp = dnresolve1(name, class, type, req, depth, recurse);
196225077b0SDavid du Colombier 	if(rp == nil) {
197225077b0SDavid du Colombier 		/*
198225077b0SDavid du Colombier 		 * try it as a canonical name if we weren't told
199225077b0SDavid du Colombier 		 * that the name didn't exist
200225077b0SDavid du Colombier 		 */
2017dd7cddfSDavid du Colombier 		dp = dnlookup(name, class, 0);
2024f8f669cSDavid du Colombier 		if(type != Tptr && dp->respcode != Rname)
203b8b25780SDavid du Colombier 			for(loops = 0; rp == nil && loops < Maxretries; loops++){
204b8b25780SDavid du Colombier 				/* retry cname, then the actual type */
205225077b0SDavid du Colombier 				rp = dnresolve1(name, class, Tcname, req,
206225077b0SDavid du Colombier 					depth, recurse);
2077dd7cddfSDavid du Colombier 				if(rp == nil)
2087dd7cddfSDavid du Colombier 					break;
20980ee5cbfSDavid du Colombier 
210c73252aeSDavid du Colombier 				/* rp->host == nil shouldn't happen, but does */
211c73252aeSDavid du Colombier 				if(rp->negative || rp->host == nil){
21280ee5cbfSDavid du Colombier 					rrfreelist(rp);
21380ee5cbfSDavid du Colombier 					rp = nil;
21480ee5cbfSDavid du Colombier 					break;
21580ee5cbfSDavid du Colombier 				}
2167dd7cddfSDavid du Colombier 
2177dd7cddfSDavid du Colombier 				name = rp->host->name;
218530fef66SDavid du Colombier 				lock(&dnlock);
2197dd7cddfSDavid du Colombier 				if(cn)
2207dd7cddfSDavid du Colombier 					rrcat(cn, rp);
2217dd7cddfSDavid du Colombier 				else
2227dd7cddfSDavid du Colombier 					rrfreelist(rp);
223530fef66SDavid du Colombier 				unlock(&dnlock);
2247dd7cddfSDavid du Colombier 
225225077b0SDavid du Colombier 				rp = dnresolve1(name, class, type, req,
226225077b0SDavid du Colombier 					depth, recurse);
2277dd7cddfSDavid du Colombier 			}
2287dd7cddfSDavid du Colombier 
2297dd7cddfSDavid du Colombier 		/* distinction between not found and not good */
230d9924332SDavid du Colombier 		if(rp == nil && status != nil && dp->respcode != Rok)
2314f8f669cSDavid du Colombier 			*status = dp->respcode;
232225077b0SDavid du Colombier 	}
2334f8f669cSDavid du Colombier 	procsetname(procname);
2344f8f669cSDavid du Colombier 	free(procname);
2357dd7cddfSDavid du Colombier 	return randomize(rp);
2363e12c5d1SDavid du Colombier }
2373e12c5d1SDavid du Colombier 
2383cbadd90SDavid du Colombier static void
2393cbadd90SDavid du Colombier queryinit(Query *qp, DN *dp, int type, Request *req)
2403cbadd90SDavid du Colombier {
2413cbadd90SDavid du Colombier 	memset(qp, 0, sizeof *qp);
2424e7b9544SDavid du Colombier 	qp->udpfd = qp->tcpfd = qp->tcpctlfd = -1;
2433cbadd90SDavid du Colombier 	qp->dp = dp;
2443cbadd90SDavid du Colombier 	qp->type = type;
245adb31a62SDavid du Colombier 	if (qp->type != type)
246adb31a62SDavid du Colombier 		dnslog("queryinit: bogus type %d", type);
2473cbadd90SDavid du Colombier 	qp->req = req;
2483cbadd90SDavid du Colombier 	qp->nsrp = nil;
2493cbadd90SDavid du Colombier 	qp->dest = qp->curdest = nil;
2503cbadd90SDavid du Colombier 	qp->magic = Querymagic;
2513cbadd90SDavid du Colombier }
2523cbadd90SDavid du Colombier 
2533cbadd90SDavid du Colombier static void
2543cbadd90SDavid du Colombier queryck(Query *qp)
2553cbadd90SDavid du Colombier {
2563cbadd90SDavid du Colombier 	assert(qp);
2573cbadd90SDavid du Colombier 	assert(qp->magic == Querymagic);
2583cbadd90SDavid du Colombier }
2593cbadd90SDavid du Colombier 
2603cbadd90SDavid du Colombier static void
2614e7b9544SDavid du Colombier querydestroy(Query *qp)
2624e7b9544SDavid du Colombier {
2634e7b9544SDavid du Colombier 	queryck(qp);
264530fef66SDavid du Colombier 	/* leave udpfd open */
2654e7b9544SDavid du Colombier 	if (qp->tcpfd > 0)
2664e7b9544SDavid du Colombier 		close(qp->tcpfd);
2674e7b9544SDavid du Colombier 	if (qp->tcpctlfd > 0) {
2684e7b9544SDavid du Colombier 		hangup(qp->tcpctlfd);
2694e7b9544SDavid du Colombier 		close(qp->tcpctlfd);
2704e7b9544SDavid du Colombier 	}
271a41547ffSDavid du Colombier 	free(qp->dest);
2724e7b9544SDavid du Colombier 	memset(qp, 0, sizeof *qp);	/* prevent accidents */
2734e7b9544SDavid du Colombier 	qp->udpfd = qp->tcpfd = qp->tcpctlfd = -1;
2744e7b9544SDavid du Colombier }
2754e7b9544SDavid du Colombier 
2764e7b9544SDavid du Colombier static void
2773cbadd90SDavid du Colombier destinit(Dest *p)
2783cbadd90SDavid du Colombier {
2793cbadd90SDavid du Colombier 	memset(p, 0, sizeof *p);
2803cbadd90SDavid du Colombier 	p->magic = Destmagic;
2813cbadd90SDavid du Colombier }
2823cbadd90SDavid du Colombier 
2833cbadd90SDavid du Colombier static void
2843cbadd90SDavid du Colombier destck(Dest *p)
2853cbadd90SDavid du Colombier {
2863cbadd90SDavid du Colombier 	assert(p);
2873cbadd90SDavid du Colombier 	assert(p->magic == Destmagic);
2883cbadd90SDavid du Colombier }
2893cbadd90SDavid du Colombier 
290a41547ffSDavid du Colombier /*
291a41547ffSDavid du Colombier  * if the response to a query hasn't arrived within 100 ms.,
292a41547ffSDavid du Colombier  * it's unlikely to arrive at all.  after 1 s., it's really unlikely.
293a41547ffSDavid du Colombier  * queries for missing RRs are likely to produce time-outs rather than
294a41547ffSDavid du Colombier  * negative responses, so cname and aaaa queries are likely to time out,
295a41547ffSDavid du Colombier  * thus we don't wait very long for them.
296a41547ffSDavid du Colombier  */
297a41547ffSDavid du Colombier static void
298a41547ffSDavid du Colombier notestats(vlong start, int tmout, int type)
299a41547ffSDavid du Colombier {
300a41547ffSDavid du Colombier 	qlock(&stats);
301a41547ffSDavid du Colombier 	if (tmout) {
302a41547ffSDavid du Colombier 		stats.tmout++;
303a41547ffSDavid du Colombier 		if (type == Taaaa)
304a41547ffSDavid du Colombier 			stats.tmoutv6++;
305a41547ffSDavid du Colombier 		else if (type == Tcname)
306a41547ffSDavid du Colombier 			stats.tmoutcname++;
307a41547ffSDavid du Colombier 	} else {
308a41547ffSDavid du Colombier 		long wait10ths = NS2MS(nsec() - start) / 100;
309a41547ffSDavid du Colombier 
31081730632SDavid du Colombier 		if (wait10ths <= 0)
311a41547ffSDavid du Colombier 			stats.under10ths[0]++;
312a41547ffSDavid du Colombier 		else if (wait10ths >= nelem(stats.under10ths))
313a41547ffSDavid du Colombier 			stats.under10ths[nelem(stats.under10ths) - 1]++;
314a41547ffSDavid du Colombier 		else
315a41547ffSDavid du Colombier 			stats.under10ths[wait10ths]++;
316a41547ffSDavid du Colombier 	}
317a41547ffSDavid du Colombier 	qunlock(&stats);
318a41547ffSDavid du Colombier }
319a41547ffSDavid du Colombier 
320a41547ffSDavid du Colombier static void
321a41547ffSDavid du Colombier noteinmem(void)
322a41547ffSDavid du Colombier {
323a41547ffSDavid du Colombier 	qlock(&stats);
324a41547ffSDavid du Colombier 	stats.answinmem++;
325a41547ffSDavid du Colombier 	qunlock(&stats);
326a41547ffSDavid du Colombier }
327a41547ffSDavid du Colombier 
3283e12c5d1SDavid du Colombier static RR*
32946595261SDavid du Colombier issuequery(Query *qp, char *name, int class, int depth, int recurse)
3303e12c5d1SDavid du Colombier {
331f46c709fSDavid du Colombier 	char *cp;
332c73252aeSDavid du Colombier 	DN *nsdp;
33346595261SDavid du Colombier 	RR *rp, *nsrp, *dbnsrp;
3343cbadd90SDavid du Colombier 
3353e12c5d1SDavid du Colombier 	/*
3364f8f669cSDavid du Colombier 	 *  if we're running as just a resolver, query our
3377dd7cddfSDavid du Colombier 	 *  designated name servers
338219b2ee8SDavid du Colombier 	 */
3394f8f669cSDavid du Colombier 	if(cfg.resolver){
3407dd7cddfSDavid du Colombier 		nsrp = randomize(getdnsservers(class));
3417dd7cddfSDavid du Colombier 		if(nsrp != nil) {
342c73252aeSDavid du Colombier 			qp->nsrp = nsrp;
343c73252aeSDavid du Colombier 			if(netquery(qp, depth+1)){
3447dd7cddfSDavid du Colombier 				rrfreelist(nsrp);
345c73252aeSDavid du Colombier 				return rrlookup(qp->dp, qp->type, OKneg);
3467dd7cddfSDavid du Colombier 			}
3477dd7cddfSDavid du Colombier 			rrfreelist(nsrp);
3487dd7cddfSDavid du Colombier 		}
349219b2ee8SDavid du Colombier 	}
350219b2ee8SDavid du Colombier 
351219b2ee8SDavid du Colombier 	/*
3523e12c5d1SDavid du Colombier  	 *  walk up the domain name looking for
3533e12c5d1SDavid du Colombier 	 *  a name server for the domain.
3543e12c5d1SDavid du Colombier 	 */
3553e12c5d1SDavid du Colombier 	for(cp = name; cp; cp = walkup(cp)){
3567dd7cddfSDavid du Colombier 		/*
3577dd7cddfSDavid du Colombier 		 *  if this is a local (served by us) domain,
3587dd7cddfSDavid du Colombier 		 *  return answer
3597dd7cddfSDavid du Colombier 		 */
3607dd7cddfSDavid du Colombier 		dbnsrp = randomize(dblookup(cp, class, Tns, 0, 0));
3617dd7cddfSDavid du Colombier 		if(dbnsrp && dbnsrp->local){
362c73252aeSDavid du Colombier 			rp = dblookup(name, class, qp->type, 1, dbnsrp->ttl);
3637dd7cddfSDavid du Colombier 			rrfreelist(dbnsrp);
3647dd7cddfSDavid du Colombier 			return rp;
3657dd7cddfSDavid du Colombier 		}
3667dd7cddfSDavid du Colombier 
3677dd7cddfSDavid du Colombier 		/*
3687dd7cddfSDavid du Colombier 		 *  if recursion isn't set, just accept local
3697dd7cddfSDavid du Colombier 		 *  entries
3707dd7cddfSDavid du Colombier 		 */
3717dd7cddfSDavid du Colombier 		if(recurse == Dontrecurse){
3727dd7cddfSDavid du Colombier 			if(dbnsrp)
3737dd7cddfSDavid du Colombier 				rrfreelist(dbnsrp);
3747dd7cddfSDavid du Colombier 			continue;
3757dd7cddfSDavid du Colombier 		}
3767dd7cddfSDavid du Colombier 
3777dd7cddfSDavid du Colombier 		/* look for ns in cache */
3783e12c5d1SDavid du Colombier 		nsdp = dnlookup(cp, class, 0);
3797dd7cddfSDavid du Colombier 		nsrp = nil;
3803e12c5d1SDavid du Colombier 		if(nsdp)
3817dd7cddfSDavid du Colombier 			nsrp = randomize(rrlookup(nsdp, Tns, NOneg));
3827dd7cddfSDavid du Colombier 
3837dd7cddfSDavid du Colombier 		/* if the entry timed out, ignore it */
3847dd7cddfSDavid du Colombier 		if(nsrp && nsrp->ttl < now){
3857dd7cddfSDavid du Colombier 			rrfreelist(nsrp);
3867dd7cddfSDavid du Colombier 			nsrp = nil;
3877dd7cddfSDavid du Colombier 		}
3883e12c5d1SDavid du Colombier 
3893e12c5d1SDavid du Colombier 		if(nsrp){
3907dd7cddfSDavid du Colombier 			rrfreelist(dbnsrp);
3917dd7cddfSDavid du Colombier 
3924f8f669cSDavid du Colombier 			/* query the name servers found in cache */
393c73252aeSDavid du Colombier 			qp->nsrp = nsrp;
394c73252aeSDavid du Colombier 			if(netquery(qp, depth+1)){
3953e12c5d1SDavid du Colombier 				rrfreelist(nsrp);
396c73252aeSDavid du Colombier 				return rrlookup(qp->dp, qp->type, OKneg);
3977dd7cddfSDavid du Colombier 			}
3987dd7cddfSDavid du Colombier 			rrfreelist(nsrp);
3997dd7cddfSDavid du Colombier 			continue;
4003e12c5d1SDavid du Colombier 		}
4013e12c5d1SDavid du Colombier 
4027dd7cddfSDavid du Colombier 		/* use ns from db */
4037dd7cddfSDavid du Colombier 		if(dbnsrp){
4047dd7cddfSDavid du Colombier 			/* try the name servers found in db */
405c73252aeSDavid du Colombier 			qp->nsrp = dbnsrp;
406c73252aeSDavid du Colombier 			if(netquery(qp, depth+1)){
4073e12c5d1SDavid du Colombier 				/* we got an answer */
4087dd7cddfSDavid du Colombier 				rrfreelist(dbnsrp);
409c73252aeSDavid du Colombier 				return rrlookup(qp->dp, qp->type, NOneg);
4103e12c5d1SDavid du Colombier 			}
4117dd7cddfSDavid du Colombier 			rrfreelist(dbnsrp);
4123e12c5d1SDavid du Colombier 		}
4133e12c5d1SDavid du Colombier 	}
41446595261SDavid du Colombier 	return nil;
415c73252aeSDavid du Colombier }
416c73252aeSDavid du Colombier 
417c73252aeSDavid du Colombier static RR*
418c73252aeSDavid du Colombier dnresolve1(char *name, int class, int type, Request *req, int depth,
419c73252aeSDavid du Colombier 	int recurse)
420c73252aeSDavid du Colombier {
421c73252aeSDavid du Colombier 	Area *area;
422c73252aeSDavid du Colombier 	DN *dp;
423c73252aeSDavid du Colombier 	RR *rp;
424c73252aeSDavid du Colombier 	Query *qp;
425c73252aeSDavid du Colombier 
426c73252aeSDavid du Colombier 	if(debug)
427c73252aeSDavid du Colombier 		dnslog("[%d] dnresolve1 %s %d %d", getpid(), name, type, class);
428c73252aeSDavid du Colombier 
429c73252aeSDavid du Colombier 	/* only class Cin implemented so far */
430c73252aeSDavid du Colombier 	if(class != Cin)
431c73252aeSDavid du Colombier 		return nil;
432c73252aeSDavid du Colombier 
433c73252aeSDavid du Colombier 	dp = dnlookup(name, class, 1);
434c73252aeSDavid du Colombier 
435c73252aeSDavid du Colombier 	/*
436c73252aeSDavid du Colombier 	 *  Try the cache first
437c73252aeSDavid du Colombier 	 */
438c73252aeSDavid du Colombier 	rp = rrlookup(dp, type, OKneg);
439c73252aeSDavid du Colombier 	if(rp)
440c73252aeSDavid du Colombier 		if(rp->db){
441c73252aeSDavid du Colombier 			/* unauthoritative db entries are hints */
442c73252aeSDavid du Colombier 			if(rp->auth) {
443c73252aeSDavid du Colombier 				noteinmem();
444d9924332SDavid du Colombier 				if(debug)
445d9924332SDavid du Colombier 					dnslog("[%d] dnresolve1 %s %d %d: auth rr in db",
446d9924332SDavid du Colombier 						getpid(), name, type, class);
447c73252aeSDavid du Colombier 				return rp;
448c73252aeSDavid du Colombier 			}
449c73252aeSDavid du Colombier 		} else
450c73252aeSDavid du Colombier 			/* cached entry must still be valid */
451c73252aeSDavid du Colombier 			if(rp->ttl > now)
452c73252aeSDavid du Colombier 				/* but Tall entries are special */
453c73252aeSDavid du Colombier 				if(type != Tall || rp->query == Tall) {
454c73252aeSDavid du Colombier 					noteinmem();
455d9924332SDavid du Colombier 					if(debug)
456d9924332SDavid du Colombier 						dnslog("[%d] dnresolve1 %s %d %d: rr not in db",
457d9924332SDavid du Colombier 							getpid(), name, type, class);
458c73252aeSDavid du Colombier 					return rp;
459c73252aeSDavid du Colombier 				}
460c73252aeSDavid du Colombier 	rrfreelist(rp);
46146595261SDavid du Colombier 	rp = nil;		/* accident prevention */
46246595261SDavid du Colombier 	USED(rp);
463c73252aeSDavid du Colombier 
464c73252aeSDavid du Colombier 	/*
465c73252aeSDavid du Colombier 	 * try the cache for a canonical name. if found punt
466c73252aeSDavid du Colombier 	 * since we'll find it during the canonical name search
467c73252aeSDavid du Colombier 	 * in dnresolve().
468c73252aeSDavid du Colombier 	 */
469c73252aeSDavid du Colombier 	if(type != Tcname){
470c73252aeSDavid du Colombier 		rp = rrlookup(dp, Tcname, NOneg);
471c73252aeSDavid du Colombier 		rrfreelist(rp);
472d9924332SDavid du Colombier 		if(rp){
473d9924332SDavid du Colombier 			if(debug)
474d9924332SDavid du Colombier 				dnslog("[%d] dnresolve1 %s %d %d: rr from rrlookup for non-cname",
475d9924332SDavid du Colombier 					getpid(), name, type, class);
476c73252aeSDavid du Colombier 			return nil;
477c73252aeSDavid du Colombier 		}
478d9924332SDavid du Colombier 	}
479c73252aeSDavid du Colombier 
480c73252aeSDavid du Colombier 	/*
481c73252aeSDavid du Colombier 	 * if the domain name is within an area of ours,
482c73252aeSDavid du Colombier 	 * we should have found its data in memory by now.
483c73252aeSDavid du Colombier 	 */
484c73252aeSDavid du Colombier 	area = inmyarea(dp->name);
485c73252aeSDavid du Colombier 	if (area || strncmp(dp->name, "local#", 6) == 0) {
486c73252aeSDavid du Colombier //		char buf[32];
487c73252aeSDavid du Colombier 
488c73252aeSDavid du Colombier //		dnslog("%s %s: no data in area %s", dp->name,
489c73252aeSDavid du Colombier //			rrname(type, buf, sizeof buf), area->soarr->owner->name);
490c73252aeSDavid du Colombier 		return nil;
491c73252aeSDavid du Colombier 	}
492c73252aeSDavid du Colombier 
493c73252aeSDavid du Colombier 	qp = emalloc(sizeof *qp);
494c73252aeSDavid du Colombier 	queryinit(qp, dp, type, req);
49546595261SDavid du Colombier 	rp = issuequery(qp, name, class, depth, recurse);
496c73252aeSDavid du Colombier 	querydestroy(qp);
497c73252aeSDavid du Colombier 	free(qp);
498d9924332SDavid du Colombier 	if(rp){
499d9924332SDavid du Colombier 		if(debug)
500d9924332SDavid du Colombier 			dnslog("[%d] dnresolve1 %s %d %d: rr from query",
501d9924332SDavid du Colombier 				getpid(), name, type, class);
502c73252aeSDavid du Colombier 		return rp;
503d9924332SDavid du Colombier 	}
5043e12c5d1SDavid du Colombier 
5057dd7cddfSDavid du Colombier 	/* settle for a non-authoritative answer */
5067dd7cddfSDavid du Colombier 	rp = rrlookup(dp, type, OKneg);
507d9924332SDavid du Colombier 	if(rp){
508d9924332SDavid du Colombier 		if(debug)
509d9924332SDavid du Colombier 			dnslog("[%d] dnresolve1 %s %d %d: rr from rrlookup",
510d9924332SDavid du Colombier 				getpid(), name, type, class);
5117dd7cddfSDavid du Colombier 		return rp;
512d9924332SDavid du Colombier 	}
5137dd7cddfSDavid du Colombier 
5147dd7cddfSDavid du Colombier 	/* noone answered.  try the database, we might have a chance. */
515d9924332SDavid du Colombier 	rp = dblookup(name, class, type, 0, 0);
516d9924332SDavid du Colombier 	if (rp) {
517d9924332SDavid du Colombier 		if(debug)
518d9924332SDavid du Colombier 			dnslog("[%d] dnresolve1 %s %d %d: rr from dblookup",
519d9924332SDavid du Colombier 				getpid(), name, type, class);
520d9924332SDavid du Colombier 	}else{
521d9924332SDavid du Colombier 		if(debug)
522d9924332SDavid du Colombier 			dnslog("[%d] dnresolve1 %s %d %d: no rr from dblookup; crapped out",
523d9924332SDavid du Colombier 				getpid(), name, type, class);
524d9924332SDavid du Colombier 	}
525d9924332SDavid du Colombier 	return rp;
5263e12c5d1SDavid du Colombier }
5273e12c5d1SDavid du Colombier 
5283e12c5d1SDavid du Colombier /*
5294f8f669cSDavid du Colombier  *  walk a domain name one element to the right.
5304f8f669cSDavid du Colombier  *  return a pointer to that element.
5313e12c5d1SDavid du Colombier  *  in other words, return a pointer to the parent domain name.
5323e12c5d1SDavid du Colombier  */
5333e12c5d1SDavid du Colombier char*
5343e12c5d1SDavid du Colombier walkup(char *name)
5353e12c5d1SDavid du Colombier {
5363e12c5d1SDavid du Colombier 	char *cp;
5373e12c5d1SDavid du Colombier 
5383e12c5d1SDavid du Colombier 	cp = strchr(name, '.');
5393e12c5d1SDavid du Colombier 	if(cp)
5403e12c5d1SDavid du Colombier 		return cp+1;
5413e12c5d1SDavid du Colombier 	else if(*name)
5423e12c5d1SDavid du Colombier 		return "";
5433e12c5d1SDavid du Colombier 	else
5443e12c5d1SDavid du Colombier 		return 0;
5453e12c5d1SDavid du Colombier }
5463e12c5d1SDavid du Colombier 
5473e12c5d1SDavid du Colombier /*
548a41547ffSDavid du Colombier  *  Get a udp port for sending requests and reading replies.  Put the port
5493e12c5d1SDavid du Colombier  *  into "headers" mode.
5503e12c5d1SDavid du Colombier  */
5513e12c5d1SDavid du Colombier static char *hmsg = "headers";
5523e12c5d1SDavid du Colombier 
553dc5a79c1SDavid du Colombier int
5544f8f669cSDavid du Colombier udpport(char *mtpt)
5553e12c5d1SDavid du Colombier {
5563e12c5d1SDavid du Colombier 	int fd, ctl;
5574f8f669cSDavid du Colombier 	char ds[64], adir[64];
5583e12c5d1SDavid du Colombier 
5593e12c5d1SDavid du Colombier 	/* get a udp port */
5604f8f669cSDavid du Colombier 	snprint(ds, sizeof ds, "%s/udp!*!0", (mtpt? mtpt: "/net"));
5617dd7cddfSDavid du Colombier 	ctl = announce(ds, adir);
5627dd7cddfSDavid du Colombier 	if(ctl < 0){
5637dd7cddfSDavid du Colombier 		/* warning("can't get udp port"); */
564bd389b36SDavid du Colombier 		return -1;
565bd389b36SDavid du Colombier 	}
5663e12c5d1SDavid du Colombier 
5673e12c5d1SDavid du Colombier 	/* turn on header style interface */
568410ea80bSDavid du Colombier 	if(write(ctl, hmsg, strlen(hmsg)) != strlen(hmsg)){
5697dd7cddfSDavid du Colombier 		close(ctl);
570bd389b36SDavid du Colombier 		warning(hmsg);
571bd389b36SDavid du Colombier 		return -1;
572bd389b36SDavid du Colombier 	}
5733e12c5d1SDavid du Colombier 
5747dd7cddfSDavid du Colombier 	/* grab the data file */
5754f8f669cSDavid du Colombier 	snprint(ds, sizeof ds, "%s/data", adir);
5767dd7cddfSDavid du Colombier 	fd = open(ds, ORDWR);
5773e12c5d1SDavid du Colombier 	close(ctl);
5784f8f669cSDavid du Colombier 	if(fd < 0)
5794f8f669cSDavid du Colombier 		warning("can't open udp port %s: %r", ds);
5803e12c5d1SDavid du Colombier 	return fd;
5813e12c5d1SDavid du Colombier }
5823e12c5d1SDavid du Colombier 
583d9924332SDavid du Colombier void
584d9924332SDavid du Colombier initdnsmsg(DNSmsg *mp, RR *rp, int flags, ushort reqno)
585d9924332SDavid du Colombier {
586d9924332SDavid du Colombier 	mp->flags = flags;
587d9924332SDavid du Colombier 	mp->id = reqno;
588d9924332SDavid du Colombier 	mp->qd = rp;
589*59f7772cSDavid du Colombier 	if(rp != nil)
590*59f7772cSDavid du Colombier 		mp->qdcount = 1;
591d9924332SDavid du Colombier }
592d9924332SDavid du Colombier 
593d9924332SDavid du Colombier DNSmsg *
594d9924332SDavid du Colombier newdnsmsg(RR *rp, int flags, ushort reqno)
595d9924332SDavid du Colombier {
596d9924332SDavid du Colombier 	DNSmsg *mp;
597d9924332SDavid du Colombier 
598d9924332SDavid du Colombier 	mp = emalloc(sizeof *mp);
599d9924332SDavid du Colombier 	initdnsmsg(mp, rp, flags, reqno);
600d9924332SDavid du Colombier 	return mp;
601d9924332SDavid du Colombier }
602d9924332SDavid du Colombier 
603d6d99297SDavid du Colombier /* generate a DNS UDP query packet */
604dc5a79c1SDavid du Colombier int
605dc5a79c1SDavid du Colombier mkreq(DN *dp, int type, uchar *buf, int flags, ushort reqno)
6063e12c5d1SDavid du Colombier {
6073e12c5d1SDavid du Colombier 	DNSmsg m;
6083e12c5d1SDavid du Colombier 	int len;
609f27a9a5aSDavid du Colombier 	Udphdr *uh = (Udphdr*)buf;
610d9924332SDavid du Colombier 	RR *rp;
6113e12c5d1SDavid du Colombier 
6123e12c5d1SDavid du Colombier 	/* stuff port number into output buffer */
6133cbadd90SDavid du Colombier 	memset(uh, 0, sizeof *uh);
6147dd7cddfSDavid du Colombier 	hnputs(uh->rport, 53);
6153e12c5d1SDavid du Colombier 
6163e12c5d1SDavid du Colombier 	/* make request and convert it to output format */
6173cbadd90SDavid du Colombier 	memset(&m, 0, sizeof m);
618d9924332SDavid du Colombier 	rp = rralloc(type);
619d9924332SDavid du Colombier 	rp->owner = dp;
620d9924332SDavid du Colombier 	initdnsmsg(&m, rp, flags, reqno);
621f27a9a5aSDavid du Colombier 	len = convDNS2M(&m, &buf[Udphdrsize], Maxudp);
6227dd7cddfSDavid du Colombier 	rrfree(m.qd);
6236aaebd7dSDavid du Colombier 	memset(&m, 0, sizeof m);		/* cause trouble */
6243e12c5d1SDavid du Colombier 	return len;
6253e12c5d1SDavid du Colombier }
6263e12c5d1SDavid du Colombier 
627a41547ffSDavid du Colombier void
6287dd7cddfSDavid du Colombier freeanswers(DNSmsg *mp)
6293e12c5d1SDavid du Colombier {
630e6d9d902SDavid du Colombier 	rrfreelistptr(&mp->qd);
631e6d9d902SDavid du Colombier 	rrfreelistptr(&mp->an);
632e6d9d902SDavid du Colombier 	rrfreelistptr(&mp->ns);
633e6d9d902SDavid du Colombier 	rrfreelistptr(&mp->ar);
6347dd7cddfSDavid du Colombier }
6357dd7cddfSDavid du Colombier 
636b8b25780SDavid du Colombier /* timed read of reply.  sets srcip */
6373cbadd90SDavid du Colombier static int
638b8b25780SDavid du Colombier readnet(Query *qp, int medium, uchar *ibuf, uvlong endms, uchar **replyp,
6393cbadd90SDavid du Colombier 	uchar *srcip)
6403cbadd90SDavid du Colombier {
6413cbadd90SDavid du Colombier 	int len, fd;
642a41547ffSDavid du Colombier 	long ms;
643a41547ffSDavid du Colombier 	vlong startns = nsec();
6443cbadd90SDavid du Colombier 	uchar *reply;
6453cbadd90SDavid du Colombier 	uchar lenbuf[2];
6463cbadd90SDavid du Colombier 
6473cbadd90SDavid du Colombier 	len = -1;			/* pessimism */
648b8b25780SDavid du Colombier 	ms = endms - NS2MS(startns);
649b8b25780SDavid du Colombier 	if (ms <= 0)
650b8b25780SDavid du Colombier 		return -1;		/* taking too long */
651b8b25780SDavid du Colombier 
652b8b25780SDavid du Colombier 	reply = ibuf;
6534e7b9544SDavid du Colombier 	memset(srcip, 0, IPaddrlen);
654b8b25780SDavid du Colombier 	alarm(ms);
655a41547ffSDavid du Colombier 	if (medium == Udp)
6564e7b9544SDavid du Colombier 		if (qp->udpfd <= 0)
6574e7b9544SDavid du Colombier 			dnslog("readnet: qp->udpfd closed");
6584e7b9544SDavid du Colombier 		else {
659f27a9a5aSDavid du Colombier 			len = read(qp->udpfd, ibuf, Udphdrsize+Maxudpin);
660a41547ffSDavid du Colombier 			alarm(0);
661a41547ffSDavid du Colombier 			notestats(startns, len < 0, qp->type);
6624e7b9544SDavid du Colombier 			if (len >= IPaddrlen)
6634e7b9544SDavid du Colombier 				memmove(srcip, ibuf, IPaddrlen);
664f27a9a5aSDavid du Colombier 			if (len >= Udphdrsize) {
665f27a9a5aSDavid du Colombier 				len   -= Udphdrsize;
666f27a9a5aSDavid du Colombier 				reply += Udphdrsize;
6674e7b9544SDavid du Colombier 			}
6684e7b9544SDavid du Colombier 		}
669a41547ffSDavid du Colombier 	else {
6703cbadd90SDavid du Colombier 		if (!qp->tcpset)
6713cbadd90SDavid du Colombier 			dnslog("readnet: tcp params not set");
6723cbadd90SDavid du Colombier 		fd = qp->tcpfd;
6733cbadd90SDavid du Colombier 		if (fd <= 0)
6743cbadd90SDavid du Colombier 			dnslog("readnet: %s: tcp fd unset for dest %I",
6753cbadd90SDavid du Colombier 				qp->dp->name, qp->tcpip);
6763cbadd90SDavid du Colombier 		else if (readn(fd, lenbuf, 2) != 2) {
677e6d9d902SDavid du Colombier 			dnslog("readnet: short read of 2-byte tcp msg size from %I",
6783cbadd90SDavid du Colombier 				qp->tcpip);
679a41547ffSDavid du Colombier 			/* probably a time-out */
680a41547ffSDavid du Colombier 			notestats(startns, 1, qp->type);
6813cbadd90SDavid du Colombier 		} else {
6823cbadd90SDavid du Colombier 			len = lenbuf[0]<<8 | lenbuf[1];
6833cbadd90SDavid du Colombier 			if (readn(fd, ibuf, len) != len) {
6843cbadd90SDavid du Colombier 				dnslog("readnet: short read of tcp data from %I",
6853cbadd90SDavid du Colombier 					qp->tcpip);
686a41547ffSDavid du Colombier 				/* probably a time-out */
687a41547ffSDavid du Colombier 				notestats(startns, 1, qp->type);
6883cbadd90SDavid du Colombier 				len = -1;
6893cbadd90SDavid du Colombier 			}
6903cbadd90SDavid du Colombier 		}
6913cbadd90SDavid du Colombier 		memmove(srcip, qp->tcpip, IPaddrlen);
6923cbadd90SDavid du Colombier 	}
693b8b25780SDavid du Colombier 	alarm(0);
6943cbadd90SDavid du Colombier 	*replyp = reply;
6953cbadd90SDavid du Colombier 	return len;
6963cbadd90SDavid du Colombier }
6973cbadd90SDavid du Colombier 
6987dd7cddfSDavid du Colombier /*
6993cbadd90SDavid du Colombier  *  read replies to a request and remember the rrs in the answer(s).
7003cbadd90SDavid du Colombier  *  ignore any of the wrong type.
701b8b25780SDavid du Colombier  *  wait at most until endms.
7027dd7cddfSDavid du Colombier  */
7037dd7cddfSDavid du Colombier static int
7043cbadd90SDavid du Colombier readreply(Query *qp, int medium, ushort req, uchar *ibuf, DNSmsg *mp,
705b8b25780SDavid du Colombier 	uvlong endms)
7067dd7cddfSDavid du Colombier {
707b8b25780SDavid du Colombier 	int len;
7087dd7cddfSDavid du Colombier 	char *err;
709a41547ffSDavid du Colombier 	char tbuf[32];
7103cbadd90SDavid du Colombier 	uchar *reply;
7113cbadd90SDavid du Colombier 	uchar srcip[IPaddrlen];
7123e12c5d1SDavid du Colombier 	RR *rp;
7137dd7cddfSDavid du Colombier 
7143cbadd90SDavid du Colombier 	queryck(qp);
7153cbadd90SDavid du Colombier 	memset(mp, 0, sizeof *mp);
716a41547ffSDavid du Colombier 	memset(srcip, 0, sizeof srcip);
717a41547ffSDavid du Colombier 	if (0)
718a41547ffSDavid du Colombier 		len = -1;
719b8b25780SDavid du Colombier 	for (; timems() < endms &&
720b8b25780SDavid du Colombier 	    (len = readnet(qp, medium, ibuf, endms, &reply, srcip)) >= 0;
7213cbadd90SDavid du Colombier 	    freeanswers(mp)){
7227dd7cddfSDavid du Colombier 		/* convert into internal format  */
7233cbadd90SDavid du Colombier 		memset(mp, 0, sizeof *mp);
7243cbadd90SDavid du Colombier 		err = convM2DNS(reply, len, mp, nil);
725d6d99297SDavid du Colombier 		if (mp->flags & Ftrunc) {
726a41547ffSDavid du Colombier 			free(err);
727a41547ffSDavid du Colombier 			freeanswers(mp);
728a41547ffSDavid du Colombier 			/* notify our caller to retry the query via tcp. */
7293cbadd90SDavid du Colombier 			return -1;
7303cbadd90SDavid du Colombier 		} else if(err){
7313cbadd90SDavid du Colombier 			dnslog("readreply: %s: input err, len %d: %s: %I",
7323cbadd90SDavid du Colombier 				qp->dp->name, len, err, srcip);
7333cbadd90SDavid du Colombier 			free(err);
7347dd7cddfSDavid du Colombier 			continue;
7357dd7cddfSDavid du Colombier 		}
7367dd7cddfSDavid du Colombier 		if(debug)
7373cbadd90SDavid du Colombier 			logreply(qp->req->id, srcip, mp);
7387dd7cddfSDavid du Colombier 
7397dd7cddfSDavid du Colombier 		/* answering the right question? */
7403cbadd90SDavid du Colombier 		if(mp->id != req)
7413cbadd90SDavid du Colombier 			dnslog("%d: id %d instead of %d: %I", qp->req->id,
7423cbadd90SDavid du Colombier 				mp->id, req, srcip);
7433cbadd90SDavid du Colombier 		else if(mp->qd == 0)
7443cbadd90SDavid du Colombier 			dnslog("%d: no question RR: %I", qp->req->id, srcip);
7453cbadd90SDavid du Colombier 		else if(mp->qd->owner != qp->dp)
7463cbadd90SDavid du Colombier 			dnslog("%d: owner %s instead of %s: %I", qp->req->id,
7473cbadd90SDavid du Colombier 				mp->qd->owner->name, qp->dp->name, srcip);
7483cbadd90SDavid du Colombier 		else if(mp->qd->type != qp->type)
7493cbadd90SDavid du Colombier 			dnslog("%d: qp->type %d instead of %d: %I",
7503cbadd90SDavid du Colombier 				qp->req->id, mp->qd->type, qp->type, srcip);
7513cbadd90SDavid du Colombier 		else {
7527dd7cddfSDavid du Colombier 			/* remember what request this is in answer to */
7537dd7cddfSDavid du Colombier 			for(rp = mp->an; rp; rp = rp->next)
7543cbadd90SDavid du Colombier 				rp->query = qp->type;
755b8b25780SDavid du Colombier 			return 0;
7567dd7cddfSDavid du Colombier 		}
7577dd7cddfSDavid du Colombier 	}
758b8b25780SDavid du Colombier 	if (timems() >= endms) {
759a41547ffSDavid du Colombier 		;				/* query expired */
760a41547ffSDavid du Colombier 	} else if (0) {
761a41547ffSDavid du Colombier 		/* this happens routinely when a read times out */
762a41547ffSDavid du Colombier 		dnslog("readreply: %s type %s: ns %I read error or eof "
763a41547ffSDavid du Colombier 			"(returned %d): %r", qp->dp->name, rrname(qp->type,
764a41547ffSDavid du Colombier 			tbuf, sizeof tbuf), srcip, len);
765a41547ffSDavid du Colombier 		if (medium == Udp)
766a41547ffSDavid du Colombier 			for (rp = qp->nsrp; rp != nil; rp = rp->next)
767a41547ffSDavid du Colombier 				if (rp->type == Tns)
768a41547ffSDavid du Colombier 					dnslog("readreply: %s: query sent to "
769a41547ffSDavid du Colombier 						"ns %s", qp->dp->name,
770a41547ffSDavid du Colombier 						rp->host->name);
771a41547ffSDavid du Colombier 	}
7723cbadd90SDavid du Colombier 	return -1;
7733cbadd90SDavid du Colombier }
7747dd7cddfSDavid du Colombier 
7757dd7cddfSDavid du Colombier /*
7767dd7cddfSDavid du Colombier  *	return non-0 if first list includes second list
7777dd7cddfSDavid du Colombier  */
7787dd7cddfSDavid du Colombier int
7797dd7cddfSDavid du Colombier contains(RR *rp1, RR *rp2)
7807dd7cddfSDavid du Colombier {
7817dd7cddfSDavid du Colombier 	RR *trp1, *trp2;
7827dd7cddfSDavid du Colombier 
7837dd7cddfSDavid du Colombier 	for(trp2 = rp2; trp2; trp2 = trp2->next){
7843cbadd90SDavid du Colombier 		for(trp1 = rp1; trp1; trp1 = trp1->next)
7857dd7cddfSDavid du Colombier 			if(trp1->type == trp2->type)
7867dd7cddfSDavid du Colombier 			if(trp1->host == trp2->host)
7877dd7cddfSDavid du Colombier 			if(trp1->owner == trp2->owner)
7887dd7cddfSDavid du Colombier 				break;
7894f8f669cSDavid du Colombier 		if(trp1 == nil)
7907dd7cddfSDavid du Colombier 			return 0;
7917dd7cddfSDavid du Colombier 	}
7927dd7cddfSDavid du Colombier 	return 1;
7937dd7cddfSDavid du Colombier }
7947dd7cddfSDavid du Colombier 
7957dd7cddfSDavid du Colombier 
7966b6b9ac8SDavid du Colombier /*
7976b6b9ac8SDavid du Colombier  *  return multicast version if any
7986b6b9ac8SDavid du Colombier  */
7996b6b9ac8SDavid du Colombier int
8006b6b9ac8SDavid du Colombier ipisbm(uchar *ip)
8016b6b9ac8SDavid du Colombier {
8026b6b9ac8SDavid du Colombier 	if(isv4(ip)){
8034f8f669cSDavid du Colombier 		if (ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0 ||
8044f8f669cSDavid du Colombier 		    ipcmp(ip, IPv4bcast) == 0)
8056b6b9ac8SDavid du Colombier 			return 4;
8064f8f669cSDavid du Colombier 	} else
8076b6b9ac8SDavid du Colombier 		if(ip[0] == 0xff)
8086b6b9ac8SDavid du Colombier 			return 6;
80928a8a86bSDavid du Colombier 	return 0;
81028a8a86bSDavid du Colombier }
81128a8a86bSDavid du Colombier 
8127dd7cddfSDavid du Colombier /*
813*59f7772cSDavid du Colombier  *  Get next server address(es) into qp->dest[nd] and beyond
8147dd7cddfSDavid du Colombier  */
8157dd7cddfSDavid du Colombier static int
8163cbadd90SDavid du Colombier serveraddrs(Query *qp, int nd, int depth)
8177dd7cddfSDavid du Colombier {
8187dd7cddfSDavid du Colombier 	RR *rp, *arp, *trp;
8197dd7cddfSDavid du Colombier 	Dest *cur;
8207dd7cddfSDavid du Colombier 
8217dd7cddfSDavid du Colombier 	if(nd >= Maxdest)
8227dd7cddfSDavid du Colombier 		return 0;
8237dd7cddfSDavid du Colombier 
8247dd7cddfSDavid du Colombier 	/*
8257dd7cddfSDavid du Colombier 	 *  look for a server whose address we already know.
8267dd7cddfSDavid du Colombier 	 *  if we find one, mark it so we ignore this on
8277dd7cddfSDavid du Colombier 	 *  subsequent passes.
8287dd7cddfSDavid du Colombier 	 */
8297dd7cddfSDavid du Colombier 	arp = 0;
8303cbadd90SDavid du Colombier 	for(rp = qp->nsrp; rp; rp = rp->next){
83134f77ae3SDavid du Colombier 		assert(rp->magic == RRmagic);
8327dd7cddfSDavid du Colombier 		if(rp->marker)
8337dd7cddfSDavid du Colombier 			continue;
8347dd7cddfSDavid du Colombier 		arp = rrlookup(rp->host, Ta, NOneg);
835d9924332SDavid du Colombier 		if(arp == nil)
836d9924332SDavid du Colombier 			arp = rrlookup(rp->host, Taaaa, NOneg);
8377dd7cddfSDavid du Colombier 		if(arp){
8387dd7cddfSDavid du Colombier 			rp->marker = 1;
8397dd7cddfSDavid du Colombier 			break;
8407dd7cddfSDavid du Colombier 		}
8417dd7cddfSDavid du Colombier 		arp = dblookup(rp->host->name, Cin, Ta, 0, 0);
842d9924332SDavid du Colombier 		if(arp == nil)
843d9924332SDavid du Colombier 			arp = dblookup(rp->host->name, Cin, Taaaa, 0, 0);
8447dd7cddfSDavid du Colombier 		if(arp){
8457dd7cddfSDavid du Colombier 			rp->marker = 1;
8467dd7cddfSDavid du Colombier 			break;
8477dd7cddfSDavid du Colombier 		}
8487dd7cddfSDavid du Colombier 	}
8497dd7cddfSDavid du Colombier 
8507dd7cddfSDavid du Colombier 	/*
8517dd7cddfSDavid du Colombier 	 *  if the cache and database lookup didn't find any new
8527dd7cddfSDavid du Colombier 	 *  server addresses, try resolving one via the network.
8537dd7cddfSDavid du Colombier 	 *  Mark any we try to resolve so we don't try a second time.
8547dd7cddfSDavid du Colombier 	 */
8554f8f669cSDavid du Colombier 	if(arp == 0)
8563cbadd90SDavid du Colombier 		for(rp = qp->nsrp; rp; rp = rp->next){
8577dd7cddfSDavid du Colombier 			if(rp->marker)
8587dd7cddfSDavid du Colombier 				continue;
8597dd7cddfSDavid du Colombier 			rp->marker = 1;
8607dd7cddfSDavid du Colombier 
8617dd7cddfSDavid du Colombier 			/*
8627dd7cddfSDavid du Colombier 			 *  avoid loops looking up a server under itself
8637dd7cddfSDavid du Colombier 			 */
8647dd7cddfSDavid du Colombier 			if(subsume(rp->owner->name, rp->host->name))
8657dd7cddfSDavid du Colombier 				continue;
8667dd7cddfSDavid du Colombier 
8673cbadd90SDavid du Colombier 			arp = dnresolve(rp->host->name, Cin, Ta, qp->req, 0,
8684f8f669cSDavid du Colombier 				depth+1, Recurse, 1, 0);
869d9924332SDavid du Colombier 			if(arp == nil)
870d9924332SDavid du Colombier 				arp = dnresolve(rp->host->name, Cin, Taaaa,
871d9924332SDavid du Colombier 					qp->req, 0, depth+1, Recurse, 1, 0);
872530fef66SDavid du Colombier 			lock(&dnlock);
8737dd7cddfSDavid du Colombier 			rrfreelist(rrremneg(&arp));
874530fef66SDavid du Colombier 			unlock(&dnlock);
8757dd7cddfSDavid du Colombier 			if(arp)
8767dd7cddfSDavid du Colombier 				break;
8777dd7cddfSDavid du Colombier 		}
8787dd7cddfSDavid du Colombier 
8797dd7cddfSDavid du Colombier 	/* use any addresses that we found */
8803cbadd90SDavid du Colombier 	for(trp = arp; trp && nd < Maxdest; trp = trp->next){
8813cbadd90SDavid du Colombier 		cur = &qp->dest[nd];
8827dd7cddfSDavid du Colombier 		parseip(cur->a, trp->ip->name);
8834f8f669cSDavid du Colombier 		/*
8844f8f669cSDavid du Colombier 		 * straddling servers can reject all nameservers if they are all
8854f8f669cSDavid du Colombier 		 * inside, so be sure to list at least one outside ns at
8864f8f669cSDavid du Colombier 		 * the end of the ns list in /lib/ndb for `dom='.
8874f8f669cSDavid du Colombier 		 */
8884f8f669cSDavid du Colombier 		if (ipisbm(cur->a) ||
8893cbadd90SDavid du Colombier 		    cfg.straddle && !insideaddr(qp->dp->name) && insidens(cur->a))
8906b6b9ac8SDavid du Colombier 			continue;
8917dd7cddfSDavid du Colombier 		cur->nx = 0;
8927dd7cddfSDavid du Colombier 		cur->s = trp->owner;
8937dd7cddfSDavid du Colombier 		cur->code = Rtimeout;
8946b6b9ac8SDavid du Colombier 		nd++;
8957dd7cddfSDavid du Colombier 	}
8967dd7cddfSDavid du Colombier 	rrfreelist(arp);
8977dd7cddfSDavid du Colombier 	return nd;
8987dd7cddfSDavid du Colombier }
8997dd7cddfSDavid du Colombier 
9007dd7cddfSDavid du Colombier /*
9017dd7cddfSDavid du Colombier  *  cache negative responses
9027dd7cddfSDavid du Colombier  */
9037dd7cddfSDavid du Colombier static void
9047dd7cddfSDavid du Colombier cacheneg(DN *dp, int type, int rcode, RR *soarr)
9057dd7cddfSDavid du Colombier {
9067dd7cddfSDavid du Colombier 	RR *rp;
9077dd7cddfSDavid du Colombier 	DN *soaowner;
9089a747e4fSDavid du Colombier 	ulong ttl;
9097dd7cddfSDavid du Colombier 
910f46c709fSDavid du Colombier 	stats.negcached++;
911f46c709fSDavid du Colombier 
9124f8f669cSDavid du Colombier 	/* no cache time specified, don't make anything up */
9137dd7cddfSDavid du Colombier 	if(soarr != nil){
914e6d9d902SDavid du Colombier 		if(soarr->next != nil)
915e6d9d902SDavid du Colombier 			rrfreelistptr(&soarr->next);
9167dd7cddfSDavid du Colombier 		soaowner = soarr->owner;
9177dd7cddfSDavid du Colombier 	} else
9187dd7cddfSDavid du Colombier 		soaowner = nil;
9197dd7cddfSDavid du Colombier 
9209a747e4fSDavid du Colombier 	/* the attach can cause soarr to be freed so mine it now */
9219a747e4fSDavid du Colombier 	if(soarr != nil && soarr->soa != nil)
9229a747e4fSDavid du Colombier 		ttl = soarr->soa->minttl+now;
9239a747e4fSDavid du Colombier 	else
9249a747e4fSDavid du Colombier 		ttl = 5*Min;
9259a747e4fSDavid du Colombier 
9267dd7cddfSDavid du Colombier 	/* add soa and negative RR to the database */
9276dc4800dSDavid du Colombier 	rrattach(soarr, Authoritative);
9287dd7cddfSDavid du Colombier 
9297dd7cddfSDavid du Colombier 	rp = rralloc(type);
9307dd7cddfSDavid du Colombier 	rp->owner = dp;
9317dd7cddfSDavid du Colombier 	rp->negative = 1;
9327dd7cddfSDavid du Colombier 	rp->negsoaowner = soaowner;
9337dd7cddfSDavid du Colombier 	rp->negrcode = rcode;
9349a747e4fSDavid du Colombier 	rp->ttl = ttl;
9356dc4800dSDavid du Colombier 	rrattach(rp, Authoritative);
9367dd7cddfSDavid du Colombier }
9377dd7cddfSDavid du Colombier 
9384f8f669cSDavid du Colombier static int
9394f8f669cSDavid du Colombier setdestoutns(Dest *p, int n)
9404f8f669cSDavid du Colombier {
9414f8f669cSDavid du Colombier 	uchar *outns = outsidens(n);
9424f8f669cSDavid du Colombier 
9433cbadd90SDavid du Colombier 	destck(p);
9443cbadd90SDavid du Colombier 	destinit(p);
9454f8f669cSDavid du Colombier 	if (outns == nil) {
9464f8f669cSDavid du Colombier 		if (n == 0)
9474f8f669cSDavid du Colombier 			dnslog("[%d] no outside-ns in ndb", getpid());
9484f8f669cSDavid du Colombier 		return -1;
9494f8f669cSDavid du Colombier 	}
9504f8f669cSDavid du Colombier 	memmove(p->a, outns, sizeof p->a);
9514f8f669cSDavid du Colombier 	p->s = dnlookup("outside-ns-ips", Cin, 1);
9524f8f669cSDavid du Colombier 	return 0;
9534f8f669cSDavid du Colombier }
9544f8f669cSDavid du Colombier 
9557dd7cddfSDavid du Colombier /*
9563cbadd90SDavid du Colombier  * issue query via UDP or TCP as appropriate.
9573cbadd90SDavid du Colombier  * for TCP, returns with qp->tcpip set from udppkt header.
9587dd7cddfSDavid du Colombier  */
9597dd7cddfSDavid du Colombier static int
9603cbadd90SDavid du Colombier mydnsquery(Query *qp, int medium, uchar *udppkt, int len)
9617dd7cddfSDavid du Colombier {
962a41547ffSDavid du Colombier 	int rv = -1, nfd;
9633cbadd90SDavid du Colombier 	char *domain;
964cc499a30SDavid du Colombier 	char conndir[40], net[40];
965a41547ffSDavid du Colombier 	uchar belen[2];
9663cbadd90SDavid du Colombier 	NetConnInfo *nci;
9674f8f669cSDavid du Colombier 
9683cbadd90SDavid du Colombier 	queryck(qp);
9694e153993SDavid du Colombier 	domain = smprint("%I", udppkt);
9704e153993SDavid du Colombier 	if (myaddr(domain)) {
9714e153993SDavid du Colombier 		dnslog("mydnsquery: trying to send to myself (%s); bzzzt",
9724e153993SDavid du Colombier 			domain);
9734e153993SDavid du Colombier 		free(domain);
9744e153993SDavid du Colombier 		return rv;
9754e153993SDavid du Colombier 	}
9764e153993SDavid du Colombier 
9773cbadd90SDavid du Colombier 	switch (medium) {
9783cbadd90SDavid du Colombier 	case Udp:
9794e153993SDavid du Colombier 		free(domain);
980a41547ffSDavid du Colombier 		nfd = dup(qp->udpfd, -1);
981a41547ffSDavid du Colombier 		if (nfd < 0) {
982a41547ffSDavid du Colombier 			warning("mydnsquery: qp->udpfd %d: %r", qp->udpfd);
983a41547ffSDavid du Colombier 			close(qp->udpfd);	/* ensure it's closed */
984a41547ffSDavid du Colombier 			qp->udpfd = -1;		/* poison it */
985a41547ffSDavid du Colombier 			return rv;
986a41547ffSDavid du Colombier 		}
987a41547ffSDavid du Colombier 		close(nfd);
988a41547ffSDavid du Colombier 
9894e7b9544SDavid du Colombier 		if (qp->udpfd <= 0)
990a41547ffSDavid du Colombier 			dnslog("mydnsquery: qp->udpfd %d closed", qp->udpfd);
9914e7b9544SDavid du Colombier 		else {
992f27a9a5aSDavid du Colombier 			if (write(qp->udpfd, udppkt, len+Udphdrsize) !=
993f27a9a5aSDavid du Colombier 			    len+Udphdrsize)
994a41547ffSDavid du Colombier 				warning("sending udp msg: %r");
995a41547ffSDavid du Colombier 			else {
996a41547ffSDavid du Colombier 				stats.qsent++;
9973cbadd90SDavid du Colombier 				rv = 0;
9984e7b9544SDavid du Colombier 			}
999a41547ffSDavid du Colombier 		}
10003cbadd90SDavid du Colombier 		break;
10013cbadd90SDavid du Colombier 	case Tcp:
10023cbadd90SDavid du Colombier 		/* send via TCP & keep fd around for reply */
1003cc499a30SDavid du Colombier 		snprint(net, sizeof net, "%s/tcp",
1004cc499a30SDavid du Colombier 			(mntpt[0] != '\0'? mntpt: "/net"));
10053cbadd90SDavid du Colombier 		alarm(10*1000);
1006cc499a30SDavid du Colombier 		qp->tcpfd = rv = dial(netmkaddr(domain, net, "dns"), nil,
10073cbadd90SDavid du Colombier 			conndir, &qp->tcpctlfd);
10083cbadd90SDavid du Colombier 		alarm(0);
10093cbadd90SDavid du Colombier 		if (qp->tcpfd < 0) {
10103cbadd90SDavid du Colombier 			dnslog("can't dial tcp!%s!dns: %r", domain);
1011a41547ffSDavid du Colombier 			free(domain);
1012a41547ffSDavid du Colombier 			break;
1013a41547ffSDavid du Colombier 		}
1014a41547ffSDavid du Colombier 		free(domain);
10153cbadd90SDavid du Colombier 		nci = getnetconninfo(conndir, qp->tcpfd);
10163cbadd90SDavid du Colombier 		if (nci) {
10173cbadd90SDavid du Colombier 			parseip(qp->tcpip, nci->rsys);
10183cbadd90SDavid du Colombier 			freenetconninfo(nci);
10193cbadd90SDavid du Colombier 		} else
10203cbadd90SDavid du Colombier 			dnslog("mydnsquery: getnetconninfo failed");
10213cbadd90SDavid du Colombier 		qp->tcpset = 1;
10223e12c5d1SDavid du Colombier 
10233cbadd90SDavid du Colombier 		belen[0] = len >> 8;
10243cbadd90SDavid du Colombier 		belen[1] = len;
10253cbadd90SDavid du Colombier 		if (write(qp->tcpfd, belen, 2) != 2 ||
1026f27a9a5aSDavid du Colombier 		    write(qp->tcpfd, udppkt + Udphdrsize, len) != len)
1027a41547ffSDavid du Colombier 			warning("sending tcp msg: %r");
10283cbadd90SDavid du Colombier 		break;
10293cbadd90SDavid du Colombier 	default:
10303cbadd90SDavid du Colombier 		sysfatal("mydnsquery: bad medium");
10313cbadd90SDavid du Colombier 	}
10323cbadd90SDavid du Colombier 	return rv;
10333cbadd90SDavid du Colombier }
10347dd7cddfSDavid du Colombier 
10353e12c5d1SDavid du Colombier /*
10363cbadd90SDavid du Colombier  * send query to all UDP destinations or one TCP destination,
10373cbadd90SDavid du Colombier  * taken from obuf (udp packet) header
10383e12c5d1SDavid du Colombier  */
10393cbadd90SDavid du Colombier static int
10403cbadd90SDavid du Colombier xmitquery(Query *qp, int medium, int depth, uchar *obuf, int inns, int len)
10413cbadd90SDavid du Colombier {
10423cbadd90SDavid du Colombier 	int j, n;
10433cbadd90SDavid du Colombier 	char buf[32];
10443cbadd90SDavid du Colombier 	Dest *p;
10453e12c5d1SDavid du Colombier 
10463cbadd90SDavid du Colombier 	queryck(qp);
1047b8b25780SDavid du Colombier 	if(timems() >= qp->req->aborttime)
10483cbadd90SDavid du Colombier 		return -1;
10493cbadd90SDavid du Colombier 
10503cbadd90SDavid du Colombier 	/*
10513cbadd90SDavid du Colombier 	 * get a nameserver address if we need one.
10523cbadd90SDavid du Colombier 	 * serveraddrs populates qp->dest.
10533cbadd90SDavid du Colombier 	 */
10543cbadd90SDavid du Colombier 	p = qp->dest;
10553cbadd90SDavid du Colombier 	destck(p);
10563cbadd90SDavid du Colombier 	if (qp->ndest < 0 || qp->ndest > Maxdest)
10573cbadd90SDavid du Colombier 		dnslog("qp->ndest %d out of range", qp->ndest);
10583468a491SDavid du Colombier 	if (qp->ndest > qp->curdest - p) {
10593468a491SDavid du Colombier 		j = serveraddrs(qp, qp->curdest - p, depth);
10603468a491SDavid du Colombier 		if (j < 0 || j >= Maxdest) {
10613468a491SDavid du Colombier 			dnslog("serveraddrs() result %d out of range", j);
10623468a491SDavid du Colombier 			abort();
10633468a491SDavid du Colombier 		}
10643468a491SDavid du Colombier 		qp->curdest = &qp->dest[j];
10653468a491SDavid du Colombier 	}
10663cbadd90SDavid du Colombier 	destck(qp->curdest);
10677dd7cddfSDavid du Colombier 
10687dd7cddfSDavid du Colombier 	/* no servers, punt */
10693468a491SDavid du Colombier 	if (qp->ndest == 0)
10704f8f669cSDavid du Colombier 		if (cfg.straddle && cfg.inside) {
10713cbadd90SDavid du Colombier 			/* get ips of "outside-ns-ips" */
10723cbadd90SDavid du Colombier 			p = qp->curdest = qp->dest;
10733cbadd90SDavid du Colombier 			for(n = 0; n < Maxdest; n++, qp->curdest++)
10743cbadd90SDavid du Colombier 				if (setdestoutns(qp->curdest, n) < 0)
10757dd7cddfSDavid du Colombier 					break;
10764f8f669cSDavid du Colombier 		} else {
10774e7b9544SDavid du Colombier 			/* it's probably just a bogus domain, don't log it */
10784e7b9544SDavid du Colombier 			// dnslog("xmitquery: %s: no nameservers", qp->dp->name);
10793cbadd90SDavid du Colombier 			return -1;
10804f8f669cSDavid du Colombier 		}
10817dd7cddfSDavid du Colombier 
10823cbadd90SDavid du Colombier 	/* send to first 'qp->ndest' destinations */
10837dd7cddfSDavid du Colombier 	j = 0;
10843cbadd90SDavid du Colombier 	if (medium == Tcp) {
10853cbadd90SDavid du Colombier 		j++;
10863cbadd90SDavid du Colombier 		queryck(qp);
10873cbadd90SDavid du Colombier 		assert(qp->dp);
10883cbadd90SDavid du Colombier 		procsetname("tcp %sside query for %s %s", (inns? "in": "out"),
10893cbadd90SDavid du Colombier 			qp->dp->name, rrname(qp->type, buf, sizeof buf));
10903cbadd90SDavid du Colombier 		mydnsquery(qp, medium, obuf, len); /* sets qp->tcpip from obuf */
10913cbadd90SDavid du Colombier 		if(debug)
10923cbadd90SDavid du Colombier 			logsend(qp->req->id, depth, qp->tcpip, "", qp->dp->name,
10933cbadd90SDavid du Colombier 				qp->type);
10943cbadd90SDavid du Colombier 	} else
10953cbadd90SDavid du Colombier 		for(; p < &qp->dest[qp->ndest] && p < qp->curdest; p++){
10967dd7cddfSDavid du Colombier 			/* skip destinations we've finished with */
10977dd7cddfSDavid du Colombier 			if(p->nx >= Maxtrans)
10987dd7cddfSDavid du Colombier 				continue;
10997dd7cddfSDavid du Colombier 
11007dd7cddfSDavid du Colombier 			j++;
11017dd7cddfSDavid du Colombier 
11027dd7cddfSDavid du Colombier 			/* exponential backoff of requests */
11033cbadd90SDavid du Colombier 			if((1<<p->nx) > qp->ndest)
11047dd7cddfSDavid du Colombier 				continue;
11057dd7cddfSDavid du Colombier 
1106*59f7772cSDavid du Colombier 			if(memcmp(p->a, IPnoaddr, sizeof IPnoaddr) == 0)
1107*59f7772cSDavid du Colombier 				continue;		/* mistake */
1108*59f7772cSDavid du Colombier 
11093cbadd90SDavid du Colombier 			procsetname("udp %sside query to %I/%s %s %s",
11103cbadd90SDavid du Colombier 				(inns? "in": "out"), p->a, p->s->name,
11113cbadd90SDavid du Colombier 				qp->dp->name, rrname(qp->type, buf, sizeof buf));
1112219b2ee8SDavid du Colombier 			if(debug)
11133cbadd90SDavid du Colombier 				logsend(qp->req->id, depth, p->a, p->s->name,
11143cbadd90SDavid du Colombier 					qp->dp->name, qp->type);
11154f8f669cSDavid du Colombier 
11163cbadd90SDavid du Colombier 			/* fill in UDP destination addr & send it */
11173cbadd90SDavid du Colombier 			memmove(obuf, p->a, sizeof p->a);
11183cbadd90SDavid du Colombier 			mydnsquery(qp, medium, obuf, len);
11197dd7cddfSDavid du Colombier 			p->nx++;
11203e12c5d1SDavid du Colombier 		}
11213cbadd90SDavid du Colombier 	if(j == 0) {
11223cbadd90SDavid du Colombier 		// dnslog("xmitquery: %s: no destinations left", qp->dp->name);
11233cbadd90SDavid du Colombier 		return -1;
11243cbadd90SDavid du Colombier 	}
11253cbadd90SDavid du Colombier 	return 0;
11263cbadd90SDavid du Colombier }
11273e12c5d1SDavid du Colombier 
1128f46c709fSDavid du Colombier static int lckindex[Maxlcks] = {
1129f46c709fSDavid du Colombier 	0,			/* all others map here */
1130f46c709fSDavid du Colombier 	Ta,
1131f46c709fSDavid du Colombier 	Tns,
1132f46c709fSDavid du Colombier 	Tcname,
1133f46c709fSDavid du Colombier 	Tsoa,
1134f46c709fSDavid du Colombier 	Tptr,
1135f46c709fSDavid du Colombier 	Tmx,
1136f46c709fSDavid du Colombier 	Ttxt,
1137f46c709fSDavid du Colombier 	Taaaa,
1138f46c709fSDavid du Colombier };
1139f46c709fSDavid du Colombier 
1140f46c709fSDavid du Colombier static int
1141f46c709fSDavid du Colombier qtype2lck(int qtype)		/* map query type to querylck index */
1142f46c709fSDavid du Colombier {
1143f46c709fSDavid du Colombier 	int i;
1144f46c709fSDavid du Colombier 
1145f46c709fSDavid du Colombier 	for (i = 1; i < nelem(lckindex); i++)
1146f46c709fSDavid du Colombier 		if (lckindex[i] == qtype)
1147f46c709fSDavid du Colombier 			return i;
1148f46c709fSDavid du Colombier 	return 0;
1149f46c709fSDavid du Colombier }
1150f46c709fSDavid du Colombier 
11510319257bSDavid du Colombier /* is mp a cachable negative response (with Rname set)? */
11520319257bSDavid du Colombier static int
11530319257bSDavid du Colombier isnegrname(DNSmsg *mp)
11540319257bSDavid du Colombier {
11550319257bSDavid du Colombier 	/* TODO: could add || cfg.justforw to RHS of && */
11560319257bSDavid du Colombier 	return mp->an == nil && (mp->flags & Rmask) == Rname;
11570319257bSDavid du Colombier }
11580319257bSDavid du Colombier 
11593cbadd90SDavid du Colombier static int
11603cbadd90SDavid du Colombier procansw(Query *qp, DNSmsg *mp, uchar *srcip, int depth, Dest *p)
11613cbadd90SDavid du Colombier {
11623cbadd90SDavid du Colombier 	int rv;
11634e5f5f32SDavid du Colombier //	int lcktype;
11643cbadd90SDavid du Colombier 	char buf[32];
11653cbadd90SDavid du Colombier 	DN *ndp;
1166c73252aeSDavid du Colombier 	Query *nqp;
11673cbadd90SDavid du Colombier 	RR *tp, *soarr;
11687dd7cddfSDavid du Colombier 
1169f46c709fSDavid du Colombier 	if (mp->an == nil)
1170f46c709fSDavid du Colombier 		stats.negans++;
1171f46c709fSDavid du Colombier 
117259cc4ca5SDavid du Colombier 	/* ignore any error replies */
11733cbadd90SDavid du Colombier 	if((mp->flags & Rmask) == Rserver){
11740319257bSDavid du Colombier 		stats.negserver++;
1175a41547ffSDavid du Colombier 		freeanswers(mp);
11763cbadd90SDavid du Colombier 		if(p != qp->curdest)
11777dd7cddfSDavid du Colombier 			p->code = Rserver;
11783cbadd90SDavid du Colombier 		return -1;
11793e12c5d1SDavid du Colombier 	}
11803e12c5d1SDavid du Colombier 
118159cc4ca5SDavid du Colombier 	/* ignore any bad delegations */
11823cbadd90SDavid du Colombier 	if(mp->ns && baddelegation(mp->ns, qp->nsrp, srcip)){
11830319257bSDavid du Colombier 		stats.negbaddeleg++;
11843cbadd90SDavid du Colombier 		if(mp->an == nil){
11850319257bSDavid du Colombier 			stats.negbdnoans++;
1186a41547ffSDavid du Colombier 			freeanswers(mp);
11873cbadd90SDavid du Colombier 			if(p != qp->curdest)
118859cc4ca5SDavid du Colombier 				p->code = Rserver;
11893cbadd90SDavid du Colombier 			return -1;
119059cc4ca5SDavid du Colombier 		}
1191e6d9d902SDavid du Colombier 		rrfreelistptr(&mp->ns);
119259cc4ca5SDavid du Colombier 	}
119359cc4ca5SDavid du Colombier 
11947dd7cddfSDavid du Colombier 	/* remove any soa's from the authority section */
1195530fef66SDavid du Colombier 	lock(&dnlock);
11963cbadd90SDavid du Colombier 	soarr = rrremtype(&mp->ns, Tsoa);
11977dd7cddfSDavid du Colombier 
11983e12c5d1SDavid du Colombier 	/* incorporate answers */
11994e153993SDavid du Colombier 	unique(mp->an);
12004e153993SDavid du Colombier 	unique(mp->ns);
12014e153993SDavid du Colombier 	unique(mp->ar);
1202530fef66SDavid du Colombier 	unlock(&dnlock);
1203e6d9d902SDavid du Colombier 
12043cbadd90SDavid du Colombier 	if(mp->an)
12053cbadd90SDavid du Colombier 		rrattach(mp->an, (mp->flags & Fauth) != 0);
12063cbadd90SDavid du Colombier 	if(mp->ar)
12076dc4800dSDavid du Colombier 		rrattach(mp->ar, Notauthoritative);
1208a41547ffSDavid du Colombier 	if(mp->ns && !cfg.justforw){
12093cbadd90SDavid du Colombier 		ndp = mp->ns->owner;
12106dc4800dSDavid du Colombier 		rrattach(mp->ns, Notauthoritative);
12110319257bSDavid du Colombier 	} else {
12124f8f669cSDavid du Colombier 		ndp = nil;
1213e6d9d902SDavid du Colombier 		rrfreelistptr(&mp->ns);
12140319257bSDavid du Colombier 	}
12157dd7cddfSDavid du Colombier 
12167dd7cddfSDavid du Colombier 	/* free the question */
1217e6d9d902SDavid du Colombier 	if(mp->qd)
1218e6d9d902SDavid du Colombier 		rrfreelistptr(&mp->qd);
12193e12c5d1SDavid du Colombier 
12203e12c5d1SDavid du Colombier 	/*
12213e12c5d1SDavid du Colombier 	 *  Any reply from an authoritative server,
1222a41547ffSDavid du Colombier 	 *  or a positive reply terminates the search.
1223a41547ffSDavid du Colombier 	 *  A negative response now also terminates the search.
12243e12c5d1SDavid du Colombier 	 */
12253cbadd90SDavid du Colombier 	if(mp->an != nil || (mp->flags & Fauth)){
12260319257bSDavid du Colombier 		if(isnegrname(mp))
12273cbadd90SDavid du Colombier 			qp->dp->respcode = Rname;
12287dd7cddfSDavid du Colombier 		else
1229d9924332SDavid du Colombier 			qp->dp->respcode = Rok;
12307dd7cddfSDavid du Colombier 
12317dd7cddfSDavid du Colombier 		/*
1232a41547ffSDavid du Colombier 		 *  cache any negative responses, free soarr.
1233a41547ffSDavid du Colombier 		 *  negative responses need not be authoritative:
1234a41547ffSDavid du Colombier 		 *  they can legitimately come from a cache.
12357dd7cddfSDavid du Colombier 		 */
1236a41547ffSDavid du Colombier 		if( /* (mp->flags & Fauth) && */ mp->an == nil)
12373cbadd90SDavid du Colombier 			cacheneg(qp->dp, qp->type, (mp->flags & Rmask), soarr);
12387dd7cddfSDavid du Colombier 		else
12397dd7cddfSDavid du Colombier 			rrfreelist(soarr);
12403e12c5d1SDavid du Colombier 		return 1;
12410319257bSDavid du Colombier 	} else if (isnegrname(mp)) {
1242a41547ffSDavid du Colombier 		qp->dp->respcode = Rname;
1243a41547ffSDavid du Colombier 		/*
1244a41547ffSDavid du Colombier 		 *  cache negative response.
1245a41547ffSDavid du Colombier 		 *  negative responses need not be authoritative:
1246a41547ffSDavid du Colombier 		 *  they can legitimately come from a cache.
1247a41547ffSDavid du Colombier 		 */
1248a41547ffSDavid du Colombier 		cacheneg(qp->dp, qp->type, (mp->flags & Rmask), soarr);
1249a41547ffSDavid du Colombier 		return 1;
12503e12c5d1SDavid du Colombier 	}
12510319257bSDavid du Colombier 	stats.negnorname++;
12527dd7cddfSDavid du Colombier 	rrfreelist(soarr);
12533e12c5d1SDavid du Colombier 
12543e12c5d1SDavid du Colombier 	/*
1255a41547ffSDavid du Colombier 	 *  if we've been given better name servers, recurse.
1256a41547ffSDavid du Colombier 	 *  if we're a pure resolver, don't recurse, we have
1257a41547ffSDavid du Colombier 	 *  to forward to a fixed set of named servers.
12583e12c5d1SDavid du Colombier 	 */
1259a41547ffSDavid du Colombier 	if(!mp->ns || cfg.resolver && cfg.justforw)
12603cbadd90SDavid du Colombier 		return 0;
12617dd7cddfSDavid du Colombier 	tp = rrlookup(ndp, Tns, NOneg);
12623cbadd90SDavid du Colombier 	if(contains(qp->nsrp, tp)){
12637dd7cddfSDavid du Colombier 		rrfreelist(tp);
12643cbadd90SDavid du Colombier 		return 0;
12653e12c5d1SDavid du Colombier 	}
12663cbadd90SDavid du Colombier 	procsetname("recursive query for %s %s", qp->dp->name,
12673cbadd90SDavid du Colombier 		rrname(qp->type, buf, sizeof buf));
12684e5f5f32SDavid du Colombier 	/*
12694e5f5f32SDavid du Colombier 	 *  we're called from udpquery, called from
12704e5f5f32SDavid du Colombier 	 *  netquery, which current holds qp->dp->querylck,
12714e5f5f32SDavid du Colombier 	 *  so release it now and acquire it upon return.
12724e5f5f32SDavid du Colombier 	 */
1273e6d9d902SDavid du Colombier //	lcktype = qtype2lck(qp->type);		/* someday try this again */
12744e5f5f32SDavid du Colombier //	qunlock(&qp->dp->querylck[lcktype]);
12753cbadd90SDavid du Colombier 
1276c73252aeSDavid du Colombier 	nqp = emalloc(sizeof *nqp);
1277c73252aeSDavid du Colombier 	queryinit(nqp, qp->dp, qp->type, qp->req);
1278c73252aeSDavid du Colombier 	nqp->nsrp = tp;
1279c73252aeSDavid du Colombier 	rv = netquery(nqp, depth+1);
12803cbadd90SDavid du Colombier 
12814e5f5f32SDavid du Colombier //	qlock(&qp->dp->querylck[lcktype]);
1282adb31a62SDavid du Colombier 	rrfreelist(nqp->nsrp);
1283c73252aeSDavid du Colombier 	querydestroy(nqp);
1284c73252aeSDavid du Colombier 	free(nqp);
12853cbadd90SDavid du Colombier 	return rv;
12863cbadd90SDavid du Colombier }
12873cbadd90SDavid du Colombier 
12883cbadd90SDavid du Colombier /*
12893cbadd90SDavid du Colombier  * send a query via tcp to a single address (from ibuf's udp header)
12903cbadd90SDavid du Colombier  * and read the answer(s) into mp->an.
12913cbadd90SDavid du Colombier  */
12923cbadd90SDavid du Colombier static int
12933cbadd90SDavid du Colombier tcpquery(Query *qp, DNSmsg *mp, int depth, uchar *ibuf, uchar *obuf, int len,
1294b8b25780SDavid du Colombier 	ulong waitms, int inns, ushort req)
12953cbadd90SDavid du Colombier {
12964e7b9544SDavid du Colombier 	int rv = 0;
1297b8b25780SDavid du Colombier 	uvlong endms;
12983cbadd90SDavid du Colombier 
1299b8b25780SDavid du Colombier 	endms = timems() + waitms;
1300b8b25780SDavid du Colombier 	if(endms > qp->req->aborttime)
1301b8b25780SDavid du Colombier 		endms = qp->req->aborttime;
13023cbadd90SDavid du Colombier 
1303a41547ffSDavid du Colombier 	if (0)
13044e7b9544SDavid du Colombier 		dnslog("%s: udp reply truncated; retrying query via tcp to %I",
13054e7b9544SDavid du Colombier 			qp->dp->name, qp->tcpip);
13064e7b9544SDavid du Colombier 
13073cbadd90SDavid du Colombier 	qlock(&qp->tcplock);
13083cbadd90SDavid du Colombier 	memmove(obuf, ibuf, IPaddrlen);		/* send back to respondent */
13093cbadd90SDavid du Colombier 	/* sets qp->tcpip from obuf's udp header */
13104e7b9544SDavid du Colombier 	if (xmitquery(qp, Tcp, depth, obuf, inns, len) < 0 ||
1311b8b25780SDavid du Colombier 	    readreply(qp, Tcp, req, ibuf, mp, endms) < 0)
13124e7b9544SDavid du Colombier 		rv = -1;
13133cbadd90SDavid du Colombier 	if (qp->tcpfd > 0) {
13143cbadd90SDavid du Colombier 		hangup(qp->tcpctlfd);
13153cbadd90SDavid du Colombier 		close(qp->tcpctlfd);
13163cbadd90SDavid du Colombier 		close(qp->tcpfd);
13173cbadd90SDavid du Colombier 	}
13183cbadd90SDavid du Colombier 	qp->tcpfd = qp->tcpctlfd = -1;
13193cbadd90SDavid du Colombier 	qunlock(&qp->tcplock);
13204e7b9544SDavid du Colombier 	return rv;
13213cbadd90SDavid du Colombier }
13223cbadd90SDavid du Colombier 
13233cbadd90SDavid du Colombier /*
1324*59f7772cSDavid du Colombier  *  query name servers.  fill in obuf with on-the-wire representation of a
1325*59f7772cSDavid du Colombier  *  DNSmsg derived from qp.  if the name server returns a pointer to another
13263cbadd90SDavid du Colombier  *  name server, recurse.
13273cbadd90SDavid du Colombier  */
13283cbadd90SDavid du Colombier static int
1329b8b25780SDavid du Colombier queryns(Query *qp, int depth, uchar *ibuf, uchar *obuf, ulong waitms, int inns)
13303cbadd90SDavid du Colombier {
13313cbadd90SDavid du Colombier 	int ndest, len, replywaits, rv;
13323cbadd90SDavid du Colombier 	ushort req;
1333b8b25780SDavid du Colombier 	uvlong endms;
13343cbadd90SDavid du Colombier 	char buf[12];
13353cbadd90SDavid du Colombier 	uchar srcip[IPaddrlen];
1336a41547ffSDavid du Colombier 	Dest *p, *np, *dest;
13373cbadd90SDavid du Colombier 
13383cbadd90SDavid du Colombier 	/* pack request into a udp message */
13393cbadd90SDavid du Colombier 	req = rand();
13403cbadd90SDavid du Colombier 	len = mkreq(qp->dp, qp->type, obuf, Frecurse|Oquery, req);
13413cbadd90SDavid du Colombier 
13423cbadd90SDavid du Colombier 	/* no server addresses yet */
13433cbadd90SDavid du Colombier 	queryck(qp);
1344a41547ffSDavid du Colombier 	dest = emalloc(Maxdest * sizeof *dest);	/* dest can't be on stack */
1345a41547ffSDavid du Colombier 	for (p = dest; p < dest + Maxdest; p++)
13463cbadd90SDavid du Colombier 		destinit(p);
13471eb0187fSDavid du Colombier 	/* this dest array is local to this call of queryns() */
13481eb0187fSDavid du Colombier 	free(qp->dest);
13493cbadd90SDavid du Colombier 	qp->curdest = qp->dest = dest;
13503cbadd90SDavid du Colombier 
13513cbadd90SDavid du Colombier 	/*
13523cbadd90SDavid du Colombier 	 *  transmit udp requests and wait for answers.
13533cbadd90SDavid du Colombier 	 *  at most Maxtrans attempts to each address.
13543cbadd90SDavid du Colombier 	 *  each cycle send one more message than the previous.
13553cbadd90SDavid du Colombier 	 *  retry a query via tcp if its response is truncated.
13563cbadd90SDavid du Colombier 	 */
13573cbadd90SDavid du Colombier 	for(ndest = 1; ndest < Maxdest; ndest++){
13583cbadd90SDavid du Colombier 		qp->ndest = ndest;
13593cbadd90SDavid du Colombier 		qp->tcpset = 0;
13603cbadd90SDavid du Colombier 		if (xmitquery(qp, Udp, depth, obuf, inns, len) < 0)
13613cbadd90SDavid du Colombier 			break;
13623cbadd90SDavid du Colombier 
1363b8b25780SDavid du Colombier 		endms = timems() + waitms;
1364b8b25780SDavid du Colombier 		if(endms > qp->req->aborttime)
1365b8b25780SDavid du Colombier 			endms = qp->req->aborttime;
13663cbadd90SDavid du Colombier 
13673cbadd90SDavid du Colombier 		for(replywaits = 0; replywaits < ndest; replywaits++){
136881730632SDavid du Colombier 			DNSmsg m;
136981730632SDavid du Colombier 
1370f46c709fSDavid du Colombier 			procsetname("reading %sside reply from %I: %s %s from %s",
1371a41547ffSDavid du Colombier 				(inns? "in": "out"), obuf, qp->dp->name,
1372f46c709fSDavid du Colombier 				rrname(qp->type, buf, sizeof buf), qp->req->from);
13733cbadd90SDavid du Colombier 
137481730632SDavid du Colombier 			/* read udp answer into m */
1375b8b25780SDavid du Colombier 			if (readreply(qp, Udp, req, ibuf, &m, endms) >= 0)
13763cbadd90SDavid du Colombier 				memmove(srcip, ibuf, IPaddrlen);
137781730632SDavid du Colombier 			else if (!(m.flags & Ftrunc)) {
137881730632SDavid du Colombier 				freeanswers(&m);
13793cbadd90SDavid du Colombier 				break;		/* timed out on this dest */
138081730632SDavid du Colombier 			} else {
13813cbadd90SDavid du Colombier 				/* whoops, it was truncated! ask again via tcp */
13826aaebd7dSDavid du Colombier 				freeanswers(&m);
13833cbadd90SDavid du Colombier 				rv = tcpquery(qp, &m, depth, ibuf, obuf, len,
1384b8b25780SDavid du Colombier 					waitms, inns, req);  /* answer in m */
138581730632SDavid du Colombier 				if (rv < 0) {
138681730632SDavid du Colombier 					freeanswers(&m);
13873cbadd90SDavid du Colombier 					break;		/* failed via tcp too */
138881730632SDavid du Colombier 				}
13893cbadd90SDavid du Colombier 				memmove(srcip, qp->tcpip, IPaddrlen);
13903cbadd90SDavid du Colombier 			}
13913cbadd90SDavid du Colombier 
13923cbadd90SDavid du Colombier 			/* find responder */
13934e5f5f32SDavid du Colombier 			// dnslog("queryns got reply from %I", srcip);
13943cbadd90SDavid du Colombier 			for(p = qp->dest; p < qp->curdest; p++)
13953cbadd90SDavid du Colombier 				if(memcmp(p->a, srcip, sizeof p->a) == 0)
13963cbadd90SDavid du Colombier 					break;
13973cbadd90SDavid du Colombier 
13983cbadd90SDavid du Colombier 			/* remove all addrs of responding server from list */
13993cbadd90SDavid du Colombier 			for(np = qp->dest; np < qp->curdest; np++)
14003cbadd90SDavid du Colombier 				if(np->s == p->s)
14013cbadd90SDavid du Colombier 					p->nx = Maxtrans;
14023cbadd90SDavid du Colombier 
140381730632SDavid du Colombier 			/* free or incorporate RRs in m */
14043cbadd90SDavid du Colombier 			rv = procansw(qp, &m, srcip, depth, p);
14051eb0187fSDavid du Colombier 			if (rv > 0) {
14061eb0187fSDavid du Colombier 				free(qp->dest);
14071eb0187fSDavid du Colombier 				qp->dest = qp->curdest = nil; /* prevent accidents */
14083cbadd90SDavid du Colombier 				return rv;
14093e12c5d1SDavid du Colombier 			}
14103e12c5d1SDavid du Colombier 		}
14111eb0187fSDavid du Colombier 	}
14127dd7cddfSDavid du Colombier 
14134f8f669cSDavid du Colombier 	/* if all servers returned failure, propagate it */
14143cbadd90SDavid du Colombier 	qp->dp->respcode = Rserver;
14153cbadd90SDavid du Colombier 	for(p = dest; p < qp->curdest; p++) {
14163cbadd90SDavid du Colombier 		destck(p);
14177dd7cddfSDavid du Colombier 		if(p->code != Rserver)
1418d9924332SDavid du Colombier 			qp->dp->respcode = Rok;
14193cbadd90SDavid du Colombier 		p->magic = 0;			/* prevent accidents */
14203cbadd90SDavid du Colombier 	}
14214f8f669cSDavid du Colombier 
14223cbadd90SDavid du Colombier //	if (qp->dp->respcode)
14234e5f5f32SDavid du Colombier //		dnslog("queryns setting Rserver for %s", qp->dp->name);
14247dd7cddfSDavid du Colombier 
1425a41547ffSDavid du Colombier 	free(qp->dest);
14263cbadd90SDavid du Colombier 	qp->dest = qp->curdest = nil;		/* prevent accidents */
14273e12c5d1SDavid du Colombier 	return 0;
14283e12c5d1SDavid du Colombier }
14297dd7cddfSDavid du Colombier 
14304f8f669cSDavid du Colombier /*
14314f8f669cSDavid du Colombier  *  run a command with a supplied fd as standard input
14324f8f669cSDavid du Colombier  */
14334f8f669cSDavid du Colombier char *
14344f8f669cSDavid du Colombier system(int fd, char *cmd)
143528a8a86bSDavid du Colombier {
14364f8f669cSDavid du Colombier 	int pid, p, i;
14374f8f669cSDavid du Colombier 	static Waitmsg msg;
14386b0d5c8bSDavid du Colombier 
14394f8f669cSDavid du Colombier 	if((pid = fork()) == -1)
14404f8f669cSDavid du Colombier 		sysfatal("fork failed: %r");
14414f8f669cSDavid du Colombier 	else if(pid == 0){
14424f8f669cSDavid du Colombier 		dup(fd, 0);
14434f8f669cSDavid du Colombier 		close(fd);
14444f8f669cSDavid du Colombier 		for (i = 3; i < 200; i++)
14454f8f669cSDavid du Colombier 			close(i);		/* don't leak fds */
14464f8f669cSDavid du Colombier 		execl("/bin/rc", "rc", "-c", cmd, nil);
14474f8f669cSDavid du Colombier 		sysfatal("exec rc: %r");
14484f8f669cSDavid du Colombier 	}
14494f8f669cSDavid du Colombier 	for(p = waitpid(); p >= 0; p = waitpid())
14504f8f669cSDavid du Colombier 		if(p == pid)
14514f8f669cSDavid du Colombier 			return msg.msg;
14524f8f669cSDavid du Colombier 	return "lost child";
14534f8f669cSDavid du Colombier }
14544f8f669cSDavid du Colombier 
1455b8b25780SDavid du Colombier /* compute wait, weighted by probability of success, with bounds */
14560319257bSDavid du Colombier static ulong
14570319257bSDavid du Colombier weight(ulong ms, unsigned pcntprob)
14580319257bSDavid du Colombier {
14590319257bSDavid du Colombier 	ulong wait;
14600319257bSDavid du Colombier 
14610319257bSDavid du Colombier 	wait = (ms * pcntprob) / 100;
1462b8b25780SDavid du Colombier 	if (wait < Minwaitms)
1463b8b25780SDavid du Colombier 		wait = Minwaitms;
1464b8b25780SDavid du Colombier 	if (wait > Maxwaitms)
1465b8b25780SDavid du Colombier 		wait = Maxwaitms;
14660319257bSDavid du Colombier 	return wait;
14670319257bSDavid du Colombier }
14680319257bSDavid du Colombier 
1469a41547ffSDavid du Colombier /*
1470a41547ffSDavid du Colombier  * in principle we could use a single descriptor for a udp port
1471a41547ffSDavid du Colombier  * to send all queries and receive all the answers to them,
1472a41547ffSDavid du Colombier  * but we'd have to sort out the answers by dns-query id.
1473a41547ffSDavid du Colombier  */
14744f8f669cSDavid du Colombier static int
14753cbadd90SDavid du Colombier udpquery(Query *qp, char *mntpt, int depth, int patient, int inns)
14764f8f669cSDavid du Colombier {
14770319257bSDavid du Colombier 	int fd, rv;
14784f8f669cSDavid du Colombier 	long now;
1479b8b25780SDavid du Colombier 	ulong pcntprob;
1480b8b25780SDavid du Colombier 	uvlong wait, reqtm;
14814f8f669cSDavid du Colombier 	char *msg;
14824f8f669cSDavid du Colombier 	uchar *obuf, *ibuf;
14834f8f669cSDavid du Colombier 	static QLock mntlck;
14844f8f669cSDavid du Colombier 	static ulong lastmount;
14857dd7cddfSDavid du Colombier 
14867dd7cddfSDavid du Colombier 	/* use alloced buffers rather than ones from the stack */
1487f27a9a5aSDavid du Colombier 	// ibuf = emalloc(Maxudpin+Udphdrsize);
14883cbadd90SDavid du Colombier 	ibuf = emalloc(64*1024);		/* max. tcp reply size */
1489f27a9a5aSDavid du Colombier 	obuf = emalloc(Maxudp+Udphdrsize);
14907dd7cddfSDavid du Colombier 
14914f8f669cSDavid du Colombier 	fd = udpport(mntpt);
14924f8f669cSDavid du Colombier 	while (fd < 0 && cfg.straddle && strcmp(mntpt, "/net.alt") == 0) {
14934f8f669cSDavid du Colombier 		/* HACK: remount /net.alt */
14944f8f669cSDavid du Colombier 		now = time(nil);
14954f8f669cSDavid du Colombier 		if (now < lastmount + Remntretry)
1496b8b25780SDavid du Colombier 			sleep(S2MS(lastmount + Remntretry - now));
14974f8f669cSDavid du Colombier 		qlock(&mntlck);
14984f8f669cSDavid du Colombier 		fd = udpport(mntpt);	/* try again under lock */
14994f8f669cSDavid du Colombier 		if (fd < 0) {
15004f8f669cSDavid du Colombier 			dnslog("[%d] remounting /net.alt", getpid());
15014f8f669cSDavid du Colombier 			unmount(nil, "/net.alt");
15024f8f669cSDavid du Colombier 
15034f8f669cSDavid du Colombier 			msg = system(open("/dev/null", ORDWR), "outside");
15044f8f669cSDavid du Colombier 
15054f8f669cSDavid du Colombier 			lastmount = time(nil);
15064f8f669cSDavid du Colombier 			if (msg && *msg) {
15074f8f669cSDavid du Colombier 				dnslog("[%d] can't remount /net.alt: %s",
15084f8f669cSDavid du Colombier 					getpid(), msg);
1509b8b25780SDavid du Colombier 				sleep(10*1000);	/* don't spin remounting */
15104f8f669cSDavid du Colombier 			} else
15114f8f669cSDavid du Colombier 				fd = udpport(mntpt);
15124f8f669cSDavid du Colombier 		}
15134f8f669cSDavid du Colombier 		qunlock(&mntlck);
15144f8f669cSDavid du Colombier 	}
1515a41547ffSDavid du Colombier 	if (fd < 0) {
15164f8f669cSDavid du Colombier 		dnslog("can't get udpport for %s query of name %s: %r",
15173cbadd90SDavid du Colombier 			mntpt, qp->dp->name);
1518a41547ffSDavid du Colombier 		sysfatal("out of udp conversations");	/* we're buggered */
1519a41547ffSDavid du Colombier 	}
1520a41547ffSDavid du Colombier 
1521a41547ffSDavid du Colombier 	/*
1522b8b25780SDavid du Colombier 	 * Our QIP servers are busted and respond to AAAA and CNAME queries
1523b8b25780SDavid du Colombier 	 * with (sometimes malformed [too short] packets and) no answers and
1524b8b25780SDavid du Colombier 	 * just NS RRs but not Rname errors.  so make time-to-wait
1525b8b25780SDavid du Colombier 	 * proportional to estimated probability of an RR of that type existing.
1526a41547ffSDavid du Colombier 	 */
1527adb31a62SDavid du Colombier 	if (qp->type >= nelem(likely))
15280319257bSDavid du Colombier 		pcntprob = 35;			/* unpopular query type */
15290319257bSDavid du Colombier 	else
15300319257bSDavid du Colombier 		pcntprob = likely[qp->type];
15310319257bSDavid du Colombier 	reqtm = (patient? 2 * Maxreqtm: Maxreqtm);
1532b8b25780SDavid du Colombier 	wait = weight(reqtm / 3, pcntprob);	/* time for one udp query */
1533b8b25780SDavid du Colombier 	qp->req->aborttime = timems() + 3*wait; /* for all udp queries */
1534a41547ffSDavid du Colombier 
15350319257bSDavid du Colombier 	qp->udpfd = fd;
1536b8b25780SDavid du Colombier 	rv = queryns(qp, depth, ibuf, obuf, wait, inns);
1537a41547ffSDavid du Colombier 	close(fd);
1538a41547ffSDavid du Colombier 	qp->udpfd = -1;
15394f8f669cSDavid du Colombier 
15404f8f669cSDavid du Colombier 	free(obuf);
15414f8f669cSDavid du Colombier 	free(ibuf);
15424f8f669cSDavid du Colombier 	return rv;
15434f8f669cSDavid du Colombier }
15444f8f669cSDavid du Colombier 
1545b8b25780SDavid du Colombier /*
1546b8b25780SDavid du Colombier  * look up (qp->dp->name, qp->type) rr in dns,
1547b8b25780SDavid du Colombier  * using nameservers in qp->nsrp.
1548b8b25780SDavid du Colombier  */
15494f8f669cSDavid du Colombier static int
15503cbadd90SDavid du Colombier netquery(Query *qp, int depth)
15514f8f669cSDavid du Colombier {
1552410ea80bSDavid du Colombier 	int lock, rv, triedin, inname;
1553b8b25780SDavid du Colombier 	char buf[32];
15544f8f669cSDavid du Colombier 	RR *rp;
15554e153993SDavid du Colombier 	DN *dp;
1556225077b0SDavid du Colombier 	Querylck *qlp;
1557225077b0SDavid du Colombier 	static int whined;
15584f8f669cSDavid du Colombier 
1559225077b0SDavid du Colombier 	rv = 0;				/* pessimism */
15604f8f669cSDavid du Colombier 	if(depth > 12)			/* in a recursive loop? */
15614f8f669cSDavid du Colombier 		return 0;
15624f8f669cSDavid du Colombier 
15633cbadd90SDavid du Colombier 	slave(qp->req);
1564d3907fe5SDavid du Colombier 	/*
1565d3907fe5SDavid du Colombier 	 * slave might have forked.  if so, the parent process longjmped to
1566d3907fe5SDavid du Colombier 	 * req->mret; we're usually the child slave, but if there are too
1567d3907fe5SDavid du Colombier 	 * many children already, we're still the same process.
1568d3907fe5SDavid du Colombier 	 */
15694f8f669cSDavid du Colombier 
15704e5f5f32SDavid du Colombier 	/*
15714e5f5f32SDavid du Colombier 	 * don't lock before call to slave so only children can block.
15724e5f5f32SDavid du Colombier 	 * just lock at top-level invocation.
15734e5f5f32SDavid du Colombier 	 */
1574225077b0SDavid du Colombier 	lock = depth <= 1 && qp->req->isslave;
15754e153993SDavid du Colombier 	dp = qp->dp;		/* ensure that it doesn't change underfoot */
1576225077b0SDavid du Colombier 	qlp = nil;
15774e5f5f32SDavid du Colombier 	if(lock) {
1578b8b25780SDavid du Colombier 		procsetname("query lock wait: %s %s from %s", dp->name,
1579b8b25780SDavid du Colombier 			rrname(qp->type, buf, sizeof buf), qp->req->from);
1580ccf6439bSDavid du Colombier 		/*
1581ccf6439bSDavid du Colombier 		 * don't make concurrent queries for this name.
158281730632SDavid du Colombier 		 * dozens of processes blocking here probably indicates
158381730632SDavid du Colombier 		 * an error in our dns data that causes us to not
158481730632SDavid du Colombier 		 * recognise a zone (area) as one of our own, thus
158581730632SDavid du Colombier 		 * causing us to query other nameservers.
1586ccf6439bSDavid du Colombier 		 */
1587225077b0SDavid du Colombier 		qlp = &dp->querylck[qtype2lck(qp->type)];
1588225077b0SDavid du Colombier 		qlock(qlp);
1589b8b25780SDavid du Colombier 		if (qlp->Ref.ref > Maxoutstanding) {
1590225077b0SDavid du Colombier 			qunlock(qlp);
1591225077b0SDavid du Colombier 			if (!whined) {
1592225077b0SDavid du Colombier 				whined = 1;
1593225077b0SDavid du Colombier 				dnslog("too many outstanding queries for %s;"
159413fec586SDavid du Colombier 					" dropping this one; no further logging"
159513fec586SDavid du Colombier 					" of drops", dp->name);
1596225077b0SDavid du Colombier 			}
1597225077b0SDavid du Colombier 			return 0;
1598225077b0SDavid du Colombier 		}
1599410ea80bSDavid du Colombier 		++qlp->Ref.ref;
1600410ea80bSDavid du Colombier 		qunlock(qlp);
1601225077b0SDavid du Colombier 	}
16024e153993SDavid du Colombier 	procsetname("netquery: %s", dp->name);
16036b0d5c8bSDavid du Colombier 
16046b0d5c8bSDavid du Colombier 	/* prepare server RR's for incremental lookup */
16053cbadd90SDavid du Colombier 	for(rp = qp->nsrp; rp; rp = rp->next)
16066b0d5c8bSDavid du Colombier 		rp->marker = 0;
16076b0d5c8bSDavid du Colombier 
16084f8f669cSDavid du Colombier 	triedin = 0;
1609a41547ffSDavid du Colombier 
16104f8f669cSDavid du Colombier 	/*
16114f8f669cSDavid du Colombier 	 * normal resolvers and servers will just use mntpt for all addresses,
16124f8f669cSDavid du Colombier 	 * even on the outside.  straddling servers will use mntpt (/net)
16134f8f669cSDavid du Colombier 	 * for inside addresses and /net.alt for outside addresses,
16144f8f669cSDavid du Colombier 	 * thus bypassing other inside nameservers.
16154f8f669cSDavid du Colombier 	 */
16164e153993SDavid du Colombier 	inname = insideaddr(dp->name);
16174f8f669cSDavid du Colombier 	if (!cfg.straddle || inname) {
16183cbadd90SDavid du Colombier 		rv = udpquery(qp, mntpt, depth, Hurry, (cfg.inside? Inns: Outns));
16194f8f669cSDavid du Colombier 		triedin = 1;
16204f8f669cSDavid du Colombier 	}
16214f8f669cSDavid du Colombier 
16224f8f669cSDavid du Colombier 	/*
16234f8f669cSDavid du Colombier 	 * if we're still looking, are inside, and have an outside domain,
16244f8f669cSDavid du Colombier 	 * try it on our outside interface, if any.
16254f8f669cSDavid du Colombier 	 */
16264f8f669cSDavid du Colombier 	if (rv == 0 && cfg.inside && !inname) {
16274f8f669cSDavid du Colombier 		if (triedin)
16284f8f669cSDavid du Colombier 			dnslog(
16294f8f669cSDavid du Colombier 	   "[%d] netquery: internal nameservers failed for %s; trying external",
16304e153993SDavid du Colombier 				getpid(), dp->name);
16314f8f669cSDavid du Colombier 
16324f8f669cSDavid du Colombier 		/* prepare server RR's for incremental lookup */
16333cbadd90SDavid du Colombier 		for(rp = qp->nsrp; rp; rp = rp->next)
16344f8f669cSDavid du Colombier 			rp->marker = 0;
16354f8f669cSDavid du Colombier 
16363cbadd90SDavid du Colombier 		rv = udpquery(qp, "/net.alt", depth, Patient, Outns);
16374f8f669cSDavid du Colombier 	}
16383cbadd90SDavid du Colombier //	if (rv == 0)		/* could ask /net.alt/dns directly */
16394e153993SDavid du Colombier //		askoutdns(dp, qp->type);
16404f8f669cSDavid du Colombier 
1641410ea80bSDavid du Colombier 	if(lock && qlp) {
1642410ea80bSDavid du Colombier 		qlock(qlp);
1643410ea80bSDavid du Colombier 		assert(qlp->Ref.ref > 0);
1644410ea80bSDavid du Colombier 		qunlock(qlp);
1645225077b0SDavid du Colombier 		decref(qlp);
1646410ea80bSDavid du Colombier 	}
16477dd7cddfSDavid du Colombier 	return rv;
16487dd7cddfSDavid du Colombier }
16494f8f669cSDavid du Colombier 
16504f8f669cSDavid du Colombier int
16514f8f669cSDavid du Colombier seerootns(void)
16524f8f669cSDavid du Colombier {
16533cbadd90SDavid du Colombier 	int rv;
16544f8f669cSDavid du Colombier 	char root[] = "";
16554f8f669cSDavid du Colombier 	Request req;
1656c73252aeSDavid du Colombier 	Query *qp;
16574f8f669cSDavid du Colombier 
16584f8f669cSDavid du Colombier 	memset(&req, 0, sizeof req);
16594f8f669cSDavid du Colombier 	req.isslave = 1;
1660b8b25780SDavid du Colombier 	req.aborttime = timems() + Maxreqtm;
1661f46c709fSDavid du Colombier 	req.from = "internal";
1662c73252aeSDavid du Colombier 	qp = emalloc(sizeof *qp);
1663c73252aeSDavid du Colombier 	queryinit(qp, dnlookup(root, Cin, 1), Tns, &req);
1664c73252aeSDavid du Colombier 
1665c73252aeSDavid du Colombier 	qp->nsrp = dblookup(root, Cin, Tns, 0, 0);
1666c73252aeSDavid du Colombier 	rv = netquery(qp, 0);
1667c73252aeSDavid du Colombier 
1668c73252aeSDavid du Colombier 	rrfreelist(qp->nsrp);
1669c73252aeSDavid du Colombier 	querydestroy(qp);
1670c73252aeSDavid du Colombier 	free(qp);
16713cbadd90SDavid du Colombier 	return rv;
16724f8f669cSDavid du Colombier }
1673