xref: /plan9/sys/src/cmd/ndb/dnresolve.c (revision 82f6abee43a55f41b2fe672a88aaa23f21f003ab)
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 
19c3617180SDavid du Colombier 	Answerr=	-1,
20c3617180SDavid du Colombier 	Answnone,
21c3617180SDavid du Colombier 
227dd7cddfSDavid du Colombier 	Maxdest=	24,	/* maximum destinations for a request message */
23b8b25780SDavid du Colombier 	Maxoutstanding=	15,	/* max. outstanding queries per domain name */
24e6d9d902SDavid du Colombier 	Remntretry=	15,	/* min. sec.s between /net.alt remount tries */
25e6d9d902SDavid du Colombier 
26e6d9d902SDavid du Colombier 	/*
27e6d9d902SDavid du Colombier 	 * these are the old values; we're trying longer timeouts now
28e6d9d902SDavid du Colombier 	 * primarily for the benefit of remote nameservers querying us
29e6d9d902SDavid du Colombier 	 * during times of bad connectivity.
30e6d9d902SDavid du Colombier 	 */
31e6d9d902SDavid du Colombier //	Maxtrans=	3,	/* maximum transmissions to a server */
32e6d9d902SDavid du Colombier //	Maxretries=	3, /* cname+actual resends: was 32; have pity on user */
33e6d9d902SDavid du Colombier //	Maxwaitms=	1000,	/* wait no longer for a remote dns query */
34e6d9d902SDavid du Colombier //	Minwaitms=	100,	/* willing to wait for a remote dns query */
35e6d9d902SDavid du Colombier 
36e6d9d902SDavid du Colombier 	Maxtrans=	5,	/* maximum transmissions to a server */
37e6d9d902SDavid du Colombier 	Maxretries=	5, /* cname+actual resends: was 32; have pity on user */
38e6d9d902SDavid du Colombier 	Maxwaitms=	5000,	/* wait no longer for a remote dns query */
39e6d9d902SDavid du Colombier 	Minwaitms=	500,	/* willing to wait for a remote dns query */
40b8b25780SDavid du Colombier 
413cbadd90SDavid du Colombier 	Destmagic=	0xcafebabe,
423cbadd90SDavid du Colombier 	Querymagic=	0xdeadbeef,
433e12c5d1SDavid du Colombier };
44a41547ffSDavid du Colombier enum { Hurry, Patient, };
45a41547ffSDavid du Colombier enum { Outns, Inns, };
463e12c5d1SDavid du Colombier 
473cbadd90SDavid du Colombier struct Ipaddr {
483cbadd90SDavid du Colombier 	Ipaddr *next;
493cbadd90SDavid du Colombier 	uchar	ip[IPaddrlen];
503cbadd90SDavid du Colombier };
513cbadd90SDavid du Colombier 
523cbadd90SDavid du Colombier struct Dest
533cbadd90SDavid du Colombier {
543cbadd90SDavid du Colombier 	uchar	a[IPaddrlen];	/* ip address */
553cbadd90SDavid du Colombier 	DN	*s;		/* name server */
563cbadd90SDavid du Colombier 	int	nx;		/* number of transmissions */
573cbadd90SDavid du Colombier 	int	code;		/* response code; used to clear dp->respcode */
583cbadd90SDavid du Colombier 
593cbadd90SDavid du Colombier 	ulong	magic;
603cbadd90SDavid du Colombier };
613cbadd90SDavid du Colombier 
62c73252aeSDavid du Colombier /*
63c73252aeSDavid du Colombier  * Query has a QLock in it, thus it can't be an automatic
64c73252aeSDavid du Colombier  * variable, since each process would see a separate copy
65c73252aeSDavid du Colombier  * of the lock on its stack.
66c73252aeSDavid du Colombier  */
673cbadd90SDavid du Colombier struct Query {
683cbadd90SDavid du Colombier 	DN	*dp;		/* domain */
69adb31a62SDavid du Colombier 	ushort	type;		/* and type to look up */
703cbadd90SDavid du Colombier 	Request *req;
713cbadd90SDavid du Colombier 	RR	*nsrp;		/* name servers to consult */
723cbadd90SDavid du Colombier 
73a41547ffSDavid du Colombier 	/* dest must not be on the stack due to forking in slave() */
743cbadd90SDavid du Colombier 	Dest	*dest;		/* array of destinations */
7559f7772cSDavid du Colombier 	Dest	*curdest;	/* pointer to next to fill */
7659f7772cSDavid du Colombier 	int	ndest;		/* transmit to this many on this round */
773cbadd90SDavid du Colombier 
78f46c709fSDavid du Colombier 	int	udpfd;
793cbadd90SDavid du Colombier 
803cbadd90SDavid du Colombier 	QLock	tcplock;	/* only one tcp call at a time per query */
813cbadd90SDavid du Colombier 	int	tcpset;
823cbadd90SDavid du Colombier 	int	tcpfd;		/* if Tcp, read replies from here */
833cbadd90SDavid du Colombier 	int	tcpctlfd;
843cbadd90SDavid du Colombier 	uchar	tcpip[IPaddrlen];
853cbadd90SDavid du Colombier 
863cbadd90SDavid du Colombier 	ulong	magic;
873cbadd90SDavid du Colombier };
883cbadd90SDavid du Colombier 
89a41547ffSDavid du Colombier /* estimated % probability of such a record existing at all */
90a41547ffSDavid du Colombier int likely[] = {
91a41547ffSDavid du Colombier 	[Ta]		95,
92a41547ffSDavid du Colombier 	[Taaaa]		10,
93a41547ffSDavid du Colombier 	[Tcname]	15,
94a41547ffSDavid du Colombier 	[Tmx]		60,
95a41547ffSDavid du Colombier 	[Tns]		90,
96a41547ffSDavid du Colombier 	[Tnull]		5,
97a41547ffSDavid du Colombier 	[Tptr]		35,
98a41547ffSDavid du Colombier 	[Tsoa]		90,
99a41547ffSDavid du Colombier 	[Tsrv]		60,
100a41547ffSDavid du Colombier 	[Ttxt]		15,
101a41547ffSDavid du Colombier 	[Tall]		95,
1023cbadd90SDavid du Colombier };
1033cbadd90SDavid du Colombier 
1047dd7cddfSDavid du Colombier static RR*	dnresolve1(char*, int, int, Request*, int, int);
1053cbadd90SDavid du Colombier static int	netquery(Query *, int);
1063cbadd90SDavid du Colombier 
1074f8f669cSDavid du Colombier /*
108530fef66SDavid du Colombier  * reading /proc/pid/args yields either "name args" or "name [display args]",
1094f8f669cSDavid du Colombier  * so return only display args, if any.
1104f8f669cSDavid du Colombier  */
1114f8f669cSDavid du Colombier static char *
1124f8f669cSDavid du Colombier procgetname(void)
1134f8f669cSDavid du Colombier {
1144f8f669cSDavid du Colombier 	int fd, n;
1154f8f669cSDavid du Colombier 	char *lp, *rp;
1164f8f669cSDavid du Colombier 	char buf[256];
1174f8f669cSDavid du Colombier 
1184f8f669cSDavid du Colombier 	snprint(buf, sizeof buf, "#p/%d/args", getpid());
1194f8f669cSDavid du Colombier 	if((fd = open(buf, OREAD)) < 0)
1204f8f669cSDavid du Colombier 		return strdup("");
1214f8f669cSDavid du Colombier 	*buf = '\0';
1224f8f669cSDavid du Colombier 	n = read(fd, buf, sizeof buf-1);
1234f8f669cSDavid du Colombier 	close(fd);
1244f8f669cSDavid du Colombier 	if (n >= 0)
1254f8f669cSDavid du Colombier 		buf[n] = '\0';
1264f8f669cSDavid du Colombier 	if ((lp = strchr(buf, '[')) == nil ||
1274f8f669cSDavid du Colombier 	    (rp = strrchr(buf, ']')) == nil)
1284f8f669cSDavid du Colombier 		return strdup("");
1294f8f669cSDavid du Colombier 	*rp = '\0';
1304f8f669cSDavid du Colombier 	return strdup(lp+1);
1314f8f669cSDavid du Colombier }
1323e12c5d1SDavid du Colombier 
133e6d9d902SDavid du Colombier void
134e6d9d902SDavid du Colombier rrfreelistptr(RR **rpp)
135e6d9d902SDavid du Colombier {
136e6d9d902SDavid du Colombier 	RR *rp;
137e6d9d902SDavid du Colombier 
138e6d9d902SDavid du Colombier 	if (rpp == nil || *rpp == nil)
139e6d9d902SDavid du Colombier 		return;
140e6d9d902SDavid du Colombier 	rp = *rpp;
141c3617180SDavid du Colombier 	*rpp = nil;	/* update pointer in memory before freeing list */
142e6d9d902SDavid du Colombier 	rrfreelist(rp);
143e6d9d902SDavid du Colombier }
144e6d9d902SDavid du Colombier 
1453e12c5d1SDavid du Colombier /*
1463e12c5d1SDavid du Colombier  *  lookup 'type' info for domain name 'name'.  If it doesn't exist, try
1473e12c5d1SDavid du Colombier  *  looking it up as a canonical name.
148b8b25780SDavid du Colombier  *
149b8b25780SDavid du Colombier  *  this process can be quite slow if time-outs are set too high when querying
150b8b25780SDavid du Colombier  *  nameservers that just don't respond to certain query types.  in that case,
151b8b25780SDavid du Colombier  *  there will be multiple udp retries, multiple nameservers will be queried,
152b8b25780SDavid du Colombier  *  and this will be repeated for a cname query.  the whole thing will be
153b8b25780SDavid du Colombier  *  retried several times until we get an answer or a time-out.
1543e12c5d1SDavid du Colombier  */
1553e12c5d1SDavid du Colombier RR*
1564f8f669cSDavid du Colombier dnresolve(char *name, int class, int type, Request *req, RR **cn, int depth,
1574f8f669cSDavid du Colombier 	int recurse, int rooted, int *status)
1583e12c5d1SDavid du Colombier {
1597dd7cddfSDavid du Colombier 	RR *rp, *nrp, *drp;
1603e12c5d1SDavid du Colombier 	DN *dp;
1617dd7cddfSDavid du Colombier 	int loops;
1624f8f669cSDavid du Colombier 	char *procname;
1637dd7cddfSDavid du Colombier 	char nname[Domlen];
1643e12c5d1SDavid du Colombier 
1657dd7cddfSDavid du Colombier 	if(status)
1667dd7cddfSDavid du Colombier 		*status = 0;
1677dd7cddfSDavid du Colombier 
168a41547ffSDavid du Colombier 	if(depth > 12)			/* in a recursive loop? */
169a41547ffSDavid du Colombier 		return nil;
170a41547ffSDavid du Colombier 
1714f8f669cSDavid du Colombier 	procname = procgetname();
1727dd7cddfSDavid du Colombier 	/*
1737dd7cddfSDavid du Colombier 	 *  hack for systems that don't have resolve search
1747dd7cddfSDavid du Colombier 	 *  lists.  Just look up the simple name in the database.
1757dd7cddfSDavid du Colombier 	 */
1766dc4800dSDavid du Colombier 	if(!rooted && strchr(name, '.') == nil){
1777dd7cddfSDavid du Colombier 		rp = nil;
1787dd7cddfSDavid du Colombier 		drp = domainlist(class);
179a41547ffSDavid du Colombier 		for(nrp = drp; rp == nil && nrp != nil; nrp = nrp->next){
1804f8f669cSDavid du Colombier 			snprint(nname, sizeof nname, "%s.%s", name,
1814f8f669cSDavid du Colombier 				nrp->ptr->name);
182a41547ffSDavid du Colombier 			rp = dnresolve(nname, class, type, req, cn, depth+1,
1834f8f669cSDavid du Colombier 				recurse, rooted, status);
184530fef66SDavid du Colombier 			lock(&dnlock);
185271b8d73SDavid du Colombier 			rrfreelist(rrremneg(&rp));
186530fef66SDavid du Colombier 			unlock(&dnlock);
1877dd7cddfSDavid du Colombier 		}
1887dd7cddfSDavid du Colombier 		if(drp != nil)
1896aaebd7dSDavid du Colombier 			rrfreelist(drp);
1904f8f669cSDavid du Colombier 		procsetname(procname);
1914f8f669cSDavid du Colombier 		free(procname);
1923e12c5d1SDavid du Colombier 		return rp;
1937dd7cddfSDavid du Colombier 	}
1943e12c5d1SDavid du Colombier 
1957dd7cddfSDavid du Colombier 	/*
1967dd7cddfSDavid du Colombier 	 *  try the name directly
1977dd7cddfSDavid du Colombier 	 */
1987dd7cddfSDavid du Colombier 	rp = dnresolve1(name, class, type, req, depth, recurse);
199225077b0SDavid du Colombier 	if(rp == nil) {
200225077b0SDavid du Colombier 		/*
201225077b0SDavid du Colombier 		 * try it as a canonical name if we weren't told
202225077b0SDavid du Colombier 		 * that the name didn't exist
203225077b0SDavid du Colombier 		 */
2047dd7cddfSDavid du Colombier 		dp = dnlookup(name, class, 0);
2054f8f669cSDavid du Colombier 		if(type != Tptr && dp->respcode != Rname)
206b8b25780SDavid du Colombier 			for(loops = 0; rp == nil && loops < Maxretries; loops++){
207b8b25780SDavid du Colombier 				/* retry cname, then the actual type */
208225077b0SDavid du Colombier 				rp = dnresolve1(name, class, Tcname, req,
209225077b0SDavid du Colombier 					depth, recurse);
2107dd7cddfSDavid du Colombier 				if(rp == nil)
2117dd7cddfSDavid du Colombier 					break;
21280ee5cbfSDavid du Colombier 
213c73252aeSDavid du Colombier 				/* rp->host == nil shouldn't happen, but does */
214c73252aeSDavid du Colombier 				if(rp->negative || rp->host == nil){
21580ee5cbfSDavid du Colombier 					rrfreelist(rp);
21680ee5cbfSDavid du Colombier 					rp = nil;
21780ee5cbfSDavid du Colombier 					break;
21880ee5cbfSDavid du Colombier 				}
2197dd7cddfSDavid du Colombier 
2207dd7cddfSDavid du Colombier 				name = rp->host->name;
221530fef66SDavid du Colombier 				lock(&dnlock);
2227dd7cddfSDavid du Colombier 				if(cn)
2237dd7cddfSDavid du Colombier 					rrcat(cn, rp);
2247dd7cddfSDavid du Colombier 				else
2257dd7cddfSDavid du Colombier 					rrfreelist(rp);
226530fef66SDavid du Colombier 				unlock(&dnlock);
2277dd7cddfSDavid du Colombier 
228225077b0SDavid du Colombier 				rp = dnresolve1(name, class, type, req,
229225077b0SDavid du Colombier 					depth, recurse);
2307dd7cddfSDavid du Colombier 			}
2317dd7cddfSDavid du Colombier 
2327dd7cddfSDavid du Colombier 		/* distinction between not found and not good */
233d9924332SDavid du Colombier 		if(rp == nil && status != nil && dp->respcode != Rok)
2344f8f669cSDavid du Colombier 			*status = dp->respcode;
235225077b0SDavid du Colombier 	}
2364f8f669cSDavid du Colombier 	procsetname(procname);
2374f8f669cSDavid du Colombier 	free(procname);
2387dd7cddfSDavid du Colombier 	return randomize(rp);
2393e12c5d1SDavid du Colombier }
2403e12c5d1SDavid du Colombier 
2413cbadd90SDavid du Colombier static void
2423cbadd90SDavid du Colombier queryinit(Query *qp, DN *dp, int type, Request *req)
2433cbadd90SDavid du Colombier {
2443cbadd90SDavid du Colombier 	memset(qp, 0, sizeof *qp);
2454e7b9544SDavid du Colombier 	qp->udpfd = qp->tcpfd = qp->tcpctlfd = -1;
2463cbadd90SDavid du Colombier 	qp->dp = dp;
2473cbadd90SDavid du Colombier 	qp->type = type;
248adb31a62SDavid du Colombier 	if (qp->type != type)
249adb31a62SDavid du Colombier 		dnslog("queryinit: bogus type %d", type);
2503cbadd90SDavid du Colombier 	qp->req = req;
2513cbadd90SDavid du Colombier 	qp->nsrp = nil;
2523cbadd90SDavid du Colombier 	qp->dest = qp->curdest = nil;
2533cbadd90SDavid du Colombier 	qp->magic = Querymagic;
2543cbadd90SDavid du Colombier }
2553cbadd90SDavid du Colombier 
2563cbadd90SDavid du Colombier static void
2573cbadd90SDavid du Colombier queryck(Query *qp)
2583cbadd90SDavid du Colombier {
2593cbadd90SDavid du Colombier 	assert(qp);
2603cbadd90SDavid du Colombier 	assert(qp->magic == Querymagic);
2613cbadd90SDavid du Colombier }
2623cbadd90SDavid du Colombier 
2633cbadd90SDavid du Colombier static void
2644e7b9544SDavid du Colombier querydestroy(Query *qp)
2654e7b9544SDavid du Colombier {
2664e7b9544SDavid du Colombier 	queryck(qp);
267530fef66SDavid du Colombier 	/* leave udpfd open */
2684e7b9544SDavid du Colombier 	if (qp->tcpfd > 0)
2694e7b9544SDavid du Colombier 		close(qp->tcpfd);
2704e7b9544SDavid du Colombier 	if (qp->tcpctlfd > 0) {
2714e7b9544SDavid du Colombier 		hangup(qp->tcpctlfd);
2724e7b9544SDavid du Colombier 		close(qp->tcpctlfd);
2734e7b9544SDavid du Colombier 	}
274a41547ffSDavid du Colombier 	free(qp->dest);
2754e7b9544SDavid du Colombier 	memset(qp, 0, sizeof *qp);	/* prevent accidents */
2764e7b9544SDavid du Colombier 	qp->udpfd = qp->tcpfd = qp->tcpctlfd = -1;
2774e7b9544SDavid du Colombier }
2784e7b9544SDavid du Colombier 
2794e7b9544SDavid du Colombier static void
2803cbadd90SDavid du Colombier destinit(Dest *p)
2813cbadd90SDavid du Colombier {
2823cbadd90SDavid du Colombier 	memset(p, 0, sizeof *p);
2833cbadd90SDavid du Colombier 	p->magic = Destmagic;
2843cbadd90SDavid du Colombier }
2853cbadd90SDavid du Colombier 
2863cbadd90SDavid du Colombier static void
2873cbadd90SDavid du Colombier destck(Dest *p)
2883cbadd90SDavid du Colombier {
2893cbadd90SDavid du Colombier 	assert(p);
2903cbadd90SDavid du Colombier 	assert(p->magic == Destmagic);
2913cbadd90SDavid du Colombier }
2923cbadd90SDavid du Colombier 
293a41547ffSDavid du Colombier /*
294a41547ffSDavid du Colombier  * if the response to a query hasn't arrived within 100 ms.,
295a41547ffSDavid du Colombier  * it's unlikely to arrive at all.  after 1 s., it's really unlikely.
296a41547ffSDavid du Colombier  * queries for missing RRs are likely to produce time-outs rather than
297a41547ffSDavid du Colombier  * negative responses, so cname and aaaa queries are likely to time out,
298a41547ffSDavid du Colombier  * thus we don't wait very long for them.
299a41547ffSDavid du Colombier  */
300a41547ffSDavid du Colombier static void
301a41547ffSDavid du Colombier notestats(vlong start, int tmout, int type)
302a41547ffSDavid du Colombier {
303a41547ffSDavid du Colombier 	qlock(&stats);
304a41547ffSDavid du Colombier 	if (tmout) {
305a41547ffSDavid du Colombier 		stats.tmout++;
306a41547ffSDavid du Colombier 		if (type == Taaaa)
307a41547ffSDavid du Colombier 			stats.tmoutv6++;
308a41547ffSDavid du Colombier 		else if (type == Tcname)
309a41547ffSDavid du Colombier 			stats.tmoutcname++;
310a41547ffSDavid du Colombier 	} else {
311a41547ffSDavid du Colombier 		long wait10ths = NS2MS(nsec() - start) / 100;
312a41547ffSDavid du Colombier 
31381730632SDavid du Colombier 		if (wait10ths <= 0)
314a41547ffSDavid du Colombier 			stats.under10ths[0]++;
315a41547ffSDavid du Colombier 		else if (wait10ths >= nelem(stats.under10ths))
316a41547ffSDavid du Colombier 			stats.under10ths[nelem(stats.under10ths) - 1]++;
317a41547ffSDavid du Colombier 		else
318a41547ffSDavid du Colombier 			stats.under10ths[wait10ths]++;
319a41547ffSDavid du Colombier 	}
320a41547ffSDavid du Colombier 	qunlock(&stats);
321a41547ffSDavid du Colombier }
322a41547ffSDavid du Colombier 
323a41547ffSDavid du Colombier static void
324a41547ffSDavid du Colombier noteinmem(void)
325a41547ffSDavid du Colombier {
326a41547ffSDavid du Colombier 	qlock(&stats);
327a41547ffSDavid du Colombier 	stats.answinmem++;
328a41547ffSDavid du Colombier 	qunlock(&stats);
329a41547ffSDavid du Colombier }
330a41547ffSDavid du Colombier 
331c3617180SDavid du Colombier /* netquery with given name servers, free ns rrs when done */
332c3617180SDavid du Colombier static int
333c3617180SDavid du Colombier netqueryns(Query *qp, int depth, RR *nsrp)
334c3617180SDavid du Colombier {
335c3617180SDavid du Colombier 	int rv;
336c3617180SDavid du Colombier 
337c3617180SDavid du Colombier 	qp->nsrp = nsrp;
338c3617180SDavid du Colombier 	rv = netquery(qp, depth);
339c3617180SDavid du Colombier 	lock(&dnlock);
340c3617180SDavid du Colombier 	rrfreelist(nsrp);
341c3617180SDavid du Colombier 	unlock(&dnlock);
342c3617180SDavid du Colombier 	return rv;
343c3617180SDavid du Colombier }
344c3617180SDavid du Colombier 
3453e12c5d1SDavid du Colombier static RR*
34646595261SDavid du Colombier issuequery(Query *qp, char *name, int class, int depth, int recurse)
3473e12c5d1SDavid du Colombier {
348f46c709fSDavid du Colombier 	char *cp;
349c73252aeSDavid du Colombier 	DN *nsdp;
35046595261SDavid du Colombier 	RR *rp, *nsrp, *dbnsrp;
3513cbadd90SDavid du Colombier 
3523e12c5d1SDavid du Colombier 	/*
3534f8f669cSDavid du Colombier 	 *  if we're running as just a resolver, query our
3547dd7cddfSDavid du Colombier 	 *  designated name servers
355219b2ee8SDavid du Colombier 	 */
3564f8f669cSDavid du Colombier 	if(cfg.resolver){
3577dd7cddfSDavid du Colombier 		nsrp = randomize(getdnsservers(class));
358c3617180SDavid du Colombier 		if(nsrp != nil)
359c3617180SDavid du Colombier 			if(netqueryns(qp, depth+1, nsrp) > Answnone)
360c73252aeSDavid du Colombier 				return rrlookup(qp->dp, qp->type, OKneg);
3617dd7cddfSDavid du Colombier 	}
362219b2ee8SDavid du Colombier 
363219b2ee8SDavid du Colombier 	/*
3643e12c5d1SDavid du Colombier  	 *  walk up the domain name looking for
3653e12c5d1SDavid du Colombier 	 *  a name server for the domain.
3663e12c5d1SDavid du Colombier 	 */
3673e12c5d1SDavid du Colombier 	for(cp = name; cp; cp = walkup(cp)){
3687dd7cddfSDavid du Colombier 		/*
3697dd7cddfSDavid du Colombier 		 *  if this is a local (served by us) domain,
3707dd7cddfSDavid du Colombier 		 *  return answer
3717dd7cddfSDavid du Colombier 		 */
3727dd7cddfSDavid du Colombier 		dbnsrp = randomize(dblookup(cp, class, Tns, 0, 0));
3737dd7cddfSDavid du Colombier 		if(dbnsrp && dbnsrp->local){
374c73252aeSDavid du Colombier 			rp = dblookup(name, class, qp->type, 1, dbnsrp->ttl);
375c3617180SDavid du Colombier 			lock(&dnlock);
3767dd7cddfSDavid du Colombier 			rrfreelist(dbnsrp);
377c3617180SDavid du Colombier 			unlock(&dnlock);
3787dd7cddfSDavid du Colombier 			return rp;
3797dd7cddfSDavid du Colombier 		}
3807dd7cddfSDavid du Colombier 
3817dd7cddfSDavid du Colombier 		/*
3827dd7cddfSDavid du Colombier 		 *  if recursion isn't set, just accept local
3837dd7cddfSDavid du Colombier 		 *  entries
3847dd7cddfSDavid du Colombier 		 */
3857dd7cddfSDavid du Colombier 		if(recurse == Dontrecurse){
386c3617180SDavid du Colombier 			if(dbnsrp) {
387c3617180SDavid du Colombier 				lock(&dnlock);
3887dd7cddfSDavid du Colombier 				rrfreelist(dbnsrp);
389c3617180SDavid du Colombier 				unlock(&dnlock);
390c3617180SDavid du Colombier 			}
3917dd7cddfSDavid du Colombier 			continue;
3927dd7cddfSDavid du Colombier 		}
3937dd7cddfSDavid du Colombier 
3947dd7cddfSDavid du Colombier 		/* look for ns in cache */
3953e12c5d1SDavid du Colombier 		nsdp = dnlookup(cp, class, 0);
3967dd7cddfSDavid du Colombier 		nsrp = nil;
3973e12c5d1SDavid du Colombier 		if(nsdp)
3987dd7cddfSDavid du Colombier 			nsrp = randomize(rrlookup(nsdp, Tns, NOneg));
3997dd7cddfSDavid du Colombier 
4007dd7cddfSDavid du Colombier 		/* if the entry timed out, ignore it */
4017dd7cddfSDavid du Colombier 		if(nsrp && nsrp->ttl < now){
402c3617180SDavid du Colombier 			lock(&dnlock);
403c3617180SDavid du Colombier 			rrfreelistptr(&nsrp);
404c3617180SDavid du Colombier 			unlock(&dnlock);
4057dd7cddfSDavid du Colombier 		}
4063e12c5d1SDavid du Colombier 
4073e12c5d1SDavid du Colombier 		if(nsrp){
408c3617180SDavid du Colombier 			lock(&dnlock);
409c3617180SDavid du Colombier 			rrfreelistptr(&dbnsrp);
410c3617180SDavid du Colombier 			unlock(&dnlock);
4117dd7cddfSDavid du Colombier 
4124f8f669cSDavid du Colombier 			/* query the name servers found in cache */
413c3617180SDavid du Colombier 			if(netqueryns(qp, depth+1, nsrp) > Answnone)
414c73252aeSDavid du Colombier 				return rrlookup(qp->dp, qp->type, OKneg);
415c3617180SDavid du Colombier 		} else if(dbnsrp)
4167dd7cddfSDavid du Colombier 			/* try the name servers found in db */
417c3617180SDavid du Colombier 			if(netqueryns(qp, depth+1, dbnsrp) > Answnone)
418c73252aeSDavid du Colombier 				return rrlookup(qp->dp, qp->type, NOneg);
4193e12c5d1SDavid du Colombier 	}
42046595261SDavid du Colombier 	return nil;
421c73252aeSDavid du Colombier }
422c73252aeSDavid du Colombier 
423c73252aeSDavid du Colombier static RR*
424c73252aeSDavid du Colombier dnresolve1(char *name, int class, int type, Request *req, int depth,
425c73252aeSDavid du Colombier 	int recurse)
426c73252aeSDavid du Colombier {
427c73252aeSDavid du Colombier 	Area *area;
428c73252aeSDavid du Colombier 	DN *dp;
429c73252aeSDavid du Colombier 	RR *rp;
430c73252aeSDavid du Colombier 	Query *qp;
431c73252aeSDavid du Colombier 
432c73252aeSDavid du Colombier 	if(debug)
433c73252aeSDavid du Colombier 		dnslog("[%d] dnresolve1 %s %d %d", getpid(), name, type, class);
434c73252aeSDavid du Colombier 
435c73252aeSDavid du Colombier 	/* only class Cin implemented so far */
436c73252aeSDavid du Colombier 	if(class != Cin)
437c73252aeSDavid du Colombier 		return nil;
438c73252aeSDavid du Colombier 
439c73252aeSDavid du Colombier 	dp = dnlookup(name, class, 1);
440c73252aeSDavid du Colombier 
441c73252aeSDavid du Colombier 	/*
442c73252aeSDavid du Colombier 	 *  Try the cache first
443c73252aeSDavid du Colombier 	 */
444c73252aeSDavid du Colombier 	rp = rrlookup(dp, type, OKneg);
445c73252aeSDavid du Colombier 	if(rp)
446c73252aeSDavid du Colombier 		if(rp->db){
447c73252aeSDavid du Colombier 			/* unauthoritative db entries are hints */
448c73252aeSDavid du Colombier 			if(rp->auth) {
449c73252aeSDavid du Colombier 				noteinmem();
450d9924332SDavid du Colombier 				if(debug)
451d9924332SDavid du Colombier 					dnslog("[%d] dnresolve1 %s %d %d: auth rr in db",
452d9924332SDavid du Colombier 						getpid(), name, type, class);
453c73252aeSDavid du Colombier 				return rp;
454c73252aeSDavid du Colombier 			}
455c73252aeSDavid du Colombier 		} else
456c73252aeSDavid du Colombier 			/* cached entry must still be valid */
457c73252aeSDavid du Colombier 			if(rp->ttl > now)
458c73252aeSDavid du Colombier 				/* but Tall entries are special */
459c73252aeSDavid du Colombier 				if(type != Tall || rp->query == Tall) {
460c73252aeSDavid du Colombier 					noteinmem();
461d9924332SDavid du Colombier 					if(debug)
462d9924332SDavid du Colombier 						dnslog("[%d] dnresolve1 %s %d %d: rr not in db",
463d9924332SDavid du Colombier 							getpid(), name, type, class);
464c73252aeSDavid du Colombier 					return rp;
465c73252aeSDavid du Colombier 				}
466c3617180SDavid du Colombier 	lock(&dnlock);
467c73252aeSDavid du Colombier 	rrfreelist(rp);
468c3617180SDavid du Colombier 	unlock(&dnlock);
46946595261SDavid du Colombier 	rp = nil;		/* accident prevention */
47046595261SDavid du Colombier 	USED(rp);
471c73252aeSDavid du Colombier 
472c73252aeSDavid du Colombier 	/*
473c73252aeSDavid du Colombier 	 * try the cache for a canonical name. if found punt
474c73252aeSDavid du Colombier 	 * since we'll find it during the canonical name search
475c73252aeSDavid du Colombier 	 * in dnresolve().
476c73252aeSDavid du Colombier 	 */
477c73252aeSDavid du Colombier 	if(type != Tcname){
478c73252aeSDavid du Colombier 		rp = rrlookup(dp, Tcname, NOneg);
479c3617180SDavid du Colombier 		lock(&dnlock);
480c73252aeSDavid du Colombier 		rrfreelist(rp);
481c3617180SDavid du Colombier 		unlock(&dnlock);
482d9924332SDavid du Colombier 		if(rp){
483d9924332SDavid du Colombier 			if(debug)
484d9924332SDavid du Colombier 				dnslog("[%d] dnresolve1 %s %d %d: rr from rrlookup for non-cname",
485d9924332SDavid du Colombier 					getpid(), name, type, class);
486c73252aeSDavid du Colombier 			return nil;
487c73252aeSDavid du Colombier 		}
488d9924332SDavid du Colombier 	}
489c73252aeSDavid du Colombier 
490c73252aeSDavid du Colombier 	/*
491c73252aeSDavid du Colombier 	 * if the domain name is within an area of ours,
492c73252aeSDavid du Colombier 	 * we should have found its data in memory by now.
493c73252aeSDavid du Colombier 	 */
494c73252aeSDavid du Colombier 	area = inmyarea(dp->name);
495c73252aeSDavid du Colombier 	if (area || strncmp(dp->name, "local#", 6) == 0) {
496c73252aeSDavid du Colombier //		char buf[32];
497c73252aeSDavid du Colombier 
498c73252aeSDavid du Colombier //		dnslog("%s %s: no data in area %s", dp->name,
499c73252aeSDavid du Colombier //			rrname(type, buf, sizeof buf), area->soarr->owner->name);
500c73252aeSDavid du Colombier 		return nil;
501c73252aeSDavid du Colombier 	}
502c73252aeSDavid du Colombier 
503c73252aeSDavid du Colombier 	qp = emalloc(sizeof *qp);
504c73252aeSDavid du Colombier 	queryinit(qp, dp, type, req);
50546595261SDavid du Colombier 	rp = issuequery(qp, name, class, depth, recurse);
506c73252aeSDavid du Colombier 	querydestroy(qp);
507c73252aeSDavid du Colombier 	free(qp);
508d9924332SDavid du Colombier 	if(rp){
509d9924332SDavid du Colombier 		if(debug)
510d9924332SDavid du Colombier 			dnslog("[%d] dnresolve1 %s %d %d: rr from query",
511d9924332SDavid du Colombier 				getpid(), name, type, class);
512c73252aeSDavid du Colombier 		return rp;
513d9924332SDavid du Colombier 	}
5143e12c5d1SDavid du Colombier 
5157dd7cddfSDavid du Colombier 	/* settle for a non-authoritative answer */
5167dd7cddfSDavid du Colombier 	rp = rrlookup(dp, type, OKneg);
517d9924332SDavid du Colombier 	if(rp){
518d9924332SDavid du Colombier 		if(debug)
519d9924332SDavid du Colombier 			dnslog("[%d] dnresolve1 %s %d %d: rr from rrlookup",
520d9924332SDavid du Colombier 				getpid(), name, type, class);
5217dd7cddfSDavid du Colombier 		return rp;
522d9924332SDavid du Colombier 	}
5237dd7cddfSDavid du Colombier 
5247dd7cddfSDavid du Colombier 	/* noone answered.  try the database, we might have a chance. */
525d9924332SDavid du Colombier 	rp = dblookup(name, class, type, 0, 0);
526d9924332SDavid du Colombier 	if (rp) {
527d9924332SDavid du Colombier 		if(debug)
528d9924332SDavid du Colombier 			dnslog("[%d] dnresolve1 %s %d %d: rr from dblookup",
529d9924332SDavid du Colombier 				getpid(), name, type, class);
530d9924332SDavid du Colombier 	}else{
531d9924332SDavid du Colombier 		if(debug)
532d9924332SDavid du Colombier 			dnslog("[%d] dnresolve1 %s %d %d: no rr from dblookup; crapped out",
533d9924332SDavid du Colombier 				getpid(), name, type, class);
534d9924332SDavid du Colombier 	}
535d9924332SDavid du Colombier 	return rp;
5363e12c5d1SDavid du Colombier }
5373e12c5d1SDavid du Colombier 
5383e12c5d1SDavid du Colombier /*
5394f8f669cSDavid du Colombier  *  walk a domain name one element to the right.
5404f8f669cSDavid du Colombier  *  return a pointer to that element.
5413e12c5d1SDavid du Colombier  *  in other words, return a pointer to the parent domain name.
5423e12c5d1SDavid du Colombier  */
5433e12c5d1SDavid du Colombier char*
5443e12c5d1SDavid du Colombier walkup(char *name)
5453e12c5d1SDavid du Colombier {
5463e12c5d1SDavid du Colombier 	char *cp;
5473e12c5d1SDavid du Colombier 
5483e12c5d1SDavid du Colombier 	cp = strchr(name, '.');
5493e12c5d1SDavid du Colombier 	if(cp)
5503e12c5d1SDavid du Colombier 		return cp+1;
5513e12c5d1SDavid du Colombier 	else if(*name)
5523e12c5d1SDavid du Colombier 		return "";
5533e12c5d1SDavid du Colombier 	else
5543e12c5d1SDavid du Colombier 		return 0;
5553e12c5d1SDavid du Colombier }
5563e12c5d1SDavid du Colombier 
5573e12c5d1SDavid du Colombier /*
558a41547ffSDavid du Colombier  *  Get a udp port for sending requests and reading replies.  Put the port
5593e12c5d1SDavid du Colombier  *  into "headers" mode.
5603e12c5d1SDavid du Colombier  */
5613e12c5d1SDavid du Colombier static char *hmsg = "headers";
5623e12c5d1SDavid du Colombier 
563dc5a79c1SDavid du Colombier int
5644f8f669cSDavid du Colombier udpport(char *mtpt)
5653e12c5d1SDavid du Colombier {
5663e12c5d1SDavid du Colombier 	int fd, ctl;
5674f8f669cSDavid du Colombier 	char ds[64], adir[64];
5683e12c5d1SDavid du Colombier 
5693e12c5d1SDavid du Colombier 	/* get a udp port */
5704f8f669cSDavid du Colombier 	snprint(ds, sizeof ds, "%s/udp!*!0", (mtpt? mtpt: "/net"));
5717dd7cddfSDavid du Colombier 	ctl = announce(ds, adir);
5727dd7cddfSDavid du Colombier 	if(ctl < 0){
5737dd7cddfSDavid du Colombier 		/* warning("can't get udp port"); */
574bd389b36SDavid du Colombier 		return -1;
575bd389b36SDavid du Colombier 	}
5763e12c5d1SDavid du Colombier 
5773e12c5d1SDavid du Colombier 	/* turn on header style interface */
578410ea80bSDavid du Colombier 	if(write(ctl, hmsg, strlen(hmsg)) != strlen(hmsg)){
5797dd7cddfSDavid du Colombier 		close(ctl);
580bd389b36SDavid du Colombier 		warning(hmsg);
581bd389b36SDavid du Colombier 		return -1;
582bd389b36SDavid du Colombier 	}
5833e12c5d1SDavid du Colombier 
5847dd7cddfSDavid du Colombier 	/* grab the data file */
5854f8f669cSDavid du Colombier 	snprint(ds, sizeof ds, "%s/data", adir);
5867dd7cddfSDavid du Colombier 	fd = open(ds, ORDWR);
5873e12c5d1SDavid du Colombier 	close(ctl);
5884f8f669cSDavid du Colombier 	if(fd < 0)
5894f8f669cSDavid du Colombier 		warning("can't open udp port %s: %r", ds);
5903e12c5d1SDavid du Colombier 	return fd;
5913e12c5d1SDavid du Colombier }
5923e12c5d1SDavid du Colombier 
593d9924332SDavid du Colombier void
594d9924332SDavid du Colombier initdnsmsg(DNSmsg *mp, RR *rp, int flags, ushort reqno)
595d9924332SDavid du Colombier {
596d9924332SDavid du Colombier 	mp->flags = flags;
597d9924332SDavid du Colombier 	mp->id = reqno;
598d9924332SDavid du Colombier 	mp->qd = rp;
59959f7772cSDavid du Colombier 	if(rp != nil)
60059f7772cSDavid du Colombier 		mp->qdcount = 1;
601d9924332SDavid du Colombier }
602d9924332SDavid du Colombier 
603d9924332SDavid du Colombier DNSmsg *
604d9924332SDavid du Colombier newdnsmsg(RR *rp, int flags, ushort reqno)
605d9924332SDavid du Colombier {
606d9924332SDavid du Colombier 	DNSmsg *mp;
607d9924332SDavid du Colombier 
608d9924332SDavid du Colombier 	mp = emalloc(sizeof *mp);
609d9924332SDavid du Colombier 	initdnsmsg(mp, rp, flags, reqno);
610d9924332SDavid du Colombier 	return mp;
611d9924332SDavid du Colombier }
612d9924332SDavid du Colombier 
613d6d99297SDavid du Colombier /* generate a DNS UDP query packet */
614dc5a79c1SDavid du Colombier int
615dc5a79c1SDavid du Colombier mkreq(DN *dp, int type, uchar *buf, int flags, ushort reqno)
6163e12c5d1SDavid du Colombier {
6173e12c5d1SDavid du Colombier 	DNSmsg m;
6183e12c5d1SDavid du Colombier 	int len;
619f27a9a5aSDavid du Colombier 	Udphdr *uh = (Udphdr*)buf;
620d9924332SDavid du Colombier 	RR *rp;
6213e12c5d1SDavid du Colombier 
6223e12c5d1SDavid du Colombier 	/* stuff port number into output buffer */
6233cbadd90SDavid du Colombier 	memset(uh, 0, sizeof *uh);
6247dd7cddfSDavid du Colombier 	hnputs(uh->rport, 53);
6253e12c5d1SDavid du Colombier 
6263e12c5d1SDavid du Colombier 	/* make request and convert it to output format */
6273cbadd90SDavid du Colombier 	memset(&m, 0, sizeof m);
628d9924332SDavid du Colombier 	rp = rralloc(type);
629d9924332SDavid du Colombier 	rp->owner = dp;
630d9924332SDavid du Colombier 	initdnsmsg(&m, rp, flags, reqno);
631f27a9a5aSDavid du Colombier 	len = convDNS2M(&m, &buf[Udphdrsize], Maxudp);
632c3617180SDavid du Colombier 	rrfreelistptr(&m.qd);
6336aaebd7dSDavid du Colombier 	memset(&m, 0, sizeof m);		/* cause trouble */
6343e12c5d1SDavid du Colombier 	return len;
6353e12c5d1SDavid du Colombier }
6363e12c5d1SDavid du Colombier 
637a41547ffSDavid du Colombier void
6387dd7cddfSDavid du Colombier freeanswers(DNSmsg *mp)
6393e12c5d1SDavid du Colombier {
640c3617180SDavid du Colombier 	lock(&dnlock);
641e6d9d902SDavid du Colombier 	rrfreelistptr(&mp->qd);
642e6d9d902SDavid du Colombier 	rrfreelistptr(&mp->an);
643e6d9d902SDavid du Colombier 	rrfreelistptr(&mp->ns);
644e6d9d902SDavid du Colombier 	rrfreelistptr(&mp->ar);
645c3617180SDavid du Colombier 	unlock(&dnlock);
646c3617180SDavid du Colombier 	mp->qdcount = mp->ancount = mp->nscount = mp->arcount = 0;
6477dd7cddfSDavid du Colombier }
6487dd7cddfSDavid du Colombier 
649*82f6abeeSDavid du Colombier /* timed read of reply.  sets srcip.  ibuf must be 64K to handle tcp answers. */
6503cbadd90SDavid du Colombier static int
651b8b25780SDavid du Colombier readnet(Query *qp, int medium, uchar *ibuf, uvlong endms, uchar **replyp,
6523cbadd90SDavid du Colombier 	uchar *srcip)
6533cbadd90SDavid du Colombier {
6543cbadd90SDavid du Colombier 	int len, fd;
655a41547ffSDavid du Colombier 	long ms;
656a41547ffSDavid du Colombier 	vlong startns = nsec();
6573cbadd90SDavid du Colombier 	uchar *reply;
6583cbadd90SDavid du Colombier 	uchar lenbuf[2];
6593cbadd90SDavid du Colombier 
6603cbadd90SDavid du Colombier 	len = -1;			/* pessimism */
661b8b25780SDavid du Colombier 	ms = endms - NS2MS(startns);
662b8b25780SDavid du Colombier 	if (ms <= 0)
663b8b25780SDavid du Colombier 		return -1;		/* taking too long */
664b8b25780SDavid du Colombier 
665b8b25780SDavid du Colombier 	reply = ibuf;
6664e7b9544SDavid du Colombier 	memset(srcip, 0, IPaddrlen);
667b8b25780SDavid du Colombier 	alarm(ms);
668a41547ffSDavid du Colombier 	if (medium == Udp)
6694e7b9544SDavid du Colombier 		if (qp->udpfd <= 0)
6704e7b9544SDavid du Colombier 			dnslog("readnet: qp->udpfd closed");
6714e7b9544SDavid du Colombier 		else {
672f27a9a5aSDavid du Colombier 			len = read(qp->udpfd, ibuf, Udphdrsize+Maxudpin);
673a41547ffSDavid du Colombier 			alarm(0);
674a41547ffSDavid du Colombier 			notestats(startns, len < 0, qp->type);
6754e7b9544SDavid du Colombier 			if (len >= IPaddrlen)
6764e7b9544SDavid du Colombier 				memmove(srcip, ibuf, IPaddrlen);
677f27a9a5aSDavid du Colombier 			if (len >= Udphdrsize) {
678f27a9a5aSDavid du Colombier 				len   -= Udphdrsize;
679f27a9a5aSDavid du Colombier 				reply += Udphdrsize;
6804e7b9544SDavid du Colombier 			}
6814e7b9544SDavid du Colombier 		}
682a41547ffSDavid du Colombier 	else {
6833cbadd90SDavid du Colombier 		if (!qp->tcpset)
6843cbadd90SDavid du Colombier 			dnslog("readnet: tcp params not set");
6853cbadd90SDavid du Colombier 		fd = qp->tcpfd;
6863cbadd90SDavid du Colombier 		if (fd <= 0)
6873cbadd90SDavid du Colombier 			dnslog("readnet: %s: tcp fd unset for dest %I",
6883cbadd90SDavid du Colombier 				qp->dp->name, qp->tcpip);
6893cbadd90SDavid du Colombier 		else if (readn(fd, lenbuf, 2) != 2) {
690e6d9d902SDavid du Colombier 			dnslog("readnet: short read of 2-byte tcp msg size from %I",
6913cbadd90SDavid du Colombier 				qp->tcpip);
692a41547ffSDavid du Colombier 			/* probably a time-out */
693a41547ffSDavid du Colombier 			notestats(startns, 1, qp->type);
6943cbadd90SDavid du Colombier 		} else {
6953cbadd90SDavid du Colombier 			len = lenbuf[0]<<8 | lenbuf[1];
6963cbadd90SDavid du Colombier 			if (readn(fd, ibuf, len) != len) {
6973cbadd90SDavid du Colombier 				dnslog("readnet: short read of tcp data from %I",
6983cbadd90SDavid du Colombier 					qp->tcpip);
699a41547ffSDavid du Colombier 				/* probably a time-out */
700a41547ffSDavid du Colombier 				notestats(startns, 1, qp->type);
7013cbadd90SDavid du Colombier 				len = -1;
7023cbadd90SDavid du Colombier 			}
7033cbadd90SDavid du Colombier 		}
7043cbadd90SDavid du Colombier 		memmove(srcip, qp->tcpip, IPaddrlen);
7053cbadd90SDavid du Colombier 	}
706b8b25780SDavid du Colombier 	alarm(0);
7073cbadd90SDavid du Colombier 	*replyp = reply;
7083cbadd90SDavid du Colombier 	return len;
7093cbadd90SDavid du Colombier }
7103cbadd90SDavid du Colombier 
7117dd7cddfSDavid du Colombier /*
7123cbadd90SDavid du Colombier  *  read replies to a request and remember the rrs in the answer(s).
7133cbadd90SDavid du Colombier  *  ignore any of the wrong type.
714b8b25780SDavid du Colombier  *  wait at most until endms.
7157dd7cddfSDavid du Colombier  */
7167dd7cddfSDavid du Colombier static int
7173cbadd90SDavid du Colombier readreply(Query *qp, int medium, ushort req, uchar *ibuf, DNSmsg *mp,
718b8b25780SDavid du Colombier 	uvlong endms)
7197dd7cddfSDavid du Colombier {
720b8b25780SDavid du Colombier 	int len;
7217dd7cddfSDavid du Colombier 	char *err;
722a41547ffSDavid du Colombier 	char tbuf[32];
7233cbadd90SDavid du Colombier 	uchar *reply;
7243cbadd90SDavid du Colombier 	uchar srcip[IPaddrlen];
7253e12c5d1SDavid du Colombier 	RR *rp;
7267dd7cddfSDavid du Colombier 
7273cbadd90SDavid du Colombier 	queryck(qp);
7283cbadd90SDavid du Colombier 	memset(mp, 0, sizeof *mp);
729a41547ffSDavid du Colombier 	memset(srcip, 0, sizeof srcip);
730a41547ffSDavid du Colombier 	if (0)
731a41547ffSDavid du Colombier 		len = -1;
732b8b25780SDavid du Colombier 	for (; timems() < endms &&
733b8b25780SDavid du Colombier 	    (len = readnet(qp, medium, ibuf, endms, &reply, srcip)) >= 0;
7343cbadd90SDavid du Colombier 	    freeanswers(mp)){
7357dd7cddfSDavid du Colombier 		/* convert into internal format  */
7363cbadd90SDavid du Colombier 		memset(mp, 0, sizeof *mp);
7373cbadd90SDavid du Colombier 		err = convM2DNS(reply, len, mp, nil);
738d6d99297SDavid du Colombier 		if (mp->flags & Ftrunc) {
739a41547ffSDavid du Colombier 			free(err);
740a41547ffSDavid du Colombier 			freeanswers(mp);
741a41547ffSDavid du Colombier 			/* notify our caller to retry the query via tcp. */
7423cbadd90SDavid du Colombier 			return -1;
7433cbadd90SDavid du Colombier 		} else if(err){
7443cbadd90SDavid du Colombier 			dnslog("readreply: %s: input err, len %d: %s: %I",
7453cbadd90SDavid du Colombier 				qp->dp->name, len, err, srcip);
7463cbadd90SDavid du Colombier 			free(err);
7477dd7cddfSDavid du Colombier 			continue;
7487dd7cddfSDavid du Colombier 		}
7497dd7cddfSDavid du Colombier 		if(debug)
7503cbadd90SDavid du Colombier 			logreply(qp->req->id, srcip, mp);
7517dd7cddfSDavid du Colombier 
7527dd7cddfSDavid du Colombier 		/* answering the right question? */
7533cbadd90SDavid du Colombier 		if(mp->id != req)
7543cbadd90SDavid du Colombier 			dnslog("%d: id %d instead of %d: %I", qp->req->id,
7553cbadd90SDavid du Colombier 				mp->id, req, srcip);
7563cbadd90SDavid du Colombier 		else if(mp->qd == 0)
7573cbadd90SDavid du Colombier 			dnslog("%d: no question RR: %I", qp->req->id, srcip);
7583cbadd90SDavid du Colombier 		else if(mp->qd->owner != qp->dp)
7593cbadd90SDavid du Colombier 			dnslog("%d: owner %s instead of %s: %I", qp->req->id,
7603cbadd90SDavid du Colombier 				mp->qd->owner->name, qp->dp->name, srcip);
7613cbadd90SDavid du Colombier 		else if(mp->qd->type != qp->type)
7623cbadd90SDavid du Colombier 			dnslog("%d: qp->type %d instead of %d: %I",
7633cbadd90SDavid du Colombier 				qp->req->id, mp->qd->type, qp->type, srcip);
7643cbadd90SDavid du Colombier 		else {
7657dd7cddfSDavid du Colombier 			/* remember what request this is in answer to */
7667dd7cddfSDavid du Colombier 			for(rp = mp->an; rp; rp = rp->next)
7673cbadd90SDavid du Colombier 				rp->query = qp->type;
768b8b25780SDavid du Colombier 			return 0;
7697dd7cddfSDavid du Colombier 		}
7707dd7cddfSDavid du Colombier 	}
771b8b25780SDavid du Colombier 	if (timems() >= endms) {
772a41547ffSDavid du Colombier 		;				/* query expired */
773a41547ffSDavid du Colombier 	} else if (0) {
774a41547ffSDavid du Colombier 		/* this happens routinely when a read times out */
775a41547ffSDavid du Colombier 		dnslog("readreply: %s type %s: ns %I read error or eof "
776a41547ffSDavid du Colombier 			"(returned %d): %r", qp->dp->name, rrname(qp->type,
777a41547ffSDavid du Colombier 			tbuf, sizeof tbuf), srcip, len);
778a41547ffSDavid du Colombier 		if (medium == Udp)
779a41547ffSDavid du Colombier 			for (rp = qp->nsrp; rp != nil; rp = rp->next)
780a41547ffSDavid du Colombier 				if (rp->type == Tns)
781a41547ffSDavid du Colombier 					dnslog("readreply: %s: query sent to "
782a41547ffSDavid du Colombier 						"ns %s", qp->dp->name,
783a41547ffSDavid du Colombier 						rp->host->name);
784a41547ffSDavid du Colombier 	}
7853cbadd90SDavid du Colombier 	return -1;
7863cbadd90SDavid du Colombier }
7877dd7cddfSDavid du Colombier 
7887dd7cddfSDavid du Colombier /*
7897dd7cddfSDavid du Colombier  *	return non-0 if first list includes second list
7907dd7cddfSDavid du Colombier  */
7917dd7cddfSDavid du Colombier int
7927dd7cddfSDavid du Colombier contains(RR *rp1, RR *rp2)
7937dd7cddfSDavid du Colombier {
7947dd7cddfSDavid du Colombier 	RR *trp1, *trp2;
7957dd7cddfSDavid du Colombier 
7967dd7cddfSDavid du Colombier 	for(trp2 = rp2; trp2; trp2 = trp2->next){
7973cbadd90SDavid du Colombier 		for(trp1 = rp1; trp1; trp1 = trp1->next)
7987dd7cddfSDavid du Colombier 			if(trp1->type == trp2->type)
7997dd7cddfSDavid du Colombier 			if(trp1->host == trp2->host)
8007dd7cddfSDavid du Colombier 			if(trp1->owner == trp2->owner)
8017dd7cddfSDavid du Colombier 				break;
8024f8f669cSDavid du Colombier 		if(trp1 == nil)
8037dd7cddfSDavid du Colombier 			return 0;
8047dd7cddfSDavid du Colombier 	}
8057dd7cddfSDavid du Colombier 	return 1;
8067dd7cddfSDavid du Colombier }
8077dd7cddfSDavid du Colombier 
8087dd7cddfSDavid du Colombier 
8096b6b9ac8SDavid du Colombier /*
8106b6b9ac8SDavid du Colombier  *  return multicast version if any
8116b6b9ac8SDavid du Colombier  */
8126b6b9ac8SDavid du Colombier int
8136b6b9ac8SDavid du Colombier ipisbm(uchar *ip)
8146b6b9ac8SDavid du Colombier {
8156b6b9ac8SDavid du Colombier 	if(isv4(ip)){
8164f8f669cSDavid du Colombier 		if (ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0 ||
8174f8f669cSDavid du Colombier 		    ipcmp(ip, IPv4bcast) == 0)
8186b6b9ac8SDavid du Colombier 			return 4;
8194f8f669cSDavid du Colombier 	} else
8206b6b9ac8SDavid du Colombier 		if(ip[0] == 0xff)
8216b6b9ac8SDavid du Colombier 			return 6;
82228a8a86bSDavid du Colombier 	return 0;
82328a8a86bSDavid du Colombier }
82428a8a86bSDavid du Colombier 
8257dd7cddfSDavid du Colombier /*
82659f7772cSDavid du Colombier  *  Get next server address(es) into qp->dest[nd] and beyond
8277dd7cddfSDavid du Colombier  */
8287dd7cddfSDavid du Colombier static int
8293cbadd90SDavid du Colombier serveraddrs(Query *qp, int nd, int depth)
8307dd7cddfSDavid du Colombier {
8317dd7cddfSDavid du Colombier 	RR *rp, *arp, *trp;
8327dd7cddfSDavid du Colombier 	Dest *cur;
8337dd7cddfSDavid du Colombier 
834c3617180SDavid du Colombier 	if(nd >= Maxdest)		/* dest array is full? */
835c3617180SDavid du Colombier 		return Maxdest - 1;
8367dd7cddfSDavid du Colombier 
8377dd7cddfSDavid du Colombier 	/*
8387dd7cddfSDavid du Colombier 	 *  look for a server whose address we already know.
8397dd7cddfSDavid du Colombier 	 *  if we find one, mark it so we ignore this on
8407dd7cddfSDavid du Colombier 	 *  subsequent passes.
8417dd7cddfSDavid du Colombier 	 */
8427dd7cddfSDavid du Colombier 	arp = 0;
8433cbadd90SDavid du Colombier 	for(rp = qp->nsrp; rp; rp = rp->next){
84434f77ae3SDavid du Colombier 		assert(rp->magic == RRmagic);
8457dd7cddfSDavid du Colombier 		if(rp->marker)
8467dd7cddfSDavid du Colombier 			continue;
8477dd7cddfSDavid du Colombier 		arp = rrlookup(rp->host, Ta, NOneg);
848d9924332SDavid du Colombier 		if(arp == nil)
849d9924332SDavid du Colombier 			arp = rrlookup(rp->host, Taaaa, NOneg);
8507dd7cddfSDavid du Colombier 		if(arp){
8517dd7cddfSDavid du Colombier 			rp->marker = 1;
8527dd7cddfSDavid du Colombier 			break;
8537dd7cddfSDavid du Colombier 		}
8547dd7cddfSDavid du Colombier 		arp = dblookup(rp->host->name, Cin, Ta, 0, 0);
855d9924332SDavid du Colombier 		if(arp == nil)
856d9924332SDavid du Colombier 			arp = dblookup(rp->host->name, Cin, Taaaa, 0, 0);
8577dd7cddfSDavid du Colombier 		if(arp){
8587dd7cddfSDavid du Colombier 			rp->marker = 1;
8597dd7cddfSDavid du Colombier 			break;
8607dd7cddfSDavid du Colombier 		}
8617dd7cddfSDavid du Colombier 	}
8627dd7cddfSDavid du Colombier 
8637dd7cddfSDavid du Colombier 	/*
8647dd7cddfSDavid du Colombier 	 *  if the cache and database lookup didn't find any new
8657dd7cddfSDavid du Colombier 	 *  server addresses, try resolving one via the network.
8667dd7cddfSDavid du Colombier 	 *  Mark any we try to resolve so we don't try a second time.
8677dd7cddfSDavid du Colombier 	 */
8684f8f669cSDavid du Colombier 	if(arp == 0)
8693cbadd90SDavid du Colombier 		for(rp = qp->nsrp; rp; rp = rp->next){
8707dd7cddfSDavid du Colombier 			if(rp->marker)
8717dd7cddfSDavid du Colombier 				continue;
8727dd7cddfSDavid du Colombier 			rp->marker = 1;
8737dd7cddfSDavid du Colombier 
8747dd7cddfSDavid du Colombier 			/*
8757dd7cddfSDavid du Colombier 			 *  avoid loops looking up a server under itself
8767dd7cddfSDavid du Colombier 			 */
8777dd7cddfSDavid du Colombier 			if(subsume(rp->owner->name, rp->host->name))
8787dd7cddfSDavid du Colombier 				continue;
8797dd7cddfSDavid du Colombier 
8803cbadd90SDavid du Colombier 			arp = dnresolve(rp->host->name, Cin, Ta, qp->req, 0,
8814f8f669cSDavid du Colombier 				depth+1, Recurse, 1, 0);
882d9924332SDavid du Colombier 			if(arp == nil)
883d9924332SDavid du Colombier 				arp = dnresolve(rp->host->name, Cin, Taaaa,
884d9924332SDavid du Colombier 					qp->req, 0, depth+1, Recurse, 1, 0);
885530fef66SDavid du Colombier 			lock(&dnlock);
8867dd7cddfSDavid du Colombier 			rrfreelist(rrremneg(&arp));
887530fef66SDavid du Colombier 			unlock(&dnlock);
8887dd7cddfSDavid du Colombier 			if(arp)
8897dd7cddfSDavid du Colombier 				break;
8907dd7cddfSDavid du Colombier 		}
8917dd7cddfSDavid du Colombier 
8927dd7cddfSDavid du Colombier 	/* use any addresses that we found */
8933cbadd90SDavid du Colombier 	for(trp = arp; trp && nd < Maxdest; trp = trp->next){
8943cbadd90SDavid du Colombier 		cur = &qp->dest[nd];
8957dd7cddfSDavid du Colombier 		parseip(cur->a, trp->ip->name);
8964f8f669cSDavid du Colombier 		/*
8974f8f669cSDavid du Colombier 		 * straddling servers can reject all nameservers if they are all
8984f8f669cSDavid du Colombier 		 * inside, so be sure to list at least one outside ns at
8994f8f669cSDavid du Colombier 		 * the end of the ns list in /lib/ndb for `dom='.
9004f8f669cSDavid du Colombier 		 */
9014f8f669cSDavid du Colombier 		if (ipisbm(cur->a) ||
9023cbadd90SDavid du Colombier 		    cfg.straddle && !insideaddr(qp->dp->name) && insidens(cur->a))
9036b6b9ac8SDavid du Colombier 			continue;
9047dd7cddfSDavid du Colombier 		cur->nx = 0;
9057dd7cddfSDavid du Colombier 		cur->s = trp->owner;
9067dd7cddfSDavid du Colombier 		cur->code = Rtimeout;
9076b6b9ac8SDavid du Colombier 		nd++;
9087dd7cddfSDavid du Colombier 	}
909c3617180SDavid du Colombier 	lock(&dnlock);
9107dd7cddfSDavid du Colombier 	rrfreelist(arp);
911c3617180SDavid du Colombier 	unlock(&dnlock);
9127dd7cddfSDavid du Colombier 	return nd;
9137dd7cddfSDavid du Colombier }
9147dd7cddfSDavid du Colombier 
9157dd7cddfSDavid du Colombier /*
9167dd7cddfSDavid du Colombier  *  cache negative responses
9177dd7cddfSDavid du Colombier  */
9187dd7cddfSDavid du Colombier static void
9197dd7cddfSDavid du Colombier cacheneg(DN *dp, int type, int rcode, RR *soarr)
9207dd7cddfSDavid du Colombier {
9217dd7cddfSDavid du Colombier 	RR *rp;
9227dd7cddfSDavid du Colombier 	DN *soaowner;
9239a747e4fSDavid du Colombier 	ulong ttl;
9247dd7cddfSDavid du Colombier 
925f46c709fSDavid du Colombier 	stats.negcached++;
926f46c709fSDavid du Colombier 
9274f8f669cSDavid du Colombier 	/* no cache time specified, don't make anything up */
9287dd7cddfSDavid du Colombier 	if(soarr != nil){
929c3617180SDavid du Colombier 		lock(&dnlock);
930e6d9d902SDavid du Colombier 		if(soarr->next != nil)
931e6d9d902SDavid du Colombier 			rrfreelistptr(&soarr->next);
932c3617180SDavid du Colombier 		unlock(&dnlock);
9337dd7cddfSDavid du Colombier 		soaowner = soarr->owner;
9347dd7cddfSDavid du Colombier 	} else
9357dd7cddfSDavid du Colombier 		soaowner = nil;
9367dd7cddfSDavid du Colombier 
9379a747e4fSDavid du Colombier 	/* the attach can cause soarr to be freed so mine it now */
9389a747e4fSDavid du Colombier 	if(soarr != nil && soarr->soa != nil)
9399a747e4fSDavid du Colombier 		ttl = soarr->soa->minttl+now;
9409a747e4fSDavid du Colombier 	else
9419a747e4fSDavid du Colombier 		ttl = 5*Min;
9429a747e4fSDavid du Colombier 
9437dd7cddfSDavid du Colombier 	/* add soa and negative RR to the database */
9446dc4800dSDavid du Colombier 	rrattach(soarr, Authoritative);
9457dd7cddfSDavid du Colombier 
9467dd7cddfSDavid du Colombier 	rp = rralloc(type);
9477dd7cddfSDavid du Colombier 	rp->owner = dp;
9487dd7cddfSDavid du Colombier 	rp->negative = 1;
9497dd7cddfSDavid du Colombier 	rp->negsoaowner = soaowner;
9507dd7cddfSDavid du Colombier 	rp->negrcode = rcode;
9519a747e4fSDavid du Colombier 	rp->ttl = ttl;
9526dc4800dSDavid du Colombier 	rrattach(rp, Authoritative);
9537dd7cddfSDavid du Colombier }
9547dd7cddfSDavid du Colombier 
9554f8f669cSDavid du Colombier static int
9564f8f669cSDavid du Colombier setdestoutns(Dest *p, int n)
9574f8f669cSDavid du Colombier {
9584f8f669cSDavid du Colombier 	uchar *outns = outsidens(n);
9594f8f669cSDavid du Colombier 
9603cbadd90SDavid du Colombier 	destck(p);
9613cbadd90SDavid du Colombier 	destinit(p);
9624f8f669cSDavid du Colombier 	if (outns == nil) {
9634f8f669cSDavid du Colombier 		if (n == 0)
9644f8f669cSDavid du Colombier 			dnslog("[%d] no outside-ns in ndb", getpid());
9654f8f669cSDavid du Colombier 		return -1;
9664f8f669cSDavid du Colombier 	}
9674f8f669cSDavid du Colombier 	memmove(p->a, outns, sizeof p->a);
9684f8f669cSDavid du Colombier 	p->s = dnlookup("outside-ns-ips", Cin, 1);
9694f8f669cSDavid du Colombier 	return 0;
9704f8f669cSDavid du Colombier }
9714f8f669cSDavid du Colombier 
9727dd7cddfSDavid du Colombier /*
9733cbadd90SDavid du Colombier  * issue query via UDP or TCP as appropriate.
9743cbadd90SDavid du Colombier  * for TCP, returns with qp->tcpip set from udppkt header.
9757dd7cddfSDavid du Colombier  */
9767dd7cddfSDavid du Colombier static int
9773cbadd90SDavid du Colombier mydnsquery(Query *qp, int medium, uchar *udppkt, int len)
9787dd7cddfSDavid du Colombier {
979a41547ffSDavid du Colombier 	int rv = -1, nfd;
9803cbadd90SDavid du Colombier 	char *domain;
981cc499a30SDavid du Colombier 	char conndir[40], net[40];
982a41547ffSDavid du Colombier 	uchar belen[2];
9833cbadd90SDavid du Colombier 	NetConnInfo *nci;
9844f8f669cSDavid du Colombier 
9853cbadd90SDavid du Colombier 	queryck(qp);
9864e153993SDavid du Colombier 	domain = smprint("%I", udppkt);
9874e153993SDavid du Colombier 	if (myaddr(domain)) {
9884e153993SDavid du Colombier 		dnslog("mydnsquery: trying to send to myself (%s); bzzzt",
9894e153993SDavid du Colombier 			domain);
9904e153993SDavid du Colombier 		free(domain);
9914e153993SDavid du Colombier 		return rv;
9924e153993SDavid du Colombier 	}
9934e153993SDavid du Colombier 
9943cbadd90SDavid du Colombier 	switch (medium) {
9953cbadd90SDavid du Colombier 	case Udp:
9964e153993SDavid du Colombier 		free(domain);
997a41547ffSDavid du Colombier 		nfd = dup(qp->udpfd, -1);
998a41547ffSDavid du Colombier 		if (nfd < 0) {
999a41547ffSDavid du Colombier 			warning("mydnsquery: qp->udpfd %d: %r", qp->udpfd);
1000a41547ffSDavid du Colombier 			close(qp->udpfd);	/* ensure it's closed */
1001a41547ffSDavid du Colombier 			qp->udpfd = -1;		/* poison it */
1002a41547ffSDavid du Colombier 			return rv;
1003a41547ffSDavid du Colombier 		}
1004a41547ffSDavid du Colombier 		close(nfd);
1005a41547ffSDavid du Colombier 
10064e7b9544SDavid du Colombier 		if (qp->udpfd <= 0)
1007a41547ffSDavid du Colombier 			dnslog("mydnsquery: qp->udpfd %d closed", qp->udpfd);
10084e7b9544SDavid du Colombier 		else {
1009f27a9a5aSDavid du Colombier 			if (write(qp->udpfd, udppkt, len+Udphdrsize) !=
1010f27a9a5aSDavid du Colombier 			    len+Udphdrsize)
1011a41547ffSDavid du Colombier 				warning("sending udp msg: %r");
1012a41547ffSDavid du Colombier 			else {
1013a41547ffSDavid du Colombier 				stats.qsent++;
10143cbadd90SDavid du Colombier 				rv = 0;
10154e7b9544SDavid du Colombier 			}
1016a41547ffSDavid du Colombier 		}
10173cbadd90SDavid du Colombier 		break;
10183cbadd90SDavid du Colombier 	case Tcp:
10193cbadd90SDavid du Colombier 		/* send via TCP & keep fd around for reply */
1020cc499a30SDavid du Colombier 		snprint(net, sizeof net, "%s/tcp",
1021cc499a30SDavid du Colombier 			(mntpt[0] != '\0'? mntpt: "/net"));
10223cbadd90SDavid du Colombier 		alarm(10*1000);
1023cc499a30SDavid du Colombier 		qp->tcpfd = rv = dial(netmkaddr(domain, net, "dns"), nil,
10243cbadd90SDavid du Colombier 			conndir, &qp->tcpctlfd);
10253cbadd90SDavid du Colombier 		alarm(0);
10263cbadd90SDavid du Colombier 		if (qp->tcpfd < 0) {
10273cbadd90SDavid du Colombier 			dnslog("can't dial tcp!%s!dns: %r", domain);
1028a41547ffSDavid du Colombier 			free(domain);
1029a41547ffSDavid du Colombier 			break;
1030a41547ffSDavid du Colombier 		}
1031a41547ffSDavid du Colombier 		free(domain);
10323cbadd90SDavid du Colombier 		nci = getnetconninfo(conndir, qp->tcpfd);
10333cbadd90SDavid du Colombier 		if (nci) {
10343cbadd90SDavid du Colombier 			parseip(qp->tcpip, nci->rsys);
10353cbadd90SDavid du Colombier 			freenetconninfo(nci);
10363cbadd90SDavid du Colombier 		} else
10373cbadd90SDavid du Colombier 			dnslog("mydnsquery: getnetconninfo failed");
10383cbadd90SDavid du Colombier 		qp->tcpset = 1;
10393e12c5d1SDavid du Colombier 
10403cbadd90SDavid du Colombier 		belen[0] = len >> 8;
10413cbadd90SDavid du Colombier 		belen[1] = len;
10423cbadd90SDavid du Colombier 		if (write(qp->tcpfd, belen, 2) != 2 ||
1043f27a9a5aSDavid du Colombier 		    write(qp->tcpfd, udppkt + Udphdrsize, len) != len)
1044a41547ffSDavid du Colombier 			warning("sending tcp msg: %r");
10453cbadd90SDavid du Colombier 		break;
10463cbadd90SDavid du Colombier 	default:
10473cbadd90SDavid du Colombier 		sysfatal("mydnsquery: bad medium");
10483cbadd90SDavid du Colombier 	}
10493cbadd90SDavid du Colombier 	return rv;
10503cbadd90SDavid du Colombier }
10517dd7cddfSDavid du Colombier 
10523e12c5d1SDavid du Colombier /*
10533cbadd90SDavid du Colombier  * send query to all UDP destinations or one TCP destination,
10543cbadd90SDavid du Colombier  * taken from obuf (udp packet) header
10553e12c5d1SDavid du Colombier  */
10563cbadd90SDavid du Colombier static int
10573cbadd90SDavid du Colombier xmitquery(Query *qp, int medium, int depth, uchar *obuf, int inns, int len)
10583cbadd90SDavid du Colombier {
10593cbadd90SDavid du Colombier 	int j, n;
10603cbadd90SDavid du Colombier 	char buf[32];
10613cbadd90SDavid du Colombier 	Dest *p;
10623e12c5d1SDavid du Colombier 
10633cbadd90SDavid du Colombier 	queryck(qp);
1064b8b25780SDavid du Colombier 	if(timems() >= qp->req->aborttime)
10653cbadd90SDavid du Colombier 		return -1;
10663cbadd90SDavid du Colombier 
10673cbadd90SDavid du Colombier 	/*
10683cbadd90SDavid du Colombier 	 * get a nameserver address if we need one.
10693cbadd90SDavid du Colombier 	 * serveraddrs populates qp->dest.
10703cbadd90SDavid du Colombier 	 */
10713cbadd90SDavid du Colombier 	p = qp->dest;
10723cbadd90SDavid du Colombier 	destck(p);
1073c3617180SDavid du Colombier 	if (qp->ndest < 0 || qp->ndest > Maxdest) {
10743cbadd90SDavid du Colombier 		dnslog("qp->ndest %d out of range", qp->ndest);
1075c3617180SDavid du Colombier 		abort();
1076c3617180SDavid du Colombier 	}
1077c3617180SDavid du Colombier 	/*
1078c3617180SDavid du Colombier 	 * we're to transmit to more destinations than we currently have,
1079c3617180SDavid du Colombier 	 * so get another.
1080c3617180SDavid du Colombier 	 */
10813468a491SDavid du Colombier 	if (qp->ndest > qp->curdest - p) {
10823468a491SDavid du Colombier 		j = serveraddrs(qp, qp->curdest - p, depth);
10833468a491SDavid du Colombier 		if (j < 0 || j >= Maxdest) {
10843468a491SDavid du Colombier 			dnslog("serveraddrs() result %d out of range", j);
10853468a491SDavid du Colombier 			abort();
10863468a491SDavid du Colombier 		}
10873468a491SDavid du Colombier 		qp->curdest = &qp->dest[j];
10883468a491SDavid du Colombier 	}
10893cbadd90SDavid du Colombier 	destck(qp->curdest);
10907dd7cddfSDavid du Colombier 
10917dd7cddfSDavid du Colombier 	/* no servers, punt */
10923468a491SDavid du Colombier 	if (qp->ndest == 0)
10934f8f669cSDavid du Colombier 		if (cfg.straddle && cfg.inside) {
10943cbadd90SDavid du Colombier 			/* get ips of "outside-ns-ips" */
1095c3617180SDavid du Colombier 			qp->curdest = qp->dest;
10963cbadd90SDavid du Colombier 			for(n = 0; n < Maxdest; n++, qp->curdest++)
10973cbadd90SDavid du Colombier 				if (setdestoutns(qp->curdest, n) < 0)
10987dd7cddfSDavid du Colombier 					break;
1099c3617180SDavid du Colombier 			if(n == 0)
1100c3617180SDavid du Colombier 				dnslog("xmitquery: %s: no outside-ns nameservers",
1101c3617180SDavid du Colombier 					qp->dp->name);
1102c3617180SDavid du Colombier 		} else
11034e7b9544SDavid du Colombier 			/* it's probably just a bogus domain, don't log it */
11043cbadd90SDavid du Colombier 			return -1;
11057dd7cddfSDavid du Colombier 
11063cbadd90SDavid du Colombier 	/* send to first 'qp->ndest' destinations */
11077dd7cddfSDavid du Colombier 	j = 0;
11083cbadd90SDavid du Colombier 	if (medium == Tcp) {
11093cbadd90SDavid du Colombier 		j++;
11103cbadd90SDavid du Colombier 		queryck(qp);
11113cbadd90SDavid du Colombier 		assert(qp->dp);
11123cbadd90SDavid du Colombier 		procsetname("tcp %sside query for %s %s", (inns? "in": "out"),
11133cbadd90SDavid du Colombier 			qp->dp->name, rrname(qp->type, buf, sizeof buf));
11143cbadd90SDavid du Colombier 		mydnsquery(qp, medium, obuf, len); /* sets qp->tcpip from obuf */
11153cbadd90SDavid du Colombier 		if(debug)
11163cbadd90SDavid du Colombier 			logsend(qp->req->id, depth, qp->tcpip, "", qp->dp->name,
11173cbadd90SDavid du Colombier 				qp->type);
11183cbadd90SDavid du Colombier 	} else
11193cbadd90SDavid du Colombier 		for(; p < &qp->dest[qp->ndest] && p < qp->curdest; p++){
11207dd7cddfSDavid du Colombier 			/* skip destinations we've finished with */
11217dd7cddfSDavid du Colombier 			if(p->nx >= Maxtrans)
11227dd7cddfSDavid du Colombier 				continue;
11237dd7cddfSDavid du Colombier 
11247dd7cddfSDavid du Colombier 			j++;
11257dd7cddfSDavid du Colombier 
11267dd7cddfSDavid du Colombier 			/* exponential backoff of requests */
11273cbadd90SDavid du Colombier 			if((1<<p->nx) > qp->ndest)
11287dd7cddfSDavid du Colombier 				continue;
11297dd7cddfSDavid du Colombier 
113059f7772cSDavid du Colombier 			if(memcmp(p->a, IPnoaddr, sizeof IPnoaddr) == 0)
113159f7772cSDavid du Colombier 				continue;		/* mistake */
113259f7772cSDavid du Colombier 
11333cbadd90SDavid du Colombier 			procsetname("udp %sside query to %I/%s %s %s",
11343cbadd90SDavid du Colombier 				(inns? "in": "out"), p->a, p->s->name,
11353cbadd90SDavid du Colombier 				qp->dp->name, rrname(qp->type, buf, sizeof buf));
1136219b2ee8SDavid du Colombier 			if(debug)
11373cbadd90SDavid du Colombier 				logsend(qp->req->id, depth, p->a, p->s->name,
11383cbadd90SDavid du Colombier 					qp->dp->name, qp->type);
11394f8f669cSDavid du Colombier 
11403cbadd90SDavid du Colombier 			/* fill in UDP destination addr & send it */
11413cbadd90SDavid du Colombier 			memmove(obuf, p->a, sizeof p->a);
11423cbadd90SDavid du Colombier 			mydnsquery(qp, medium, obuf, len);
11437dd7cddfSDavid du Colombier 			p->nx++;
11443e12c5d1SDavid du Colombier 		}
11453cbadd90SDavid du Colombier 	if(j == 0) {
11463cbadd90SDavid du Colombier 		return -1;
11473cbadd90SDavid du Colombier 	}
11483cbadd90SDavid du Colombier 	return 0;
11493cbadd90SDavid du Colombier }
11503e12c5d1SDavid du Colombier 
1151f46c709fSDavid du Colombier static int lckindex[Maxlcks] = {
1152f46c709fSDavid du Colombier 	0,			/* all others map here */
1153f46c709fSDavid du Colombier 	Ta,
1154f46c709fSDavid du Colombier 	Tns,
1155f46c709fSDavid du Colombier 	Tcname,
1156f46c709fSDavid du Colombier 	Tsoa,
1157f46c709fSDavid du Colombier 	Tptr,
1158f46c709fSDavid du Colombier 	Tmx,
1159f46c709fSDavid du Colombier 	Ttxt,
1160f46c709fSDavid du Colombier 	Taaaa,
1161f46c709fSDavid du Colombier };
1162f46c709fSDavid du Colombier 
1163f46c709fSDavid du Colombier static int
1164f46c709fSDavid du Colombier qtype2lck(int qtype)		/* map query type to querylck index */
1165f46c709fSDavid du Colombier {
1166f46c709fSDavid du Colombier 	int i;
1167f46c709fSDavid du Colombier 
1168f46c709fSDavid du Colombier 	for (i = 1; i < nelem(lckindex); i++)
1169f46c709fSDavid du Colombier 		if (lckindex[i] == qtype)
1170f46c709fSDavid du Colombier 			return i;
1171f46c709fSDavid du Colombier 	return 0;
1172f46c709fSDavid du Colombier }
1173f46c709fSDavid du Colombier 
11740319257bSDavid du Colombier /* is mp a cachable negative response (with Rname set)? */
11750319257bSDavid du Colombier static int
11760319257bSDavid du Colombier isnegrname(DNSmsg *mp)
11770319257bSDavid du Colombier {
11780319257bSDavid du Colombier 	/* TODO: could add || cfg.justforw to RHS of && */
11790319257bSDavid du Colombier 	return mp->an == nil && (mp->flags & Rmask) == Rname;
11800319257bSDavid du Colombier }
11810319257bSDavid du Colombier 
1182c3617180SDavid du Colombier /* returns Answerr (-1) on errors, else number of answers, which can be zero. */
11833cbadd90SDavid du Colombier static int
11843cbadd90SDavid du Colombier procansw(Query *qp, DNSmsg *mp, uchar *srcip, int depth, Dest *p)
11853cbadd90SDavid du Colombier {
11863cbadd90SDavid du Colombier 	int rv;
11874e5f5f32SDavid du Colombier //	int lcktype;
11883cbadd90SDavid du Colombier 	char buf[32];
11893cbadd90SDavid du Colombier 	DN *ndp;
1190c73252aeSDavid du Colombier 	Query *nqp;
11913cbadd90SDavid du Colombier 	RR *tp, *soarr;
11927dd7cddfSDavid du Colombier 
1193f46c709fSDavid du Colombier 	if (mp->an == nil)
1194f46c709fSDavid du Colombier 		stats.negans++;
1195f46c709fSDavid du Colombier 
119659cc4ca5SDavid du Colombier 	/* ignore any error replies */
11973cbadd90SDavid du Colombier 	if((mp->flags & Rmask) == Rserver){
11980319257bSDavid du Colombier 		stats.negserver++;
1199a41547ffSDavid du Colombier 		freeanswers(mp);
12003cbadd90SDavid du Colombier 		if(p != qp->curdest)
12017dd7cddfSDavid du Colombier 			p->code = Rserver;
1202c3617180SDavid du Colombier 		return Answerr;
12033e12c5d1SDavid du Colombier 	}
12043e12c5d1SDavid du Colombier 
120559cc4ca5SDavid du Colombier 	/* ignore any bad delegations */
12063cbadd90SDavid du Colombier 	if(mp->ns && baddelegation(mp->ns, qp->nsrp, srcip)){
12070319257bSDavid du Colombier 		stats.negbaddeleg++;
12083cbadd90SDavid du Colombier 		if(mp->an == nil){
12090319257bSDavid du Colombier 			stats.negbdnoans++;
1210a41547ffSDavid du Colombier 			freeanswers(mp);
12113cbadd90SDavid du Colombier 			if(p != qp->curdest)
121259cc4ca5SDavid du Colombier 				p->code = Rserver;
1213c3617180SDavid du Colombier 			dnslog(" and no answers");
1214c3617180SDavid du Colombier 			return Answerr;
121559cc4ca5SDavid du Colombier 		}
1216c3617180SDavid du Colombier 		dnslog(" but has answers; ignoring ns");
1217c3617180SDavid du Colombier 		lock(&dnlock);
1218e6d9d902SDavid du Colombier 		rrfreelistptr(&mp->ns);
1219c3617180SDavid du Colombier 		unlock(&dnlock);
1220c3617180SDavid du Colombier 		mp->nscount = 0;
122159cc4ca5SDavid du Colombier 	}
122259cc4ca5SDavid du Colombier 
12237dd7cddfSDavid du Colombier 	/* remove any soa's from the authority section */
1224530fef66SDavid du Colombier 	lock(&dnlock);
12253cbadd90SDavid du Colombier 	soarr = rrremtype(&mp->ns, Tsoa);
12267dd7cddfSDavid du Colombier 
12273e12c5d1SDavid du Colombier 	/* incorporate answers */
12284e153993SDavid du Colombier 	unique(mp->an);
12294e153993SDavid du Colombier 	unique(mp->ns);
12304e153993SDavid du Colombier 	unique(mp->ar);
1231530fef66SDavid du Colombier 	unlock(&dnlock);
1232e6d9d902SDavid du Colombier 
12333cbadd90SDavid du Colombier 	if(mp->an)
12343cbadd90SDavid du Colombier 		rrattach(mp->an, (mp->flags & Fauth) != 0);
12353cbadd90SDavid du Colombier 	if(mp->ar)
12366dc4800dSDavid du Colombier 		rrattach(mp->ar, Notauthoritative);
1237a41547ffSDavid du Colombier 	if(mp->ns && !cfg.justforw){
12383cbadd90SDavid du Colombier 		ndp = mp->ns->owner;
12396dc4800dSDavid du Colombier 		rrattach(mp->ns, Notauthoritative);
12400319257bSDavid du Colombier 	} else {
12414f8f669cSDavid du Colombier 		ndp = nil;
1242c3617180SDavid du Colombier 		lock(&dnlock);
1243e6d9d902SDavid du Colombier 		rrfreelistptr(&mp->ns);
1244c3617180SDavid du Colombier 		unlock(&dnlock);
1245c3617180SDavid du Colombier 		mp->nscount = 0;
12460319257bSDavid du Colombier 	}
12477dd7cddfSDavid du Colombier 
12487dd7cddfSDavid du Colombier 	/* free the question */
1249c3617180SDavid du Colombier 	if(mp->qd) {
1250c3617180SDavid du Colombier 		lock(&dnlock);
1251e6d9d902SDavid du Colombier 		rrfreelistptr(&mp->qd);
1252c3617180SDavid du Colombier 		unlock(&dnlock);
1253c3617180SDavid du Colombier 		mp->qdcount = 0;
1254c3617180SDavid du Colombier 	}
12553e12c5d1SDavid du Colombier 
12563e12c5d1SDavid du Colombier 	/*
12573e12c5d1SDavid du Colombier 	 *  Any reply from an authoritative server,
1258a41547ffSDavid du Colombier 	 *  or a positive reply terminates the search.
1259a41547ffSDavid du Colombier 	 *  A negative response now also terminates the search.
12603e12c5d1SDavid du Colombier 	 */
12613cbadd90SDavid du Colombier 	if(mp->an != nil || (mp->flags & Fauth)){
12620319257bSDavid du Colombier 		if(isnegrname(mp))
12633cbadd90SDavid du Colombier 			qp->dp->respcode = Rname;
12647dd7cddfSDavid du Colombier 		else
1265d9924332SDavid du Colombier 			qp->dp->respcode = Rok;
12667dd7cddfSDavid du Colombier 
12677dd7cddfSDavid du Colombier 		/*
1268a41547ffSDavid du Colombier 		 *  cache any negative responses, free soarr.
1269a41547ffSDavid du Colombier 		 *  negative responses need not be authoritative:
1270a41547ffSDavid du Colombier 		 *  they can legitimately come from a cache.
12717dd7cddfSDavid du Colombier 		 */
1272a41547ffSDavid du Colombier 		if( /* (mp->flags & Fauth) && */ mp->an == nil)
12733cbadd90SDavid du Colombier 			cacheneg(qp->dp, qp->type, (mp->flags & Rmask), soarr);
1274c3617180SDavid du Colombier 		else {
1275c3617180SDavid du Colombier 			lock(&dnlock);
12767dd7cddfSDavid du Colombier 			rrfreelist(soarr);
1277c3617180SDavid du Colombier 			unlock(&dnlock);
1278c3617180SDavid du Colombier 		}
12793e12c5d1SDavid du Colombier 		return 1;
12800319257bSDavid du Colombier 	} else if (isnegrname(mp)) {
1281a41547ffSDavid du Colombier 		qp->dp->respcode = Rname;
1282a41547ffSDavid du Colombier 		/*
1283a41547ffSDavid du Colombier 		 *  cache negative response.
1284a41547ffSDavid du Colombier 		 *  negative responses need not be authoritative:
1285a41547ffSDavid du Colombier 		 *  they can legitimately come from a cache.
1286a41547ffSDavid du Colombier 		 */
1287a41547ffSDavid du Colombier 		cacheneg(qp->dp, qp->type, (mp->flags & Rmask), soarr);
1288a41547ffSDavid du Colombier 		return 1;
12893e12c5d1SDavid du Colombier 	}
12900319257bSDavid du Colombier 	stats.negnorname++;
1291c3617180SDavid du Colombier 	lock(&dnlock);
12927dd7cddfSDavid du Colombier 	rrfreelist(soarr);
1293c3617180SDavid du Colombier 	unlock(&dnlock);
12943e12c5d1SDavid du Colombier 
12953e12c5d1SDavid du Colombier 	/*
1296a41547ffSDavid du Colombier 	 *  if we've been given better name servers, recurse.
1297a41547ffSDavid du Colombier 	 *  if we're a pure resolver, don't recurse, we have
1298a41547ffSDavid du Colombier 	 *  to forward to a fixed set of named servers.
12993e12c5d1SDavid du Colombier 	 */
1300a41547ffSDavid du Colombier 	if(!mp->ns || cfg.resolver && cfg.justforw)
1301c3617180SDavid du Colombier 		return Answnone;
13027dd7cddfSDavid du Colombier 	tp = rrlookup(ndp, Tns, NOneg);
13033cbadd90SDavid du Colombier 	if(contains(qp->nsrp, tp)){
1304c3617180SDavid du Colombier 		lock(&dnlock);
13057dd7cddfSDavid du Colombier 		rrfreelist(tp);
1306c3617180SDavid du Colombier 		unlock(&dnlock);
1307c3617180SDavid du Colombier 		return Answnone;
13083e12c5d1SDavid du Colombier 	}
13093cbadd90SDavid du Colombier 	procsetname("recursive query for %s %s", qp->dp->name,
13103cbadd90SDavid du Colombier 		rrname(qp->type, buf, sizeof buf));
13114e5f5f32SDavid du Colombier 	/*
13124e5f5f32SDavid du Colombier 	 *  we're called from udpquery, called from
13134e5f5f32SDavid du Colombier 	 *  netquery, which current holds qp->dp->querylck,
13144e5f5f32SDavid du Colombier 	 *  so release it now and acquire it upon return.
13154e5f5f32SDavid du Colombier 	 */
1316e6d9d902SDavid du Colombier //	lcktype = qtype2lck(qp->type);		/* someday try this again */
13174e5f5f32SDavid du Colombier //	qunlock(&qp->dp->querylck[lcktype]);
13183cbadd90SDavid du Colombier 
1319c73252aeSDavid du Colombier 	nqp = emalloc(sizeof *nqp);
1320c73252aeSDavid du Colombier 	queryinit(nqp, qp->dp, qp->type, qp->req);
1321c73252aeSDavid du Colombier 	nqp->nsrp = tp;
1322c73252aeSDavid du Colombier 	rv = netquery(nqp, depth+1);
13233cbadd90SDavid du Colombier 
13244e5f5f32SDavid du Colombier //	qlock(&qp->dp->querylck[lcktype]);
1325adb31a62SDavid du Colombier 	rrfreelist(nqp->nsrp);
1326c73252aeSDavid du Colombier 	querydestroy(nqp);
1327c73252aeSDavid du Colombier 	free(nqp);
13283cbadd90SDavid du Colombier 	return rv;
13293cbadd90SDavid du Colombier }
13303cbadd90SDavid du Colombier 
13313cbadd90SDavid du Colombier /*
13323cbadd90SDavid du Colombier  * send a query via tcp to a single address (from ibuf's udp header)
13333cbadd90SDavid du Colombier  * and read the answer(s) into mp->an.
13343cbadd90SDavid du Colombier  */
13353cbadd90SDavid du Colombier static int
13363cbadd90SDavid du Colombier tcpquery(Query *qp, DNSmsg *mp, int depth, uchar *ibuf, uchar *obuf, int len,
1337b8b25780SDavid du Colombier 	ulong waitms, int inns, ushort req)
13383cbadd90SDavid du Colombier {
13394e7b9544SDavid du Colombier 	int rv = 0;
1340b8b25780SDavid du Colombier 	uvlong endms;
13413cbadd90SDavid du Colombier 
1342b8b25780SDavid du Colombier 	endms = timems() + waitms;
1343b8b25780SDavid du Colombier 	if(endms > qp->req->aborttime)
1344b8b25780SDavid du Colombier 		endms = qp->req->aborttime;
13453cbadd90SDavid du Colombier 
1346a41547ffSDavid du Colombier 	if (0)
13474e7b9544SDavid du Colombier 		dnslog("%s: udp reply truncated; retrying query via tcp to %I",
13484e7b9544SDavid du Colombier 			qp->dp->name, qp->tcpip);
13494e7b9544SDavid du Colombier 
13503cbadd90SDavid du Colombier 	qlock(&qp->tcplock);
13513cbadd90SDavid du Colombier 	memmove(obuf, ibuf, IPaddrlen);		/* send back to respondent */
13523cbadd90SDavid du Colombier 	/* sets qp->tcpip from obuf's udp header */
13534e7b9544SDavid du Colombier 	if (xmitquery(qp, Tcp, depth, obuf, inns, len) < 0 ||
1354b8b25780SDavid du Colombier 	    readreply(qp, Tcp, req, ibuf, mp, endms) < 0)
13554e7b9544SDavid du Colombier 		rv = -1;
13563cbadd90SDavid du Colombier 	if (qp->tcpfd > 0) {
13573cbadd90SDavid du Colombier 		hangup(qp->tcpctlfd);
13583cbadd90SDavid du Colombier 		close(qp->tcpctlfd);
13593cbadd90SDavid du Colombier 		close(qp->tcpfd);
13603cbadd90SDavid du Colombier 	}
13613cbadd90SDavid du Colombier 	qp->tcpfd = qp->tcpctlfd = -1;
13623cbadd90SDavid du Colombier 	qunlock(&qp->tcplock);
13634e7b9544SDavid du Colombier 	return rv;
13643cbadd90SDavid du Colombier }
13653cbadd90SDavid du Colombier 
13663cbadd90SDavid du Colombier /*
136759f7772cSDavid du Colombier  *  query name servers.  fill in obuf with on-the-wire representation of a
136859f7772cSDavid du Colombier  *  DNSmsg derived from qp.  if the name server returns a pointer to another
13693cbadd90SDavid du Colombier  *  name server, recurse.
13703cbadd90SDavid du Colombier  */
13713cbadd90SDavid du Colombier static int
1372b8b25780SDavid du Colombier queryns(Query *qp, int depth, uchar *ibuf, uchar *obuf, ulong waitms, int inns)
13733cbadd90SDavid du Colombier {
13743cbadd90SDavid du Colombier 	int ndest, len, replywaits, rv;
13753cbadd90SDavid du Colombier 	ushort req;
1376b8b25780SDavid du Colombier 	uvlong endms;
13773cbadd90SDavid du Colombier 	char buf[12];
13783cbadd90SDavid du Colombier 	uchar srcip[IPaddrlen];
1379a41547ffSDavid du Colombier 	Dest *p, *np, *dest;
13803cbadd90SDavid du Colombier 
13813cbadd90SDavid du Colombier 	/* pack request into a udp message */
13823cbadd90SDavid du Colombier 	req = rand();
13833cbadd90SDavid du Colombier 	len = mkreq(qp->dp, qp->type, obuf, Frecurse|Oquery, req);
13843cbadd90SDavid du Colombier 
13853cbadd90SDavid du Colombier 	/* no server addresses yet */
13863cbadd90SDavid du Colombier 	queryck(qp);
1387a41547ffSDavid du Colombier 	dest = emalloc(Maxdest * sizeof *dest);	/* dest can't be on stack */
1388a41547ffSDavid du Colombier 	for (p = dest; p < dest + Maxdest; p++)
13893cbadd90SDavid du Colombier 		destinit(p);
13901eb0187fSDavid du Colombier 	/* this dest array is local to this call of queryns() */
13911eb0187fSDavid du Colombier 	free(qp->dest);
13923cbadd90SDavid du Colombier 	qp->curdest = qp->dest = dest;
13933cbadd90SDavid du Colombier 
13943cbadd90SDavid du Colombier 	/*
13953cbadd90SDavid du Colombier 	 *  transmit udp requests and wait for answers.
13963cbadd90SDavid du Colombier 	 *  at most Maxtrans attempts to each address.
13973cbadd90SDavid du Colombier 	 *  each cycle send one more message than the previous.
13983cbadd90SDavid du Colombier 	 *  retry a query via tcp if its response is truncated.
13993cbadd90SDavid du Colombier 	 */
14003cbadd90SDavid du Colombier 	for(ndest = 1; ndest < Maxdest; ndest++){
14013cbadd90SDavid du Colombier 		qp->ndest = ndest;
14023cbadd90SDavid du Colombier 		qp->tcpset = 0;
14033cbadd90SDavid du Colombier 		if (xmitquery(qp, Udp, depth, obuf, inns, len) < 0)
14043cbadd90SDavid du Colombier 			break;
14053cbadd90SDavid du Colombier 
1406b8b25780SDavid du Colombier 		endms = timems() + waitms;
1407b8b25780SDavid du Colombier 		if(endms > qp->req->aborttime)
1408b8b25780SDavid du Colombier 			endms = qp->req->aborttime;
14093cbadd90SDavid du Colombier 
14103cbadd90SDavid du Colombier 		for(replywaits = 0; replywaits < ndest; replywaits++){
141181730632SDavid du Colombier 			DNSmsg m;
141281730632SDavid du Colombier 
1413f46c709fSDavid du Colombier 			procsetname("reading %sside reply from %I: %s %s from %s",
1414a41547ffSDavid du Colombier 				(inns? "in": "out"), obuf, qp->dp->name,
1415f46c709fSDavid du Colombier 				rrname(qp->type, buf, sizeof buf), qp->req->from);
14163cbadd90SDavid du Colombier 
141781730632SDavid du Colombier 			/* read udp answer into m */
1418b8b25780SDavid du Colombier 			if (readreply(qp, Udp, req, ibuf, &m, endms) >= 0)
14193cbadd90SDavid du Colombier 				memmove(srcip, ibuf, IPaddrlen);
142081730632SDavid du Colombier 			else if (!(m.flags & Ftrunc)) {
142181730632SDavid du Colombier 				freeanswers(&m);
14223cbadd90SDavid du Colombier 				break;		/* timed out on this dest */
142381730632SDavid du Colombier 			} else {
14243cbadd90SDavid du Colombier 				/* whoops, it was truncated! ask again via tcp */
14256aaebd7dSDavid du Colombier 				freeanswers(&m);
14263cbadd90SDavid du Colombier 				rv = tcpquery(qp, &m, depth, ibuf, obuf, len,
1427b8b25780SDavid du Colombier 					waitms, inns, req);  /* answer in m */
142881730632SDavid du Colombier 				if (rv < 0) {
142981730632SDavid du Colombier 					freeanswers(&m);
14303cbadd90SDavid du Colombier 					break;		/* failed via tcp too */
143181730632SDavid du Colombier 				}
14323cbadd90SDavid du Colombier 				memmove(srcip, qp->tcpip, IPaddrlen);
14333cbadd90SDavid du Colombier 			}
14343cbadd90SDavid du Colombier 
14353cbadd90SDavid du Colombier 			/* find responder */
14364e5f5f32SDavid du Colombier 			// dnslog("queryns got reply from %I", srcip);
14373cbadd90SDavid du Colombier 			for(p = qp->dest; p < qp->curdest; p++)
14383cbadd90SDavid du Colombier 				if(memcmp(p->a, srcip, sizeof p->a) == 0)
14393cbadd90SDavid du Colombier 					break;
14403cbadd90SDavid du Colombier 
14413cbadd90SDavid du Colombier 			/* remove all addrs of responding server from list */
14423cbadd90SDavid du Colombier 			for(np = qp->dest; np < qp->curdest; np++)
14433cbadd90SDavid du Colombier 				if(np->s == p->s)
14443cbadd90SDavid du Colombier 					p->nx = Maxtrans;
14453cbadd90SDavid du Colombier 
144681730632SDavid du Colombier 			/* free or incorporate RRs in m */
14473cbadd90SDavid du Colombier 			rv = procansw(qp, &m, srcip, depth, p);
1448c3617180SDavid du Colombier 			if (rv > Answnone) {
14491eb0187fSDavid du Colombier 				free(qp->dest);
14501eb0187fSDavid du Colombier 				qp->dest = qp->curdest = nil; /* prevent accidents */
14513cbadd90SDavid du Colombier 				return rv;
14523e12c5d1SDavid du Colombier 			}
14533e12c5d1SDavid du Colombier 		}
14541eb0187fSDavid du Colombier 	}
14557dd7cddfSDavid du Colombier 
14564f8f669cSDavid du Colombier 	/* if all servers returned failure, propagate it */
14573cbadd90SDavid du Colombier 	qp->dp->respcode = Rserver;
14583cbadd90SDavid du Colombier 	for(p = dest; p < qp->curdest; p++) {
14593cbadd90SDavid du Colombier 		destck(p);
14607dd7cddfSDavid du Colombier 		if(p->code != Rserver)
1461d9924332SDavid du Colombier 			qp->dp->respcode = Rok;
14623cbadd90SDavid du Colombier 		p->magic = 0;			/* prevent accidents */
14633cbadd90SDavid du Colombier 	}
14644f8f669cSDavid du Colombier 
14653cbadd90SDavid du Colombier //	if (qp->dp->respcode)
14664e5f5f32SDavid du Colombier //		dnslog("queryns setting Rserver for %s", qp->dp->name);
14677dd7cddfSDavid du Colombier 
1468a41547ffSDavid du Colombier 	free(qp->dest);
14693cbadd90SDavid du Colombier 	qp->dest = qp->curdest = nil;		/* prevent accidents */
1470c3617180SDavid du Colombier 	return Answnone;
14713e12c5d1SDavid du Colombier }
14727dd7cddfSDavid du Colombier 
14734f8f669cSDavid du Colombier /*
14744f8f669cSDavid du Colombier  *  run a command with a supplied fd as standard input
14754f8f669cSDavid du Colombier  */
14764f8f669cSDavid du Colombier char *
14774f8f669cSDavid du Colombier system(int fd, char *cmd)
147828a8a86bSDavid du Colombier {
14794f8f669cSDavid du Colombier 	int pid, p, i;
14804f8f669cSDavid du Colombier 	static Waitmsg msg;
14816b0d5c8bSDavid du Colombier 
14824f8f669cSDavid du Colombier 	if((pid = fork()) == -1)
14834f8f669cSDavid du Colombier 		sysfatal("fork failed: %r");
14844f8f669cSDavid du Colombier 	else if(pid == 0){
14854f8f669cSDavid du Colombier 		dup(fd, 0);
14864f8f669cSDavid du Colombier 		close(fd);
14874f8f669cSDavid du Colombier 		for (i = 3; i < 200; i++)
14884f8f669cSDavid du Colombier 			close(i);		/* don't leak fds */
14894f8f669cSDavid du Colombier 		execl("/bin/rc", "rc", "-c", cmd, nil);
14904f8f669cSDavid du Colombier 		sysfatal("exec rc: %r");
14914f8f669cSDavid du Colombier 	}
14924f8f669cSDavid du Colombier 	for(p = waitpid(); p >= 0; p = waitpid())
14934f8f669cSDavid du Colombier 		if(p == pid)
14944f8f669cSDavid du Colombier 			return msg.msg;
14954f8f669cSDavid du Colombier 	return "lost child";
14964f8f669cSDavid du Colombier }
14974f8f669cSDavid du Colombier 
1498b8b25780SDavid du Colombier /* compute wait, weighted by probability of success, with bounds */
14990319257bSDavid du Colombier static ulong
15000319257bSDavid du Colombier weight(ulong ms, unsigned pcntprob)
15010319257bSDavid du Colombier {
15020319257bSDavid du Colombier 	ulong wait;
15030319257bSDavid du Colombier 
15040319257bSDavid du Colombier 	wait = (ms * pcntprob) / 100;
1505b8b25780SDavid du Colombier 	if (wait < Minwaitms)
1506b8b25780SDavid du Colombier 		wait = Minwaitms;
1507b8b25780SDavid du Colombier 	if (wait > Maxwaitms)
1508b8b25780SDavid du Colombier 		wait = Maxwaitms;
15090319257bSDavid du Colombier 	return wait;
15100319257bSDavid du Colombier }
15110319257bSDavid du Colombier 
1512a41547ffSDavid du Colombier /*
1513a41547ffSDavid du Colombier  * in principle we could use a single descriptor for a udp port
1514a41547ffSDavid du Colombier  * to send all queries and receive all the answers to them,
1515a41547ffSDavid du Colombier  * but we'd have to sort out the answers by dns-query id.
1516a41547ffSDavid du Colombier  */
15174f8f669cSDavid du Colombier static int
15183cbadd90SDavid du Colombier udpquery(Query *qp, char *mntpt, int depth, int patient, int inns)
15194f8f669cSDavid du Colombier {
15200319257bSDavid du Colombier 	int fd, rv;
15214f8f669cSDavid du Colombier 	long now;
1522b8b25780SDavid du Colombier 	ulong pcntprob;
1523b8b25780SDavid du Colombier 	uvlong wait, reqtm;
15244f8f669cSDavid du Colombier 	char *msg;
15254f8f669cSDavid du Colombier 	uchar *obuf, *ibuf;
15264f8f669cSDavid du Colombier 	static QLock mntlck;
15274f8f669cSDavid du Colombier 	static ulong lastmount;
15287dd7cddfSDavid du Colombier 
15297dd7cddfSDavid du Colombier 	/* use alloced buffers rather than ones from the stack */
15303cbadd90SDavid du Colombier 	ibuf = emalloc(64*1024);		/* max. tcp reply size */
1531f27a9a5aSDavid du Colombier 	obuf = emalloc(Maxudp+Udphdrsize);
15327dd7cddfSDavid du Colombier 
15334f8f669cSDavid du Colombier 	fd = udpport(mntpt);
15344f8f669cSDavid du Colombier 	while (fd < 0 && cfg.straddle && strcmp(mntpt, "/net.alt") == 0) {
15354f8f669cSDavid du Colombier 		/* HACK: remount /net.alt */
15364f8f669cSDavid du Colombier 		now = time(nil);
15374f8f669cSDavid du Colombier 		if (now < lastmount + Remntretry)
1538b8b25780SDavid du Colombier 			sleep(S2MS(lastmount + Remntretry - now));
15394f8f669cSDavid du Colombier 		qlock(&mntlck);
15404f8f669cSDavid du Colombier 		fd = udpport(mntpt);	/* try again under lock */
15414f8f669cSDavid du Colombier 		if (fd < 0) {
15424f8f669cSDavid du Colombier 			dnslog("[%d] remounting /net.alt", getpid());
15434f8f669cSDavid du Colombier 			unmount(nil, "/net.alt");
15444f8f669cSDavid du Colombier 
15454f8f669cSDavid du Colombier 			msg = system(open("/dev/null", ORDWR), "outside");
15464f8f669cSDavid du Colombier 
15474f8f669cSDavid du Colombier 			lastmount = time(nil);
15484f8f669cSDavid du Colombier 			if (msg && *msg) {
15494f8f669cSDavid du Colombier 				dnslog("[%d] can't remount /net.alt: %s",
15504f8f669cSDavid du Colombier 					getpid(), msg);
1551b8b25780SDavid du Colombier 				sleep(10*1000);	/* don't spin remounting */
15524f8f669cSDavid du Colombier 			} else
15534f8f669cSDavid du Colombier 				fd = udpport(mntpt);
15544f8f669cSDavid du Colombier 		}
15554f8f669cSDavid du Colombier 		qunlock(&mntlck);
15564f8f669cSDavid du Colombier 	}
1557a41547ffSDavid du Colombier 	if (fd < 0) {
15584f8f669cSDavid du Colombier 		dnslog("can't get udpport for %s query of name %s: %r",
15593cbadd90SDavid du Colombier 			mntpt, qp->dp->name);
1560a41547ffSDavid du Colombier 		sysfatal("out of udp conversations");	/* we're buggered */
1561a41547ffSDavid du Colombier 	}
1562a41547ffSDavid du Colombier 
1563a41547ffSDavid du Colombier 	/*
1564b8b25780SDavid du Colombier 	 * Our QIP servers are busted and respond to AAAA and CNAME queries
1565b8b25780SDavid du Colombier 	 * with (sometimes malformed [too short] packets and) no answers and
1566b8b25780SDavid du Colombier 	 * just NS RRs but not Rname errors.  so make time-to-wait
1567b8b25780SDavid du Colombier 	 * proportional to estimated probability of an RR of that type existing.
1568a41547ffSDavid du Colombier 	 */
1569adb31a62SDavid du Colombier 	if (qp->type >= nelem(likely))
15700319257bSDavid du Colombier 		pcntprob = 35;			/* unpopular query type */
15710319257bSDavid du Colombier 	else
15720319257bSDavid du Colombier 		pcntprob = likely[qp->type];
15730319257bSDavid du Colombier 	reqtm = (patient? 2 * Maxreqtm: Maxreqtm);
1574b8b25780SDavid du Colombier 	wait = weight(reqtm / 3, pcntprob);	/* time for one udp query */
1575b8b25780SDavid du Colombier 	qp->req->aborttime = timems() + 3*wait; /* for all udp queries */
1576a41547ffSDavid du Colombier 
15770319257bSDavid du Colombier 	qp->udpfd = fd;
1578b8b25780SDavid du Colombier 	rv = queryns(qp, depth, ibuf, obuf, wait, inns);
1579a41547ffSDavid du Colombier 	close(fd);
1580a41547ffSDavid du Colombier 	qp->udpfd = -1;
15814f8f669cSDavid du Colombier 
15824f8f669cSDavid du Colombier 	free(obuf);
15834f8f669cSDavid du Colombier 	free(ibuf);
15844f8f669cSDavid du Colombier 	return rv;
15854f8f669cSDavid du Colombier }
15864f8f669cSDavid du Colombier 
1587b8b25780SDavid du Colombier /*
1588b8b25780SDavid du Colombier  * look up (qp->dp->name, qp->type) rr in dns,
1589b8b25780SDavid du Colombier  * using nameservers in qp->nsrp.
1590b8b25780SDavid du Colombier  */
15914f8f669cSDavid du Colombier static int
15923cbadd90SDavid du Colombier netquery(Query *qp, int depth)
15934f8f669cSDavid du Colombier {
1594410ea80bSDavid du Colombier 	int lock, rv, triedin, inname;
1595b8b25780SDavid du Colombier 	char buf[32];
15964f8f669cSDavid du Colombier 	RR *rp;
15974e153993SDavid du Colombier 	DN *dp;
1598225077b0SDavid du Colombier 	Querylck *qlp;
1599225077b0SDavid du Colombier 	static int whined;
16004f8f669cSDavid du Colombier 
1601c3617180SDavid du Colombier 	rv = Answnone;			/* pessimism */
16024f8f669cSDavid du Colombier 	if(depth > 12)			/* in a recursive loop? */
1603c3617180SDavid du Colombier 		return Answnone;
16044f8f669cSDavid du Colombier 
16053cbadd90SDavid du Colombier 	slave(qp->req);
1606d3907fe5SDavid du Colombier 	/*
1607d3907fe5SDavid du Colombier 	 * slave might have forked.  if so, the parent process longjmped to
1608d3907fe5SDavid du Colombier 	 * req->mret; we're usually the child slave, but if there are too
1609d3907fe5SDavid du Colombier 	 * many children already, we're still the same process.
1610d3907fe5SDavid du Colombier 	 */
16114f8f669cSDavid du Colombier 
16124e5f5f32SDavid du Colombier 	/*
16134e5f5f32SDavid du Colombier 	 * don't lock before call to slave so only children can block.
16144e5f5f32SDavid du Colombier 	 * just lock at top-level invocation.
16154e5f5f32SDavid du Colombier 	 */
1616225077b0SDavid du Colombier 	lock = depth <= 1 && qp->req->isslave;
16174e153993SDavid du Colombier 	dp = qp->dp;		/* ensure that it doesn't change underfoot */
1618225077b0SDavid du Colombier 	qlp = nil;
16194e5f5f32SDavid du Colombier 	if(lock) {
1620b8b25780SDavid du Colombier 		procsetname("query lock wait: %s %s from %s", dp->name,
1621b8b25780SDavid du Colombier 			rrname(qp->type, buf, sizeof buf), qp->req->from);
1622ccf6439bSDavid du Colombier 		/*
1623ccf6439bSDavid du Colombier 		 * don't make concurrent queries for this name.
162481730632SDavid du Colombier 		 * dozens of processes blocking here probably indicates
162581730632SDavid du Colombier 		 * an error in our dns data that causes us to not
162681730632SDavid du Colombier 		 * recognise a zone (area) as one of our own, thus
162781730632SDavid du Colombier 		 * causing us to query other nameservers.
1628ccf6439bSDavid du Colombier 		 */
1629225077b0SDavid du Colombier 		qlp = &dp->querylck[qtype2lck(qp->type)];
1630225077b0SDavid du Colombier 		qlock(qlp);
1631b8b25780SDavid du Colombier 		if (qlp->Ref.ref > Maxoutstanding) {
1632225077b0SDavid du Colombier 			qunlock(qlp);
1633225077b0SDavid du Colombier 			if (!whined) {
1634225077b0SDavid du Colombier 				whined = 1;
1635225077b0SDavid du Colombier 				dnslog("too many outstanding queries for %s;"
163613fec586SDavid du Colombier 					" dropping this one; no further logging"
163713fec586SDavid du Colombier 					" of drops", dp->name);
1638225077b0SDavid du Colombier 			}
1639225077b0SDavid du Colombier 			return 0;
1640225077b0SDavid du Colombier 		}
1641410ea80bSDavid du Colombier 		++qlp->Ref.ref;
1642410ea80bSDavid du Colombier 		qunlock(qlp);
1643225077b0SDavid du Colombier 	}
16444e153993SDavid du Colombier 	procsetname("netquery: %s", dp->name);
16456b0d5c8bSDavid du Colombier 
16466b0d5c8bSDavid du Colombier 	/* prepare server RR's for incremental lookup */
16473cbadd90SDavid du Colombier 	for(rp = qp->nsrp; rp; rp = rp->next)
16486b0d5c8bSDavid du Colombier 		rp->marker = 0;
16496b0d5c8bSDavid du Colombier 
16504f8f669cSDavid du Colombier 	triedin = 0;
1651a41547ffSDavid du Colombier 
16524f8f669cSDavid du Colombier 	/*
16534f8f669cSDavid du Colombier 	 * normal resolvers and servers will just use mntpt for all addresses,
16544f8f669cSDavid du Colombier 	 * even on the outside.  straddling servers will use mntpt (/net)
16554f8f669cSDavid du Colombier 	 * for inside addresses and /net.alt for outside addresses,
16564f8f669cSDavid du Colombier 	 * thus bypassing other inside nameservers.
16574f8f669cSDavid du Colombier 	 */
16584e153993SDavid du Colombier 	inname = insideaddr(dp->name);
16594f8f669cSDavid du Colombier 	if (!cfg.straddle || inname) {
16603cbadd90SDavid du Colombier 		rv = udpquery(qp, mntpt, depth, Hurry, (cfg.inside? Inns: Outns));
16614f8f669cSDavid du Colombier 		triedin = 1;
16624f8f669cSDavid du Colombier 	}
16634f8f669cSDavid du Colombier 
16644f8f669cSDavid du Colombier 	/*
16654f8f669cSDavid du Colombier 	 * if we're still looking, are inside, and have an outside domain,
16664f8f669cSDavid du Colombier 	 * try it on our outside interface, if any.
16674f8f669cSDavid du Colombier 	 */
1668c3617180SDavid du Colombier 	if (rv == Answnone && cfg.inside && !inname) {
16694f8f669cSDavid du Colombier 		if (triedin)
16704f8f669cSDavid du Colombier 			dnslog(
16714f8f669cSDavid du Colombier 	   "[%d] netquery: internal nameservers failed for %s; trying external",
16724e153993SDavid du Colombier 				getpid(), dp->name);
16734f8f669cSDavid du Colombier 
16744f8f669cSDavid du Colombier 		/* prepare server RR's for incremental lookup */
16753cbadd90SDavid du Colombier 		for(rp = qp->nsrp; rp; rp = rp->next)
16764f8f669cSDavid du Colombier 			rp->marker = 0;
16774f8f669cSDavid du Colombier 
16783cbadd90SDavid du Colombier 		rv = udpquery(qp, "/net.alt", depth, Patient, Outns);
16794f8f669cSDavid du Colombier 	}
1680c3617180SDavid du Colombier //	if (rv == Answnone)		/* could ask /net.alt/dns directly */
16814e153993SDavid du Colombier //		askoutdns(dp, qp->type);
16824f8f669cSDavid du Colombier 
1683410ea80bSDavid du Colombier 	if(lock && qlp) {
1684410ea80bSDavid du Colombier 		qlock(qlp);
1685410ea80bSDavid du Colombier 		assert(qlp->Ref.ref > 0);
1686410ea80bSDavid du Colombier 		qunlock(qlp);
1687225077b0SDavid du Colombier 		decref(qlp);
1688410ea80bSDavid du Colombier 	}
16897dd7cddfSDavid du Colombier 	return rv;
16907dd7cddfSDavid du Colombier }
16914f8f669cSDavid du Colombier 
16924f8f669cSDavid du Colombier int
16934f8f669cSDavid du Colombier seerootns(void)
16944f8f669cSDavid du Colombier {
16953cbadd90SDavid du Colombier 	int rv;
16964f8f669cSDavid du Colombier 	char root[] = "";
16974f8f669cSDavid du Colombier 	Request req;
1698c3617180SDavid du Colombier 	RR *rr;
1699c73252aeSDavid du Colombier 	Query *qp;
17004f8f669cSDavid du Colombier 
17014f8f669cSDavid du Colombier 	memset(&req, 0, sizeof req);
17024f8f669cSDavid du Colombier 	req.isslave = 1;
1703b8b25780SDavid du Colombier 	req.aborttime = timems() + Maxreqtm;
1704f46c709fSDavid du Colombier 	req.from = "internal";
1705c3617180SDavid du Colombier 
1706c73252aeSDavid du Colombier 	qp = emalloc(sizeof *qp);
1707c73252aeSDavid du Colombier 	queryinit(qp, dnlookup(root, Cin, 1), Tns, &req);
1708c73252aeSDavid du Colombier 	qp->nsrp = dblookup(root, Cin, Tns, 0, 0);
1709c3617180SDavid du Colombier 	for (rr = qp->nsrp; rr != nil; rr = rr->next)	/* DEBUG */
1710c3617180SDavid du Colombier 		dnslog("seerootns query nsrp: %R", rr);
1711c3617180SDavid du Colombier 
1712c3617180SDavid du Colombier 	rv = netquery(qp, 0);		/* lookup ". ns" using qp->nsrp */
1713c73252aeSDavid du Colombier 
1714c73252aeSDavid du Colombier 	rrfreelist(qp->nsrp);
1715c73252aeSDavid du Colombier 	querydestroy(qp);
1716c73252aeSDavid du Colombier 	free(qp);
17173cbadd90SDavid du Colombier 	return rv;
17184f8f669cSDavid du Colombier }
1719