xref: /plan9/sys/src/cmd/ndb/dnresolve.c (revision cc499a30a6f1302a973149384e954d8486f8a058)
13cbadd90SDavid du Colombier /*
23cbadd90SDavid du Colombier  * domain name resolvers, see rfcs 1035 and 1123
33cbadd90SDavid du Colombier  */
43e12c5d1SDavid du Colombier #include <u.h>
53e12c5d1SDavid du Colombier #include <libc.h>
64f8f669cSDavid du Colombier #include <ip.h>
74f8f669cSDavid du Colombier #include <bio.h>
84f8f669cSDavid du Colombier #include <ndb.h>
93e12c5d1SDavid du Colombier #include "dns.h"
103e12c5d1SDavid du Colombier 
113cbadd90SDavid du Colombier typedef struct Dest Dest;
123cbadd90SDavid du Colombier typedef struct Ipaddr Ipaddr;
133cbadd90SDavid du Colombier typedef struct Query Query;
143cbadd90SDavid du Colombier 
153e12c5d1SDavid du Colombier enum
163e12c5d1SDavid du Colombier {
173cbadd90SDavid du Colombier 	Udp, Tcp,
18b8b25780SDavid du Colombier 
197dd7cddfSDavid du Colombier 	Maxdest=	24,	/* maximum destinations for a request message */
203e12c5d1SDavid du Colombier 	Maxtrans=	3,	/* maximum transmissions to a server */
21b8b25780SDavid du Colombier 	Maxretries=	3, /* cname+actual resends: was 32; have pity on user */
22b8b25780SDavid du Colombier 	Maxwaitms=	1000,	/* wait no longer for a remote dns query */
23b8b25780SDavid du Colombier 	Minwaitms=	100,	/* willing to wait for a remote dns query */
24b8b25780SDavid du Colombier 	Remntretry=	15,	/* min. sec.s between /net.alt remount tries */
25b8b25780SDavid du Colombier 	Maxoutstanding=	15,	/* max. outstanding queries per domain name */
26b8b25780SDavid du Colombier 
273cbadd90SDavid du Colombier 	Destmagic=	0xcafebabe,
283cbadd90SDavid du Colombier 	Querymagic=	0xdeadbeef,
293e12c5d1SDavid du Colombier };
30a41547ffSDavid du Colombier enum { Hurry, Patient, };
31a41547ffSDavid du Colombier enum { Outns, Inns, };
323e12c5d1SDavid du Colombier 
333cbadd90SDavid du Colombier struct Ipaddr {
343cbadd90SDavid du Colombier 	Ipaddr *next;
353cbadd90SDavid du Colombier 	uchar	ip[IPaddrlen];
363cbadd90SDavid du Colombier };
373cbadd90SDavid du Colombier 
383cbadd90SDavid du Colombier struct Dest
393cbadd90SDavid du Colombier {
403cbadd90SDavid du Colombier 	uchar	a[IPaddrlen];	/* ip address */
413cbadd90SDavid du Colombier 	DN	*s;		/* name server */
423cbadd90SDavid du Colombier 	int	nx;		/* number of transmissions */
433cbadd90SDavid du Colombier 	int	code;		/* response code; used to clear dp->respcode */
443cbadd90SDavid du Colombier 
453cbadd90SDavid du Colombier 	ulong	magic;
463cbadd90SDavid du Colombier };
473cbadd90SDavid du Colombier 
48c73252aeSDavid du Colombier /*
49c73252aeSDavid du Colombier  * Query has a QLock in it, thus it can't be an automatic
50c73252aeSDavid du Colombier  * variable, since each process would see a separate copy
51c73252aeSDavid du Colombier  * of the lock on its stack.
52c73252aeSDavid du Colombier  */
533cbadd90SDavid du Colombier struct Query {
543cbadd90SDavid du Colombier 	DN	*dp;		/* domain */
55adb31a62SDavid du Colombier 	ushort	type;		/* and type to look up */
563cbadd90SDavid du Colombier 	Request *req;
573cbadd90SDavid du Colombier 	RR	*nsrp;		/* name servers to consult */
583cbadd90SDavid du Colombier 
59a41547ffSDavid du Colombier 	/* dest must not be on the stack due to forking in slave() */
603cbadd90SDavid du Colombier 	Dest	*dest;		/* array of destinations */
613cbadd90SDavid du Colombier 	Dest	*curdest;	/* pointer to one of them */
623cbadd90SDavid du Colombier 	int	ndest;
633cbadd90SDavid du Colombier 
64f46c709fSDavid du Colombier 	int	udpfd;
653cbadd90SDavid du Colombier 
663cbadd90SDavid du Colombier 	QLock	tcplock;	/* only one tcp call at a time per query */
673cbadd90SDavid du Colombier 	int	tcpset;
683cbadd90SDavid du Colombier 	int	tcpfd;		/* if Tcp, read replies from here */
693cbadd90SDavid du Colombier 	int	tcpctlfd;
703cbadd90SDavid du Colombier 	uchar	tcpip[IPaddrlen];
713cbadd90SDavid du Colombier 
723cbadd90SDavid du Colombier 	ulong	magic;
733cbadd90SDavid du Colombier };
743cbadd90SDavid du Colombier 
75a41547ffSDavid du Colombier /* estimated % probability of such a record existing at all */
76a41547ffSDavid du Colombier int likely[] = {
77a41547ffSDavid du Colombier 	[Ta]		95,
78a41547ffSDavid du Colombier 	[Taaaa]		10,
79a41547ffSDavid du Colombier 	[Tcname]	15,
80a41547ffSDavid du Colombier 	[Tmx]		60,
81a41547ffSDavid du Colombier 	[Tns]		90,
82a41547ffSDavid du Colombier 	[Tnull]		5,
83a41547ffSDavid du Colombier 	[Tptr]		35,
84a41547ffSDavid du Colombier 	[Tsoa]		90,
85a41547ffSDavid du Colombier 	[Tsrv]		60,
86a41547ffSDavid du Colombier 	[Ttxt]		15,
87a41547ffSDavid du Colombier 	[Tall]		95,
883cbadd90SDavid du Colombier };
893cbadd90SDavid du Colombier 
907dd7cddfSDavid du Colombier static RR*	dnresolve1(char*, int, int, Request*, int, int);
913cbadd90SDavid du Colombier static int	netquery(Query *, int);
923cbadd90SDavid du Colombier 
934f8f669cSDavid du Colombier /*
94530fef66SDavid du Colombier  * reading /proc/pid/args yields either "name args" or "name [display args]",
954f8f669cSDavid du Colombier  * so return only display args, if any.
964f8f669cSDavid du Colombier  */
974f8f669cSDavid du Colombier static char *
984f8f669cSDavid du Colombier procgetname(void)
994f8f669cSDavid du Colombier {
1004f8f669cSDavid du Colombier 	int fd, n;
1014f8f669cSDavid du Colombier 	char *lp, *rp;
1024f8f669cSDavid du Colombier 	char buf[256];
1034f8f669cSDavid du Colombier 
1044f8f669cSDavid du Colombier 	snprint(buf, sizeof buf, "#p/%d/args", getpid());
1054f8f669cSDavid du Colombier 	if((fd = open(buf, OREAD)) < 0)
1064f8f669cSDavid du Colombier 		return strdup("");
1074f8f669cSDavid du Colombier 	*buf = '\0';
1084f8f669cSDavid du Colombier 	n = read(fd, buf, sizeof buf-1);
1094f8f669cSDavid du Colombier 	close(fd);
1104f8f669cSDavid du Colombier 	if (n >= 0)
1114f8f669cSDavid du Colombier 		buf[n] = '\0';
1124f8f669cSDavid du Colombier 	if ((lp = strchr(buf, '[')) == nil ||
1134f8f669cSDavid du Colombier 	    (rp = strrchr(buf, ']')) == nil)
1144f8f669cSDavid du Colombier 		return strdup("");
1154f8f669cSDavid du Colombier 	*rp = '\0';
1164f8f669cSDavid du Colombier 	return strdup(lp+1);
1174f8f669cSDavid du Colombier }
1183e12c5d1SDavid du Colombier 
1193e12c5d1SDavid du Colombier /*
1203e12c5d1SDavid du Colombier  *  lookup 'type' info for domain name 'name'.  If it doesn't exist, try
1213e12c5d1SDavid du Colombier  *  looking it up as a canonical name.
122b8b25780SDavid du Colombier  *
123b8b25780SDavid du Colombier  *  this process can be quite slow if time-outs are set too high when querying
124b8b25780SDavid du Colombier  *  nameservers that just don't respond to certain query types.  in that case,
125b8b25780SDavid du Colombier  *  there will be multiple udp retries, multiple nameservers will be queried,
126b8b25780SDavid du Colombier  *  and this will be repeated for a cname query.  the whole thing will be
127b8b25780SDavid du Colombier  *  retried several times until we get an answer or a time-out.
1283e12c5d1SDavid du Colombier  */
1293e12c5d1SDavid du Colombier RR*
1304f8f669cSDavid du Colombier dnresolve(char *name, int class, int type, Request *req, RR **cn, int depth,
1314f8f669cSDavid du Colombier 	int recurse, int rooted, int *status)
1323e12c5d1SDavid du Colombier {
1337dd7cddfSDavid du Colombier 	RR *rp, *nrp, *drp;
1343e12c5d1SDavid du Colombier 	DN *dp;
1357dd7cddfSDavid du Colombier 	int loops;
1364f8f669cSDavid du Colombier 	char *procname;
1377dd7cddfSDavid du Colombier 	char nname[Domlen];
1383e12c5d1SDavid du Colombier 
1397dd7cddfSDavid du Colombier 	if(status)
1407dd7cddfSDavid du Colombier 		*status = 0;
1417dd7cddfSDavid du Colombier 
142a41547ffSDavid du Colombier 	if(depth > 12)			/* in a recursive loop? */
143a41547ffSDavid du Colombier 		return nil;
144a41547ffSDavid du Colombier 
1454f8f669cSDavid du Colombier 	procname = procgetname();
1467dd7cddfSDavid du Colombier 	/*
1477dd7cddfSDavid du Colombier 	 *  hack for systems that don't have resolve search
1487dd7cddfSDavid du Colombier 	 *  lists.  Just look up the simple name in the database.
1497dd7cddfSDavid du Colombier 	 */
1506dc4800dSDavid du Colombier 	if(!rooted && strchr(name, '.') == nil){
1517dd7cddfSDavid du Colombier 		rp = nil;
1527dd7cddfSDavid du Colombier 		drp = domainlist(class);
153a41547ffSDavid du Colombier 		for(nrp = drp; rp == nil && nrp != nil; nrp = nrp->next){
1544f8f669cSDavid du Colombier 			snprint(nname, sizeof nname, "%s.%s", name,
1554f8f669cSDavid du Colombier 				nrp->ptr->name);
156a41547ffSDavid du Colombier 			rp = dnresolve(nname, class, type, req, cn, depth+1,
1574f8f669cSDavid du Colombier 				recurse, rooted, status);
158530fef66SDavid du Colombier 			lock(&dnlock);
159271b8d73SDavid du Colombier 			rrfreelist(rrremneg(&rp));
160530fef66SDavid du Colombier 			unlock(&dnlock);
1617dd7cddfSDavid du Colombier 		}
1627dd7cddfSDavid du Colombier 		if(drp != nil)
1636aaebd7dSDavid du Colombier 			rrfreelist(drp);
1644f8f669cSDavid du Colombier 		procsetname(procname);
1654f8f669cSDavid du Colombier 		free(procname);
1663e12c5d1SDavid du Colombier 		return rp;
1677dd7cddfSDavid du Colombier 	}
1683e12c5d1SDavid du Colombier 
1697dd7cddfSDavid du Colombier 	/*
1707dd7cddfSDavid du Colombier 	 *  try the name directly
1717dd7cddfSDavid du Colombier 	 */
1727dd7cddfSDavid du Colombier 	rp = dnresolve1(name, class, type, req, depth, recurse);
173225077b0SDavid du Colombier 	if(rp == nil) {
174225077b0SDavid du Colombier 		/*
175225077b0SDavid du Colombier 		 * try it as a canonical name if we weren't told
176225077b0SDavid du Colombier 		 * that the name didn't exist
177225077b0SDavid du Colombier 		 */
1787dd7cddfSDavid du Colombier 		dp = dnlookup(name, class, 0);
1794f8f669cSDavid du Colombier 		if(type != Tptr && dp->respcode != Rname)
180b8b25780SDavid du Colombier 			for(loops = 0; rp == nil && loops < Maxretries; loops++){
181b8b25780SDavid du Colombier 				/* retry cname, then the actual type */
182225077b0SDavid du Colombier 				rp = dnresolve1(name, class, Tcname, req,
183225077b0SDavid du Colombier 					depth, recurse);
1847dd7cddfSDavid du Colombier 				if(rp == nil)
1857dd7cddfSDavid du Colombier 					break;
18680ee5cbfSDavid du Colombier 
187c73252aeSDavid du Colombier 				/* rp->host == nil shouldn't happen, but does */
188c73252aeSDavid du Colombier 				if(rp->negative || rp->host == nil){
18980ee5cbfSDavid du Colombier 					rrfreelist(rp);
19080ee5cbfSDavid du Colombier 					rp = nil;
19180ee5cbfSDavid du Colombier 					break;
19280ee5cbfSDavid du Colombier 				}
1937dd7cddfSDavid du Colombier 
1947dd7cddfSDavid du Colombier 				name = rp->host->name;
195530fef66SDavid du Colombier 				lock(&dnlock);
1967dd7cddfSDavid du Colombier 				if(cn)
1977dd7cddfSDavid du Colombier 					rrcat(cn, rp);
1987dd7cddfSDavid du Colombier 				else
1997dd7cddfSDavid du Colombier 					rrfreelist(rp);
200530fef66SDavid du Colombier 				unlock(&dnlock);
2017dd7cddfSDavid du Colombier 
202225077b0SDavid du Colombier 				rp = dnresolve1(name, class, type, req,
203225077b0SDavid du Colombier 					depth, recurse);
2047dd7cddfSDavid du Colombier 			}
2057dd7cddfSDavid du Colombier 
2067dd7cddfSDavid du Colombier 		/* distinction between not found and not good */
207d9924332SDavid du Colombier 		if(rp == nil && status != nil && dp->respcode != Rok)
2084f8f669cSDavid du Colombier 			*status = dp->respcode;
209225077b0SDavid du Colombier 	}
2104f8f669cSDavid du Colombier 	procsetname(procname);
2114f8f669cSDavid du Colombier 	free(procname);
2127dd7cddfSDavid du Colombier 	return randomize(rp);
2133e12c5d1SDavid du Colombier }
2143e12c5d1SDavid du Colombier 
2153cbadd90SDavid du Colombier static void
2163cbadd90SDavid du Colombier queryinit(Query *qp, DN *dp, int type, Request *req)
2173cbadd90SDavid du Colombier {
2183cbadd90SDavid du Colombier 	memset(qp, 0, sizeof *qp);
2194e7b9544SDavid du Colombier 	qp->udpfd = qp->tcpfd = qp->tcpctlfd = -1;
2203cbadd90SDavid du Colombier 	qp->dp = dp;
2213cbadd90SDavid du Colombier 	qp->type = type;
222adb31a62SDavid du Colombier 	if (qp->type != type)
223adb31a62SDavid du Colombier 		dnslog("queryinit: bogus type %d", type);
2243cbadd90SDavid du Colombier 	qp->req = req;
2253cbadd90SDavid du Colombier 	qp->nsrp = nil;
2263cbadd90SDavid du Colombier 	qp->dest = qp->curdest = nil;
2273cbadd90SDavid du Colombier 	qp->magic = Querymagic;
2283cbadd90SDavid du Colombier }
2293cbadd90SDavid du Colombier 
2303cbadd90SDavid du Colombier static void
2313cbadd90SDavid du Colombier queryck(Query *qp)
2323cbadd90SDavid du Colombier {
2333cbadd90SDavid du Colombier 	assert(qp);
2343cbadd90SDavid du Colombier 	assert(qp->magic == Querymagic);
2353cbadd90SDavid du Colombier }
2363cbadd90SDavid du Colombier 
2373cbadd90SDavid du Colombier static void
2384e7b9544SDavid du Colombier querydestroy(Query *qp)
2394e7b9544SDavid du Colombier {
2404e7b9544SDavid du Colombier 	queryck(qp);
241530fef66SDavid du Colombier 	/* leave udpfd open */
2424e7b9544SDavid du Colombier 	if (qp->tcpfd > 0)
2434e7b9544SDavid du Colombier 		close(qp->tcpfd);
2444e7b9544SDavid du Colombier 	if (qp->tcpctlfd > 0) {
2454e7b9544SDavid du Colombier 		hangup(qp->tcpctlfd);
2464e7b9544SDavid du Colombier 		close(qp->tcpctlfd);
2474e7b9544SDavid du Colombier 	}
248a41547ffSDavid du Colombier 	free(qp->dest);
2494e7b9544SDavid du Colombier 	memset(qp, 0, sizeof *qp);	/* prevent accidents */
2504e7b9544SDavid du Colombier 	qp->udpfd = qp->tcpfd = qp->tcpctlfd = -1;
2514e7b9544SDavid du Colombier }
2524e7b9544SDavid du Colombier 
2534e7b9544SDavid du Colombier static void
2543cbadd90SDavid du Colombier destinit(Dest *p)
2553cbadd90SDavid du Colombier {
2563cbadd90SDavid du Colombier 	memset(p, 0, sizeof *p);
2573cbadd90SDavid du Colombier 	p->magic = Destmagic;
2583cbadd90SDavid du Colombier }
2593cbadd90SDavid du Colombier 
2603cbadd90SDavid du Colombier static void
2613cbadd90SDavid du Colombier destck(Dest *p)
2623cbadd90SDavid du Colombier {
2633cbadd90SDavid du Colombier 	assert(p);
2643cbadd90SDavid du Colombier 	assert(p->magic == Destmagic);
2653cbadd90SDavid du Colombier }
2663cbadd90SDavid du Colombier 
267a41547ffSDavid du Colombier static void
268a41547ffSDavid du Colombier destdestroy(Dest *p)
269a41547ffSDavid du Colombier {
270a41547ffSDavid du Colombier 	USED(p);
271a41547ffSDavid du Colombier }
272a41547ffSDavid du Colombier 
273a41547ffSDavid du Colombier /*
274a41547ffSDavid du Colombier  * if the response to a query hasn't arrived within 100 ms.,
275a41547ffSDavid du Colombier  * it's unlikely to arrive at all.  after 1 s., it's really unlikely.
276a41547ffSDavid du Colombier  * queries for missing RRs are likely to produce time-outs rather than
277a41547ffSDavid du Colombier  * negative responses, so cname and aaaa queries are likely to time out,
278a41547ffSDavid du Colombier  * thus we don't wait very long for them.
279a41547ffSDavid du Colombier  */
280a41547ffSDavid du Colombier static void
281a41547ffSDavid du Colombier notestats(vlong start, int tmout, int type)
282a41547ffSDavid du Colombier {
283a41547ffSDavid du Colombier 	qlock(&stats);
284a41547ffSDavid du Colombier 	if (tmout) {
285a41547ffSDavid du Colombier 		stats.tmout++;
286a41547ffSDavid du Colombier 		if (type == Taaaa)
287a41547ffSDavid du Colombier 			stats.tmoutv6++;
288a41547ffSDavid du Colombier 		else if (type == Tcname)
289a41547ffSDavid du Colombier 			stats.tmoutcname++;
290a41547ffSDavid du Colombier 	} else {
291a41547ffSDavid du Colombier 		long wait10ths = NS2MS(nsec() - start) / 100;
292a41547ffSDavid du Colombier 
29381730632SDavid du Colombier 		if (wait10ths <= 0)
294a41547ffSDavid du Colombier 			stats.under10ths[0]++;
295a41547ffSDavid du Colombier 		else if (wait10ths >= nelem(stats.under10ths))
296a41547ffSDavid du Colombier 			stats.under10ths[nelem(stats.under10ths) - 1]++;
297a41547ffSDavid du Colombier 		else
298a41547ffSDavid du Colombier 			stats.under10ths[wait10ths]++;
299a41547ffSDavid du Colombier 	}
300a41547ffSDavid du Colombier 	qunlock(&stats);
301a41547ffSDavid du Colombier }
302a41547ffSDavid du Colombier 
303a41547ffSDavid du Colombier static void
304a41547ffSDavid du Colombier noteinmem(void)
305a41547ffSDavid du Colombier {
306a41547ffSDavid du Colombier 	qlock(&stats);
307a41547ffSDavid du Colombier 	stats.answinmem++;
308a41547ffSDavid du Colombier 	qunlock(&stats);
309a41547ffSDavid du Colombier }
310a41547ffSDavid du Colombier 
3113e12c5d1SDavid du Colombier static RR*
31246595261SDavid du Colombier issuequery(Query *qp, char *name, int class, int depth, int recurse)
3133e12c5d1SDavid du Colombier {
314f46c709fSDavid du Colombier 	char *cp;
315c73252aeSDavid du Colombier 	DN *nsdp;
31646595261SDavid du Colombier 	RR *rp, *nsrp, *dbnsrp;
3173cbadd90SDavid du Colombier 
3183e12c5d1SDavid du Colombier 	/*
3194f8f669cSDavid du Colombier 	 *  if we're running as just a resolver, query our
3207dd7cddfSDavid du Colombier 	 *  designated name servers
321219b2ee8SDavid du Colombier 	 */
3224f8f669cSDavid du Colombier 	if(cfg.resolver){
3237dd7cddfSDavid du Colombier 		nsrp = randomize(getdnsservers(class));
3247dd7cddfSDavid du Colombier 		if(nsrp != nil) {
325c73252aeSDavid du Colombier 			qp->nsrp = nsrp;
326c73252aeSDavid du Colombier 			if(netquery(qp, depth+1)){
3277dd7cddfSDavid du Colombier 				rrfreelist(nsrp);
328c73252aeSDavid du Colombier 				return rrlookup(qp->dp, qp->type, OKneg);
3297dd7cddfSDavid du Colombier 			}
3307dd7cddfSDavid du Colombier 			rrfreelist(nsrp);
3317dd7cddfSDavid du Colombier 		}
332219b2ee8SDavid du Colombier 	}
333219b2ee8SDavid du Colombier 
334219b2ee8SDavid du Colombier 	/*
3353e12c5d1SDavid du Colombier  	 *  walk up the domain name looking for
3363e12c5d1SDavid du Colombier 	 *  a name server for the domain.
3373e12c5d1SDavid du Colombier 	 */
3383e12c5d1SDavid du Colombier 	for(cp = name; cp; cp = walkup(cp)){
3397dd7cddfSDavid du Colombier 		/*
3407dd7cddfSDavid du Colombier 		 *  if this is a local (served by us) domain,
3417dd7cddfSDavid du Colombier 		 *  return answer
3427dd7cddfSDavid du Colombier 		 */
3437dd7cddfSDavid du Colombier 		dbnsrp = randomize(dblookup(cp, class, Tns, 0, 0));
3447dd7cddfSDavid du Colombier 		if(dbnsrp && dbnsrp->local){
345c73252aeSDavid du Colombier 			rp = dblookup(name, class, qp->type, 1, dbnsrp->ttl);
3467dd7cddfSDavid du Colombier 			rrfreelist(dbnsrp);
3477dd7cddfSDavid du Colombier 			return rp;
3487dd7cddfSDavid du Colombier 		}
3497dd7cddfSDavid du Colombier 
3507dd7cddfSDavid du Colombier 		/*
3517dd7cddfSDavid du Colombier 		 *  if recursion isn't set, just accept local
3527dd7cddfSDavid du Colombier 		 *  entries
3537dd7cddfSDavid du Colombier 		 */
3547dd7cddfSDavid du Colombier 		if(recurse == Dontrecurse){
3557dd7cddfSDavid du Colombier 			if(dbnsrp)
3567dd7cddfSDavid du Colombier 				rrfreelist(dbnsrp);
3577dd7cddfSDavid du Colombier 			continue;
3587dd7cddfSDavid du Colombier 		}
3597dd7cddfSDavid du Colombier 
3607dd7cddfSDavid du Colombier 		/* look for ns in cache */
3613e12c5d1SDavid du Colombier 		nsdp = dnlookup(cp, class, 0);
3627dd7cddfSDavid du Colombier 		nsrp = nil;
3633e12c5d1SDavid du Colombier 		if(nsdp)
3647dd7cddfSDavid du Colombier 			nsrp = randomize(rrlookup(nsdp, Tns, NOneg));
3657dd7cddfSDavid du Colombier 
3667dd7cddfSDavid du Colombier 		/* if the entry timed out, ignore it */
3677dd7cddfSDavid du Colombier 		if(nsrp && nsrp->ttl < now){
3687dd7cddfSDavid du Colombier 			rrfreelist(nsrp);
3697dd7cddfSDavid du Colombier 			nsrp = nil;
3707dd7cddfSDavid du Colombier 		}
3713e12c5d1SDavid du Colombier 
3723e12c5d1SDavid du Colombier 		if(nsrp){
3737dd7cddfSDavid du Colombier 			rrfreelist(dbnsrp);
3747dd7cddfSDavid du Colombier 
3754f8f669cSDavid du Colombier 			/* query the name servers found in cache */
376c73252aeSDavid du Colombier 			qp->nsrp = nsrp;
377c73252aeSDavid du Colombier 			if(netquery(qp, depth+1)){
3783e12c5d1SDavid du Colombier 				rrfreelist(nsrp);
379c73252aeSDavid du Colombier 				return rrlookup(qp->dp, qp->type, OKneg);
3807dd7cddfSDavid du Colombier 			}
3817dd7cddfSDavid du Colombier 			rrfreelist(nsrp);
3827dd7cddfSDavid du Colombier 			continue;
3833e12c5d1SDavid du Colombier 		}
3843e12c5d1SDavid du Colombier 
3857dd7cddfSDavid du Colombier 		/* use ns from db */
3867dd7cddfSDavid du Colombier 		if(dbnsrp){
3877dd7cddfSDavid du Colombier 			/* try the name servers found in db */
388c73252aeSDavid du Colombier 			qp->nsrp = dbnsrp;
389c73252aeSDavid du Colombier 			if(netquery(qp, depth+1)){
3903e12c5d1SDavid du Colombier 				/* we got an answer */
3917dd7cddfSDavid du Colombier 				rrfreelist(dbnsrp);
392c73252aeSDavid du Colombier 				return rrlookup(qp->dp, qp->type, NOneg);
3933e12c5d1SDavid du Colombier 			}
3947dd7cddfSDavid du Colombier 			rrfreelist(dbnsrp);
3953e12c5d1SDavid du Colombier 		}
3963e12c5d1SDavid du Colombier 	}
39746595261SDavid du Colombier 	return nil;
398c73252aeSDavid du Colombier }
399c73252aeSDavid du Colombier 
400c73252aeSDavid du Colombier static RR*
401c73252aeSDavid du Colombier dnresolve1(char *name, int class, int type, Request *req, int depth,
402c73252aeSDavid du Colombier 	int recurse)
403c73252aeSDavid du Colombier {
404c73252aeSDavid du Colombier 	Area *area;
405c73252aeSDavid du Colombier 	DN *dp;
406c73252aeSDavid du Colombier 	RR *rp;
407c73252aeSDavid du Colombier 	Query *qp;
408c73252aeSDavid du Colombier 
409c73252aeSDavid du Colombier 	if(debug)
410c73252aeSDavid du Colombier 		dnslog("[%d] dnresolve1 %s %d %d", getpid(), name, type, class);
411c73252aeSDavid du Colombier 
412c73252aeSDavid du Colombier 	/* only class Cin implemented so far */
413c73252aeSDavid du Colombier 	if(class != Cin)
414c73252aeSDavid du Colombier 		return nil;
415c73252aeSDavid du Colombier 
416c73252aeSDavid du Colombier 	dp = dnlookup(name, class, 1);
417c73252aeSDavid du Colombier 
418c73252aeSDavid du Colombier 	/*
419c73252aeSDavid du Colombier 	 *  Try the cache first
420c73252aeSDavid du Colombier 	 */
421c73252aeSDavid du Colombier 	rp = rrlookup(dp, type, OKneg);
422c73252aeSDavid du Colombier 	if(rp)
423c73252aeSDavid du Colombier 		if(rp->db){
424c73252aeSDavid du Colombier 			/* unauthoritative db entries are hints */
425c73252aeSDavid du Colombier 			if(rp->auth) {
426c73252aeSDavid du Colombier 				noteinmem();
427d9924332SDavid du Colombier 				if(debug)
428d9924332SDavid du Colombier 					dnslog("[%d] dnresolve1 %s %d %d: auth rr in db",
429d9924332SDavid du Colombier 						getpid(), name, type, class);
430c73252aeSDavid du Colombier 				return rp;
431c73252aeSDavid du Colombier 			}
432c73252aeSDavid du Colombier 		} else
433c73252aeSDavid du Colombier 			/* cached entry must still be valid */
434c73252aeSDavid du Colombier 			if(rp->ttl > now)
435c73252aeSDavid du Colombier 				/* but Tall entries are special */
436c73252aeSDavid du Colombier 				if(type != Tall || rp->query == Tall) {
437c73252aeSDavid du Colombier 					noteinmem();
438d9924332SDavid du Colombier 					if(debug)
439d9924332SDavid du Colombier 						dnslog("[%d] dnresolve1 %s %d %d: rr not in db",
440d9924332SDavid du Colombier 							getpid(), name, type, class);
441c73252aeSDavid du Colombier 					return rp;
442c73252aeSDavid du Colombier 				}
443c73252aeSDavid du Colombier 	rrfreelist(rp);
44446595261SDavid du Colombier 	rp = nil;		/* accident prevention */
44546595261SDavid du Colombier 	USED(rp);
446c73252aeSDavid du Colombier 
447c73252aeSDavid du Colombier 	/*
448c73252aeSDavid du Colombier 	 * try the cache for a canonical name. if found punt
449c73252aeSDavid du Colombier 	 * since we'll find it during the canonical name search
450c73252aeSDavid du Colombier 	 * in dnresolve().
451c73252aeSDavid du Colombier 	 */
452c73252aeSDavid du Colombier 	if(type != Tcname){
453c73252aeSDavid du Colombier 		rp = rrlookup(dp, Tcname, NOneg);
454c73252aeSDavid du Colombier 		rrfreelist(rp);
455d9924332SDavid du Colombier 		if(rp){
456d9924332SDavid du Colombier 			if(debug)
457d9924332SDavid du Colombier 				dnslog("[%d] dnresolve1 %s %d %d: rr from rrlookup for non-cname",
458d9924332SDavid du Colombier 					getpid(), name, type, class);
459c73252aeSDavid du Colombier 			return nil;
460c73252aeSDavid du Colombier 		}
461d9924332SDavid du Colombier 	}
462c73252aeSDavid du Colombier 
463c73252aeSDavid du Colombier 	/*
464c73252aeSDavid du Colombier 	 * if the domain name is within an area of ours,
465c73252aeSDavid du Colombier 	 * we should have found its data in memory by now.
466c73252aeSDavid du Colombier 	 */
467c73252aeSDavid du Colombier 	area = inmyarea(dp->name);
468c73252aeSDavid du Colombier 	if (area || strncmp(dp->name, "local#", 6) == 0) {
469c73252aeSDavid du Colombier //		char buf[32];
470c73252aeSDavid du Colombier 
471c73252aeSDavid du Colombier //		dnslog("%s %s: no data in area %s", dp->name,
472c73252aeSDavid du Colombier //			rrname(type, buf, sizeof buf), area->soarr->owner->name);
473c73252aeSDavid du Colombier 		return nil;
474c73252aeSDavid du Colombier 	}
475c73252aeSDavid du Colombier 
476c73252aeSDavid du Colombier 	qp = emalloc(sizeof *qp);
477c73252aeSDavid du Colombier 	queryinit(qp, dp, type, req);
47846595261SDavid du Colombier 	rp = issuequery(qp, name, class, depth, recurse);
479c73252aeSDavid du Colombier 	querydestroy(qp);
480c73252aeSDavid du Colombier 	free(qp);
481d9924332SDavid du Colombier 	if(rp){
482d9924332SDavid du Colombier 		if(debug)
483d9924332SDavid du Colombier 			dnslog("[%d] dnresolve1 %s %d %d: rr from query",
484d9924332SDavid du Colombier 				getpid(), name, type, class);
485c73252aeSDavid du Colombier 		return rp;
486d9924332SDavid du Colombier 	}
4873e12c5d1SDavid du Colombier 
4887dd7cddfSDavid du Colombier 	/* settle for a non-authoritative answer */
4897dd7cddfSDavid du Colombier 	rp = rrlookup(dp, type, OKneg);
490d9924332SDavid du Colombier 	if(rp){
491d9924332SDavid du Colombier 		if(debug)
492d9924332SDavid du Colombier 			dnslog("[%d] dnresolve1 %s %d %d: rr from rrlookup",
493d9924332SDavid du Colombier 				getpid(), name, type, class);
4947dd7cddfSDavid du Colombier 		return rp;
495d9924332SDavid du Colombier 	}
4967dd7cddfSDavid du Colombier 
4977dd7cddfSDavid du Colombier 	/* noone answered.  try the database, we might have a chance. */
498d9924332SDavid du Colombier 	rp = dblookup(name, class, type, 0, 0);
499d9924332SDavid du Colombier 	if (rp) {
500d9924332SDavid du Colombier 		if(debug)
501d9924332SDavid du Colombier 			dnslog("[%d] dnresolve1 %s %d %d: rr from dblookup",
502d9924332SDavid du Colombier 				getpid(), name, type, class);
503d9924332SDavid du Colombier 	}else{
504d9924332SDavid du Colombier 		if(debug)
505d9924332SDavid du Colombier 			dnslog("[%d] dnresolve1 %s %d %d: no rr from dblookup; crapped out",
506d9924332SDavid du Colombier 				getpid(), name, type, class);
507d9924332SDavid du Colombier 	}
508d9924332SDavid du Colombier 	return rp;
5093e12c5d1SDavid du Colombier }
5103e12c5d1SDavid du Colombier 
5113e12c5d1SDavid du Colombier /*
5124f8f669cSDavid du Colombier  *  walk a domain name one element to the right.
5134f8f669cSDavid du Colombier  *  return a pointer to that element.
5143e12c5d1SDavid du Colombier  *  in other words, return a pointer to the parent domain name.
5153e12c5d1SDavid du Colombier  */
5163e12c5d1SDavid du Colombier char*
5173e12c5d1SDavid du Colombier walkup(char *name)
5183e12c5d1SDavid du Colombier {
5193e12c5d1SDavid du Colombier 	char *cp;
5203e12c5d1SDavid du Colombier 
5213e12c5d1SDavid du Colombier 	cp = strchr(name, '.');
5223e12c5d1SDavid du Colombier 	if(cp)
5233e12c5d1SDavid du Colombier 		return cp+1;
5243e12c5d1SDavid du Colombier 	else if(*name)
5253e12c5d1SDavid du Colombier 		return "";
5263e12c5d1SDavid du Colombier 	else
5273e12c5d1SDavid du Colombier 		return 0;
5283e12c5d1SDavid du Colombier }
5293e12c5d1SDavid du Colombier 
5303e12c5d1SDavid du Colombier /*
531a41547ffSDavid du Colombier  *  Get a udp port for sending requests and reading replies.  Put the port
5323e12c5d1SDavid du Colombier  *  into "headers" mode.
5333e12c5d1SDavid du Colombier  */
5343e12c5d1SDavid du Colombier static char *hmsg = "headers";
5353e12c5d1SDavid du Colombier 
536dc5a79c1SDavid du Colombier int
5374f8f669cSDavid du Colombier udpport(char *mtpt)
5383e12c5d1SDavid du Colombier {
5393e12c5d1SDavid du Colombier 	int fd, ctl;
5404f8f669cSDavid du Colombier 	char ds[64], adir[64];
5413e12c5d1SDavid du Colombier 
5423e12c5d1SDavid du Colombier 	/* get a udp port */
5434f8f669cSDavid du Colombier 	snprint(ds, sizeof ds, "%s/udp!*!0", (mtpt? mtpt: "/net"));
5447dd7cddfSDavid du Colombier 	ctl = announce(ds, adir);
5457dd7cddfSDavid du Colombier 	if(ctl < 0){
5467dd7cddfSDavid du Colombier 		/* warning("can't get udp port"); */
547bd389b36SDavid du Colombier 		return -1;
548bd389b36SDavid du Colombier 	}
5493e12c5d1SDavid du Colombier 
5503e12c5d1SDavid du Colombier 	/* turn on header style interface */
551410ea80bSDavid du Colombier 	if(write(ctl, hmsg, strlen(hmsg)) != strlen(hmsg)){
5527dd7cddfSDavid du Colombier 		close(ctl);
553bd389b36SDavid du Colombier 		warning(hmsg);
554bd389b36SDavid du Colombier 		return -1;
555bd389b36SDavid du Colombier 	}
5563e12c5d1SDavid du Colombier 
5577dd7cddfSDavid du Colombier 	/* grab the data file */
5584f8f669cSDavid du Colombier 	snprint(ds, sizeof ds, "%s/data", adir);
5597dd7cddfSDavid du Colombier 	fd = open(ds, ORDWR);
5603e12c5d1SDavid du Colombier 	close(ctl);
5614f8f669cSDavid du Colombier 	if(fd < 0)
5624f8f669cSDavid du Colombier 		warning("can't open udp port %s: %r", ds);
5633e12c5d1SDavid du Colombier 	return fd;
5643e12c5d1SDavid du Colombier }
5653e12c5d1SDavid du Colombier 
566d9924332SDavid du Colombier void
567d9924332SDavid du Colombier initdnsmsg(DNSmsg *mp, RR *rp, int flags, ushort reqno)
568d9924332SDavid du Colombier {
569d9924332SDavid du Colombier 	mp->flags = flags;
570d9924332SDavid du Colombier 	mp->id = reqno;
571d9924332SDavid du Colombier 	mp->qd = rp;
572d9924332SDavid du Colombier }
573d9924332SDavid du Colombier 
574d9924332SDavid du Colombier DNSmsg *
575d9924332SDavid du Colombier newdnsmsg(RR *rp, int flags, ushort reqno)
576d9924332SDavid du Colombier {
577d9924332SDavid du Colombier 	DNSmsg *mp;
578d9924332SDavid du Colombier 
579d9924332SDavid du Colombier 	mp = emalloc(sizeof *mp);
580d9924332SDavid du Colombier 	initdnsmsg(mp, rp, flags, reqno);
581d9924332SDavid du Colombier 	return mp;
582d9924332SDavid du Colombier }
583d9924332SDavid du Colombier 
584d6d99297SDavid du Colombier /* generate a DNS UDP query packet */
585dc5a79c1SDavid du Colombier int
586dc5a79c1SDavid du Colombier mkreq(DN *dp, int type, uchar *buf, int flags, ushort reqno)
5873e12c5d1SDavid du Colombier {
5883e12c5d1SDavid du Colombier 	DNSmsg m;
5893e12c5d1SDavid du Colombier 	int len;
590f27a9a5aSDavid du Colombier 	Udphdr *uh = (Udphdr*)buf;
591d9924332SDavid du Colombier 	RR *rp;
5923e12c5d1SDavid du Colombier 
5933e12c5d1SDavid du Colombier 	/* stuff port number into output buffer */
5943cbadd90SDavid du Colombier 	memset(uh, 0, sizeof *uh);
5957dd7cddfSDavid du Colombier 	hnputs(uh->rport, 53);
5963e12c5d1SDavid du Colombier 
5973e12c5d1SDavid du Colombier 	/* make request and convert it to output format */
5983cbadd90SDavid du Colombier 	memset(&m, 0, sizeof m);
599d9924332SDavid du Colombier 	rp = rralloc(type);
600d9924332SDavid du Colombier 	rp->owner = dp;
601d9924332SDavid du Colombier 	initdnsmsg(&m, rp, flags, reqno);
602f27a9a5aSDavid du Colombier 	len = convDNS2M(&m, &buf[Udphdrsize], Maxudp);
6037dd7cddfSDavid du Colombier 	rrfree(m.qd);
6046aaebd7dSDavid du Colombier 	memset(&m, 0, sizeof m);		/* cause trouble */
6053e12c5d1SDavid du Colombier 	return len;
6063e12c5d1SDavid du Colombier }
6073e12c5d1SDavid du Colombier 
608a41547ffSDavid du Colombier void
6097dd7cddfSDavid du Colombier freeanswers(DNSmsg *mp)
6103e12c5d1SDavid du Colombier {
6117dd7cddfSDavid du Colombier 	rrfreelist(mp->qd);
6127dd7cddfSDavid du Colombier 	rrfreelist(mp->an);
6137dd7cddfSDavid du Colombier 	rrfreelist(mp->ns);
6147dd7cddfSDavid du Colombier 	rrfreelist(mp->ar);
6154f8f669cSDavid du Colombier 	mp->qd = mp->an = mp->ns = mp->ar = nil;
6167dd7cddfSDavid du Colombier }
6177dd7cddfSDavid du Colombier 
618b8b25780SDavid du Colombier /* timed read of reply.  sets srcip */
6193cbadd90SDavid du Colombier static int
620b8b25780SDavid du Colombier readnet(Query *qp, int medium, uchar *ibuf, uvlong endms, uchar **replyp,
6213cbadd90SDavid du Colombier 	uchar *srcip)
6223cbadd90SDavid du Colombier {
6233cbadd90SDavid du Colombier 	int len, fd;
624a41547ffSDavid du Colombier 	long ms;
625a41547ffSDavid du Colombier 	vlong startns = nsec();
6263cbadd90SDavid du Colombier 	uchar *reply;
6273cbadd90SDavid du Colombier 	uchar lenbuf[2];
6283cbadd90SDavid du Colombier 
6293cbadd90SDavid du Colombier 	len = -1;			/* pessimism */
630b8b25780SDavid du Colombier 	ms = endms - NS2MS(startns);
631b8b25780SDavid du Colombier 	if (ms <= 0)
632b8b25780SDavid du Colombier 		return -1;		/* taking too long */
633b8b25780SDavid du Colombier 
634b8b25780SDavid du Colombier 	reply = ibuf;
6354e7b9544SDavid du Colombier 	memset(srcip, 0, IPaddrlen);
636b8b25780SDavid du Colombier 	alarm(ms);
637a41547ffSDavid du Colombier 	if (medium == Udp)
6384e7b9544SDavid du Colombier 		if (qp->udpfd <= 0)
6394e7b9544SDavid du Colombier 			dnslog("readnet: qp->udpfd closed");
6404e7b9544SDavid du Colombier 		else {
641f27a9a5aSDavid du Colombier 			len = read(qp->udpfd, ibuf, Udphdrsize+Maxudpin);
642a41547ffSDavid du Colombier 			alarm(0);
643a41547ffSDavid du Colombier 			notestats(startns, len < 0, qp->type);
6444e7b9544SDavid du Colombier 			if (len >= IPaddrlen)
6454e7b9544SDavid du Colombier 				memmove(srcip, ibuf, IPaddrlen);
646f27a9a5aSDavid du Colombier 			if (len >= Udphdrsize) {
647f27a9a5aSDavid du Colombier 				len   -= Udphdrsize;
648f27a9a5aSDavid du Colombier 				reply += Udphdrsize;
6494e7b9544SDavid du Colombier 			}
6504e7b9544SDavid du Colombier 		}
651a41547ffSDavid du Colombier 	else {
6523cbadd90SDavid du Colombier 		if (!qp->tcpset)
6533cbadd90SDavid du Colombier 			dnslog("readnet: tcp params not set");
6543cbadd90SDavid du Colombier 		fd = qp->tcpfd;
6553cbadd90SDavid du Colombier 		if (fd <= 0)
6563cbadd90SDavid du Colombier 			dnslog("readnet: %s: tcp fd unset for dest %I",
6573cbadd90SDavid du Colombier 				qp->dp->name, qp->tcpip);
6583cbadd90SDavid du Colombier 		else if (readn(fd, lenbuf, 2) != 2) {
6593cbadd90SDavid du Colombier 			dnslog("readnet: short read of tcp size from %I",
6603cbadd90SDavid du Colombier 				qp->tcpip);
661a41547ffSDavid du Colombier 			/* probably a time-out */
662a41547ffSDavid du Colombier 			notestats(startns, 1, qp->type);
6633cbadd90SDavid du Colombier 		} else {
6643cbadd90SDavid du Colombier 			len = lenbuf[0]<<8 | lenbuf[1];
6653cbadd90SDavid du Colombier 			if (readn(fd, ibuf, len) != len) {
6663cbadd90SDavid du Colombier 				dnslog("readnet: short read of tcp data from %I",
6673cbadd90SDavid du Colombier 					qp->tcpip);
668a41547ffSDavid du Colombier 				/* probably a time-out */
669a41547ffSDavid du Colombier 				notestats(startns, 1, qp->type);
6703cbadd90SDavid du Colombier 				len = -1;
6713cbadd90SDavid du Colombier 			}
6723cbadd90SDavid du Colombier 		}
6733cbadd90SDavid du Colombier 		memmove(srcip, qp->tcpip, IPaddrlen);
6743cbadd90SDavid du Colombier 	}
675b8b25780SDavid du Colombier 	alarm(0);
6763cbadd90SDavid du Colombier 	*replyp = reply;
6773cbadd90SDavid du Colombier 	return len;
6783cbadd90SDavid du Colombier }
6793cbadd90SDavid du Colombier 
6807dd7cddfSDavid du Colombier /*
6813cbadd90SDavid du Colombier  *  read replies to a request and remember the rrs in the answer(s).
6823cbadd90SDavid du Colombier  *  ignore any of the wrong type.
683b8b25780SDavid du Colombier  *  wait at most until endms.
6847dd7cddfSDavid du Colombier  */
6857dd7cddfSDavid du Colombier static int
6863cbadd90SDavid du Colombier readreply(Query *qp, int medium, ushort req, uchar *ibuf, DNSmsg *mp,
687b8b25780SDavid du Colombier 	uvlong endms)
6887dd7cddfSDavid du Colombier {
689b8b25780SDavid du Colombier 	int len;
6907dd7cddfSDavid du Colombier 	char *err;
691a41547ffSDavid du Colombier 	char tbuf[32];
6923cbadd90SDavid du Colombier 	uchar *reply;
6933cbadd90SDavid du Colombier 	uchar srcip[IPaddrlen];
6943e12c5d1SDavid du Colombier 	RR *rp;
6957dd7cddfSDavid du Colombier 
6963cbadd90SDavid du Colombier 	queryck(qp);
6973cbadd90SDavid du Colombier 	memset(mp, 0, sizeof *mp);
698a41547ffSDavid du Colombier 	memset(srcip, 0, sizeof srcip);
699a41547ffSDavid du Colombier 	if (0)
700a41547ffSDavid du Colombier 		len = -1;
701b8b25780SDavid du Colombier 	for (; timems() < endms &&
702b8b25780SDavid du Colombier 	    (len = readnet(qp, medium, ibuf, endms, &reply, srcip)) >= 0;
7033cbadd90SDavid du Colombier 	    freeanswers(mp)){
7047dd7cddfSDavid du Colombier 		/* convert into internal format  */
7053cbadd90SDavid du Colombier 		memset(mp, 0, sizeof *mp);
7063cbadd90SDavid du Colombier 		err = convM2DNS(reply, len, mp, nil);
707d6d99297SDavid du Colombier 		if (mp->flags & Ftrunc) {
708a41547ffSDavid du Colombier 			free(err);
709a41547ffSDavid du Colombier 			freeanswers(mp);
710a41547ffSDavid du Colombier 			/* notify our caller to retry the query via tcp. */
7113cbadd90SDavid du Colombier 			return -1;
7123cbadd90SDavid du Colombier 		} else if(err){
7133cbadd90SDavid du Colombier 			dnslog("readreply: %s: input err, len %d: %s: %I",
7143cbadd90SDavid du Colombier 				qp->dp->name, len, err, srcip);
7153cbadd90SDavid du Colombier 			free(err);
7167dd7cddfSDavid du Colombier 			continue;
7177dd7cddfSDavid du Colombier 		}
7187dd7cddfSDavid du Colombier 		if(debug)
7193cbadd90SDavid du Colombier 			logreply(qp->req->id, srcip, mp);
7207dd7cddfSDavid du Colombier 
7217dd7cddfSDavid du Colombier 		/* answering the right question? */
7223cbadd90SDavid du Colombier 		if(mp->id != req)
7233cbadd90SDavid du Colombier 			dnslog("%d: id %d instead of %d: %I", qp->req->id,
7243cbadd90SDavid du Colombier 				mp->id, req, srcip);
7253cbadd90SDavid du Colombier 		else if(mp->qd == 0)
7263cbadd90SDavid du Colombier 			dnslog("%d: no question RR: %I", qp->req->id, srcip);
7273cbadd90SDavid du Colombier 		else if(mp->qd->owner != qp->dp)
7283cbadd90SDavid du Colombier 			dnslog("%d: owner %s instead of %s: %I", qp->req->id,
7293cbadd90SDavid du Colombier 				mp->qd->owner->name, qp->dp->name, srcip);
7303cbadd90SDavid du Colombier 		else if(mp->qd->type != qp->type)
7313cbadd90SDavid du Colombier 			dnslog("%d: qp->type %d instead of %d: %I",
7323cbadd90SDavid du Colombier 				qp->req->id, mp->qd->type, qp->type, srcip);
7333cbadd90SDavid du Colombier 		else {
7347dd7cddfSDavid du Colombier 			/* remember what request this is in answer to */
7357dd7cddfSDavid du Colombier 			for(rp = mp->an; rp; rp = rp->next)
7363cbadd90SDavid du Colombier 				rp->query = qp->type;
737b8b25780SDavid du Colombier 			return 0;
7387dd7cddfSDavid du Colombier 		}
7397dd7cddfSDavid du Colombier 	}
740b8b25780SDavid du Colombier 	if (timems() >= endms) {
741a41547ffSDavid du Colombier 		;				/* query expired */
742a41547ffSDavid du Colombier 	} else if (0) {
743a41547ffSDavid du Colombier 		/* this happens routinely when a read times out */
744a41547ffSDavid du Colombier 		dnslog("readreply: %s type %s: ns %I read error or eof "
745a41547ffSDavid du Colombier 			"(returned %d): %r", qp->dp->name, rrname(qp->type,
746a41547ffSDavid du Colombier 			tbuf, sizeof tbuf), srcip, len);
747a41547ffSDavid du Colombier 		if (medium == Udp)
748a41547ffSDavid du Colombier 			for (rp = qp->nsrp; rp != nil; rp = rp->next)
749a41547ffSDavid du Colombier 				if (rp->type == Tns)
750a41547ffSDavid du Colombier 					dnslog("readreply: %s: query sent to "
751a41547ffSDavid du Colombier 						"ns %s", qp->dp->name,
752a41547ffSDavid du Colombier 						rp->host->name);
753a41547ffSDavid du Colombier 	}
7543cbadd90SDavid du Colombier 	return -1;
7553cbadd90SDavid du Colombier }
7567dd7cddfSDavid du Colombier 
7577dd7cddfSDavid du Colombier /*
7587dd7cddfSDavid du Colombier  *	return non-0 if first list includes second list
7597dd7cddfSDavid du Colombier  */
7607dd7cddfSDavid du Colombier int
7617dd7cddfSDavid du Colombier contains(RR *rp1, RR *rp2)
7627dd7cddfSDavid du Colombier {
7637dd7cddfSDavid du Colombier 	RR *trp1, *trp2;
7647dd7cddfSDavid du Colombier 
7657dd7cddfSDavid du Colombier 	for(trp2 = rp2; trp2; trp2 = trp2->next){
7663cbadd90SDavid du Colombier 		for(trp1 = rp1; trp1; trp1 = trp1->next)
7677dd7cddfSDavid du Colombier 			if(trp1->type == trp2->type)
7687dd7cddfSDavid du Colombier 			if(trp1->host == trp2->host)
7697dd7cddfSDavid du Colombier 			if(trp1->owner == trp2->owner)
7707dd7cddfSDavid du Colombier 				break;
7714f8f669cSDavid du Colombier 		if(trp1 == nil)
7727dd7cddfSDavid du Colombier 			return 0;
7737dd7cddfSDavid du Colombier 	}
7747dd7cddfSDavid du Colombier 	return 1;
7757dd7cddfSDavid du Colombier }
7767dd7cddfSDavid du Colombier 
7777dd7cddfSDavid du Colombier 
7786b6b9ac8SDavid du Colombier /*
7796b6b9ac8SDavid du Colombier  *  return multicast version if any
7806b6b9ac8SDavid du Colombier  */
7816b6b9ac8SDavid du Colombier int
7826b6b9ac8SDavid du Colombier ipisbm(uchar *ip)
7836b6b9ac8SDavid du Colombier {
7846b6b9ac8SDavid du Colombier 	if(isv4(ip)){
7854f8f669cSDavid du Colombier 		if (ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0 ||
7864f8f669cSDavid du Colombier 		    ipcmp(ip, IPv4bcast) == 0)
7876b6b9ac8SDavid du Colombier 			return 4;
7884f8f669cSDavid du Colombier 	} else
7896b6b9ac8SDavid du Colombier 		if(ip[0] == 0xff)
7906b6b9ac8SDavid du Colombier 			return 6;
79128a8a86bSDavid du Colombier 	return 0;
79228a8a86bSDavid du Colombier }
79328a8a86bSDavid du Colombier 
7947dd7cddfSDavid du Colombier /*
7957dd7cddfSDavid du Colombier  *  Get next server address
7967dd7cddfSDavid du Colombier  */
7977dd7cddfSDavid du Colombier static int
7983cbadd90SDavid du Colombier serveraddrs(Query *qp, int nd, int depth)
7997dd7cddfSDavid du Colombier {
8007dd7cddfSDavid du Colombier 	RR *rp, *arp, *trp;
8017dd7cddfSDavid du Colombier 	Dest *cur;
8027dd7cddfSDavid du Colombier 
8037dd7cddfSDavid du Colombier 	if(nd >= Maxdest)
8047dd7cddfSDavid du Colombier 		return 0;
8057dd7cddfSDavid du Colombier 
8067dd7cddfSDavid du Colombier 	/*
8077dd7cddfSDavid du Colombier 	 *  look for a server whose address we already know.
8087dd7cddfSDavid du Colombier 	 *  if we find one, mark it so we ignore this on
8097dd7cddfSDavid du Colombier 	 *  subsequent passes.
8107dd7cddfSDavid du Colombier 	 */
8117dd7cddfSDavid du Colombier 	arp = 0;
8123cbadd90SDavid du Colombier 	for(rp = qp->nsrp; rp; rp = rp->next){
81334f77ae3SDavid du Colombier 		assert(rp->magic == RRmagic);
8147dd7cddfSDavid du Colombier 		if(rp->marker)
8157dd7cddfSDavid du Colombier 			continue;
8167dd7cddfSDavid du Colombier 		arp = rrlookup(rp->host, Ta, NOneg);
817d9924332SDavid du Colombier 		if(arp == nil)
818d9924332SDavid du Colombier 			arp = rrlookup(rp->host, Taaaa, NOneg);
8197dd7cddfSDavid du Colombier 		if(arp){
8207dd7cddfSDavid du Colombier 			rp->marker = 1;
8217dd7cddfSDavid du Colombier 			break;
8227dd7cddfSDavid du Colombier 		}
8237dd7cddfSDavid du Colombier 		arp = dblookup(rp->host->name, Cin, Ta, 0, 0);
824d9924332SDavid du Colombier 		if(arp == nil)
825d9924332SDavid du Colombier 			arp = dblookup(rp->host->name, Cin, Taaaa, 0, 0);
8267dd7cddfSDavid du Colombier 		if(arp){
8277dd7cddfSDavid du Colombier 			rp->marker = 1;
8287dd7cddfSDavid du Colombier 			break;
8297dd7cddfSDavid du Colombier 		}
8307dd7cddfSDavid du Colombier 	}
8317dd7cddfSDavid du Colombier 
8327dd7cddfSDavid du Colombier 	/*
8337dd7cddfSDavid du Colombier 	 *  if the cache and database lookup didn't find any new
8347dd7cddfSDavid du Colombier 	 *  server addresses, try resolving one via the network.
8357dd7cddfSDavid du Colombier 	 *  Mark any we try to resolve so we don't try a second time.
8367dd7cddfSDavid du Colombier 	 */
8374f8f669cSDavid du Colombier 	if(arp == 0)
8383cbadd90SDavid du Colombier 		for(rp = qp->nsrp; rp; rp = rp->next){
8397dd7cddfSDavid du Colombier 			if(rp->marker)
8407dd7cddfSDavid du Colombier 				continue;
8417dd7cddfSDavid du Colombier 			rp->marker = 1;
8427dd7cddfSDavid du Colombier 
8437dd7cddfSDavid du Colombier 			/*
8447dd7cddfSDavid du Colombier 			 *  avoid loops looking up a server under itself
8457dd7cddfSDavid du Colombier 			 */
8467dd7cddfSDavid du Colombier 			if(subsume(rp->owner->name, rp->host->name))
8477dd7cddfSDavid du Colombier 				continue;
8487dd7cddfSDavid du Colombier 
8493cbadd90SDavid du Colombier 			arp = dnresolve(rp->host->name, Cin, Ta, qp->req, 0,
8504f8f669cSDavid du Colombier 				depth+1, Recurse, 1, 0);
851d9924332SDavid du Colombier 			if(arp == nil)
852d9924332SDavid du Colombier 				arp = dnresolve(rp->host->name, Cin, Taaaa,
853d9924332SDavid du Colombier 					qp->req, 0, depth+1, Recurse, 1, 0);
854530fef66SDavid du Colombier 			lock(&dnlock);
8557dd7cddfSDavid du Colombier 			rrfreelist(rrremneg(&arp));
856530fef66SDavid du Colombier 			unlock(&dnlock);
8577dd7cddfSDavid du Colombier 			if(arp)
8587dd7cddfSDavid du Colombier 				break;
8597dd7cddfSDavid du Colombier 		}
8607dd7cddfSDavid du Colombier 
8617dd7cddfSDavid du Colombier 	/* use any addresses that we found */
8623cbadd90SDavid du Colombier 	for(trp = arp; trp && nd < Maxdest; trp = trp->next){
8633cbadd90SDavid du Colombier 		cur = &qp->dest[nd];
8647dd7cddfSDavid du Colombier 		parseip(cur->a, trp->ip->name);
8654f8f669cSDavid du Colombier 		/*
8664f8f669cSDavid du Colombier 		 * straddling servers can reject all nameservers if they are all
8674f8f669cSDavid du Colombier 		 * inside, so be sure to list at least one outside ns at
8684f8f669cSDavid du Colombier 		 * the end of the ns list in /lib/ndb for `dom='.
8694f8f669cSDavid du Colombier 		 */
8704f8f669cSDavid du Colombier 		if (ipisbm(cur->a) ||
8713cbadd90SDavid du Colombier 		    cfg.straddle && !insideaddr(qp->dp->name) && insidens(cur->a))
8726b6b9ac8SDavid du Colombier 			continue;
8737dd7cddfSDavid du Colombier 		cur->nx = 0;
8747dd7cddfSDavid du Colombier 		cur->s = trp->owner;
8757dd7cddfSDavid du Colombier 		cur->code = Rtimeout;
8766b6b9ac8SDavid du Colombier 		nd++;
8777dd7cddfSDavid du Colombier 	}
8787dd7cddfSDavid du Colombier 	rrfreelist(arp);
8797dd7cddfSDavid du Colombier 	return nd;
8807dd7cddfSDavid du Colombier }
8817dd7cddfSDavid du Colombier 
8827dd7cddfSDavid du Colombier /*
8837dd7cddfSDavid du Colombier  *  cache negative responses
8847dd7cddfSDavid du Colombier  */
8857dd7cddfSDavid du Colombier static void
8867dd7cddfSDavid du Colombier cacheneg(DN *dp, int type, int rcode, RR *soarr)
8877dd7cddfSDavid du Colombier {
8887dd7cddfSDavid du Colombier 	RR *rp;
8897dd7cddfSDavid du Colombier 	DN *soaowner;
8909a747e4fSDavid du Colombier 	ulong ttl;
8917dd7cddfSDavid du Colombier 
892f46c709fSDavid du Colombier 	stats.negcached++;
893f46c709fSDavid du Colombier 
8944f8f669cSDavid du Colombier 	/* no cache time specified, don't make anything up */
8957dd7cddfSDavid du Colombier 	if(soarr != nil){
8967dd7cddfSDavid du Colombier 		if(soarr->next != nil){
8977dd7cddfSDavid du Colombier 			rrfreelist(soarr->next);
8987dd7cddfSDavid du Colombier 			soarr->next = nil;
8997dd7cddfSDavid du Colombier 		}
9007dd7cddfSDavid du Colombier 		soaowner = soarr->owner;
9017dd7cddfSDavid du Colombier 	} else
9027dd7cddfSDavid du Colombier 		soaowner = nil;
9037dd7cddfSDavid du Colombier 
9049a747e4fSDavid du Colombier 	/* the attach can cause soarr to be freed so mine it now */
9059a747e4fSDavid du Colombier 	if(soarr != nil && soarr->soa != nil)
9069a747e4fSDavid du Colombier 		ttl = soarr->soa->minttl+now;
9079a747e4fSDavid du Colombier 	else
9089a747e4fSDavid du Colombier 		ttl = 5*Min;
9099a747e4fSDavid du Colombier 
9107dd7cddfSDavid du Colombier 	/* add soa and negative RR to the database */
9116dc4800dSDavid du Colombier 	rrattach(soarr, Authoritative);
9127dd7cddfSDavid du Colombier 
9137dd7cddfSDavid du Colombier 	rp = rralloc(type);
9147dd7cddfSDavid du Colombier 	rp->owner = dp;
9157dd7cddfSDavid du Colombier 	rp->negative = 1;
9167dd7cddfSDavid du Colombier 	rp->negsoaowner = soaowner;
9177dd7cddfSDavid du Colombier 	rp->negrcode = rcode;
9189a747e4fSDavid du Colombier 	rp->ttl = ttl;
9196dc4800dSDavid du Colombier 	rrattach(rp, Authoritative);
9207dd7cddfSDavid du Colombier }
9217dd7cddfSDavid du Colombier 
9224f8f669cSDavid du Colombier static int
9234f8f669cSDavid du Colombier setdestoutns(Dest *p, int n)
9244f8f669cSDavid du Colombier {
9254f8f669cSDavid du Colombier 	uchar *outns = outsidens(n);
9264f8f669cSDavid du Colombier 
9273cbadd90SDavid du Colombier 	destck(p);
9283cbadd90SDavid du Colombier 	destinit(p);
9294f8f669cSDavid du Colombier 	if (outns == nil) {
9304f8f669cSDavid du Colombier 		if (n == 0)
9314f8f669cSDavid du Colombier 			dnslog("[%d] no outside-ns in ndb", getpid());
9324f8f669cSDavid du Colombier 		return -1;
9334f8f669cSDavid du Colombier 	}
9344f8f669cSDavid du Colombier 	memmove(p->a, outns, sizeof p->a);
9354f8f669cSDavid du Colombier 	p->s = dnlookup("outside-ns-ips", Cin, 1);
9364f8f669cSDavid du Colombier 	return 0;
9374f8f669cSDavid du Colombier }
9384f8f669cSDavid du Colombier 
9397dd7cddfSDavid du Colombier /*
9403cbadd90SDavid du Colombier  * issue query via UDP or TCP as appropriate.
9413cbadd90SDavid du Colombier  * for TCP, returns with qp->tcpip set from udppkt header.
9427dd7cddfSDavid du Colombier  */
9437dd7cddfSDavid du Colombier static int
9443cbadd90SDavid du Colombier mydnsquery(Query *qp, int medium, uchar *udppkt, int len)
9457dd7cddfSDavid du Colombier {
946a41547ffSDavid du Colombier 	int rv = -1, nfd;
9473cbadd90SDavid du Colombier 	char *domain;
948*cc499a30SDavid du Colombier 	char conndir[40], net[40];
949a41547ffSDavid du Colombier 	uchar belen[2];
9503cbadd90SDavid du Colombier 	NetConnInfo *nci;
9514f8f669cSDavid du Colombier 
9523cbadd90SDavid du Colombier 	queryck(qp);
9534e153993SDavid du Colombier 	domain = smprint("%I", udppkt);
9544e153993SDavid du Colombier 	if (myaddr(domain)) {
9554e153993SDavid du Colombier 		dnslog("mydnsquery: trying to send to myself (%s); bzzzt",
9564e153993SDavid du Colombier 			domain);
9574e153993SDavid du Colombier 		free(domain);
9584e153993SDavid du Colombier 		return rv;
9594e153993SDavid du Colombier 	}
9604e153993SDavid du Colombier 
9613cbadd90SDavid du Colombier 	switch (medium) {
9623cbadd90SDavid du Colombier 	case Udp:
9634e153993SDavid du Colombier 		free(domain);
964a41547ffSDavid du Colombier 		nfd = dup(qp->udpfd, -1);
965a41547ffSDavid du Colombier 		if (nfd < 0) {
966a41547ffSDavid du Colombier 			warning("mydnsquery: qp->udpfd %d: %r", qp->udpfd);
967a41547ffSDavid du Colombier 			close(qp->udpfd);	/* ensure it's closed */
968a41547ffSDavid du Colombier 			qp->udpfd = -1;		/* poison it */
969a41547ffSDavid du Colombier 			return rv;
970a41547ffSDavid du Colombier 		}
971a41547ffSDavid du Colombier 		close(nfd);
972a41547ffSDavid du Colombier 
9734e7b9544SDavid du Colombier 		if (qp->udpfd <= 0)
974a41547ffSDavid du Colombier 			dnslog("mydnsquery: qp->udpfd %d closed", qp->udpfd);
9754e7b9544SDavid du Colombier 		else {
976f27a9a5aSDavid du Colombier 			if (write(qp->udpfd, udppkt, len+Udphdrsize) !=
977f27a9a5aSDavid du Colombier 			    len+Udphdrsize)
978a41547ffSDavid du Colombier 				warning("sending udp msg: %r");
979a41547ffSDavid du Colombier 			else {
980a41547ffSDavid du Colombier 				stats.qsent++;
9813cbadd90SDavid du Colombier 				rv = 0;
9824e7b9544SDavid du Colombier 			}
983a41547ffSDavid du Colombier 		}
9843cbadd90SDavid du Colombier 		break;
9853cbadd90SDavid du Colombier 	case Tcp:
9863cbadd90SDavid du Colombier 		/* send via TCP & keep fd around for reply */
987*cc499a30SDavid du Colombier 		snprint(net, sizeof net, "%s/tcp",
988*cc499a30SDavid du Colombier 			(mntpt[0] != '\0'? mntpt: "/net"));
9893cbadd90SDavid du Colombier 		alarm(10*1000);
990*cc499a30SDavid du Colombier 		qp->tcpfd = rv = dial(netmkaddr(domain, net, "dns"), nil,
9913cbadd90SDavid du Colombier 			conndir, &qp->tcpctlfd);
9923cbadd90SDavid du Colombier 		alarm(0);
9933cbadd90SDavid du Colombier 		if (qp->tcpfd < 0) {
9943cbadd90SDavid du Colombier 			dnslog("can't dial tcp!%s!dns: %r", domain);
995a41547ffSDavid du Colombier 			free(domain);
996a41547ffSDavid du Colombier 			break;
997a41547ffSDavid du Colombier 		}
998a41547ffSDavid du Colombier 		free(domain);
9993cbadd90SDavid du Colombier 		nci = getnetconninfo(conndir, qp->tcpfd);
10003cbadd90SDavid du Colombier 		if (nci) {
10013cbadd90SDavid du Colombier 			parseip(qp->tcpip, nci->rsys);
10023cbadd90SDavid du Colombier 			freenetconninfo(nci);
10033cbadd90SDavid du Colombier 		} else
10043cbadd90SDavid du Colombier 			dnslog("mydnsquery: getnetconninfo failed");
10053cbadd90SDavid du Colombier 		qp->tcpset = 1;
10063e12c5d1SDavid du Colombier 
10073cbadd90SDavid du Colombier 		belen[0] = len >> 8;
10083cbadd90SDavid du Colombier 		belen[1] = len;
10093cbadd90SDavid du Colombier 		if (write(qp->tcpfd, belen, 2) != 2 ||
1010f27a9a5aSDavid du Colombier 		    write(qp->tcpfd, udppkt + Udphdrsize, len) != len)
1011a41547ffSDavid du Colombier 			warning("sending tcp msg: %r");
10123cbadd90SDavid du Colombier 		break;
10133cbadd90SDavid du Colombier 	default:
10143cbadd90SDavid du Colombier 		sysfatal("mydnsquery: bad medium");
10153cbadd90SDavid du Colombier 	}
10163cbadd90SDavid du Colombier 	return rv;
10173cbadd90SDavid du Colombier }
10187dd7cddfSDavid du Colombier 
10193e12c5d1SDavid du Colombier /*
10203cbadd90SDavid du Colombier  * send query to all UDP destinations or one TCP destination,
10213cbadd90SDavid du Colombier  * taken from obuf (udp packet) header
10223e12c5d1SDavid du Colombier  */
10233cbadd90SDavid du Colombier static int
10243cbadd90SDavid du Colombier xmitquery(Query *qp, int medium, int depth, uchar *obuf, int inns, int len)
10253cbadd90SDavid du Colombier {
10263cbadd90SDavid du Colombier 	int j, n;
10273cbadd90SDavid du Colombier 	char buf[32];
10283cbadd90SDavid du Colombier 	Dest *p;
10293e12c5d1SDavid du Colombier 
10303cbadd90SDavid du Colombier 	queryck(qp);
1031b8b25780SDavid du Colombier 	if(timems() >= qp->req->aborttime)
10323cbadd90SDavid du Colombier 		return -1;
10333cbadd90SDavid du Colombier 
10343cbadd90SDavid du Colombier 	/*
10353cbadd90SDavid du Colombier 	 * get a nameserver address if we need one.
10363cbadd90SDavid du Colombier 	 * serveraddrs populates qp->dest.
10373cbadd90SDavid du Colombier 	 */
10383cbadd90SDavid du Colombier 	p = qp->dest;
10393cbadd90SDavid du Colombier 	destck(p);
10403cbadd90SDavid du Colombier 	if (qp->ndest < 0 || qp->ndest > Maxdest)
10413cbadd90SDavid du Colombier 		dnslog("qp->ndest %d out of range", qp->ndest);
10423468a491SDavid du Colombier 	if (qp->ndest > qp->curdest - p) {
10433468a491SDavid du Colombier 		j = serveraddrs(qp, qp->curdest - p, depth);
10443468a491SDavid du Colombier 		if (j < 0 || j >= Maxdest) {
10453468a491SDavid du Colombier 			dnslog("serveraddrs() result %d out of range", j);
10463468a491SDavid du Colombier 			abort();
10473468a491SDavid du Colombier 		}
10483468a491SDavid du Colombier 		qp->curdest = &qp->dest[j];
10493468a491SDavid du Colombier 	}
10503cbadd90SDavid du Colombier 	destck(qp->curdest);
10517dd7cddfSDavid du Colombier 
10527dd7cddfSDavid du Colombier 	/* no servers, punt */
10533468a491SDavid du Colombier 	if (qp->ndest == 0)
10544f8f669cSDavid du Colombier 		if (cfg.straddle && cfg.inside) {
10553cbadd90SDavid du Colombier 			/* get ips of "outside-ns-ips" */
10563cbadd90SDavid du Colombier 			p = qp->curdest = qp->dest;
10573cbadd90SDavid du Colombier 			for(n = 0; n < Maxdest; n++, qp->curdest++)
10583cbadd90SDavid du Colombier 				if (setdestoutns(qp->curdest, n) < 0)
10597dd7cddfSDavid du Colombier 					break;
10604f8f669cSDavid du Colombier 		} else {
10614e7b9544SDavid du Colombier 			/* it's probably just a bogus domain, don't log it */
10624e7b9544SDavid du Colombier 			// dnslog("xmitquery: %s: no nameservers", qp->dp->name);
10633cbadd90SDavid du Colombier 			return -1;
10644f8f669cSDavid du Colombier 		}
10657dd7cddfSDavid du Colombier 
10663cbadd90SDavid du Colombier 	/* send to first 'qp->ndest' destinations */
10677dd7cddfSDavid du Colombier 	j = 0;
10683cbadd90SDavid du Colombier 	if (medium == Tcp) {
10693cbadd90SDavid du Colombier 		j++;
10703cbadd90SDavid du Colombier 		queryck(qp);
10713cbadd90SDavid du Colombier 		assert(qp->dp);
10723cbadd90SDavid du Colombier 		procsetname("tcp %sside query for %s %s", (inns? "in": "out"),
10733cbadd90SDavid du Colombier 			qp->dp->name, rrname(qp->type, buf, sizeof buf));
10743cbadd90SDavid du Colombier 		mydnsquery(qp, medium, obuf, len); /* sets qp->tcpip from obuf */
10753cbadd90SDavid du Colombier 		if(debug)
10763cbadd90SDavid du Colombier 			logsend(qp->req->id, depth, qp->tcpip, "", qp->dp->name,
10773cbadd90SDavid du Colombier 				qp->type);
10783cbadd90SDavid du Colombier 	} else
10793cbadd90SDavid du Colombier 		for(; p < &qp->dest[qp->ndest] && p < qp->curdest; p++){
10807dd7cddfSDavid du Colombier 			/* skip destinations we've finished with */
10817dd7cddfSDavid du Colombier 			if(p->nx >= Maxtrans)
10827dd7cddfSDavid du Colombier 				continue;
10837dd7cddfSDavid du Colombier 
10847dd7cddfSDavid du Colombier 			j++;
10857dd7cddfSDavid du Colombier 
10867dd7cddfSDavid du Colombier 			/* exponential backoff of requests */
10873cbadd90SDavid du Colombier 			if((1<<p->nx) > qp->ndest)
10887dd7cddfSDavid du Colombier 				continue;
10897dd7cddfSDavid du Colombier 
10903cbadd90SDavid du Colombier 			procsetname("udp %sside query to %I/%s %s %s",
10913cbadd90SDavid du Colombier 				(inns? "in": "out"), p->a, p->s->name,
10923cbadd90SDavid du Colombier 				qp->dp->name, rrname(qp->type, buf, sizeof buf));
1093219b2ee8SDavid du Colombier 			if(debug)
10943cbadd90SDavid du Colombier 				logsend(qp->req->id, depth, p->a, p->s->name,
10953cbadd90SDavid du Colombier 					qp->dp->name, qp->type);
10964f8f669cSDavid du Colombier 
10973cbadd90SDavid du Colombier 			/* fill in UDP destination addr & send it */
10983cbadd90SDavid du Colombier 			memmove(obuf, p->a, sizeof p->a);
10993cbadd90SDavid du Colombier 			mydnsquery(qp, medium, obuf, len);
11007dd7cddfSDavid du Colombier 			p->nx++;
11013e12c5d1SDavid du Colombier 		}
11023cbadd90SDavid du Colombier 	if(j == 0) {
11033cbadd90SDavid du Colombier 		// dnslog("xmitquery: %s: no destinations left", qp->dp->name);
11043cbadd90SDavid du Colombier 		return -1;
11053cbadd90SDavid du Colombier 	}
11063cbadd90SDavid du Colombier 	return 0;
11073cbadd90SDavid du Colombier }
11083e12c5d1SDavid du Colombier 
1109f46c709fSDavid du Colombier static int lckindex[Maxlcks] = {
1110f46c709fSDavid du Colombier 	0,			/* all others map here */
1111f46c709fSDavid du Colombier 	Ta,
1112f46c709fSDavid du Colombier 	Tns,
1113f46c709fSDavid du Colombier 	Tcname,
1114f46c709fSDavid du Colombier 	Tsoa,
1115f46c709fSDavid du Colombier 	Tptr,
1116f46c709fSDavid du Colombier 	Tmx,
1117f46c709fSDavid du Colombier 	Ttxt,
1118f46c709fSDavid du Colombier 	Taaaa,
1119f46c709fSDavid du Colombier };
1120f46c709fSDavid du Colombier 
1121f46c709fSDavid du Colombier static int
1122f46c709fSDavid du Colombier qtype2lck(int qtype)		/* map query type to querylck index */
1123f46c709fSDavid du Colombier {
1124f46c709fSDavid du Colombier 	int i;
1125f46c709fSDavid du Colombier 
1126f46c709fSDavid du Colombier 	for (i = 1; i < nelem(lckindex); i++)
1127f46c709fSDavid du Colombier 		if (lckindex[i] == qtype)
1128f46c709fSDavid du Colombier 			return i;
1129f46c709fSDavid du Colombier 	return 0;
1130f46c709fSDavid du Colombier }
1131f46c709fSDavid du Colombier 
11320319257bSDavid du Colombier /* is mp a cachable negative response (with Rname set)? */
11330319257bSDavid du Colombier static int
11340319257bSDavid du Colombier isnegrname(DNSmsg *mp)
11350319257bSDavid du Colombier {
11360319257bSDavid du Colombier 	/* TODO: could add || cfg.justforw to RHS of && */
11370319257bSDavid du Colombier 	return mp->an == nil && (mp->flags & Rmask) == Rname;
11380319257bSDavid du Colombier }
11390319257bSDavid du Colombier 
11403cbadd90SDavid du Colombier static int
11413cbadd90SDavid du Colombier procansw(Query *qp, DNSmsg *mp, uchar *srcip, int depth, Dest *p)
11423cbadd90SDavid du Colombier {
11433cbadd90SDavid du Colombier 	int rv;
11444e5f5f32SDavid du Colombier //	int lcktype;
11453cbadd90SDavid du Colombier 	char buf[32];
11463cbadd90SDavid du Colombier 	DN *ndp;
1147c73252aeSDavid du Colombier 	Query *nqp;
11483cbadd90SDavid du Colombier 	RR *tp, *soarr;
11497dd7cddfSDavid du Colombier 
1150f46c709fSDavid du Colombier 	if (mp->an == nil)
1151f46c709fSDavid du Colombier 		stats.negans++;
1152f46c709fSDavid du Colombier 
115359cc4ca5SDavid du Colombier 	/* ignore any error replies */
11543cbadd90SDavid du Colombier 	if((mp->flags & Rmask) == Rserver){
11550319257bSDavid du Colombier 		stats.negserver++;
1156a41547ffSDavid du Colombier 		freeanswers(mp);
11573cbadd90SDavid du Colombier 		if(p != qp->curdest)
11587dd7cddfSDavid du Colombier 			p->code = Rserver;
11593cbadd90SDavid du Colombier 		return -1;
11603e12c5d1SDavid du Colombier 	}
11613e12c5d1SDavid du Colombier 
116259cc4ca5SDavid du Colombier 	/* ignore any bad delegations */
11633cbadd90SDavid du Colombier 	if(mp->ns && baddelegation(mp->ns, qp->nsrp, srcip)){
11640319257bSDavid du Colombier 		stats.negbaddeleg++;
11653cbadd90SDavid du Colombier 		if(mp->an == nil){
11660319257bSDavid du Colombier 			stats.negbdnoans++;
1167a41547ffSDavid du Colombier 			freeanswers(mp);
11683cbadd90SDavid du Colombier 			if(p != qp->curdest)
116959cc4ca5SDavid du Colombier 				p->code = Rserver;
11703cbadd90SDavid du Colombier 			return -1;
117159cc4ca5SDavid du Colombier 		}
1172a41547ffSDavid du Colombier 		rrfreelist(mp->ns);
1173a41547ffSDavid du Colombier 		mp->ns = nil;
117459cc4ca5SDavid du Colombier 	}
117559cc4ca5SDavid du Colombier 
11767dd7cddfSDavid du Colombier 	/* remove any soa's from the authority section */
1177530fef66SDavid du Colombier 	lock(&dnlock);
11783cbadd90SDavid du Colombier 	soarr = rrremtype(&mp->ns, Tsoa);
11797dd7cddfSDavid du Colombier 
11803e12c5d1SDavid du Colombier 	/* incorporate answers */
11814e153993SDavid du Colombier 	unique(mp->an);
11824e153993SDavid du Colombier 	unique(mp->ns);
11834e153993SDavid du Colombier 	unique(mp->ar);
1184530fef66SDavid du Colombier 	unlock(&dnlock);
11853cbadd90SDavid du Colombier 	if(mp->an)
11863cbadd90SDavid du Colombier 		rrattach(mp->an, (mp->flags & Fauth) != 0);
11873cbadd90SDavid du Colombier 	if(mp->ar)
11886dc4800dSDavid du Colombier 		rrattach(mp->ar, Notauthoritative);
1189a41547ffSDavid du Colombier 	if(mp->ns && !cfg.justforw){
11903cbadd90SDavid du Colombier 		ndp = mp->ns->owner;
11916dc4800dSDavid du Colombier 		rrattach(mp->ns, Notauthoritative);
11920319257bSDavid du Colombier 	} else {
11934f8f669cSDavid du Colombier 		ndp = nil;
11940319257bSDavid du Colombier 		rrfreelist(mp->ns);
11950319257bSDavid du Colombier 		mp->ns = nil;
11960319257bSDavid du Colombier 	}
11977dd7cddfSDavid du Colombier 
11987dd7cddfSDavid du Colombier 	/* free the question */
1199a41547ffSDavid du Colombier 	if(mp->qd) {
12003cbadd90SDavid du Colombier 		rrfreelist(mp->qd);
1201a41547ffSDavid du Colombier 		mp->qd = nil;
1202a41547ffSDavid du Colombier 	}
12033e12c5d1SDavid du Colombier 
12043e12c5d1SDavid du Colombier 	/*
12053e12c5d1SDavid du Colombier 	 *  Any reply from an authoritative server,
1206a41547ffSDavid du Colombier 	 *  or a positive reply terminates the search.
1207a41547ffSDavid du Colombier 	 *  A negative response now also terminates the search.
12083e12c5d1SDavid du Colombier 	 */
12093cbadd90SDavid du Colombier 	if(mp->an != nil || (mp->flags & Fauth)){
12100319257bSDavid du Colombier 		if(isnegrname(mp))
12113cbadd90SDavid du Colombier 			qp->dp->respcode = Rname;
12127dd7cddfSDavid du Colombier 		else
1213d9924332SDavid du Colombier 			qp->dp->respcode = Rok;
12147dd7cddfSDavid du Colombier 
12157dd7cddfSDavid du Colombier 		/*
1216a41547ffSDavid du Colombier 		 *  cache any negative responses, free soarr.
1217a41547ffSDavid du Colombier 		 *  negative responses need not be authoritative:
1218a41547ffSDavid du Colombier 		 *  they can legitimately come from a cache.
12197dd7cddfSDavid du Colombier 		 */
1220a41547ffSDavid du Colombier 		if( /* (mp->flags & Fauth) && */ mp->an == nil)
12213cbadd90SDavid du Colombier 			cacheneg(qp->dp, qp->type, (mp->flags & Rmask), soarr);
12227dd7cddfSDavid du Colombier 		else
12237dd7cddfSDavid du Colombier 			rrfreelist(soarr);
12243e12c5d1SDavid du Colombier 		return 1;
12250319257bSDavid du Colombier 	} else if (isnegrname(mp)) {
1226a41547ffSDavid du Colombier 		qp->dp->respcode = Rname;
1227a41547ffSDavid du Colombier 		/*
1228a41547ffSDavid du Colombier 		 *  cache negative response.
1229a41547ffSDavid du Colombier 		 *  negative responses need not be authoritative:
1230a41547ffSDavid du Colombier 		 *  they can legitimately come from a cache.
1231a41547ffSDavid du Colombier 		 */
1232a41547ffSDavid du Colombier 		cacheneg(qp->dp, qp->type, (mp->flags & Rmask), soarr);
1233a41547ffSDavid du Colombier 		return 1;
12343e12c5d1SDavid du Colombier 	}
12350319257bSDavid du Colombier 	stats.negnorname++;
12367dd7cddfSDavid du Colombier 	rrfreelist(soarr);
12373e12c5d1SDavid du Colombier 
12383e12c5d1SDavid du Colombier 	/*
1239a41547ffSDavid du Colombier 	 *  if we've been given better name servers, recurse.
1240a41547ffSDavid du Colombier 	 *  if we're a pure resolver, don't recurse, we have
1241a41547ffSDavid du Colombier 	 *  to forward to a fixed set of named servers.
12423e12c5d1SDavid du Colombier 	 */
1243a41547ffSDavid du Colombier 	if(!mp->ns || cfg.resolver && cfg.justforw)
12443cbadd90SDavid du Colombier 		return 0;
12457dd7cddfSDavid du Colombier 	tp = rrlookup(ndp, Tns, NOneg);
12463cbadd90SDavid du Colombier 	if(contains(qp->nsrp, tp)){
12477dd7cddfSDavid du Colombier 		rrfreelist(tp);
12483cbadd90SDavid du Colombier 		return 0;
12493e12c5d1SDavid du Colombier 	}
12503cbadd90SDavid du Colombier 	procsetname("recursive query for %s %s", qp->dp->name,
12513cbadd90SDavid du Colombier 		rrname(qp->type, buf, sizeof buf));
12524e5f5f32SDavid du Colombier 	/*
12534e5f5f32SDavid du Colombier 	 *  we're called from udpquery, called from
12544e5f5f32SDavid du Colombier 	 *  netquery, which current holds qp->dp->querylck,
12554e5f5f32SDavid du Colombier 	 *  so release it now and acquire it upon return.
12564e5f5f32SDavid du Colombier 	 */
12574e5f5f32SDavid du Colombier //	lcktype = qtype2lck(qp->type);
12584e5f5f32SDavid du Colombier //	qunlock(&qp->dp->querylck[lcktype]);
12593cbadd90SDavid du Colombier 
1260c73252aeSDavid du Colombier 	nqp = emalloc(sizeof *nqp);
1261c73252aeSDavid du Colombier 	queryinit(nqp, qp->dp, qp->type, qp->req);
1262c73252aeSDavid du Colombier 	nqp->nsrp = tp;
1263c73252aeSDavid du Colombier 	rv = netquery(nqp, depth+1);
12643cbadd90SDavid du Colombier 
12654e5f5f32SDavid du Colombier //	qlock(&qp->dp->querylck[lcktype]);
1266adb31a62SDavid du Colombier 	rrfreelist(nqp->nsrp);
1267c73252aeSDavid du Colombier 	querydestroy(nqp);
1268c73252aeSDavid du Colombier 	free(nqp);
12693cbadd90SDavid du Colombier 	return rv;
12703cbadd90SDavid du Colombier }
12713cbadd90SDavid du Colombier 
12723cbadd90SDavid du Colombier /*
12733cbadd90SDavid du Colombier  * send a query via tcp to a single address (from ibuf's udp header)
12743cbadd90SDavid du Colombier  * and read the answer(s) into mp->an.
12753cbadd90SDavid du Colombier  */
12763cbadd90SDavid du Colombier static int
12773cbadd90SDavid du Colombier tcpquery(Query *qp, DNSmsg *mp, int depth, uchar *ibuf, uchar *obuf, int len,
1278b8b25780SDavid du Colombier 	ulong waitms, int inns, ushort req)
12793cbadd90SDavid du Colombier {
12804e7b9544SDavid du Colombier 	int rv = 0;
1281b8b25780SDavid du Colombier 	uvlong endms;
12823cbadd90SDavid du Colombier 
1283b8b25780SDavid du Colombier 	endms = timems() + waitms;
1284b8b25780SDavid du Colombier 	if(endms > qp->req->aborttime)
1285b8b25780SDavid du Colombier 		endms = qp->req->aborttime;
12863cbadd90SDavid du Colombier 
1287a41547ffSDavid du Colombier 	if (0)
12884e7b9544SDavid du Colombier 		dnslog("%s: udp reply truncated; retrying query via tcp to %I",
12894e7b9544SDavid du Colombier 			qp->dp->name, qp->tcpip);
12904e7b9544SDavid du Colombier 
12913cbadd90SDavid du Colombier 	qlock(&qp->tcplock);
12923cbadd90SDavid du Colombier 	memmove(obuf, ibuf, IPaddrlen);		/* send back to respondent */
12933cbadd90SDavid du Colombier 	/* sets qp->tcpip from obuf's udp header */
12944e7b9544SDavid du Colombier 	if (xmitquery(qp, Tcp, depth, obuf, inns, len) < 0 ||
1295b8b25780SDavid du Colombier 	    readreply(qp, Tcp, req, ibuf, mp, endms) < 0)
12964e7b9544SDavid du Colombier 		rv = -1;
12973cbadd90SDavid du Colombier 	if (qp->tcpfd > 0) {
12983cbadd90SDavid du Colombier 		hangup(qp->tcpctlfd);
12993cbadd90SDavid du Colombier 		close(qp->tcpctlfd);
13003cbadd90SDavid du Colombier 		close(qp->tcpfd);
13013cbadd90SDavid du Colombier 	}
13023cbadd90SDavid du Colombier 	qp->tcpfd = qp->tcpctlfd = -1;
13033cbadd90SDavid du Colombier 	qunlock(&qp->tcplock);
13044e7b9544SDavid du Colombier 	return rv;
13053cbadd90SDavid du Colombier }
13063cbadd90SDavid du Colombier 
13073cbadd90SDavid du Colombier /*
13083cbadd90SDavid du Colombier  *  query name servers.  If the name server returns a pointer to another
13093cbadd90SDavid du Colombier  *  name server, recurse.
13103cbadd90SDavid du Colombier  */
13113cbadd90SDavid du Colombier static int
1312b8b25780SDavid du Colombier queryns(Query *qp, int depth, uchar *ibuf, uchar *obuf, ulong waitms, int inns)
13133cbadd90SDavid du Colombier {
13143cbadd90SDavid du Colombier 	int ndest, len, replywaits, rv;
13153cbadd90SDavid du Colombier 	ushort req;
1316b8b25780SDavid du Colombier 	uvlong endms;
13173cbadd90SDavid du Colombier 	char buf[12];
13183cbadd90SDavid du Colombier 	uchar srcip[IPaddrlen];
1319a41547ffSDavid du Colombier 	Dest *p, *np, *dest;
13203cbadd90SDavid du Colombier 
13213cbadd90SDavid du Colombier 	/* pack request into a udp message */
13223cbadd90SDavid du Colombier 	req = rand();
13233cbadd90SDavid du Colombier 	len = mkreq(qp->dp, qp->type, obuf, Frecurse|Oquery, req);
13243cbadd90SDavid du Colombier 
13253cbadd90SDavid du Colombier 	/* no server addresses yet */
13263cbadd90SDavid du Colombier 	queryck(qp);
1327a41547ffSDavid du Colombier 	dest = emalloc(Maxdest * sizeof *dest);	/* dest can't be on stack */
1328a41547ffSDavid du Colombier 	for (p = dest; p < dest + Maxdest; p++)
13293cbadd90SDavid du Colombier 		destinit(p);
13301eb0187fSDavid du Colombier 	/* this dest array is local to this call of queryns() */
13311eb0187fSDavid du Colombier 	free(qp->dest);
13323cbadd90SDavid du Colombier 	qp->curdest = qp->dest = dest;
13333cbadd90SDavid du Colombier 
13343cbadd90SDavid du Colombier 	/*
13353cbadd90SDavid du Colombier 	 *  transmit udp requests and wait for answers.
13363cbadd90SDavid du Colombier 	 *  at most Maxtrans attempts to each address.
13373cbadd90SDavid du Colombier 	 *  each cycle send one more message than the previous.
13383cbadd90SDavid du Colombier 	 *  retry a query via tcp if its response is truncated.
13393cbadd90SDavid du Colombier 	 */
13403cbadd90SDavid du Colombier 	for(ndest = 1; ndest < Maxdest; ndest++){
13413cbadd90SDavid du Colombier 		qp->ndest = ndest;
13423cbadd90SDavid du Colombier 		qp->tcpset = 0;
13433cbadd90SDavid du Colombier 		if (xmitquery(qp, Udp, depth, obuf, inns, len) < 0)
13443cbadd90SDavid du Colombier 			break;
13453cbadd90SDavid du Colombier 
1346b8b25780SDavid du Colombier 		endms = timems() + waitms;
1347b8b25780SDavid du Colombier 		if(endms > qp->req->aborttime)
1348b8b25780SDavid du Colombier 			endms = qp->req->aborttime;
13493cbadd90SDavid du Colombier 
13503cbadd90SDavid du Colombier 		for(replywaits = 0; replywaits < ndest; replywaits++){
135181730632SDavid du Colombier 			DNSmsg m;
135281730632SDavid du Colombier 
1353f46c709fSDavid du Colombier 			procsetname("reading %sside reply from %I: %s %s from %s",
1354a41547ffSDavid du Colombier 				(inns? "in": "out"), obuf, qp->dp->name,
1355f46c709fSDavid du Colombier 				rrname(qp->type, buf, sizeof buf), qp->req->from);
13563cbadd90SDavid du Colombier 
135781730632SDavid du Colombier 			/* read udp answer into m */
1358b8b25780SDavid du Colombier 			if (readreply(qp, Udp, req, ibuf, &m, endms) >= 0)
13593cbadd90SDavid du Colombier 				memmove(srcip, ibuf, IPaddrlen);
136081730632SDavid du Colombier 			else if (!(m.flags & Ftrunc)) {
136181730632SDavid du Colombier 				freeanswers(&m);
13623cbadd90SDavid du Colombier 				break;		/* timed out on this dest */
136381730632SDavid du Colombier 			} else {
13643cbadd90SDavid du Colombier 				/* whoops, it was truncated! ask again via tcp */
13656aaebd7dSDavid du Colombier 				freeanswers(&m);
13663cbadd90SDavid du Colombier 				rv = tcpquery(qp, &m, depth, ibuf, obuf, len,
1367b8b25780SDavid du Colombier 					waitms, inns, req);  /* answer in m */
136881730632SDavid du Colombier 				if (rv < 0) {
136981730632SDavid du Colombier 					freeanswers(&m);
13703cbadd90SDavid du Colombier 					break;		/* failed via tcp too */
137181730632SDavid du Colombier 				}
13723cbadd90SDavid du Colombier 				memmove(srcip, qp->tcpip, IPaddrlen);
13733cbadd90SDavid du Colombier 			}
13743cbadd90SDavid du Colombier 
13753cbadd90SDavid du Colombier 			/* find responder */
13764e5f5f32SDavid du Colombier 			// dnslog("queryns got reply from %I", srcip);
13773cbadd90SDavid du Colombier 			for(p = qp->dest; p < qp->curdest; p++)
13783cbadd90SDavid du Colombier 				if(memcmp(p->a, srcip, sizeof p->a) == 0)
13793cbadd90SDavid du Colombier 					break;
13803cbadd90SDavid du Colombier 
13813cbadd90SDavid du Colombier 			/* remove all addrs of responding server from list */
13823cbadd90SDavid du Colombier 			for(np = qp->dest; np < qp->curdest; np++)
13833cbadd90SDavid du Colombier 				if(np->s == p->s)
13843cbadd90SDavid du Colombier 					p->nx = Maxtrans;
13853cbadd90SDavid du Colombier 
138681730632SDavid du Colombier 			/* free or incorporate RRs in m */
13873cbadd90SDavid du Colombier 			rv = procansw(qp, &m, srcip, depth, p);
13881eb0187fSDavid du Colombier 			if (rv > 0) {
13891eb0187fSDavid du Colombier 				free(qp->dest);
13901eb0187fSDavid du Colombier 				qp->dest = qp->curdest = nil; /* prevent accidents */
13913cbadd90SDavid du Colombier 				return rv;
13923e12c5d1SDavid du Colombier 			}
13933e12c5d1SDavid du Colombier 		}
13941eb0187fSDavid du Colombier 	}
13957dd7cddfSDavid du Colombier 
13964f8f669cSDavid du Colombier 	/* if all servers returned failure, propagate it */
13973cbadd90SDavid du Colombier 	qp->dp->respcode = Rserver;
13983cbadd90SDavid du Colombier 	for(p = dest; p < qp->curdest; p++) {
13993cbadd90SDavid du Colombier 		destck(p);
14007dd7cddfSDavid du Colombier 		if(p->code != Rserver)
1401d9924332SDavid du Colombier 			qp->dp->respcode = Rok;
14023cbadd90SDavid du Colombier 		p->magic = 0;			/* prevent accidents */
14033cbadd90SDavid du Colombier 	}
14044f8f669cSDavid du Colombier 
14053cbadd90SDavid du Colombier //	if (qp->dp->respcode)
14064e5f5f32SDavid du Colombier //		dnslog("queryns setting Rserver for %s", qp->dp->name);
14077dd7cddfSDavid du Colombier 
1408a41547ffSDavid du Colombier 	free(qp->dest);
14093cbadd90SDavid du Colombier 	qp->dest = qp->curdest = nil;		/* prevent accidents */
14103e12c5d1SDavid du Colombier 	return 0;
14113e12c5d1SDavid du Colombier }
14127dd7cddfSDavid du Colombier 
14134f8f669cSDavid du Colombier /*
14144f8f669cSDavid du Colombier  *  run a command with a supplied fd as standard input
14154f8f669cSDavid du Colombier  */
14164f8f669cSDavid du Colombier char *
14174f8f669cSDavid du Colombier system(int fd, char *cmd)
141828a8a86bSDavid du Colombier {
14194f8f669cSDavid du Colombier 	int pid, p, i;
14204f8f669cSDavid du Colombier 	static Waitmsg msg;
14216b0d5c8bSDavid du Colombier 
14224f8f669cSDavid du Colombier 	if((pid = fork()) == -1)
14234f8f669cSDavid du Colombier 		sysfatal("fork failed: %r");
14244f8f669cSDavid du Colombier 	else if(pid == 0){
14254f8f669cSDavid du Colombier 		dup(fd, 0);
14264f8f669cSDavid du Colombier 		close(fd);
14274f8f669cSDavid du Colombier 		for (i = 3; i < 200; i++)
14284f8f669cSDavid du Colombier 			close(i);		/* don't leak fds */
14294f8f669cSDavid du Colombier 		execl("/bin/rc", "rc", "-c", cmd, nil);
14304f8f669cSDavid du Colombier 		sysfatal("exec rc: %r");
14314f8f669cSDavid du Colombier 	}
14324f8f669cSDavid du Colombier 	for(p = waitpid(); p >= 0; p = waitpid())
14334f8f669cSDavid du Colombier 		if(p == pid)
14344f8f669cSDavid du Colombier 			return msg.msg;
14354f8f669cSDavid du Colombier 	return "lost child";
14364f8f669cSDavid du Colombier }
14374f8f669cSDavid du Colombier 
1438b8b25780SDavid du Colombier /* compute wait, weighted by probability of success, with bounds */
14390319257bSDavid du Colombier static ulong
14400319257bSDavid du Colombier weight(ulong ms, unsigned pcntprob)
14410319257bSDavid du Colombier {
14420319257bSDavid du Colombier 	ulong wait;
14430319257bSDavid du Colombier 
14440319257bSDavid du Colombier 	wait = (ms * pcntprob) / 100;
1445b8b25780SDavid du Colombier 	if (wait < Minwaitms)
1446b8b25780SDavid du Colombier 		wait = Minwaitms;
1447b8b25780SDavid du Colombier 	if (wait > Maxwaitms)
1448b8b25780SDavid du Colombier 		wait = Maxwaitms;
14490319257bSDavid du Colombier 	return wait;
14500319257bSDavid du Colombier }
14510319257bSDavid du Colombier 
1452a41547ffSDavid du Colombier /*
1453a41547ffSDavid du Colombier  * in principle we could use a single descriptor for a udp port
1454a41547ffSDavid du Colombier  * to send all queries and receive all the answers to them,
1455a41547ffSDavid du Colombier  * but we'd have to sort out the answers by dns-query id.
1456a41547ffSDavid du Colombier  */
14574f8f669cSDavid du Colombier static int
14583cbadd90SDavid du Colombier udpquery(Query *qp, char *mntpt, int depth, int patient, int inns)
14594f8f669cSDavid du Colombier {
14600319257bSDavid du Colombier 	int fd, rv;
14614f8f669cSDavid du Colombier 	long now;
1462b8b25780SDavid du Colombier 	ulong pcntprob;
1463b8b25780SDavid du Colombier 	uvlong wait, reqtm;
14644f8f669cSDavid du Colombier 	char *msg;
14654f8f669cSDavid du Colombier 	uchar *obuf, *ibuf;
14664f8f669cSDavid du Colombier 	static QLock mntlck;
14674f8f669cSDavid du Colombier 	static ulong lastmount;
14687dd7cddfSDavid du Colombier 
14697dd7cddfSDavid du Colombier 	/* use alloced buffers rather than ones from the stack */
1470f27a9a5aSDavid du Colombier 	// ibuf = emalloc(Maxudpin+Udphdrsize);
14713cbadd90SDavid du Colombier 	ibuf = emalloc(64*1024);		/* max. tcp reply size */
1472f27a9a5aSDavid du Colombier 	obuf = emalloc(Maxudp+Udphdrsize);
14737dd7cddfSDavid du Colombier 
14744f8f669cSDavid du Colombier 	fd = udpport(mntpt);
14754f8f669cSDavid du Colombier 	while (fd < 0 && cfg.straddle && strcmp(mntpt, "/net.alt") == 0) {
14764f8f669cSDavid du Colombier 		/* HACK: remount /net.alt */
14774f8f669cSDavid du Colombier 		now = time(nil);
14784f8f669cSDavid du Colombier 		if (now < lastmount + Remntretry)
1479b8b25780SDavid du Colombier 			sleep(S2MS(lastmount + Remntretry - now));
14804f8f669cSDavid du Colombier 		qlock(&mntlck);
14814f8f669cSDavid du Colombier 		fd = udpport(mntpt);	/* try again under lock */
14824f8f669cSDavid du Colombier 		if (fd < 0) {
14834f8f669cSDavid du Colombier 			dnslog("[%d] remounting /net.alt", getpid());
14844f8f669cSDavid du Colombier 			unmount(nil, "/net.alt");
14854f8f669cSDavid du Colombier 
14864f8f669cSDavid du Colombier 			msg = system(open("/dev/null", ORDWR), "outside");
14874f8f669cSDavid du Colombier 
14884f8f669cSDavid du Colombier 			lastmount = time(nil);
14894f8f669cSDavid du Colombier 			if (msg && *msg) {
14904f8f669cSDavid du Colombier 				dnslog("[%d] can't remount /net.alt: %s",
14914f8f669cSDavid du Colombier 					getpid(), msg);
1492b8b25780SDavid du Colombier 				sleep(10*1000);	/* don't spin remounting */
14934f8f669cSDavid du Colombier 			} else
14944f8f669cSDavid du Colombier 				fd = udpport(mntpt);
14954f8f669cSDavid du Colombier 		}
14964f8f669cSDavid du Colombier 		qunlock(&mntlck);
14974f8f669cSDavid du Colombier 	}
1498a41547ffSDavid du Colombier 	if (fd < 0) {
14994f8f669cSDavid du Colombier 		dnslog("can't get udpport for %s query of name %s: %r",
15003cbadd90SDavid du Colombier 			mntpt, qp->dp->name);
1501a41547ffSDavid du Colombier 		sysfatal("out of udp conversations");	/* we're buggered */
1502a41547ffSDavid du Colombier 	}
1503a41547ffSDavid du Colombier 
1504a41547ffSDavid du Colombier 	/*
1505b8b25780SDavid du Colombier 	 * Our QIP servers are busted and respond to AAAA and CNAME queries
1506b8b25780SDavid du Colombier 	 * with (sometimes malformed [too short] packets and) no answers and
1507b8b25780SDavid du Colombier 	 * just NS RRs but not Rname errors.  so make time-to-wait
1508b8b25780SDavid du Colombier 	 * proportional to estimated probability of an RR of that type existing.
1509a41547ffSDavid du Colombier 	 */
1510adb31a62SDavid du Colombier 	if (qp->type >= nelem(likely))
15110319257bSDavid du Colombier 		pcntprob = 35;			/* unpopular query type */
15120319257bSDavid du Colombier 	else
15130319257bSDavid du Colombier 		pcntprob = likely[qp->type];
15140319257bSDavid du Colombier 	reqtm = (patient? 2 * Maxreqtm: Maxreqtm);
1515b8b25780SDavid du Colombier 	wait = weight(reqtm / 3, pcntprob);	/* time for one udp query */
1516b8b25780SDavid du Colombier 	qp->req->aborttime = timems() + 3*wait; /* for all udp queries */
1517a41547ffSDavid du Colombier 
15180319257bSDavid du Colombier 	qp->udpfd = fd;
1519b8b25780SDavid du Colombier 	rv = queryns(qp, depth, ibuf, obuf, wait, inns);
1520a41547ffSDavid du Colombier 	close(fd);
1521a41547ffSDavid du Colombier 	qp->udpfd = -1;
15224f8f669cSDavid du Colombier 
15234f8f669cSDavid du Colombier 	free(obuf);
15244f8f669cSDavid du Colombier 	free(ibuf);
15254f8f669cSDavid du Colombier 	return rv;
15264f8f669cSDavid du Colombier }
15274f8f669cSDavid du Colombier 
1528b8b25780SDavid du Colombier /*
1529b8b25780SDavid du Colombier  * look up (qp->dp->name, qp->type) rr in dns,
1530b8b25780SDavid du Colombier  * using nameservers in qp->nsrp.
1531b8b25780SDavid du Colombier  */
15324f8f669cSDavid du Colombier static int
15333cbadd90SDavid du Colombier netquery(Query *qp, int depth)
15344f8f669cSDavid du Colombier {
1535410ea80bSDavid du Colombier 	int lock, rv, triedin, inname;
1536b8b25780SDavid du Colombier 	char buf[32];
15374f8f669cSDavid du Colombier 	RR *rp;
15384e153993SDavid du Colombier 	DN *dp;
1539225077b0SDavid du Colombier 	Querylck *qlp;
1540225077b0SDavid du Colombier 	static int whined;
15414f8f669cSDavid du Colombier 
1542225077b0SDavid du Colombier 	rv = 0;				/* pessimism */
15434f8f669cSDavid du Colombier 	if(depth > 12)			/* in a recursive loop? */
15444f8f669cSDavid du Colombier 		return 0;
15454f8f669cSDavid du Colombier 
15463cbadd90SDavid du Colombier 	slave(qp->req);
1547d3907fe5SDavid du Colombier 	/*
1548d3907fe5SDavid du Colombier 	 * slave might have forked.  if so, the parent process longjmped to
1549d3907fe5SDavid du Colombier 	 * req->mret; we're usually the child slave, but if there are too
1550d3907fe5SDavid du Colombier 	 * many children already, we're still the same process.
1551d3907fe5SDavid du Colombier 	 */
15524f8f669cSDavid du Colombier 
15534e5f5f32SDavid du Colombier 	/*
15544e5f5f32SDavid du Colombier 	 * don't lock before call to slave so only children can block.
15554e5f5f32SDavid du Colombier 	 * just lock at top-level invocation.
15564e5f5f32SDavid du Colombier 	 */
1557225077b0SDavid du Colombier 	lock = depth <= 1 && qp->req->isslave;
15584e153993SDavid du Colombier 	dp = qp->dp;		/* ensure that it doesn't change underfoot */
1559225077b0SDavid du Colombier 	qlp = nil;
15604e5f5f32SDavid du Colombier 	if(lock) {
1561b8b25780SDavid du Colombier 		procsetname("query lock wait: %s %s from %s", dp->name,
1562b8b25780SDavid du Colombier 			rrname(qp->type, buf, sizeof buf), qp->req->from);
1563ccf6439bSDavid du Colombier 		/*
1564ccf6439bSDavid du Colombier 		 * don't make concurrent queries for this name.
156581730632SDavid du Colombier 		 * dozens of processes blocking here probably indicates
156681730632SDavid du Colombier 		 * an error in our dns data that causes us to not
156781730632SDavid du Colombier 		 * recognise a zone (area) as one of our own, thus
156881730632SDavid du Colombier 		 * causing us to query other nameservers.
1569ccf6439bSDavid du Colombier 		 */
1570225077b0SDavid du Colombier 		qlp = &dp->querylck[qtype2lck(qp->type)];
1571225077b0SDavid du Colombier 		qlock(qlp);
1572b8b25780SDavid du Colombier 		if (qlp->Ref.ref > Maxoutstanding) {
1573225077b0SDavid du Colombier 			qunlock(qlp);
1574225077b0SDavid du Colombier 			if (!whined) {
1575225077b0SDavid du Colombier 				whined = 1;
1576225077b0SDavid du Colombier 				dnslog("too many outstanding queries for %s;"
157713fec586SDavid du Colombier 					" dropping this one; no further logging"
157813fec586SDavid du Colombier 					" of drops", dp->name);
1579225077b0SDavid du Colombier 			}
1580225077b0SDavid du Colombier 			return 0;
1581225077b0SDavid du Colombier 		}
1582410ea80bSDavid du Colombier 		++qlp->Ref.ref;
1583410ea80bSDavid du Colombier 		qunlock(qlp);
1584225077b0SDavid du Colombier 	}
15854e153993SDavid du Colombier 	procsetname("netquery: %s", dp->name);
15866b0d5c8bSDavid du Colombier 
15876b0d5c8bSDavid du Colombier 	/* prepare server RR's for incremental lookup */
15883cbadd90SDavid du Colombier 	for(rp = qp->nsrp; rp; rp = rp->next)
15896b0d5c8bSDavid du Colombier 		rp->marker = 0;
15906b0d5c8bSDavid du Colombier 
15914f8f669cSDavid du Colombier 	triedin = 0;
1592a41547ffSDavid du Colombier 
15934f8f669cSDavid du Colombier 	/*
15944f8f669cSDavid du Colombier 	 * normal resolvers and servers will just use mntpt for all addresses,
15954f8f669cSDavid du Colombier 	 * even on the outside.  straddling servers will use mntpt (/net)
15964f8f669cSDavid du Colombier 	 * for inside addresses and /net.alt for outside addresses,
15974f8f669cSDavid du Colombier 	 * thus bypassing other inside nameservers.
15984f8f669cSDavid du Colombier 	 */
15994e153993SDavid du Colombier 	inname = insideaddr(dp->name);
16004f8f669cSDavid du Colombier 	if (!cfg.straddle || inname) {
16013cbadd90SDavid du Colombier 		rv = udpquery(qp, mntpt, depth, Hurry, (cfg.inside? Inns: Outns));
16024f8f669cSDavid du Colombier 		triedin = 1;
16034f8f669cSDavid du Colombier 	}
16044f8f669cSDavid du Colombier 
16054f8f669cSDavid du Colombier 	/*
16064f8f669cSDavid du Colombier 	 * if we're still looking, are inside, and have an outside domain,
16074f8f669cSDavid du Colombier 	 * try it on our outside interface, if any.
16084f8f669cSDavid du Colombier 	 */
16094f8f669cSDavid du Colombier 	if (rv == 0 && cfg.inside && !inname) {
16104f8f669cSDavid du Colombier 		if (triedin)
16114f8f669cSDavid du Colombier 			dnslog(
16124f8f669cSDavid du Colombier 	   "[%d] netquery: internal nameservers failed for %s; trying external",
16134e153993SDavid du Colombier 				getpid(), dp->name);
16144f8f669cSDavid du Colombier 
16154f8f669cSDavid du Colombier 		/* prepare server RR's for incremental lookup */
16163cbadd90SDavid du Colombier 		for(rp = qp->nsrp; rp; rp = rp->next)
16174f8f669cSDavid du Colombier 			rp->marker = 0;
16184f8f669cSDavid du Colombier 
16193cbadd90SDavid du Colombier 		rv = udpquery(qp, "/net.alt", depth, Patient, Outns);
16204f8f669cSDavid du Colombier 	}
16213cbadd90SDavid du Colombier //	if (rv == 0)		/* could ask /net.alt/dns directly */
16224e153993SDavid du Colombier //		askoutdns(dp, qp->type);
16234f8f669cSDavid du Colombier 
1624410ea80bSDavid du Colombier 	if(lock && qlp) {
1625410ea80bSDavid du Colombier 		qlock(qlp);
1626410ea80bSDavid du Colombier 		assert(qlp->Ref.ref > 0);
1627410ea80bSDavid du Colombier 		qunlock(qlp);
1628225077b0SDavid du Colombier 		decref(qlp);
1629410ea80bSDavid du Colombier 	}
16307dd7cddfSDavid du Colombier 	return rv;
16317dd7cddfSDavid du Colombier }
16324f8f669cSDavid du Colombier 
16334f8f669cSDavid du Colombier int
16344f8f669cSDavid du Colombier seerootns(void)
16354f8f669cSDavid du Colombier {
16363cbadd90SDavid du Colombier 	int rv;
16374f8f669cSDavid du Colombier 	char root[] = "";
16384f8f669cSDavid du Colombier 	Request req;
1639c73252aeSDavid du Colombier 	Query *qp;
16404f8f669cSDavid du Colombier 
16414f8f669cSDavid du Colombier 	memset(&req, 0, sizeof req);
16424f8f669cSDavid du Colombier 	req.isslave = 1;
1643b8b25780SDavid du Colombier 	req.aborttime = timems() + Maxreqtm;
1644f46c709fSDavid du Colombier 	req.from = "internal";
1645c73252aeSDavid du Colombier 	qp = emalloc(sizeof *qp);
1646c73252aeSDavid du Colombier 	queryinit(qp, dnlookup(root, Cin, 1), Tns, &req);
1647c73252aeSDavid du Colombier 
1648c73252aeSDavid du Colombier 	qp->nsrp = dblookup(root, Cin, Tns, 0, 0);
1649c73252aeSDavid du Colombier 	rv = netquery(qp, 0);
1650c73252aeSDavid du Colombier 
1651c73252aeSDavid du Colombier 	rrfreelist(qp->nsrp);
1652c73252aeSDavid du Colombier 	querydestroy(qp);
1653c73252aeSDavid du Colombier 	free(qp);
16543cbadd90SDavid du Colombier 	return rv;
16554f8f669cSDavid du Colombier }
1656