xref: /plan9/sys/src/cmd/ndb/dnresolve.c (revision 98813beef1db23409911a4b339e6bb9c03d0a5c0)
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 *
procgetname(void)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
rrfreelistptr(RR ** rpp)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*
dnresolve(char * name,int class,int type,Request * req,RR ** cn,int depth,int recurse,int rooted,int * status)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
queryinit(Query * qp,DN * dp,int type,Request * req)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
queryck(Query * qp)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
querydestroy(Query * qp)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
destinit(Dest * p)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
destck(Dest * p)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
notestats(vlong start,int tmout,int type)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
noteinmem(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
netqueryns(Query * qp,int depth,RR * nsrp)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*
issuequery(Query * qp,char * name,int class,int depth,int recurse)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*
dnresolve1(char * name,int class,int type,Request * req,int depth,int recurse)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*
walkup(char * name)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
udpport(char * mtpt)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
initdnsmsg(DNSmsg * mp,RR * rp,int flags,ushort reqno)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 *
newdnsmsg(RR * rp,int flags,ushort reqno)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
mkreq(DN * dp,int type,uchar * buf,int flags,ushort reqno)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);
624*98813beeSDavid du Colombier 	hnputs(uh->rport, Dnsport);
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);
631*98813beeSDavid du Colombier 	len = convDNS2M(&m, &buf[Udphdrsize], Maxdnspayload);
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
freeanswers(DNSmsg * mp)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 
64982f6abeeSDavid du Colombier /* timed read of reply.  sets srcip.  ibuf must be 64K to handle tcp answers. */
6503cbadd90SDavid du Colombier static int
readnet(Query * qp,int medium,uchar * ibuf,uvlong endms,uchar ** replyp,uchar * srcip)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 {
672*98813beeSDavid du Colombier 			len = read(qp->udpfd, ibuf, Udphdrsize+Maxpayload);
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
readreply(Query * qp,int medium,ushort req,uchar * ibuf,DNSmsg * mp,uvlong endms)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
contains(RR * rp1,RR * rp2)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
ipisbm(uchar * ip)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
serveraddrs(Query * qp,int nd,int depth)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);
9120bbfaeb2SDavid du Colombier 	if(nd >= Maxdest)		/* dest array is full? */
9130bbfaeb2SDavid du Colombier 		return Maxdest - 1;
9147dd7cddfSDavid du Colombier 	return nd;
9157dd7cddfSDavid du Colombier }
9167dd7cddfSDavid du Colombier 
9177dd7cddfSDavid du Colombier /*
9187dd7cddfSDavid du Colombier  *  cache negative responses
9197dd7cddfSDavid du Colombier  */
9207dd7cddfSDavid du Colombier static void
cacheneg(DN * dp,int type,int rcode,RR * soarr)9217dd7cddfSDavid du Colombier cacheneg(DN *dp, int type, int rcode, RR *soarr)
9227dd7cddfSDavid du Colombier {
9237dd7cddfSDavid du Colombier 	RR *rp;
9247dd7cddfSDavid du Colombier 	DN *soaowner;
9259a747e4fSDavid du Colombier 	ulong ttl;
9267dd7cddfSDavid du Colombier 
927f46c709fSDavid du Colombier 	stats.negcached++;
928f46c709fSDavid du Colombier 
9294f8f669cSDavid du Colombier 	/* no cache time specified, don't make anything up */
9307dd7cddfSDavid du Colombier 	if(soarr != nil){
931c3617180SDavid du Colombier 		lock(&dnlock);
932e6d9d902SDavid du Colombier 		if(soarr->next != nil)
933e6d9d902SDavid du Colombier 			rrfreelistptr(&soarr->next);
934c3617180SDavid du Colombier 		unlock(&dnlock);
9357dd7cddfSDavid du Colombier 		soaowner = soarr->owner;
9367dd7cddfSDavid du Colombier 	} else
9377dd7cddfSDavid du Colombier 		soaowner = nil;
9387dd7cddfSDavid du Colombier 
9399a747e4fSDavid du Colombier 	/* the attach can cause soarr to be freed so mine it now */
9409a747e4fSDavid du Colombier 	if(soarr != nil && soarr->soa != nil)
9419a747e4fSDavid du Colombier 		ttl = soarr->soa->minttl+now;
9429a747e4fSDavid du Colombier 	else
9439a747e4fSDavid du Colombier 		ttl = 5*Min;
9449a747e4fSDavid du Colombier 
9457dd7cddfSDavid du Colombier 	/* add soa and negative RR to the database */
9466dc4800dSDavid du Colombier 	rrattach(soarr, Authoritative);
9477dd7cddfSDavid du Colombier 
9487dd7cddfSDavid du Colombier 	rp = rralloc(type);
9497dd7cddfSDavid du Colombier 	rp->owner = dp;
9507dd7cddfSDavid du Colombier 	rp->negative = 1;
9517dd7cddfSDavid du Colombier 	rp->negsoaowner = soaowner;
9527dd7cddfSDavid du Colombier 	rp->negrcode = rcode;
9539a747e4fSDavid du Colombier 	rp->ttl = ttl;
9546dc4800dSDavid du Colombier 	rrattach(rp, Authoritative);
9557dd7cddfSDavid du Colombier }
9567dd7cddfSDavid du Colombier 
9574f8f669cSDavid du Colombier static int
setdestoutns(Dest * p,int n)9584f8f669cSDavid du Colombier setdestoutns(Dest *p, int n)
9594f8f669cSDavid du Colombier {
9604f8f669cSDavid du Colombier 	uchar *outns = outsidens(n);
9614f8f669cSDavid du Colombier 
9623cbadd90SDavid du Colombier 	destck(p);
9633cbadd90SDavid du Colombier 	destinit(p);
9644f8f669cSDavid du Colombier 	if (outns == nil) {
9654f8f669cSDavid du Colombier 		if (n == 0)
9664f8f669cSDavid du Colombier 			dnslog("[%d] no outside-ns in ndb", getpid());
9674f8f669cSDavid du Colombier 		return -1;
9684f8f669cSDavid du Colombier 	}
9694f8f669cSDavid du Colombier 	memmove(p->a, outns, sizeof p->a);
9704f8f669cSDavid du Colombier 	p->s = dnlookup("outside-ns-ips", Cin, 1);
9714f8f669cSDavid du Colombier 	return 0;
9724f8f669cSDavid du Colombier }
9734f8f669cSDavid du Colombier 
9747dd7cddfSDavid du Colombier /*
9753cbadd90SDavid du Colombier  * issue query via UDP or TCP as appropriate.
9763cbadd90SDavid du Colombier  * for TCP, returns with qp->tcpip set from udppkt header.
9777dd7cddfSDavid du Colombier  */
9787dd7cddfSDavid du Colombier static int
mydnsquery(Query * qp,int medium,uchar * udppkt,int len)9793cbadd90SDavid du Colombier mydnsquery(Query *qp, int medium, uchar *udppkt, int len)
9807dd7cddfSDavid du Colombier {
981a41547ffSDavid du Colombier 	int rv = -1, nfd;
9823cbadd90SDavid du Colombier 	char *domain;
9830bbfaeb2SDavid du Colombier 	char conndir[NETPATHLEN], net[NETPATHLEN];
984a41547ffSDavid du Colombier 	uchar belen[2];
9853cbadd90SDavid du Colombier 	NetConnInfo *nci;
9864f8f669cSDavid du Colombier 
9873cbadd90SDavid du Colombier 	queryck(qp);
9884e153993SDavid du Colombier 	domain = smprint("%I", udppkt);
9894e153993SDavid du Colombier 	if (myaddr(domain)) {
9904e153993SDavid du Colombier 		dnslog("mydnsquery: trying to send to myself (%s); bzzzt",
9914e153993SDavid du Colombier 			domain);
9924e153993SDavid du Colombier 		free(domain);
9934e153993SDavid du Colombier 		return rv;
9944e153993SDavid du Colombier 	}
9954e153993SDavid du Colombier 
9963cbadd90SDavid du Colombier 	switch (medium) {
9973cbadd90SDavid du Colombier 	case Udp:
9984e153993SDavid du Colombier 		free(domain);
999a41547ffSDavid du Colombier 		nfd = dup(qp->udpfd, -1);
1000a41547ffSDavid du Colombier 		if (nfd < 0) {
1001a41547ffSDavid du Colombier 			warning("mydnsquery: qp->udpfd %d: %r", qp->udpfd);
1002a41547ffSDavid du Colombier 			close(qp->udpfd);	/* ensure it's closed */
1003a41547ffSDavid du Colombier 			qp->udpfd = -1;		/* poison it */
1004a41547ffSDavid du Colombier 			return rv;
1005a41547ffSDavid du Colombier 		}
1006a41547ffSDavid du Colombier 		close(nfd);
1007a41547ffSDavid du Colombier 
10084e7b9544SDavid du Colombier 		if (qp->udpfd <= 0)
1009a41547ffSDavid du Colombier 			dnslog("mydnsquery: qp->udpfd %d closed", qp->udpfd);
10104e7b9544SDavid du Colombier 		else {
1011f27a9a5aSDavid du Colombier 			if (write(qp->udpfd, udppkt, len+Udphdrsize) !=
1012f27a9a5aSDavid du Colombier 			    len+Udphdrsize)
1013a41547ffSDavid du Colombier 				warning("sending udp msg: %r");
1014a41547ffSDavid du Colombier 			else {
1015a41547ffSDavid du Colombier 				stats.qsent++;
10163cbadd90SDavid du Colombier 				rv = 0;
10174e7b9544SDavid du Colombier 			}
1018a41547ffSDavid du Colombier 		}
10193cbadd90SDavid du Colombier 		break;
10203cbadd90SDavid du Colombier 	case Tcp:
10213cbadd90SDavid du Colombier 		/* send via TCP & keep fd around for reply */
1022cc499a30SDavid du Colombier 		snprint(net, sizeof net, "%s/tcp",
1023cc499a30SDavid du Colombier 			(mntpt[0] != '\0'? mntpt: "/net"));
10243cbadd90SDavid du Colombier 		alarm(10*1000);
1025cc499a30SDavid du Colombier 		qp->tcpfd = rv = dial(netmkaddr(domain, net, "dns"), nil,
10263cbadd90SDavid du Colombier 			conndir, &qp->tcpctlfd);
10273cbadd90SDavid du Colombier 		alarm(0);
10283cbadd90SDavid du Colombier 		if (qp->tcpfd < 0) {
10293cbadd90SDavid du Colombier 			dnslog("can't dial tcp!%s!dns: %r", domain);
1030a41547ffSDavid du Colombier 			free(domain);
1031a41547ffSDavid du Colombier 			break;
1032a41547ffSDavid du Colombier 		}
1033a41547ffSDavid du Colombier 		free(domain);
10343cbadd90SDavid du Colombier 		nci = getnetconninfo(conndir, qp->tcpfd);
10353cbadd90SDavid du Colombier 		if (nci) {
10363cbadd90SDavid du Colombier 			parseip(qp->tcpip, nci->rsys);
10373cbadd90SDavid du Colombier 			freenetconninfo(nci);
10383cbadd90SDavid du Colombier 		} else
10393cbadd90SDavid du Colombier 			dnslog("mydnsquery: getnetconninfo failed");
10403cbadd90SDavid du Colombier 		qp->tcpset = 1;
10413e12c5d1SDavid du Colombier 
10423cbadd90SDavid du Colombier 		belen[0] = len >> 8;
10433cbadd90SDavid du Colombier 		belen[1] = len;
10443cbadd90SDavid du Colombier 		if (write(qp->tcpfd, belen, 2) != 2 ||
1045f27a9a5aSDavid du Colombier 		    write(qp->tcpfd, udppkt + Udphdrsize, len) != len)
1046a41547ffSDavid du Colombier 			warning("sending tcp msg: %r");
10473cbadd90SDavid du Colombier 		break;
10483cbadd90SDavid du Colombier 	default:
10493cbadd90SDavid du Colombier 		sysfatal("mydnsquery: bad medium");
10503cbadd90SDavid du Colombier 	}
10513cbadd90SDavid du Colombier 	return rv;
10523cbadd90SDavid du Colombier }
10537dd7cddfSDavid du Colombier 
10543e12c5d1SDavid du Colombier /*
10553cbadd90SDavid du Colombier  * send query to all UDP destinations or one TCP destination,
10563cbadd90SDavid du Colombier  * taken from obuf (udp packet) header
10573e12c5d1SDavid du Colombier  */
10583cbadd90SDavid du Colombier static int
xmitquery(Query * qp,int medium,int depth,uchar * obuf,int inns,int len)10593cbadd90SDavid du Colombier xmitquery(Query *qp, int medium, int depth, uchar *obuf, int inns, int len)
10603cbadd90SDavid du Colombier {
10613cbadd90SDavid du Colombier 	int j, n;
10623cbadd90SDavid du Colombier 	char buf[32];
10633cbadd90SDavid du Colombier 	Dest *p;
10643e12c5d1SDavid du Colombier 
10653cbadd90SDavid du Colombier 	queryck(qp);
1066b8b25780SDavid du Colombier 	if(timems() >= qp->req->aborttime)
10673cbadd90SDavid du Colombier 		return -1;
10683cbadd90SDavid du Colombier 
10693cbadd90SDavid du Colombier 	/*
10703cbadd90SDavid du Colombier 	 * get a nameserver address if we need one.
10713cbadd90SDavid du Colombier 	 * serveraddrs populates qp->dest.
10723cbadd90SDavid du Colombier 	 */
10733cbadd90SDavid du Colombier 	p = qp->dest;
10743cbadd90SDavid du Colombier 	destck(p);
1075c3617180SDavid du Colombier 	if (qp->ndest < 0 || qp->ndest > Maxdest) {
10763cbadd90SDavid du Colombier 		dnslog("qp->ndest %d out of range", qp->ndest);
1077c3617180SDavid du Colombier 		abort();
1078c3617180SDavid du Colombier 	}
1079c3617180SDavid du Colombier 	/*
1080c3617180SDavid du Colombier 	 * we're to transmit to more destinations than we currently have,
1081c3617180SDavid du Colombier 	 * so get another.
1082c3617180SDavid du Colombier 	 */
10833468a491SDavid du Colombier 	if (qp->ndest > qp->curdest - p) {
10843468a491SDavid du Colombier 		j = serveraddrs(qp, qp->curdest - p, depth);
10853468a491SDavid du Colombier 		if (j < 0 || j >= Maxdest) {
10863468a491SDavid du Colombier 			dnslog("serveraddrs() result %d out of range", j);
10873468a491SDavid du Colombier 			abort();
10883468a491SDavid du Colombier 		}
10893468a491SDavid du Colombier 		qp->curdest = &qp->dest[j];
10903468a491SDavid du Colombier 	}
10913cbadd90SDavid du Colombier 	destck(qp->curdest);
10927dd7cddfSDavid du Colombier 
10937dd7cddfSDavid du Colombier 	/* no servers, punt */
10943468a491SDavid du Colombier 	if (qp->ndest == 0)
10954f8f669cSDavid du Colombier 		if (cfg.straddle && cfg.inside) {
10963cbadd90SDavid du Colombier 			/* get ips of "outside-ns-ips" */
1097c3617180SDavid du Colombier 			qp->curdest = qp->dest;
10983cbadd90SDavid du Colombier 			for(n = 0; n < Maxdest; n++, qp->curdest++)
10993cbadd90SDavid du Colombier 				if (setdestoutns(qp->curdest, n) < 0)
11007dd7cddfSDavid du Colombier 					break;
1101c3617180SDavid du Colombier 			if(n == 0)
1102c3617180SDavid du Colombier 				dnslog("xmitquery: %s: no outside-ns nameservers",
1103c3617180SDavid du Colombier 					qp->dp->name);
1104c3617180SDavid du Colombier 		} else
11054e7b9544SDavid du Colombier 			/* it's probably just a bogus domain, don't log it */
11063cbadd90SDavid du Colombier 			return -1;
11077dd7cddfSDavid du Colombier 
11083cbadd90SDavid du Colombier 	/* send to first 'qp->ndest' destinations */
11097dd7cddfSDavid du Colombier 	j = 0;
11103cbadd90SDavid du Colombier 	if (medium == Tcp) {
11113cbadd90SDavid du Colombier 		j++;
11123cbadd90SDavid du Colombier 		queryck(qp);
11133cbadd90SDavid du Colombier 		assert(qp->dp);
11143cbadd90SDavid du Colombier 		procsetname("tcp %sside query for %s %s", (inns? "in": "out"),
11153cbadd90SDavid du Colombier 			qp->dp->name, rrname(qp->type, buf, sizeof buf));
11163cbadd90SDavid du Colombier 		mydnsquery(qp, medium, obuf, len); /* sets qp->tcpip from obuf */
11173cbadd90SDavid du Colombier 		if(debug)
11183cbadd90SDavid du Colombier 			logsend(qp->req->id, depth, qp->tcpip, "", qp->dp->name,
11193cbadd90SDavid du Colombier 				qp->type);
11203cbadd90SDavid du Colombier 	} else
11213cbadd90SDavid du Colombier 		for(; p < &qp->dest[qp->ndest] && p < qp->curdest; p++){
11227dd7cddfSDavid du Colombier 			/* skip destinations we've finished with */
11237dd7cddfSDavid du Colombier 			if(p->nx >= Maxtrans)
11247dd7cddfSDavid du Colombier 				continue;
11257dd7cddfSDavid du Colombier 
11267dd7cddfSDavid du Colombier 			j++;
11277dd7cddfSDavid du Colombier 
11287dd7cddfSDavid du Colombier 			/* exponential backoff of requests */
11293cbadd90SDavid du Colombier 			if((1<<p->nx) > qp->ndest)
11307dd7cddfSDavid du Colombier 				continue;
11317dd7cddfSDavid du Colombier 
113259f7772cSDavid du Colombier 			if(memcmp(p->a, IPnoaddr, sizeof IPnoaddr) == 0)
113359f7772cSDavid du Colombier 				continue;		/* mistake */
113459f7772cSDavid du Colombier 
11353cbadd90SDavid du Colombier 			procsetname("udp %sside query to %I/%s %s %s",
11363cbadd90SDavid du Colombier 				(inns? "in": "out"), p->a, p->s->name,
11373cbadd90SDavid du Colombier 				qp->dp->name, rrname(qp->type, buf, sizeof buf));
1138219b2ee8SDavid du Colombier 			if(debug)
11393cbadd90SDavid du Colombier 				logsend(qp->req->id, depth, p->a, p->s->name,
11403cbadd90SDavid du Colombier 					qp->dp->name, qp->type);
11414f8f669cSDavid du Colombier 
11423cbadd90SDavid du Colombier 			/* fill in UDP destination addr & send it */
11433cbadd90SDavid du Colombier 			memmove(obuf, p->a, sizeof p->a);
11443cbadd90SDavid du Colombier 			mydnsquery(qp, medium, obuf, len);
11457dd7cddfSDavid du Colombier 			p->nx++;
11463e12c5d1SDavid du Colombier 		}
11473cbadd90SDavid du Colombier 	if(j == 0) {
11483cbadd90SDavid du Colombier 		return -1;
11493cbadd90SDavid du Colombier 	}
11503cbadd90SDavid du Colombier 	return 0;
11513cbadd90SDavid du Colombier }
11523e12c5d1SDavid du Colombier 
1153f46c709fSDavid du Colombier static int lckindex[Maxlcks] = {
1154f46c709fSDavid du Colombier 	0,			/* all others map here */
1155f46c709fSDavid du Colombier 	Ta,
1156f46c709fSDavid du Colombier 	Tns,
1157f46c709fSDavid du Colombier 	Tcname,
1158f46c709fSDavid du Colombier 	Tsoa,
1159f46c709fSDavid du Colombier 	Tptr,
1160f46c709fSDavid du Colombier 	Tmx,
1161f46c709fSDavid du Colombier 	Ttxt,
1162f46c709fSDavid du Colombier 	Taaaa,
1163f46c709fSDavid du Colombier };
1164f46c709fSDavid du Colombier 
1165f46c709fSDavid du Colombier static int
qtype2lck(int qtype)1166f46c709fSDavid du Colombier qtype2lck(int qtype)		/* map query type to querylck index */
1167f46c709fSDavid du Colombier {
1168f46c709fSDavid du Colombier 	int i;
1169f46c709fSDavid du Colombier 
1170f46c709fSDavid du Colombier 	for (i = 1; i < nelem(lckindex); i++)
1171f46c709fSDavid du Colombier 		if (lckindex[i] == qtype)
1172f46c709fSDavid du Colombier 			return i;
1173f46c709fSDavid du Colombier 	return 0;
1174f46c709fSDavid du Colombier }
1175f46c709fSDavid du Colombier 
11760319257bSDavid du Colombier /* is mp a cachable negative response (with Rname set)? */
11770319257bSDavid du Colombier static int
isnegrname(DNSmsg * mp)11780319257bSDavid du Colombier isnegrname(DNSmsg *mp)
11790319257bSDavid du Colombier {
11800319257bSDavid du Colombier 	/* TODO: could add || cfg.justforw to RHS of && */
11810319257bSDavid du Colombier 	return mp->an == nil && (mp->flags & Rmask) == Rname;
11820319257bSDavid du Colombier }
11830319257bSDavid du Colombier 
1184c3617180SDavid du Colombier /* returns Answerr (-1) on errors, else number of answers, which can be zero. */
11853cbadd90SDavid du Colombier static int
procansw(Query * qp,DNSmsg * mp,uchar * srcip,int depth,Dest * p)11863cbadd90SDavid du Colombier procansw(Query *qp, DNSmsg *mp, uchar *srcip, int depth, Dest *p)
11873cbadd90SDavid du Colombier {
11883cbadd90SDavid du Colombier 	int rv;
11894e5f5f32SDavid du Colombier //	int lcktype;
11903cbadd90SDavid du Colombier 	char buf[32];
11913cbadd90SDavid du Colombier 	DN *ndp;
1192c73252aeSDavid du Colombier 	Query *nqp;
11933cbadd90SDavid du Colombier 	RR *tp, *soarr;
11947dd7cddfSDavid du Colombier 
1195f46c709fSDavid du Colombier 	if (mp->an == nil)
1196f46c709fSDavid du Colombier 		stats.negans++;
1197f46c709fSDavid du Colombier 
119859cc4ca5SDavid du Colombier 	/* ignore any error replies */
11993cbadd90SDavid du Colombier 	if((mp->flags & Rmask) == Rserver){
12000319257bSDavid du Colombier 		stats.negserver++;
1201a41547ffSDavid du Colombier 		freeanswers(mp);
12023cbadd90SDavid du Colombier 		if(p != qp->curdest)
12037dd7cddfSDavid du Colombier 			p->code = Rserver;
1204c3617180SDavid du Colombier 		return Answerr;
12053e12c5d1SDavid du Colombier 	}
12063e12c5d1SDavid du Colombier 
120759cc4ca5SDavid du Colombier 	/* ignore any bad delegations */
12083cbadd90SDavid du Colombier 	if(mp->ns && baddelegation(mp->ns, qp->nsrp, srcip)){
12090319257bSDavid du Colombier 		stats.negbaddeleg++;
12103cbadd90SDavid du Colombier 		if(mp->an == nil){
12110319257bSDavid du Colombier 			stats.negbdnoans++;
1212a41547ffSDavid du Colombier 			freeanswers(mp);
12133cbadd90SDavid du Colombier 			if(p != qp->curdest)
121459cc4ca5SDavid du Colombier 				p->code = Rserver;
1215c3617180SDavid du Colombier 			dnslog(" and no answers");
1216c3617180SDavid du Colombier 			return Answerr;
121759cc4ca5SDavid du Colombier 		}
1218c3617180SDavid du Colombier 		dnslog(" but has answers; ignoring ns");
1219c3617180SDavid du Colombier 		lock(&dnlock);
1220e6d9d902SDavid du Colombier 		rrfreelistptr(&mp->ns);
1221c3617180SDavid du Colombier 		unlock(&dnlock);
1222c3617180SDavid du Colombier 		mp->nscount = 0;
122359cc4ca5SDavid du Colombier 	}
122459cc4ca5SDavid du Colombier 
12257dd7cddfSDavid du Colombier 	/* remove any soa's from the authority section */
1226530fef66SDavid du Colombier 	lock(&dnlock);
12273cbadd90SDavid du Colombier 	soarr = rrremtype(&mp->ns, Tsoa);
12287dd7cddfSDavid du Colombier 
12293e12c5d1SDavid du Colombier 	/* incorporate answers */
12304e153993SDavid du Colombier 	unique(mp->an);
12314e153993SDavid du Colombier 	unique(mp->ns);
12324e153993SDavid du Colombier 	unique(mp->ar);
1233530fef66SDavid du Colombier 	unlock(&dnlock);
1234e6d9d902SDavid du Colombier 
12353cbadd90SDavid du Colombier 	if(mp->an)
12363cbadd90SDavid du Colombier 		rrattach(mp->an, (mp->flags & Fauth) != 0);
12373cbadd90SDavid du Colombier 	if(mp->ar)
12386dc4800dSDavid du Colombier 		rrattach(mp->ar, Notauthoritative);
1239a41547ffSDavid du Colombier 	if(mp->ns && !cfg.justforw){
12403cbadd90SDavid du Colombier 		ndp = mp->ns->owner;
12416dc4800dSDavid du Colombier 		rrattach(mp->ns, Notauthoritative);
12420319257bSDavid du Colombier 	} else {
12434f8f669cSDavid du Colombier 		ndp = nil;
1244c3617180SDavid du Colombier 		lock(&dnlock);
1245e6d9d902SDavid du Colombier 		rrfreelistptr(&mp->ns);
1246c3617180SDavid du Colombier 		unlock(&dnlock);
1247c3617180SDavid du Colombier 		mp->nscount = 0;
12480319257bSDavid du Colombier 	}
12497dd7cddfSDavid du Colombier 
12507dd7cddfSDavid du Colombier 	/* free the question */
1251c3617180SDavid du Colombier 	if(mp->qd) {
1252c3617180SDavid du Colombier 		lock(&dnlock);
1253e6d9d902SDavid du Colombier 		rrfreelistptr(&mp->qd);
1254c3617180SDavid du Colombier 		unlock(&dnlock);
1255c3617180SDavid du Colombier 		mp->qdcount = 0;
1256c3617180SDavid du Colombier 	}
12573e12c5d1SDavid du Colombier 
12583e12c5d1SDavid du Colombier 	/*
12593e12c5d1SDavid du Colombier 	 *  Any reply from an authoritative server,
1260a41547ffSDavid du Colombier 	 *  or a positive reply terminates the search.
1261a41547ffSDavid du Colombier 	 *  A negative response now also terminates the search.
12623e12c5d1SDavid du Colombier 	 */
12633cbadd90SDavid du Colombier 	if(mp->an != nil || (mp->flags & Fauth)){
12640319257bSDavid du Colombier 		if(isnegrname(mp))
12653cbadd90SDavid du Colombier 			qp->dp->respcode = Rname;
12667dd7cddfSDavid du Colombier 		else
1267d9924332SDavid du Colombier 			qp->dp->respcode = Rok;
12687dd7cddfSDavid du Colombier 
12697dd7cddfSDavid du Colombier 		/*
1270a41547ffSDavid du Colombier 		 *  cache any negative responses, free soarr.
1271a41547ffSDavid du Colombier 		 *  negative responses need not be authoritative:
1272a41547ffSDavid du Colombier 		 *  they can legitimately come from a cache.
12737dd7cddfSDavid du Colombier 		 */
1274a41547ffSDavid du Colombier 		if( /* (mp->flags & Fauth) && */ mp->an == nil)
12753cbadd90SDavid du Colombier 			cacheneg(qp->dp, qp->type, (mp->flags & Rmask), soarr);
1276c3617180SDavid du Colombier 		else {
1277c3617180SDavid du Colombier 			lock(&dnlock);
12787dd7cddfSDavid du Colombier 			rrfreelist(soarr);
1279c3617180SDavid du Colombier 			unlock(&dnlock);
1280c3617180SDavid du Colombier 		}
12813e12c5d1SDavid du Colombier 		return 1;
12820319257bSDavid du Colombier 	} else if (isnegrname(mp)) {
1283a41547ffSDavid du Colombier 		qp->dp->respcode = Rname;
1284a41547ffSDavid du Colombier 		/*
1285a41547ffSDavid du Colombier 		 *  cache negative response.
1286a41547ffSDavid du Colombier 		 *  negative responses need not be authoritative:
1287a41547ffSDavid du Colombier 		 *  they can legitimately come from a cache.
1288a41547ffSDavid du Colombier 		 */
1289a41547ffSDavid du Colombier 		cacheneg(qp->dp, qp->type, (mp->flags & Rmask), soarr);
1290a41547ffSDavid du Colombier 		return 1;
12913e12c5d1SDavid du Colombier 	}
12920319257bSDavid du Colombier 	stats.negnorname++;
1293c3617180SDavid du Colombier 	lock(&dnlock);
12947dd7cddfSDavid du Colombier 	rrfreelist(soarr);
1295c3617180SDavid du Colombier 	unlock(&dnlock);
12963e12c5d1SDavid du Colombier 
12973e12c5d1SDavid du Colombier 	/*
1298a41547ffSDavid du Colombier 	 *  if we've been given better name servers, recurse.
1299a41547ffSDavid du Colombier 	 *  if we're a pure resolver, don't recurse, we have
1300a41547ffSDavid du Colombier 	 *  to forward to a fixed set of named servers.
13013e12c5d1SDavid du Colombier 	 */
1302a41547ffSDavid du Colombier 	if(!mp->ns || cfg.resolver && cfg.justforw)
1303c3617180SDavid du Colombier 		return Answnone;
13047dd7cddfSDavid du Colombier 	tp = rrlookup(ndp, Tns, NOneg);
13053cbadd90SDavid du Colombier 	if(contains(qp->nsrp, tp)){
1306c3617180SDavid du Colombier 		lock(&dnlock);
13077dd7cddfSDavid du Colombier 		rrfreelist(tp);
1308c3617180SDavid du Colombier 		unlock(&dnlock);
1309c3617180SDavid du Colombier 		return Answnone;
13103e12c5d1SDavid du Colombier 	}
13113cbadd90SDavid du Colombier 	procsetname("recursive query for %s %s", qp->dp->name,
13123cbadd90SDavid du Colombier 		rrname(qp->type, buf, sizeof buf));
13134e5f5f32SDavid du Colombier 	/*
13144e5f5f32SDavid du Colombier 	 *  we're called from udpquery, called from
13154e5f5f32SDavid du Colombier 	 *  netquery, which current holds qp->dp->querylck,
13164e5f5f32SDavid du Colombier 	 *  so release it now and acquire it upon return.
13174e5f5f32SDavid du Colombier 	 */
1318e6d9d902SDavid du Colombier //	lcktype = qtype2lck(qp->type);		/* someday try this again */
13194e5f5f32SDavid du Colombier //	qunlock(&qp->dp->querylck[lcktype]);
13203cbadd90SDavid du Colombier 
1321c73252aeSDavid du Colombier 	nqp = emalloc(sizeof *nqp);
1322c73252aeSDavid du Colombier 	queryinit(nqp, qp->dp, qp->type, qp->req);
1323c73252aeSDavid du Colombier 	nqp->nsrp = tp;
1324c73252aeSDavid du Colombier 	rv = netquery(nqp, depth+1);
13253cbadd90SDavid du Colombier 
13264e5f5f32SDavid du Colombier //	qlock(&qp->dp->querylck[lcktype]);
1327adb31a62SDavid du Colombier 	rrfreelist(nqp->nsrp);
1328c73252aeSDavid du Colombier 	querydestroy(nqp);
1329c73252aeSDavid du Colombier 	free(nqp);
13303cbadd90SDavid du Colombier 	return rv;
13313cbadd90SDavid du Colombier }
13323cbadd90SDavid du Colombier 
13333cbadd90SDavid du Colombier /*
13343cbadd90SDavid du Colombier  * send a query via tcp to a single address (from ibuf's udp header)
13353cbadd90SDavid du Colombier  * and read the answer(s) into mp->an.
13363cbadd90SDavid du Colombier  */
13373cbadd90SDavid du Colombier static int
tcpquery(Query * qp,DNSmsg * mp,int depth,uchar * ibuf,uchar * obuf,int len,ulong waitms,int inns,ushort req)13383cbadd90SDavid du Colombier tcpquery(Query *qp, DNSmsg *mp, int depth, uchar *ibuf, uchar *obuf, int len,
1339b8b25780SDavid du Colombier 	ulong waitms, int inns, ushort req)
13403cbadd90SDavid du Colombier {
13414e7b9544SDavid du Colombier 	int rv = 0;
1342b8b25780SDavid du Colombier 	uvlong endms;
13433cbadd90SDavid du Colombier 
1344b8b25780SDavid du Colombier 	endms = timems() + waitms;
1345b8b25780SDavid du Colombier 	if(endms > qp->req->aborttime)
1346b8b25780SDavid du Colombier 		endms = qp->req->aborttime;
13473cbadd90SDavid du Colombier 
1348a41547ffSDavid du Colombier 	if (0)
13494e7b9544SDavid du Colombier 		dnslog("%s: udp reply truncated; retrying query via tcp to %I",
13504e7b9544SDavid du Colombier 			qp->dp->name, qp->tcpip);
13514e7b9544SDavid du Colombier 
13523cbadd90SDavid du Colombier 	qlock(&qp->tcplock);
13533cbadd90SDavid du Colombier 	memmove(obuf, ibuf, IPaddrlen);		/* send back to respondent */
13543cbadd90SDavid du Colombier 	/* sets qp->tcpip from obuf's udp header */
13554e7b9544SDavid du Colombier 	if (xmitquery(qp, Tcp, depth, obuf, inns, len) < 0 ||
1356b8b25780SDavid du Colombier 	    readreply(qp, Tcp, req, ibuf, mp, endms) < 0)
13574e7b9544SDavid du Colombier 		rv = -1;
13583cbadd90SDavid du Colombier 	if (qp->tcpfd > 0) {
13593cbadd90SDavid du Colombier 		hangup(qp->tcpctlfd);
13603cbadd90SDavid du Colombier 		close(qp->tcpctlfd);
13613cbadd90SDavid du Colombier 		close(qp->tcpfd);
13623cbadd90SDavid du Colombier 	}
13633cbadd90SDavid du Colombier 	qp->tcpfd = qp->tcpctlfd = -1;
13643cbadd90SDavid du Colombier 	qunlock(&qp->tcplock);
13654e7b9544SDavid du Colombier 	return rv;
13663cbadd90SDavid du Colombier }
13673cbadd90SDavid du Colombier 
13683cbadd90SDavid du Colombier /*
136959f7772cSDavid du Colombier  *  query name servers.  fill in obuf with on-the-wire representation of a
137059f7772cSDavid du Colombier  *  DNSmsg derived from qp.  if the name server returns a pointer to another
13713cbadd90SDavid du Colombier  *  name server, recurse.
13723cbadd90SDavid du Colombier  */
13733cbadd90SDavid du Colombier static int
queryns(Query * qp,int depth,uchar * ibuf,uchar * obuf,ulong waitms,int inns)1374b8b25780SDavid du Colombier queryns(Query *qp, int depth, uchar *ibuf, uchar *obuf, ulong waitms, int inns)
13753cbadd90SDavid du Colombier {
13763cbadd90SDavid du Colombier 	int ndest, len, replywaits, rv;
13773cbadd90SDavid du Colombier 	ushort req;
1378b8b25780SDavid du Colombier 	uvlong endms;
13793cbadd90SDavid du Colombier 	char buf[12];
13803cbadd90SDavid du Colombier 	uchar srcip[IPaddrlen];
1381a41547ffSDavid du Colombier 	Dest *p, *np, *dest;
13823cbadd90SDavid du Colombier 
13833cbadd90SDavid du Colombier 	/* pack request into a udp message */
13843cbadd90SDavid du Colombier 	req = rand();
13853cbadd90SDavid du Colombier 	len = mkreq(qp->dp, qp->type, obuf, Frecurse|Oquery, req);
13863cbadd90SDavid du Colombier 
13873cbadd90SDavid du Colombier 	/* no server addresses yet */
13883cbadd90SDavid du Colombier 	queryck(qp);
1389a41547ffSDavid du Colombier 	dest = emalloc(Maxdest * sizeof *dest);	/* dest can't be on stack */
1390a41547ffSDavid du Colombier 	for (p = dest; p < dest + Maxdest; p++)
13913cbadd90SDavid du Colombier 		destinit(p);
13921eb0187fSDavid du Colombier 	/* this dest array is local to this call of queryns() */
13931eb0187fSDavid du Colombier 	free(qp->dest);
13943cbadd90SDavid du Colombier 	qp->curdest = qp->dest = dest;
13953cbadd90SDavid du Colombier 
13963cbadd90SDavid du Colombier 	/*
13973cbadd90SDavid du Colombier 	 *  transmit udp requests and wait for answers.
13983cbadd90SDavid du Colombier 	 *  at most Maxtrans attempts to each address.
13993cbadd90SDavid du Colombier 	 *  each cycle send one more message than the previous.
14003cbadd90SDavid du Colombier 	 *  retry a query via tcp if its response is truncated.
14013cbadd90SDavid du Colombier 	 */
14023cbadd90SDavid du Colombier 	for(ndest = 1; ndest < Maxdest; ndest++){
14033cbadd90SDavid du Colombier 		qp->ndest = ndest;
14043cbadd90SDavid du Colombier 		qp->tcpset = 0;
14053cbadd90SDavid du Colombier 		if (xmitquery(qp, Udp, depth, obuf, inns, len) < 0)
14063cbadd90SDavid du Colombier 			break;
14073cbadd90SDavid du Colombier 
1408b8b25780SDavid du Colombier 		endms = timems() + waitms;
1409b8b25780SDavid du Colombier 		if(endms > qp->req->aborttime)
1410b8b25780SDavid du Colombier 			endms = qp->req->aborttime;
14113cbadd90SDavid du Colombier 
14123cbadd90SDavid du Colombier 		for(replywaits = 0; replywaits < ndest; replywaits++){
141381730632SDavid du Colombier 			DNSmsg m;
141481730632SDavid du Colombier 
1415f46c709fSDavid du Colombier 			procsetname("reading %sside reply from %I: %s %s from %s",
1416a41547ffSDavid du Colombier 				(inns? "in": "out"), obuf, qp->dp->name,
1417f46c709fSDavid du Colombier 				rrname(qp->type, buf, sizeof buf), qp->req->from);
14183cbadd90SDavid du Colombier 
141981730632SDavid du Colombier 			/* read udp answer into m */
1420b8b25780SDavid du Colombier 			if (readreply(qp, Udp, req, ibuf, &m, endms) >= 0)
14213cbadd90SDavid du Colombier 				memmove(srcip, ibuf, IPaddrlen);
142281730632SDavid du Colombier 			else if (!(m.flags & Ftrunc)) {
142381730632SDavid du Colombier 				freeanswers(&m);
14243cbadd90SDavid du Colombier 				break;		/* timed out on this dest */
142581730632SDavid du Colombier 			} else {
14263cbadd90SDavid du Colombier 				/* whoops, it was truncated! ask again via tcp */
14276aaebd7dSDavid du Colombier 				freeanswers(&m);
14283cbadd90SDavid du Colombier 				rv = tcpquery(qp, &m, depth, ibuf, obuf, len,
1429b8b25780SDavid du Colombier 					waitms, inns, req);  /* answer in m */
143081730632SDavid du Colombier 				if (rv < 0) {
143181730632SDavid du Colombier 					freeanswers(&m);
14323cbadd90SDavid du Colombier 					break;		/* failed via tcp too */
143381730632SDavid du Colombier 				}
14343cbadd90SDavid du Colombier 				memmove(srcip, qp->tcpip, IPaddrlen);
14353cbadd90SDavid du Colombier 			}
14363cbadd90SDavid du Colombier 
14373cbadd90SDavid du Colombier 			/* find responder */
14384e5f5f32SDavid du Colombier 			// dnslog("queryns got reply from %I", srcip);
14393cbadd90SDavid du Colombier 			for(p = qp->dest; p < qp->curdest; p++)
14403cbadd90SDavid du Colombier 				if(memcmp(p->a, srcip, sizeof p->a) == 0)
14413cbadd90SDavid du Colombier 					break;
14423cbadd90SDavid du Colombier 
14433cbadd90SDavid du Colombier 			/* remove all addrs of responding server from list */
14443cbadd90SDavid du Colombier 			for(np = qp->dest; np < qp->curdest; np++)
14453cbadd90SDavid du Colombier 				if(np->s == p->s)
14460bbfaeb2SDavid du Colombier 					np->nx = Maxtrans;
14473cbadd90SDavid du Colombier 
144881730632SDavid du Colombier 			/* free or incorporate RRs in m */
14493cbadd90SDavid du Colombier 			rv = procansw(qp, &m, srcip, depth, p);
1450c3617180SDavid du Colombier 			if (rv > Answnone) {
14511eb0187fSDavid du Colombier 				free(qp->dest);
14521eb0187fSDavid du Colombier 				qp->dest = qp->curdest = nil; /* prevent accidents */
14533cbadd90SDavid du Colombier 				return rv;
14543e12c5d1SDavid du Colombier 			}
14553e12c5d1SDavid du Colombier 		}
14561eb0187fSDavid du Colombier 	}
14577dd7cddfSDavid du Colombier 
14584f8f669cSDavid du Colombier 	/* if all servers returned failure, propagate it */
14593cbadd90SDavid du Colombier 	qp->dp->respcode = Rserver;
14603cbadd90SDavid du Colombier 	for(p = dest; p < qp->curdest; p++) {
14613cbadd90SDavid du Colombier 		destck(p);
14627dd7cddfSDavid du Colombier 		if(p->code != Rserver)
1463d9924332SDavid du Colombier 			qp->dp->respcode = Rok;
14643cbadd90SDavid du Colombier 		p->magic = 0;			/* prevent accidents */
14653cbadd90SDavid du Colombier 	}
14664f8f669cSDavid du Colombier 
14673cbadd90SDavid du Colombier //	if (qp->dp->respcode)
14684e5f5f32SDavid du Colombier //		dnslog("queryns setting Rserver for %s", qp->dp->name);
14697dd7cddfSDavid du Colombier 
1470a41547ffSDavid du Colombier 	free(qp->dest);
14713cbadd90SDavid du Colombier 	qp->dest = qp->curdest = nil;		/* prevent accidents */
1472c3617180SDavid du Colombier 	return Answnone;
14733e12c5d1SDavid du Colombier }
14747dd7cddfSDavid du Colombier 
14754f8f669cSDavid du Colombier /*
14764f8f669cSDavid du Colombier  *  run a command with a supplied fd as standard input
14774f8f669cSDavid du Colombier  */
14784f8f669cSDavid du Colombier char *
system(int fd,char * cmd)14794f8f669cSDavid du Colombier system(int fd, char *cmd)
148028a8a86bSDavid du Colombier {
14814f8f669cSDavid du Colombier 	int pid, p, i;
14824f8f669cSDavid du Colombier 	static Waitmsg msg;
14836b0d5c8bSDavid du Colombier 
14844f8f669cSDavid du Colombier 	if((pid = fork()) == -1)
14854f8f669cSDavid du Colombier 		sysfatal("fork failed: %r");
14864f8f669cSDavid du Colombier 	else if(pid == 0){
14874f8f669cSDavid du Colombier 		dup(fd, 0);
14884f8f669cSDavid du Colombier 		close(fd);
14894f8f669cSDavid du Colombier 		for (i = 3; i < 200; i++)
14904f8f669cSDavid du Colombier 			close(i);		/* don't leak fds */
14914f8f669cSDavid du Colombier 		execl("/bin/rc", "rc", "-c", cmd, nil);
14924f8f669cSDavid du Colombier 		sysfatal("exec rc: %r");
14934f8f669cSDavid du Colombier 	}
14944f8f669cSDavid du Colombier 	for(p = waitpid(); p >= 0; p = waitpid())
14954f8f669cSDavid du Colombier 		if(p == pid)
14964f8f669cSDavid du Colombier 			return msg.msg;
14974f8f669cSDavid du Colombier 	return "lost child";
14984f8f669cSDavid du Colombier }
14994f8f669cSDavid du Colombier 
1500b8b25780SDavid du Colombier /* compute wait, weighted by probability of success, with bounds */
15010319257bSDavid du Colombier static ulong
weight(ulong ms,unsigned pcntprob)15020319257bSDavid du Colombier weight(ulong ms, unsigned pcntprob)
15030319257bSDavid du Colombier {
15040319257bSDavid du Colombier 	ulong wait;
15050319257bSDavid du Colombier 
15060319257bSDavid du Colombier 	wait = (ms * pcntprob) / 100;
1507b8b25780SDavid du Colombier 	if (wait < Minwaitms)
1508b8b25780SDavid du Colombier 		wait = Minwaitms;
1509b8b25780SDavid du Colombier 	if (wait > Maxwaitms)
1510b8b25780SDavid du Colombier 		wait = Maxwaitms;
15110319257bSDavid du Colombier 	return wait;
15120319257bSDavid du Colombier }
15130319257bSDavid du Colombier 
1514a41547ffSDavid du Colombier /*
1515a41547ffSDavid du Colombier  * in principle we could use a single descriptor for a udp port
1516a41547ffSDavid du Colombier  * to send all queries and receive all the answers to them,
1517a41547ffSDavid du Colombier  * but we'd have to sort out the answers by dns-query id.
1518a41547ffSDavid du Colombier  */
15194f8f669cSDavid du Colombier static int
udpquery(Query * qp,char * mntpt,int depth,int patient,int inns)15203cbadd90SDavid du Colombier udpquery(Query *qp, char *mntpt, int depth, int patient, int inns)
15214f8f669cSDavid du Colombier {
15220319257bSDavid du Colombier 	int fd, rv;
1523*98813beeSDavid du Colombier 	ulong now, pcntprob;
1524b8b25780SDavid du Colombier 	uvlong wait, reqtm;
15254f8f669cSDavid du Colombier 	char *msg;
15264f8f669cSDavid du Colombier 	uchar *obuf, *ibuf;
15274f8f669cSDavid du Colombier 	static QLock mntlck;
15284f8f669cSDavid du Colombier 	static ulong lastmount;
15297dd7cddfSDavid du Colombier 
15307dd7cddfSDavid du Colombier 	/* use alloced buffers rather than ones from the stack */
15313cbadd90SDavid du Colombier 	ibuf = emalloc(64*1024);		/* max. tcp reply size */
1532*98813beeSDavid du Colombier 	obuf = emalloc(Maxpayload+Udphdrsize);
15337dd7cddfSDavid du Colombier 
15344f8f669cSDavid du Colombier 	fd = udpport(mntpt);
15354f8f669cSDavid du Colombier 	while (fd < 0 && cfg.straddle && strcmp(mntpt, "/net.alt") == 0) {
15364f8f669cSDavid du Colombier 		/* HACK: remount /net.alt */
15374f8f669cSDavid du Colombier 		now = time(nil);
15384f8f669cSDavid du Colombier 		if (now < lastmount + Remntretry)
1539b8b25780SDavid du Colombier 			sleep(S2MS(lastmount + Remntretry - now));
15404f8f669cSDavid du Colombier 		qlock(&mntlck);
15414f8f669cSDavid du Colombier 		fd = udpport(mntpt);	/* try again under lock */
15424f8f669cSDavid du Colombier 		if (fd < 0) {
15434f8f669cSDavid du Colombier 			dnslog("[%d] remounting /net.alt", getpid());
15444f8f669cSDavid du Colombier 			unmount(nil, "/net.alt");
15454f8f669cSDavid du Colombier 
15464f8f669cSDavid du Colombier 			msg = system(open("/dev/null", ORDWR), "outside");
15474f8f669cSDavid du Colombier 
15484f8f669cSDavid du Colombier 			lastmount = time(nil);
15494f8f669cSDavid du Colombier 			if (msg && *msg) {
15504f8f669cSDavid du Colombier 				dnslog("[%d] can't remount /net.alt: %s",
15514f8f669cSDavid du Colombier 					getpid(), msg);
1552b8b25780SDavid du Colombier 				sleep(10*1000);	/* don't spin remounting */
15534f8f669cSDavid du Colombier 			} else
15544f8f669cSDavid du Colombier 				fd = udpport(mntpt);
15554f8f669cSDavid du Colombier 		}
15564f8f669cSDavid du Colombier 		qunlock(&mntlck);
15574f8f669cSDavid du Colombier 	}
1558a41547ffSDavid du Colombier 	if (fd < 0) {
15594f8f669cSDavid du Colombier 		dnslog("can't get udpport for %s query of name %s: %r",
15603cbadd90SDavid du Colombier 			mntpt, qp->dp->name);
1561a41547ffSDavid du Colombier 		sysfatal("out of udp conversations");	/* we're buggered */
1562a41547ffSDavid du Colombier 	}
1563a41547ffSDavid du Colombier 
1564a41547ffSDavid du Colombier 	/*
1565b8b25780SDavid du Colombier 	 * Our QIP servers are busted and respond to AAAA and CNAME queries
1566b8b25780SDavid du Colombier 	 * with (sometimes malformed [too short] packets and) no answers and
1567b8b25780SDavid du Colombier 	 * just NS RRs but not Rname errors.  so make time-to-wait
1568b8b25780SDavid du Colombier 	 * proportional to estimated probability of an RR of that type existing.
1569a41547ffSDavid du Colombier 	 */
1570adb31a62SDavid du Colombier 	if (qp->type >= nelem(likely))
15710319257bSDavid du Colombier 		pcntprob = 35;			/* unpopular query type */
15720319257bSDavid du Colombier 	else
15730319257bSDavid du Colombier 		pcntprob = likely[qp->type];
15740319257bSDavid du Colombier 	reqtm = (patient? 2 * Maxreqtm: Maxreqtm);
1575b8b25780SDavid du Colombier 	wait = weight(reqtm / 3, pcntprob);	/* time for one udp query */
1576b8b25780SDavid du Colombier 	qp->req->aborttime = timems() + 3*wait; /* for all udp queries */
1577a41547ffSDavid du Colombier 
15780319257bSDavid du Colombier 	qp->udpfd = fd;
1579b8b25780SDavid du Colombier 	rv = queryns(qp, depth, ibuf, obuf, wait, inns);
1580a41547ffSDavid du Colombier 	close(fd);
1581a41547ffSDavid du Colombier 	qp->udpfd = -1;
15824f8f669cSDavid du Colombier 
15834f8f669cSDavid du Colombier 	free(obuf);
15844f8f669cSDavid du Colombier 	free(ibuf);
15854f8f669cSDavid du Colombier 	return rv;
15864f8f669cSDavid du Colombier }
15874f8f669cSDavid du Colombier 
1588b8b25780SDavid du Colombier /*
1589b8b25780SDavid du Colombier  * look up (qp->dp->name, qp->type) rr in dns,
1590b8b25780SDavid du Colombier  * using nameservers in qp->nsrp.
1591b8b25780SDavid du Colombier  */
15924f8f669cSDavid du Colombier static int
netquery(Query * qp,int depth)15933cbadd90SDavid du Colombier netquery(Query *qp, int depth)
15944f8f669cSDavid du Colombier {
1595410ea80bSDavid du Colombier 	int lock, rv, triedin, inname;
1596b8b25780SDavid du Colombier 	char buf[32];
15974f8f669cSDavid du Colombier 	RR *rp;
15984e153993SDavid du Colombier 	DN *dp;
1599225077b0SDavid du Colombier 	Querylck *qlp;
1600225077b0SDavid du Colombier 	static int whined;
16014f8f669cSDavid du Colombier 
1602c3617180SDavid du Colombier 	rv = Answnone;			/* pessimism */
16034f8f669cSDavid du Colombier 	if(depth > 12)			/* in a recursive loop? */
1604c3617180SDavid du Colombier 		return Answnone;
16054f8f669cSDavid du Colombier 
16063cbadd90SDavid du Colombier 	slave(qp->req);
1607d3907fe5SDavid du Colombier 	/*
1608d3907fe5SDavid du Colombier 	 * slave might have forked.  if so, the parent process longjmped to
1609d3907fe5SDavid du Colombier 	 * req->mret; we're usually the child slave, but if there are too
1610d3907fe5SDavid du Colombier 	 * many children already, we're still the same process.
1611d3907fe5SDavid du Colombier 	 */
16124f8f669cSDavid du Colombier 
16134e5f5f32SDavid du Colombier 	/*
16144e5f5f32SDavid du Colombier 	 * don't lock before call to slave so only children can block.
16154e5f5f32SDavid du Colombier 	 * just lock at top-level invocation.
16164e5f5f32SDavid du Colombier 	 */
1617225077b0SDavid du Colombier 	lock = depth <= 1 && qp->req->isslave;
16184e153993SDavid du Colombier 	dp = qp->dp;		/* ensure that it doesn't change underfoot */
1619225077b0SDavid du Colombier 	qlp = nil;
16204e5f5f32SDavid du Colombier 	if(lock) {
1621b8b25780SDavid du Colombier 		procsetname("query lock wait: %s %s from %s", dp->name,
1622b8b25780SDavid du Colombier 			rrname(qp->type, buf, sizeof buf), qp->req->from);
1623ccf6439bSDavid du Colombier 		/*
1624ccf6439bSDavid du Colombier 		 * don't make concurrent queries for this name.
162581730632SDavid du Colombier 		 * dozens of processes blocking here probably indicates
162681730632SDavid du Colombier 		 * an error in our dns data that causes us to not
162781730632SDavid du Colombier 		 * recognise a zone (area) as one of our own, thus
162881730632SDavid du Colombier 		 * causing us to query other nameservers.
1629ccf6439bSDavid du Colombier 		 */
1630225077b0SDavid du Colombier 		qlp = &dp->querylck[qtype2lck(qp->type)];
1631225077b0SDavid du Colombier 		qlock(qlp);
1632b8b25780SDavid du Colombier 		if (qlp->Ref.ref > Maxoutstanding) {
1633225077b0SDavid du Colombier 			qunlock(qlp);
1634225077b0SDavid du Colombier 			if (!whined) {
1635225077b0SDavid du Colombier 				whined = 1;
1636225077b0SDavid du Colombier 				dnslog("too many outstanding queries for %s;"
163713fec586SDavid du Colombier 					" dropping this one; no further logging"
163813fec586SDavid du Colombier 					" of drops", dp->name);
1639225077b0SDavid du Colombier 			}
1640225077b0SDavid du Colombier 			return 0;
1641225077b0SDavid du Colombier 		}
1642410ea80bSDavid du Colombier 		++qlp->Ref.ref;
1643410ea80bSDavid du Colombier 		qunlock(qlp);
1644225077b0SDavid du Colombier 	}
16454e153993SDavid du Colombier 	procsetname("netquery: %s", dp->name);
16466b0d5c8bSDavid du Colombier 
16476b0d5c8bSDavid du Colombier 	/* prepare server RR's for incremental lookup */
16483cbadd90SDavid du Colombier 	for(rp = qp->nsrp; rp; rp = rp->next)
16496b0d5c8bSDavid du Colombier 		rp->marker = 0;
16506b0d5c8bSDavid du Colombier 
16514f8f669cSDavid du Colombier 	triedin = 0;
1652a41547ffSDavid du Colombier 
16534f8f669cSDavid du Colombier 	/*
16544f8f669cSDavid du Colombier 	 * normal resolvers and servers will just use mntpt for all addresses,
16554f8f669cSDavid du Colombier 	 * even on the outside.  straddling servers will use mntpt (/net)
16564f8f669cSDavid du Colombier 	 * for inside addresses and /net.alt for outside addresses,
16574f8f669cSDavid du Colombier 	 * thus bypassing other inside nameservers.
16584f8f669cSDavid du Colombier 	 */
16594e153993SDavid du Colombier 	inname = insideaddr(dp->name);
16604f8f669cSDavid du Colombier 	if (!cfg.straddle || inname) {
16613cbadd90SDavid du Colombier 		rv = udpquery(qp, mntpt, depth, Hurry, (cfg.inside? Inns: Outns));
16624f8f669cSDavid du Colombier 		triedin = 1;
16634f8f669cSDavid du Colombier 	}
16644f8f669cSDavid du Colombier 
16654f8f669cSDavid du Colombier 	/*
16664f8f669cSDavid du Colombier 	 * if we're still looking, are inside, and have an outside domain,
16674f8f669cSDavid du Colombier 	 * try it on our outside interface, if any.
16684f8f669cSDavid du Colombier 	 */
1669c3617180SDavid du Colombier 	if (rv == Answnone && cfg.inside && !inname) {
16704f8f669cSDavid du Colombier 		if (triedin)
16714f8f669cSDavid du Colombier 			dnslog(
16724f8f669cSDavid du Colombier 	   "[%d] netquery: internal nameservers failed for %s; trying external",
16734e153993SDavid du Colombier 				getpid(), dp->name);
16744f8f669cSDavid du Colombier 
16754f8f669cSDavid du Colombier 		/* prepare server RR's for incremental lookup */
16763cbadd90SDavid du Colombier 		for(rp = qp->nsrp; rp; rp = rp->next)
16774f8f669cSDavid du Colombier 			rp->marker = 0;
16784f8f669cSDavid du Colombier 
16793cbadd90SDavid du Colombier 		rv = udpquery(qp, "/net.alt", depth, Patient, Outns);
16804f8f669cSDavid du Colombier 	}
1681c3617180SDavid du Colombier //	if (rv == Answnone)		/* could ask /net.alt/dns directly */
16824e153993SDavid du Colombier //		askoutdns(dp, qp->type);
16834f8f669cSDavid du Colombier 
1684410ea80bSDavid du Colombier 	if(lock && qlp) {
1685410ea80bSDavid du Colombier 		qlock(qlp);
1686410ea80bSDavid du Colombier 		assert(qlp->Ref.ref > 0);
1687410ea80bSDavid du Colombier 		qunlock(qlp);
1688225077b0SDavid du Colombier 		decref(qlp);
1689410ea80bSDavid du Colombier 	}
16907dd7cddfSDavid du Colombier 	return rv;
16917dd7cddfSDavid du Colombier }
16924f8f669cSDavid du Colombier 
16934f8f669cSDavid du Colombier int
seerootns(void)16944f8f669cSDavid du Colombier seerootns(void)
16954f8f669cSDavid du Colombier {
16963cbadd90SDavid du Colombier 	int rv;
16974f8f669cSDavid du Colombier 	char root[] = "";
16984f8f669cSDavid du Colombier 	Request req;
1699c3617180SDavid du Colombier 	RR *rr;
1700c73252aeSDavid du Colombier 	Query *qp;
17014f8f669cSDavid du Colombier 
17024f8f669cSDavid du Colombier 	memset(&req, 0, sizeof req);
17034f8f669cSDavid du Colombier 	req.isslave = 1;
1704b8b25780SDavid du Colombier 	req.aborttime = timems() + Maxreqtm;
1705f46c709fSDavid du Colombier 	req.from = "internal";
1706c3617180SDavid du Colombier 
1707c73252aeSDavid du Colombier 	qp = emalloc(sizeof *qp);
1708c73252aeSDavid du Colombier 	queryinit(qp, dnlookup(root, Cin, 1), Tns, &req);
1709c73252aeSDavid du Colombier 	qp->nsrp = dblookup(root, Cin, Tns, 0, 0);
1710c3617180SDavid du Colombier 	for (rr = qp->nsrp; rr != nil; rr = rr->next)	/* DEBUG */
1711c3617180SDavid du Colombier 		dnslog("seerootns query nsrp: %R", rr);
1712c3617180SDavid du Colombier 
1713c3617180SDavid du Colombier 	rv = netquery(qp, 0);		/* lookup ". ns" using qp->nsrp */
1714c73252aeSDavid du Colombier 
1715c73252aeSDavid du Colombier 	rrfreelist(qp->nsrp);
1716c73252aeSDavid du Colombier 	querydestroy(qp);
1717c73252aeSDavid du Colombier 	free(qp);
17183cbadd90SDavid du Colombier 	return rv;
17194f8f669cSDavid du Colombier }
1720