xref: /plan9-contrib/sys/src/cmd/ndb/dnresolve.c (revision 530fef6600a0fb31e4c0a6ecda1320e97bcd937c)
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 
11a41547ffSDavid du Colombier #define NS2MS(ns) ((ns) / 1000000L)
12a41547ffSDavid du Colombier #define S2MS(s)   ((s)  * 1000)
130319257bSDavid du Colombier #define MS2S(ms)  ((ms) / 1000)
14a41547ffSDavid du Colombier 
153cbadd90SDavid du Colombier typedef struct Dest Dest;
163cbadd90SDavid du Colombier typedef struct Ipaddr Ipaddr;
173cbadd90SDavid du Colombier typedef struct Query Query;
183cbadd90SDavid du Colombier 
193e12c5d1SDavid du Colombier enum
203e12c5d1SDavid du Colombier {
213cbadd90SDavid du Colombier 	Udp, Tcp,
227dd7cddfSDavid du Colombier 	Maxdest=	24,	/* maximum destinations for a request message */
233e12c5d1SDavid du Colombier 	Maxtrans=	3,	/* maximum transmissions to a server */
243cbadd90SDavid du Colombier 	Destmagic=	0xcafebabe,
253cbadd90SDavid du Colombier 	Querymagic=	0xdeadbeef,
263e12c5d1SDavid du Colombier };
27a41547ffSDavid du Colombier enum { Hurry, Patient, };
28a41547ffSDavid du Colombier enum { Outns, Inns, };
29a41547ffSDavid du Colombier enum { Remntretry = 15, };	/* min. sec.s between remount attempts */
303e12c5d1SDavid du Colombier 
313cbadd90SDavid du Colombier struct Ipaddr {
323cbadd90SDavid du Colombier 	Ipaddr *next;
333cbadd90SDavid du Colombier 	uchar	ip[IPaddrlen];
343cbadd90SDavid du Colombier };
353cbadd90SDavid du Colombier 
363cbadd90SDavid du Colombier struct Dest
373cbadd90SDavid du Colombier {
383cbadd90SDavid du Colombier 	uchar	a[IPaddrlen];	/* ip address */
393cbadd90SDavid du Colombier 	DN	*s;		/* name server */
403cbadd90SDavid du Colombier 	int	nx;		/* number of transmissions */
413cbadd90SDavid du Colombier 	int	code;		/* response code; used to clear dp->respcode */
423cbadd90SDavid du Colombier 
433cbadd90SDavid du Colombier 	ulong	magic;
443cbadd90SDavid du Colombier };
453cbadd90SDavid du Colombier 
46c73252aeSDavid du Colombier /*
47c73252aeSDavid du Colombier  * Query has a QLock in it, thus it can't be an automatic
48c73252aeSDavid du Colombier  * variable, since each process would see a separate copy
49c73252aeSDavid du Colombier  * of the lock on its stack.
50c73252aeSDavid du Colombier  */
513cbadd90SDavid du Colombier struct Query {
523cbadd90SDavid du Colombier 	DN	*dp;		/* domain */
53adb31a62SDavid du Colombier 	ushort	type;		/* and type to look up */
543cbadd90SDavid du Colombier 	Request *req;
553cbadd90SDavid du Colombier 	RR	*nsrp;		/* name servers to consult */
563cbadd90SDavid du Colombier 
57a41547ffSDavid du Colombier 	/* dest must not be on the stack due to forking in slave() */
583cbadd90SDavid du Colombier 	Dest	*dest;		/* array of destinations */
593cbadd90SDavid du Colombier 	Dest	*curdest;	/* pointer to one of them */
603cbadd90SDavid du Colombier 	int	ndest;
613cbadd90SDavid du Colombier 
62f46c709fSDavid du Colombier 	int	udpfd;
633cbadd90SDavid du Colombier 
643cbadd90SDavid du Colombier 	QLock	tcplock;	/* only one tcp call at a time per query */
653cbadd90SDavid du Colombier 	int	tcpset;
663cbadd90SDavid du Colombier 	int	tcpfd;		/* if Tcp, read replies from here */
673cbadd90SDavid du Colombier 	int	tcpctlfd;
683cbadd90SDavid du Colombier 	uchar	tcpip[IPaddrlen];
693cbadd90SDavid du Colombier 
703cbadd90SDavid du Colombier 	ulong	magic;
713cbadd90SDavid du Colombier };
723cbadd90SDavid du Colombier 
73a41547ffSDavid du Colombier /* estimated % probability of such a record existing at all */
74a41547ffSDavid du Colombier int likely[] = {
75a41547ffSDavid du Colombier 	[Ta]		95,
76a41547ffSDavid du Colombier 	[Taaaa]		10,
77a41547ffSDavid du Colombier 	[Tcname]	15,
78a41547ffSDavid du Colombier 	[Tmx]		60,
79a41547ffSDavid du Colombier 	[Tns]		90,
80a41547ffSDavid du Colombier 	[Tnull]		5,
81a41547ffSDavid du Colombier 	[Tptr]		35,
82a41547ffSDavid du Colombier 	[Tsoa]		90,
83a41547ffSDavid du Colombier 	[Tsrv]		60,
84a41547ffSDavid du Colombier 	[Ttxt]		15,
85a41547ffSDavid du Colombier 	[Tall]		95,
863cbadd90SDavid du Colombier };
873cbadd90SDavid du Colombier 
887dd7cddfSDavid du Colombier static RR*	dnresolve1(char*, int, int, Request*, int, int);
893cbadd90SDavid du Colombier static int	netquery(Query *, int);
903cbadd90SDavid du Colombier 
914f8f669cSDavid du Colombier /*
92*530fef66SDavid du Colombier  * reading /proc/pid/args yields either "name args" or "name [display args]",
934f8f669cSDavid du Colombier  * so return only display args, if any.
944f8f669cSDavid du Colombier  */
954f8f669cSDavid du Colombier static char *
964f8f669cSDavid du Colombier procgetname(void)
974f8f669cSDavid du Colombier {
984f8f669cSDavid du Colombier 	int fd, n;
994f8f669cSDavid du Colombier 	char *lp, *rp;
1004f8f669cSDavid du Colombier 	char buf[256];
1014f8f669cSDavid du Colombier 
1024f8f669cSDavid du Colombier 	snprint(buf, sizeof buf, "#p/%d/args", getpid());
1034f8f669cSDavid du Colombier 	if((fd = open(buf, OREAD)) < 0)
1044f8f669cSDavid du Colombier 		return strdup("");
1054f8f669cSDavid du Colombier 	*buf = '\0';
1064f8f669cSDavid du Colombier 	n = read(fd, buf, sizeof buf-1);
1074f8f669cSDavid du Colombier 	close(fd);
1084f8f669cSDavid du Colombier 	if (n >= 0)
1094f8f669cSDavid du Colombier 		buf[n] = '\0';
1104f8f669cSDavid du Colombier 	if ((lp = strchr(buf, '[')) == nil ||
1114f8f669cSDavid du Colombier 	    (rp = strrchr(buf, ']')) == nil)
1124f8f669cSDavid du Colombier 		return strdup("");
1134f8f669cSDavid du Colombier 	*rp = '\0';
1144f8f669cSDavid du Colombier 	return strdup(lp+1);
1154f8f669cSDavid du Colombier }
1163e12c5d1SDavid du Colombier 
1173e12c5d1SDavid du Colombier /*
1183e12c5d1SDavid du Colombier  *  lookup 'type' info for domain name 'name'.  If it doesn't exist, try
1193e12c5d1SDavid du Colombier  *  looking it up as a canonical name.
1203e12c5d1SDavid du Colombier  */
1213e12c5d1SDavid du Colombier RR*
1224f8f669cSDavid du Colombier dnresolve(char *name, int class, int type, Request *req, RR **cn, int depth,
1234f8f669cSDavid du Colombier 	int recurse, int rooted, int *status)
1243e12c5d1SDavid du Colombier {
1257dd7cddfSDavid du Colombier 	RR *rp, *nrp, *drp;
1263e12c5d1SDavid du Colombier 	DN *dp;
1277dd7cddfSDavid du Colombier 	int loops;
1284f8f669cSDavid du Colombier 	char *procname;
1297dd7cddfSDavid du Colombier 	char nname[Domlen];
1303e12c5d1SDavid du Colombier 
1317dd7cddfSDavid du Colombier 	if(status)
1327dd7cddfSDavid du Colombier 		*status = 0;
1337dd7cddfSDavid du Colombier 
134a41547ffSDavid du Colombier 	if(depth > 12)			/* in a recursive loop? */
135a41547ffSDavid du Colombier 		return nil;
136a41547ffSDavid du Colombier 
1374f8f669cSDavid du Colombier 	procname = procgetname();
1387dd7cddfSDavid du Colombier 	/*
1397dd7cddfSDavid du Colombier 	 *  hack for systems that don't have resolve search
1407dd7cddfSDavid du Colombier 	 *  lists.  Just look up the simple name in the database.
1417dd7cddfSDavid du Colombier 	 */
1426dc4800dSDavid du Colombier 	if(!rooted && strchr(name, '.') == nil){
1437dd7cddfSDavid du Colombier 		rp = nil;
1447dd7cddfSDavid du Colombier 		drp = domainlist(class);
145a41547ffSDavid du Colombier 		for(nrp = drp; rp == nil && nrp != nil; nrp = nrp->next){
1464f8f669cSDavid du Colombier 			snprint(nname, sizeof nname, "%s.%s", name,
1474f8f669cSDavid du Colombier 				nrp->ptr->name);
148a41547ffSDavid du Colombier 			rp = dnresolve(nname, class, type, req, cn, depth+1,
1494f8f669cSDavid du Colombier 				recurse, rooted, status);
150*530fef66SDavid du Colombier 			lock(&dnlock);
151271b8d73SDavid du Colombier 			rrfreelist(rrremneg(&rp));
152*530fef66SDavid du Colombier 			unlock(&dnlock);
1537dd7cddfSDavid du Colombier 		}
1547dd7cddfSDavid du Colombier 		if(drp != nil)
1556aaebd7dSDavid du Colombier 			rrfreelist(drp);
1564f8f669cSDavid du Colombier 		procsetname(procname);
1574f8f669cSDavid du Colombier 		free(procname);
1583e12c5d1SDavid du Colombier 		return rp;
1597dd7cddfSDavid du Colombier 	}
1603e12c5d1SDavid du Colombier 
1617dd7cddfSDavid du Colombier 	/*
1627dd7cddfSDavid du Colombier 	 *  try the name directly
1637dd7cddfSDavid du Colombier 	 */
1647dd7cddfSDavid du Colombier 	rp = dnresolve1(name, class, type, req, depth, recurse);
165225077b0SDavid du Colombier 	if(rp == nil) {
166225077b0SDavid du Colombier 		/*
167225077b0SDavid du Colombier 		 * try it as a canonical name if we weren't told
168225077b0SDavid du Colombier 		 * that the name didn't exist
169225077b0SDavid du Colombier 		 */
1707dd7cddfSDavid du Colombier 		dp = dnlookup(name, class, 0);
1714f8f669cSDavid du Colombier 		if(type != Tptr && dp->respcode != Rname)
1727dd7cddfSDavid du Colombier 			for(loops = 0; rp == nil && loops < 32; loops++){
173225077b0SDavid du Colombier 				rp = dnresolve1(name, class, Tcname, req,
174225077b0SDavid du Colombier 					depth, recurse);
1757dd7cddfSDavid du Colombier 				if(rp == nil)
1767dd7cddfSDavid du Colombier 					break;
17780ee5cbfSDavid du Colombier 
178c73252aeSDavid du Colombier 				/* rp->host == nil shouldn't happen, but does */
179c73252aeSDavid du Colombier 				if(rp->negative || rp->host == nil){
18080ee5cbfSDavid du Colombier 					rrfreelist(rp);
18180ee5cbfSDavid du Colombier 					rp = nil;
18280ee5cbfSDavid du Colombier 					break;
18380ee5cbfSDavid du Colombier 				}
1847dd7cddfSDavid du Colombier 
1857dd7cddfSDavid du Colombier 				name = rp->host->name;
186*530fef66SDavid du Colombier 				lock(&dnlock);
1877dd7cddfSDavid du Colombier 				if(cn)
1887dd7cddfSDavid du Colombier 					rrcat(cn, rp);
1897dd7cddfSDavid du Colombier 				else
1907dd7cddfSDavid du Colombier 					rrfreelist(rp);
191*530fef66SDavid du Colombier 				unlock(&dnlock);
1927dd7cddfSDavid du Colombier 
193225077b0SDavid du Colombier 				rp = dnresolve1(name, class, type, req,
194225077b0SDavid du Colombier 					depth, recurse);
1957dd7cddfSDavid du Colombier 			}
1967dd7cddfSDavid du Colombier 
1977dd7cddfSDavid du Colombier 		/* distinction between not found and not good */
1984f8f669cSDavid du Colombier 		if(rp == nil && status != nil && dp->respcode != 0)
1994f8f669cSDavid du Colombier 			*status = dp->respcode;
200225077b0SDavid du Colombier 	}
2014f8f669cSDavid du Colombier 	procsetname(procname);
2024f8f669cSDavid du Colombier 	free(procname);
2037dd7cddfSDavid du Colombier 	return randomize(rp);
2043e12c5d1SDavid du Colombier }
2053e12c5d1SDavid du Colombier 
2063cbadd90SDavid du Colombier static void
2073cbadd90SDavid du Colombier queryinit(Query *qp, DN *dp, int type, Request *req)
2083cbadd90SDavid du Colombier {
2093cbadd90SDavid du Colombier 	memset(qp, 0, sizeof *qp);
2104e7b9544SDavid du Colombier 	qp->udpfd = qp->tcpfd = qp->tcpctlfd = -1;
2113cbadd90SDavid du Colombier 	qp->dp = dp;
2123cbadd90SDavid du Colombier 	qp->type = type;
213adb31a62SDavid du Colombier 	if (qp->type != type)
214adb31a62SDavid du Colombier 		dnslog("queryinit: bogus type %d", type);
2153cbadd90SDavid du Colombier 	qp->req = req;
2163cbadd90SDavid du Colombier 	qp->nsrp = nil;
2173cbadd90SDavid du Colombier 	qp->dest = qp->curdest = nil;
2183cbadd90SDavid du Colombier 	qp->magic = Querymagic;
2193cbadd90SDavid du Colombier }
2203cbadd90SDavid du Colombier 
2213cbadd90SDavid du Colombier static void
2223cbadd90SDavid du Colombier queryck(Query *qp)
2233cbadd90SDavid du Colombier {
2243cbadd90SDavid du Colombier 	assert(qp);
2253cbadd90SDavid du Colombier 	assert(qp->magic == Querymagic);
2263cbadd90SDavid du Colombier }
2273cbadd90SDavid du Colombier 
2283cbadd90SDavid du Colombier static void
2294e7b9544SDavid du Colombier querydestroy(Query *qp)
2304e7b9544SDavid du Colombier {
2314e7b9544SDavid du Colombier 	queryck(qp);
232*530fef66SDavid du Colombier 	/* leave udpfd open */
2334e7b9544SDavid du Colombier 	if (qp->tcpfd > 0)
2344e7b9544SDavid du Colombier 		close(qp->tcpfd);
2354e7b9544SDavid du Colombier 	if (qp->tcpctlfd > 0) {
2364e7b9544SDavid du Colombier 		hangup(qp->tcpctlfd);
2374e7b9544SDavid du Colombier 		close(qp->tcpctlfd);
2384e7b9544SDavid du Colombier 	}
239a41547ffSDavid du Colombier 	free(qp->dest);
2404e7b9544SDavid du Colombier 	memset(qp, 0, sizeof *qp);	/* prevent accidents */
2414e7b9544SDavid du Colombier 	qp->udpfd = qp->tcpfd = qp->tcpctlfd = -1;
2424e7b9544SDavid du Colombier }
2434e7b9544SDavid du Colombier 
2444e7b9544SDavid du Colombier static void
2453cbadd90SDavid du Colombier destinit(Dest *p)
2463cbadd90SDavid du Colombier {
2473cbadd90SDavid du Colombier 	memset(p, 0, sizeof *p);
2483cbadd90SDavid du Colombier 	p->magic = Destmagic;
2493cbadd90SDavid du Colombier }
2503cbadd90SDavid du Colombier 
2513cbadd90SDavid du Colombier static void
2523cbadd90SDavid du Colombier destck(Dest *p)
2533cbadd90SDavid du Colombier {
2543cbadd90SDavid du Colombier 	assert(p);
2553cbadd90SDavid du Colombier 	assert(p->magic == Destmagic);
2563cbadd90SDavid du Colombier }
2573cbadd90SDavid du Colombier 
258a41547ffSDavid du Colombier static void
259a41547ffSDavid du Colombier destdestroy(Dest *p)
260a41547ffSDavid du Colombier {
261a41547ffSDavid du Colombier 	USED(p);
262a41547ffSDavid du Colombier }
263a41547ffSDavid du Colombier 
264a41547ffSDavid du Colombier /*
265a41547ffSDavid du Colombier  * if the response to a query hasn't arrived within 100 ms.,
266a41547ffSDavid du Colombier  * it's unlikely to arrive at all.  after 1 s., it's really unlikely.
267a41547ffSDavid du Colombier  * queries for missing RRs are likely to produce time-outs rather than
268a41547ffSDavid du Colombier  * negative responses, so cname and aaaa queries are likely to time out,
269a41547ffSDavid du Colombier  * thus we don't wait very long for them.
270a41547ffSDavid du Colombier  */
271a41547ffSDavid du Colombier static void
272a41547ffSDavid du Colombier notestats(vlong start, int tmout, int type)
273a41547ffSDavid du Colombier {
274a41547ffSDavid du Colombier 	qlock(&stats);
275a41547ffSDavid du Colombier 	if (tmout) {
276a41547ffSDavid du Colombier 		stats.tmout++;
277a41547ffSDavid du Colombier 		if (type == Taaaa)
278a41547ffSDavid du Colombier 			stats.tmoutv6++;
279a41547ffSDavid du Colombier 		else if (type == Tcname)
280a41547ffSDavid du Colombier 			stats.tmoutcname++;
281a41547ffSDavid du Colombier 	} else {
282a41547ffSDavid du Colombier 		long wait10ths = NS2MS(nsec() - start) / 100;
283a41547ffSDavid du Colombier 
28481730632SDavid du Colombier 		if (wait10ths <= 0)
285a41547ffSDavid du Colombier 			stats.under10ths[0]++;
286a41547ffSDavid du Colombier 		else if (wait10ths >= nelem(stats.under10ths))
287a41547ffSDavid du Colombier 			stats.under10ths[nelem(stats.under10ths) - 1]++;
288a41547ffSDavid du Colombier 		else
289a41547ffSDavid du Colombier 			stats.under10ths[wait10ths]++;
290a41547ffSDavid du Colombier 	}
291a41547ffSDavid du Colombier 	qunlock(&stats);
292a41547ffSDavid du Colombier }
293a41547ffSDavid du Colombier 
294a41547ffSDavid du Colombier static void
295a41547ffSDavid du Colombier noteinmem(void)
296a41547ffSDavid du Colombier {
297a41547ffSDavid du Colombier 	qlock(&stats);
298a41547ffSDavid du Colombier 	stats.answinmem++;
299a41547ffSDavid du Colombier 	qunlock(&stats);
300a41547ffSDavid du Colombier }
301a41547ffSDavid du Colombier 
3023e12c5d1SDavid du Colombier static RR*
30346595261SDavid du Colombier issuequery(Query *qp, char *name, int class, int depth, int recurse)
3043e12c5d1SDavid du Colombier {
305f46c709fSDavid du Colombier 	char *cp;
306c73252aeSDavid du Colombier 	DN *nsdp;
30746595261SDavid du Colombier 	RR *rp, *nsrp, *dbnsrp;
3083cbadd90SDavid du Colombier 
3093e12c5d1SDavid du Colombier 	/*
3104f8f669cSDavid du Colombier 	 *  if we're running as just a resolver, query our
3117dd7cddfSDavid du Colombier 	 *  designated name servers
312219b2ee8SDavid du Colombier 	 */
3134f8f669cSDavid du Colombier 	if(cfg.resolver){
3147dd7cddfSDavid du Colombier 		nsrp = randomize(getdnsservers(class));
3157dd7cddfSDavid du Colombier 		if(nsrp != nil) {
316c73252aeSDavid du Colombier 			qp->nsrp = nsrp;
317c73252aeSDavid du Colombier 			if(netquery(qp, depth+1)){
3187dd7cddfSDavid du Colombier 				rrfreelist(nsrp);
319c73252aeSDavid du Colombier 				return rrlookup(qp->dp, qp->type, OKneg);
3207dd7cddfSDavid du Colombier 			}
3217dd7cddfSDavid du Colombier 			rrfreelist(nsrp);
3227dd7cddfSDavid du Colombier 		}
323219b2ee8SDavid du Colombier 	}
324219b2ee8SDavid du Colombier 
325219b2ee8SDavid du Colombier 	/*
3263e12c5d1SDavid du Colombier  	 *  walk up the domain name looking for
3273e12c5d1SDavid du Colombier 	 *  a name server for the domain.
3283e12c5d1SDavid du Colombier 	 */
3293e12c5d1SDavid du Colombier 	for(cp = name; cp; cp = walkup(cp)){
3307dd7cddfSDavid du Colombier 		/*
3317dd7cddfSDavid du Colombier 		 *  if this is a local (served by us) domain,
3327dd7cddfSDavid du Colombier 		 *  return answer
3337dd7cddfSDavid du Colombier 		 */
3347dd7cddfSDavid du Colombier 		dbnsrp = randomize(dblookup(cp, class, Tns, 0, 0));
3357dd7cddfSDavid du Colombier 		if(dbnsrp && dbnsrp->local){
336c73252aeSDavid du Colombier 			rp = dblookup(name, class, qp->type, 1, dbnsrp->ttl);
3377dd7cddfSDavid du Colombier 			rrfreelist(dbnsrp);
3387dd7cddfSDavid du Colombier 			return rp;
3397dd7cddfSDavid du Colombier 		}
3407dd7cddfSDavid du Colombier 
3417dd7cddfSDavid du Colombier 		/*
3427dd7cddfSDavid du Colombier 		 *  if recursion isn't set, just accept local
3437dd7cddfSDavid du Colombier 		 *  entries
3447dd7cddfSDavid du Colombier 		 */
3457dd7cddfSDavid du Colombier 		if(recurse == Dontrecurse){
3467dd7cddfSDavid du Colombier 			if(dbnsrp)
3477dd7cddfSDavid du Colombier 				rrfreelist(dbnsrp);
3487dd7cddfSDavid du Colombier 			continue;
3497dd7cddfSDavid du Colombier 		}
3507dd7cddfSDavid du Colombier 
3517dd7cddfSDavid du Colombier 		/* look for ns in cache */
3523e12c5d1SDavid du Colombier 		nsdp = dnlookup(cp, class, 0);
3537dd7cddfSDavid du Colombier 		nsrp = nil;
3543e12c5d1SDavid du Colombier 		if(nsdp)
3557dd7cddfSDavid du Colombier 			nsrp = randomize(rrlookup(nsdp, Tns, NOneg));
3567dd7cddfSDavid du Colombier 
3577dd7cddfSDavid du Colombier 		/* if the entry timed out, ignore it */
3587dd7cddfSDavid du Colombier 		if(nsrp && nsrp->ttl < now){
3597dd7cddfSDavid du Colombier 			rrfreelist(nsrp);
3607dd7cddfSDavid du Colombier 			nsrp = nil;
3617dd7cddfSDavid du Colombier 		}
3623e12c5d1SDavid du Colombier 
3633e12c5d1SDavid du Colombier 		if(nsrp){
3647dd7cddfSDavid du Colombier 			rrfreelist(dbnsrp);
3657dd7cddfSDavid du Colombier 
3664f8f669cSDavid du Colombier 			/* query the name servers found in cache */
367c73252aeSDavid du Colombier 			qp->nsrp = nsrp;
368c73252aeSDavid du Colombier 			if(netquery(qp, depth+1)){
3693e12c5d1SDavid du Colombier 				rrfreelist(nsrp);
370c73252aeSDavid du Colombier 				return rrlookup(qp->dp, qp->type, OKneg);
3717dd7cddfSDavid du Colombier 			}
3727dd7cddfSDavid du Colombier 			rrfreelist(nsrp);
3737dd7cddfSDavid du Colombier 			continue;
3743e12c5d1SDavid du Colombier 		}
3753e12c5d1SDavid du Colombier 
3767dd7cddfSDavid du Colombier 		/* use ns from db */
3777dd7cddfSDavid du Colombier 		if(dbnsrp){
3787dd7cddfSDavid du Colombier 			/* try the name servers found in db */
379c73252aeSDavid du Colombier 			qp->nsrp = dbnsrp;
380c73252aeSDavid du Colombier 			if(netquery(qp, depth+1)){
3813e12c5d1SDavid du Colombier 				/* we got an answer */
3827dd7cddfSDavid du Colombier 				rrfreelist(dbnsrp);
383c73252aeSDavid du Colombier 				return rrlookup(qp->dp, qp->type, NOneg);
3843e12c5d1SDavid du Colombier 			}
3857dd7cddfSDavid du Colombier 			rrfreelist(dbnsrp);
3863e12c5d1SDavid du Colombier 		}
3873e12c5d1SDavid du Colombier 	}
38846595261SDavid du Colombier 	return nil;
389c73252aeSDavid du Colombier }
390c73252aeSDavid du Colombier 
391c73252aeSDavid du Colombier static RR*
392c73252aeSDavid du Colombier dnresolve1(char *name, int class, int type, Request *req, int depth,
393c73252aeSDavid du Colombier 	int recurse)
394c73252aeSDavid du Colombier {
395c73252aeSDavid du Colombier 	Area *area;
396c73252aeSDavid du Colombier 	DN *dp;
397c73252aeSDavid du Colombier 	RR *rp;
398c73252aeSDavid du Colombier 	Query *qp;
399c73252aeSDavid du Colombier 
400c73252aeSDavid du Colombier 	if(debug)
401c73252aeSDavid du Colombier 		dnslog("[%d] dnresolve1 %s %d %d", getpid(), name, type, class);
402c73252aeSDavid du Colombier 
403c73252aeSDavid du Colombier 	/* only class Cin implemented so far */
404c73252aeSDavid du Colombier 	if(class != Cin)
405c73252aeSDavid du Colombier 		return nil;
406c73252aeSDavid du Colombier 
407c73252aeSDavid du Colombier 	dp = dnlookup(name, class, 1);
408c73252aeSDavid du Colombier 
409c73252aeSDavid du Colombier 	/*
410c73252aeSDavid du Colombier 	 *  Try the cache first
411c73252aeSDavid du Colombier 	 */
412c73252aeSDavid du Colombier 	rp = rrlookup(dp, type, OKneg);
413c73252aeSDavid du Colombier 	if(rp)
414c73252aeSDavid du Colombier 		if(rp->db){
415c73252aeSDavid du Colombier 			/* unauthoritative db entries are hints */
416c73252aeSDavid du Colombier 			if(rp->auth) {
417c73252aeSDavid du Colombier 				noteinmem();
418c73252aeSDavid du Colombier 				return rp;
419c73252aeSDavid du Colombier 			}
420c73252aeSDavid du Colombier 		} else
421c73252aeSDavid du Colombier 			/* cached entry must still be valid */
422c73252aeSDavid du Colombier 			if(rp->ttl > now)
423c73252aeSDavid du Colombier 				/* but Tall entries are special */
424c73252aeSDavid du Colombier 				if(type != Tall || rp->query == Tall) {
425c73252aeSDavid du Colombier 					noteinmem();
426c73252aeSDavid du Colombier 					return rp;
427c73252aeSDavid du Colombier 				}
428c73252aeSDavid du Colombier 	rrfreelist(rp);
42946595261SDavid du Colombier 	rp = nil;		/* accident prevention */
43046595261SDavid du Colombier 	USED(rp);
431c73252aeSDavid du Colombier 
432c73252aeSDavid du Colombier 	/*
433c73252aeSDavid du Colombier 	 * try the cache for a canonical name. if found punt
434c73252aeSDavid du Colombier 	 * since we'll find it during the canonical name search
435c73252aeSDavid du Colombier 	 * in dnresolve().
436c73252aeSDavid du Colombier 	 */
437c73252aeSDavid du Colombier 	if(type != Tcname){
438c73252aeSDavid du Colombier 		rp = rrlookup(dp, Tcname, NOneg);
439c73252aeSDavid du Colombier 		rrfreelist(rp);
440c73252aeSDavid du Colombier 		if(rp)
441c73252aeSDavid du Colombier 			return nil;
442c73252aeSDavid du Colombier 	}
443c73252aeSDavid du Colombier 
444c73252aeSDavid du Colombier 	/*
445c73252aeSDavid du Colombier 	 * if the domain name is within an area of ours,
446c73252aeSDavid du Colombier 	 * we should have found its data in memory by now.
447c73252aeSDavid du Colombier 	 */
448c73252aeSDavid du Colombier 	area = inmyarea(dp->name);
449c73252aeSDavid du Colombier 	if (area || strncmp(dp->name, "local#", 6) == 0) {
450c73252aeSDavid du Colombier //		char buf[32];
451c73252aeSDavid du Colombier 
452c73252aeSDavid du Colombier //		dnslog("%s %s: no data in area %s", dp->name,
453c73252aeSDavid du Colombier //			rrname(type, buf, sizeof buf), area->soarr->owner->name);
454c73252aeSDavid du Colombier 		return nil;
455c73252aeSDavid du Colombier 	}
456c73252aeSDavid du Colombier 
457c73252aeSDavid du Colombier 	qp = emalloc(sizeof *qp);
458c73252aeSDavid du Colombier 	queryinit(qp, dp, type, req);
45946595261SDavid du Colombier 	rp = issuequery(qp, name, class, depth, recurse);
460c73252aeSDavid du Colombier 	querydestroy(qp);
461c73252aeSDavid du Colombier 	free(qp);
462c73252aeSDavid du Colombier 	if(rp)
463c73252aeSDavid du Colombier 		return rp;
4643e12c5d1SDavid du Colombier 
4657dd7cddfSDavid du Colombier 	/* settle for a non-authoritative answer */
4667dd7cddfSDavid du Colombier 	rp = rrlookup(dp, type, OKneg);
4677dd7cddfSDavid du Colombier 	if(rp)
4687dd7cddfSDavid du Colombier 		return rp;
4697dd7cddfSDavid du Colombier 
4707dd7cddfSDavid du Colombier 	/* noone answered.  try the database, we might have a chance. */
4717dd7cddfSDavid du Colombier 	return dblookup(name, class, type, 0, 0);
4723e12c5d1SDavid du Colombier }
4733e12c5d1SDavid du Colombier 
4743e12c5d1SDavid du Colombier /*
4754f8f669cSDavid du Colombier  *  walk a domain name one element to the right.
4764f8f669cSDavid du Colombier  *  return a pointer to that element.
4773e12c5d1SDavid du Colombier  *  in other words, return a pointer to the parent domain name.
4783e12c5d1SDavid du Colombier  */
4793e12c5d1SDavid du Colombier char*
4803e12c5d1SDavid du Colombier walkup(char *name)
4813e12c5d1SDavid du Colombier {
4823e12c5d1SDavid du Colombier 	char *cp;
4833e12c5d1SDavid du Colombier 
4843e12c5d1SDavid du Colombier 	cp = strchr(name, '.');
4853e12c5d1SDavid du Colombier 	if(cp)
4863e12c5d1SDavid du Colombier 		return cp+1;
4873e12c5d1SDavid du Colombier 	else if(*name)
4883e12c5d1SDavid du Colombier 		return "";
4893e12c5d1SDavid du Colombier 	else
4903e12c5d1SDavid du Colombier 		return 0;
4913e12c5d1SDavid du Colombier }
4923e12c5d1SDavid du Colombier 
4933e12c5d1SDavid du Colombier /*
494a41547ffSDavid du Colombier  *  Get a udp port for sending requests and reading replies.  Put the port
4953e12c5d1SDavid du Colombier  *  into "headers" mode.
4963e12c5d1SDavid du Colombier  */
4973e12c5d1SDavid du Colombier static char *hmsg = "headers";
4983e12c5d1SDavid du Colombier 
499dc5a79c1SDavid du Colombier int
5004f8f669cSDavid du Colombier udpport(char *mtpt)
5013e12c5d1SDavid du Colombier {
5023e12c5d1SDavid du Colombier 	int fd, ctl;
5034f8f669cSDavid du Colombier 	char ds[64], adir[64];
5043e12c5d1SDavid du Colombier 
5053e12c5d1SDavid du Colombier 	/* get a udp port */
5064f8f669cSDavid du Colombier 	snprint(ds, sizeof ds, "%s/udp!*!0", (mtpt? mtpt: "/net"));
5077dd7cddfSDavid du Colombier 	ctl = announce(ds, adir);
5087dd7cddfSDavid du Colombier 	if(ctl < 0){
5097dd7cddfSDavid du Colombier 		/* warning("can't get udp port"); */
510bd389b36SDavid du Colombier 		return -1;
511bd389b36SDavid du Colombier 	}
5123e12c5d1SDavid du Colombier 
5133e12c5d1SDavid du Colombier 	/* turn on header style interface */
514410ea80bSDavid du Colombier 	if(write(ctl, hmsg, strlen(hmsg)) != strlen(hmsg)){
5157dd7cddfSDavid du Colombier 		close(ctl);
516bd389b36SDavid du Colombier 		warning(hmsg);
517bd389b36SDavid du Colombier 		return -1;
518bd389b36SDavid du Colombier 	}
5193e12c5d1SDavid du Colombier 
5207dd7cddfSDavid du Colombier 	/* grab the data file */
5214f8f669cSDavid du Colombier 	snprint(ds, sizeof ds, "%s/data", adir);
5227dd7cddfSDavid du Colombier 	fd = open(ds, ORDWR);
5233e12c5d1SDavid du Colombier 	close(ctl);
5244f8f669cSDavid du Colombier 	if(fd < 0)
5254f8f669cSDavid du Colombier 		warning("can't open udp port %s: %r", ds);
5263e12c5d1SDavid du Colombier 	return fd;
5273e12c5d1SDavid du Colombier }
5283e12c5d1SDavid du Colombier 
529d6d99297SDavid du Colombier /* generate a DNS UDP query packet */
530dc5a79c1SDavid du Colombier int
531dc5a79c1SDavid du Colombier mkreq(DN *dp, int type, uchar *buf, int flags, ushort reqno)
5323e12c5d1SDavid du Colombier {
5333e12c5d1SDavid du Colombier 	DNSmsg m;
5343e12c5d1SDavid du Colombier 	int len;
535f27a9a5aSDavid du Colombier 	Udphdr *uh = (Udphdr*)buf;
5363e12c5d1SDavid du Colombier 
5373e12c5d1SDavid du Colombier 	/* stuff port number into output buffer */
5383cbadd90SDavid du Colombier 	memset(uh, 0, sizeof *uh);
5397dd7cddfSDavid du Colombier 	hnputs(uh->rport, 53);
5403e12c5d1SDavid du Colombier 
5413e12c5d1SDavid du Colombier 	/* make request and convert it to output format */
5423cbadd90SDavid du Colombier 	memset(&m, 0, sizeof m);
543dc5a79c1SDavid du Colombier 	m.flags = flags;
5443e12c5d1SDavid du Colombier 	m.id = reqno;
5453e12c5d1SDavid du Colombier 	m.qd = rralloc(type);
5463e12c5d1SDavid du Colombier 	m.qd->owner = dp;
5473e12c5d1SDavid du Colombier 	m.qd->type = type;
548adb31a62SDavid du Colombier 	if (m.qd->type != type)
549adb31a62SDavid du Colombier 		dnslog("mkreq: bogus type %d", type);
550f27a9a5aSDavid du Colombier 	len = convDNS2M(&m, &buf[Udphdrsize], Maxudp);
5517dd7cddfSDavid du Colombier 	rrfree(m.qd);
5526aaebd7dSDavid du Colombier 	memset(&m, 0, sizeof m);		/* cause trouble */
5533e12c5d1SDavid du Colombier 	return len;
5543e12c5d1SDavid du Colombier }
5553e12c5d1SDavid du Colombier 
556a41547ffSDavid du Colombier void
5577dd7cddfSDavid du Colombier freeanswers(DNSmsg *mp)
5583e12c5d1SDavid du Colombier {
5597dd7cddfSDavid du Colombier 	rrfreelist(mp->qd);
5607dd7cddfSDavid du Colombier 	rrfreelist(mp->an);
5617dd7cddfSDavid du Colombier 	rrfreelist(mp->ns);
5627dd7cddfSDavid du Colombier 	rrfreelist(mp->ar);
5634f8f669cSDavid du Colombier 	mp->qd = mp->an = mp->ns = mp->ar = nil;
5647dd7cddfSDavid du Colombier }
5657dd7cddfSDavid du Colombier 
5663cbadd90SDavid du Colombier /* sets srcip */
5673cbadd90SDavid du Colombier static int
5683cbadd90SDavid du Colombier readnet(Query *qp, int medium, uchar *ibuf, ulong endtime, uchar **replyp,
5693cbadd90SDavid du Colombier 	uchar *srcip)
5703cbadd90SDavid du Colombier {
5713cbadd90SDavid du Colombier 	int len, fd;
572a41547ffSDavid du Colombier 	long ms;
573a41547ffSDavid du Colombier 	vlong startns = nsec();
5743cbadd90SDavid du Colombier 	uchar *reply;
5753cbadd90SDavid du Colombier 	uchar lenbuf[2];
5763cbadd90SDavid du Colombier 
5773cbadd90SDavid du Colombier 	/* timed read of reply */
578a41547ffSDavid du Colombier 	ms = S2MS(endtime) - NS2MS(startns);
579a41547ffSDavid du Colombier 	if (ms < 2000)
580a41547ffSDavid du Colombier 		ms = 2000;	/* give the remote ns a fighting chance */
5813cbadd90SDavid du Colombier 	reply = ibuf;
5823cbadd90SDavid du Colombier 	len = -1;			/* pessimism */
5834e7b9544SDavid du Colombier 	memset(srcip, 0, IPaddrlen);
584a41547ffSDavid du Colombier 	if (medium == Udp)
5854e7b9544SDavid du Colombier 		if (qp->udpfd <= 0)
5864e7b9544SDavid du Colombier 			dnslog("readnet: qp->udpfd closed");
5874e7b9544SDavid du Colombier 		else {
588a41547ffSDavid du Colombier 			alarm(ms);
589f27a9a5aSDavid du Colombier 			len = read(qp->udpfd, ibuf, Udphdrsize+Maxudpin);
590a41547ffSDavid du Colombier 			alarm(0);
591a41547ffSDavid du Colombier 			notestats(startns, len < 0, qp->type);
5924e7b9544SDavid du Colombier 			if (len >= IPaddrlen)
5934e7b9544SDavid du Colombier 				memmove(srcip, ibuf, IPaddrlen);
594f27a9a5aSDavid du Colombier 			if (len >= Udphdrsize) {
595f27a9a5aSDavid du Colombier 				len   -= Udphdrsize;
596f27a9a5aSDavid du Colombier 				reply += Udphdrsize;
5974e7b9544SDavid du Colombier 			}
5984e7b9544SDavid du Colombier 		}
599a41547ffSDavid du Colombier 	else {
6003cbadd90SDavid du Colombier 		if (!qp->tcpset)
6013cbadd90SDavid du Colombier 			dnslog("readnet: tcp params not set");
602a41547ffSDavid du Colombier 		alarm(ms);
6033cbadd90SDavid du Colombier 		fd = qp->tcpfd;
6043cbadd90SDavid du Colombier 		if (fd <= 0)
6053cbadd90SDavid du Colombier 			dnslog("readnet: %s: tcp fd unset for dest %I",
6063cbadd90SDavid du Colombier 				qp->dp->name, qp->tcpip);
6073cbadd90SDavid du Colombier 		else if (readn(fd, lenbuf, 2) != 2) {
6083cbadd90SDavid du Colombier 			dnslog("readnet: short read of tcp size from %I",
6093cbadd90SDavid du Colombier 				qp->tcpip);
610a41547ffSDavid du Colombier 			/* probably a time-out */
611a41547ffSDavid du Colombier 			notestats(startns, 1, qp->type);
6123cbadd90SDavid du Colombier 		} else {
6133cbadd90SDavid du Colombier 			len = lenbuf[0]<<8 | lenbuf[1];
6143cbadd90SDavid du Colombier 			if (readn(fd, ibuf, len) != len) {
6153cbadd90SDavid du Colombier 				dnslog("readnet: short read of tcp data from %I",
6163cbadd90SDavid du Colombier 					qp->tcpip);
617a41547ffSDavid du Colombier 				/* probably a time-out */
618a41547ffSDavid du Colombier 				notestats(startns, 1, qp->type);
6193cbadd90SDavid du Colombier 				len = -1;
6203cbadd90SDavid du Colombier 			}
6213cbadd90SDavid du Colombier 		}
622a41547ffSDavid du Colombier 		alarm(0);
6233cbadd90SDavid du Colombier 		memmove(srcip, qp->tcpip, IPaddrlen);
6243cbadd90SDavid du Colombier 	}
6253cbadd90SDavid du Colombier 	*replyp = reply;
6263cbadd90SDavid du Colombier 	return len;
6273cbadd90SDavid du Colombier }
6283cbadd90SDavid du Colombier 
6297dd7cddfSDavid du Colombier /*
6303cbadd90SDavid du Colombier  *  read replies to a request and remember the rrs in the answer(s).
6313cbadd90SDavid du Colombier  *  ignore any of the wrong type.
6324f8f669cSDavid du Colombier  *  wait at most until endtime.
6337dd7cddfSDavid du Colombier  */
6347dd7cddfSDavid du Colombier static int
6353cbadd90SDavid du Colombier readreply(Query *qp, int medium, ushort req, uchar *ibuf, DNSmsg *mp,
6363cbadd90SDavid du Colombier 	ulong endtime)
6377dd7cddfSDavid du Colombier {
638a41547ffSDavid du Colombier 	int len, rv;
6397dd7cddfSDavid du Colombier 	char *err;
640a41547ffSDavid du Colombier 	char tbuf[32];
6413cbadd90SDavid du Colombier 	uchar *reply;
6423cbadd90SDavid du Colombier 	uchar srcip[IPaddrlen];
6433e12c5d1SDavid du Colombier 	RR *rp;
6447dd7cddfSDavid du Colombier 
6453cbadd90SDavid du Colombier 	queryck(qp);
6463cbadd90SDavid du Colombier 	rv = 0;
6473cbadd90SDavid du Colombier 	memset(mp, 0, sizeof *mp);
6483cbadd90SDavid du Colombier 	if (time(nil) >= endtime)
6493cbadd90SDavid du Colombier 		return -1;		/* timed out before we started */
6507dd7cddfSDavid du Colombier 
651a41547ffSDavid du Colombier 	memset(srcip, 0, sizeof srcip);
652a41547ffSDavid du Colombier 	if (0)
653a41547ffSDavid du Colombier 		len = -1;
6543cbadd90SDavid du Colombier 	for (; time(nil) < endtime &&
6553cbadd90SDavid du Colombier 	    (len = readnet(qp, medium, ibuf, endtime, &reply, srcip)) >= 0;
6563cbadd90SDavid du Colombier 	    freeanswers(mp)){
6577dd7cddfSDavid du Colombier 		/* convert into internal format  */
6583cbadd90SDavid du Colombier 		memset(mp, 0, sizeof *mp);
6593cbadd90SDavid du Colombier 		err = convM2DNS(reply, len, mp, nil);
660d6d99297SDavid du Colombier 		if (mp->flags & Ftrunc) {
661a41547ffSDavid du Colombier 			free(err);
662a41547ffSDavid du Colombier 			freeanswers(mp);
663a41547ffSDavid du Colombier 			/* notify our caller to retry the query via tcp. */
6643cbadd90SDavid du Colombier 			return -1;
6653cbadd90SDavid du Colombier 		} else if(err){
6663cbadd90SDavid du Colombier 			dnslog("readreply: %s: input err, len %d: %s: %I",
6673cbadd90SDavid du Colombier 				qp->dp->name, len, err, srcip);
6683cbadd90SDavid du Colombier 			free(err);
6697dd7cddfSDavid du Colombier 			continue;
6707dd7cddfSDavid du Colombier 		}
6717dd7cddfSDavid du Colombier 		if(debug)
6723cbadd90SDavid du Colombier 			logreply(qp->req->id, srcip, mp);
6737dd7cddfSDavid du Colombier 
6747dd7cddfSDavid du Colombier 		/* answering the right question? */
6753cbadd90SDavid du Colombier 		if(mp->id != req)
6763cbadd90SDavid du Colombier 			dnslog("%d: id %d instead of %d: %I", qp->req->id,
6773cbadd90SDavid du Colombier 				mp->id, req, srcip);
6783cbadd90SDavid du Colombier 		else if(mp->qd == 0)
6793cbadd90SDavid du Colombier 			dnslog("%d: no question RR: %I", qp->req->id, srcip);
6803cbadd90SDavid du Colombier 		else if(mp->qd->owner != qp->dp)
6813cbadd90SDavid du Colombier 			dnslog("%d: owner %s instead of %s: %I", qp->req->id,
6823cbadd90SDavid du Colombier 				mp->qd->owner->name, qp->dp->name, srcip);
6833cbadd90SDavid du Colombier 		else if(mp->qd->type != qp->type)
6843cbadd90SDavid du Colombier 			dnslog("%d: qp->type %d instead of %d: %I",
6853cbadd90SDavid du Colombier 				qp->req->id, mp->qd->type, qp->type, srcip);
6863cbadd90SDavid du Colombier 		else {
6877dd7cddfSDavid du Colombier 			/* remember what request this is in answer to */
6887dd7cddfSDavid du Colombier 			for(rp = mp->an; rp; rp = rp->next)
6893cbadd90SDavid du Colombier 				rp->query = qp->type;
6903cbadd90SDavid du Colombier 			return rv;
6917dd7cddfSDavid du Colombier 		}
6927dd7cddfSDavid du Colombier 	}
693a41547ffSDavid du Colombier 	if (time(nil) >= endtime) {
694a41547ffSDavid du Colombier 		;				/* query expired */
695a41547ffSDavid du Colombier 	} else if (0) {
696a41547ffSDavid du Colombier 		/* this happens routinely when a read times out */
697a41547ffSDavid du Colombier 		dnslog("readreply: %s type %s: ns %I read error or eof "
698a41547ffSDavid du Colombier 			"(returned %d): %r", qp->dp->name, rrname(qp->type,
699a41547ffSDavid du Colombier 			tbuf, sizeof tbuf), srcip, len);
700a41547ffSDavid du Colombier 		if (medium == Udp)
701a41547ffSDavid du Colombier 			for (rp = qp->nsrp; rp != nil; rp = rp->next)
702a41547ffSDavid du Colombier 				if (rp->type == Tns)
703a41547ffSDavid du Colombier 					dnslog("readreply: %s: query sent to "
704a41547ffSDavid du Colombier 						"ns %s", qp->dp->name,
705a41547ffSDavid du Colombier 						rp->host->name);
706a41547ffSDavid du Colombier 	}
7073cbadd90SDavid du Colombier 	return -1;
7083cbadd90SDavid du Colombier }
7097dd7cddfSDavid du Colombier 
7107dd7cddfSDavid du Colombier /*
7117dd7cddfSDavid du Colombier  *	return non-0 if first list includes second list
7127dd7cddfSDavid du Colombier  */
7137dd7cddfSDavid du Colombier int
7147dd7cddfSDavid du Colombier contains(RR *rp1, RR *rp2)
7157dd7cddfSDavid du Colombier {
7167dd7cddfSDavid du Colombier 	RR *trp1, *trp2;
7177dd7cddfSDavid du Colombier 
7187dd7cddfSDavid du Colombier 	for(trp2 = rp2; trp2; trp2 = trp2->next){
7193cbadd90SDavid du Colombier 		for(trp1 = rp1; trp1; trp1 = trp1->next)
7207dd7cddfSDavid du Colombier 			if(trp1->type == trp2->type)
7217dd7cddfSDavid du Colombier 			if(trp1->host == trp2->host)
7227dd7cddfSDavid du Colombier 			if(trp1->owner == trp2->owner)
7237dd7cddfSDavid du Colombier 				break;
7244f8f669cSDavid du Colombier 		if(trp1 == nil)
7257dd7cddfSDavid du Colombier 			return 0;
7267dd7cddfSDavid du Colombier 	}
7277dd7cddfSDavid du Colombier 	return 1;
7287dd7cddfSDavid du Colombier }
7297dd7cddfSDavid du Colombier 
7307dd7cddfSDavid du Colombier 
7316b6b9ac8SDavid du Colombier /*
7326b6b9ac8SDavid du Colombier  *  return multicast version if any
7336b6b9ac8SDavid du Colombier  */
7346b6b9ac8SDavid du Colombier int
7356b6b9ac8SDavid du Colombier ipisbm(uchar *ip)
7366b6b9ac8SDavid du Colombier {
7376b6b9ac8SDavid du Colombier 	if(isv4(ip)){
7384f8f669cSDavid du Colombier 		if (ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0 ||
7394f8f669cSDavid du Colombier 		    ipcmp(ip, IPv4bcast) == 0)
7406b6b9ac8SDavid du Colombier 			return 4;
7414f8f669cSDavid du Colombier 	} else
7426b6b9ac8SDavid du Colombier 		if(ip[0] == 0xff)
7436b6b9ac8SDavid du Colombier 			return 6;
74428a8a86bSDavid du Colombier 	return 0;
74528a8a86bSDavid du Colombier }
74628a8a86bSDavid du Colombier 
7477dd7cddfSDavid du Colombier /*
7487dd7cddfSDavid du Colombier  *  Get next server address
7497dd7cddfSDavid du Colombier  */
7507dd7cddfSDavid du Colombier static int
7513cbadd90SDavid du Colombier serveraddrs(Query *qp, int nd, int depth)
7527dd7cddfSDavid du Colombier {
7537dd7cddfSDavid du Colombier 	RR *rp, *arp, *trp;
7547dd7cddfSDavid du Colombier 	Dest *cur;
7557dd7cddfSDavid du Colombier 
7567dd7cddfSDavid du Colombier 	if(nd >= Maxdest)
7577dd7cddfSDavid du Colombier 		return 0;
7587dd7cddfSDavid du Colombier 
7597dd7cddfSDavid du Colombier 	/*
7607dd7cddfSDavid du Colombier 	 *  look for a server whose address we already know.
7617dd7cddfSDavid du Colombier 	 *  if we find one, mark it so we ignore this on
7627dd7cddfSDavid du Colombier 	 *  subsequent passes.
7637dd7cddfSDavid du Colombier 	 */
7647dd7cddfSDavid du Colombier 	arp = 0;
7653cbadd90SDavid du Colombier 	for(rp = qp->nsrp; rp; rp = rp->next){
76634f77ae3SDavid du Colombier 		assert(rp->magic == RRmagic);
7677dd7cddfSDavid du Colombier 		if(rp->marker)
7687dd7cddfSDavid du Colombier 			continue;
7697dd7cddfSDavid du Colombier 		arp = rrlookup(rp->host, Ta, NOneg);
7707dd7cddfSDavid du Colombier 		if(arp){
7717dd7cddfSDavid du Colombier 			rp->marker = 1;
7727dd7cddfSDavid du Colombier 			break;
7737dd7cddfSDavid du Colombier 		}
7747dd7cddfSDavid du Colombier 		arp = dblookup(rp->host->name, Cin, Ta, 0, 0);
7757dd7cddfSDavid du Colombier 		if(arp){
7767dd7cddfSDavid du Colombier 			rp->marker = 1;
7777dd7cddfSDavid du Colombier 			break;
7787dd7cddfSDavid du Colombier 		}
7797dd7cddfSDavid du Colombier 	}
7807dd7cddfSDavid du Colombier 
7817dd7cddfSDavid du Colombier 	/*
7827dd7cddfSDavid du Colombier 	 *  if the cache and database lookup didn't find any new
7837dd7cddfSDavid du Colombier 	 *  server addresses, try resolving one via the network.
7847dd7cddfSDavid du Colombier 	 *  Mark any we try to resolve so we don't try a second time.
7857dd7cddfSDavid du Colombier 	 */
7864f8f669cSDavid du Colombier 	if(arp == 0)
7873cbadd90SDavid du Colombier 		for(rp = qp->nsrp; rp; rp = rp->next){
7887dd7cddfSDavid du Colombier 			if(rp->marker)
7897dd7cddfSDavid du Colombier 				continue;
7907dd7cddfSDavid du Colombier 			rp->marker = 1;
7917dd7cddfSDavid du Colombier 
7927dd7cddfSDavid du Colombier 			/*
7937dd7cddfSDavid du Colombier 			 *  avoid loops looking up a server under itself
7947dd7cddfSDavid du Colombier 			 */
7957dd7cddfSDavid du Colombier 			if(subsume(rp->owner->name, rp->host->name))
7967dd7cddfSDavid du Colombier 				continue;
7977dd7cddfSDavid du Colombier 
7983cbadd90SDavid du Colombier 			arp = dnresolve(rp->host->name, Cin, Ta, qp->req, 0,
7994f8f669cSDavid du Colombier 				depth+1, Recurse, 1, 0);
800*530fef66SDavid du Colombier 			lock(&dnlock);
8017dd7cddfSDavid du Colombier 			rrfreelist(rrremneg(&arp));
802*530fef66SDavid du Colombier 			unlock(&dnlock);
8037dd7cddfSDavid du Colombier 			if(arp)
8047dd7cddfSDavid du Colombier 				break;
8057dd7cddfSDavid du Colombier 		}
8067dd7cddfSDavid du Colombier 
8077dd7cddfSDavid du Colombier 	/* use any addresses that we found */
8083cbadd90SDavid du Colombier 	for(trp = arp; trp && nd < Maxdest; trp = trp->next){
8093cbadd90SDavid du Colombier 		cur = &qp->dest[nd];
8107dd7cddfSDavid du Colombier 		parseip(cur->a, trp->ip->name);
8114f8f669cSDavid du Colombier 		/*
8124f8f669cSDavid du Colombier 		 * straddling servers can reject all nameservers if they are all
8134f8f669cSDavid du Colombier 		 * inside, so be sure to list at least one outside ns at
8144f8f669cSDavid du Colombier 		 * the end of the ns list in /lib/ndb for `dom='.
8154f8f669cSDavid du Colombier 		 */
8164f8f669cSDavid du Colombier 		if (ipisbm(cur->a) ||
8173cbadd90SDavid du Colombier 		    cfg.straddle && !insideaddr(qp->dp->name) && insidens(cur->a))
8186b6b9ac8SDavid du Colombier 			continue;
8197dd7cddfSDavid du Colombier 		cur->nx = 0;
8207dd7cddfSDavid du Colombier 		cur->s = trp->owner;
8217dd7cddfSDavid du Colombier 		cur->code = Rtimeout;
8226b6b9ac8SDavid du Colombier 		nd++;
8237dd7cddfSDavid du Colombier 	}
8247dd7cddfSDavid du Colombier 	rrfreelist(arp);
8257dd7cddfSDavid du Colombier 	return nd;
8267dd7cddfSDavid du Colombier }
8277dd7cddfSDavid du Colombier 
8287dd7cddfSDavid du Colombier /*
8297dd7cddfSDavid du Colombier  *  cache negative responses
8307dd7cddfSDavid du Colombier  */
8317dd7cddfSDavid du Colombier static void
8327dd7cddfSDavid du Colombier cacheneg(DN *dp, int type, int rcode, RR *soarr)
8337dd7cddfSDavid du Colombier {
8347dd7cddfSDavid du Colombier 	RR *rp;
8357dd7cddfSDavid du Colombier 	DN *soaowner;
8369a747e4fSDavid du Colombier 	ulong ttl;
8377dd7cddfSDavid du Colombier 
838f46c709fSDavid du Colombier 	stats.negcached++;
839f46c709fSDavid du Colombier 
8404f8f669cSDavid du Colombier 	/* no cache time specified, don't make anything up */
8417dd7cddfSDavid du Colombier 	if(soarr != nil){
8427dd7cddfSDavid du Colombier 		if(soarr->next != nil){
8437dd7cddfSDavid du Colombier 			rrfreelist(soarr->next);
8447dd7cddfSDavid du Colombier 			soarr->next = nil;
8457dd7cddfSDavid du Colombier 		}
8467dd7cddfSDavid du Colombier 		soaowner = soarr->owner;
8477dd7cddfSDavid du Colombier 	} else
8487dd7cddfSDavid du Colombier 		soaowner = nil;
8497dd7cddfSDavid du Colombier 
8509a747e4fSDavid du Colombier 	/* the attach can cause soarr to be freed so mine it now */
8519a747e4fSDavid du Colombier 	if(soarr != nil && soarr->soa != nil)
8529a747e4fSDavid du Colombier 		ttl = soarr->soa->minttl+now;
8539a747e4fSDavid du Colombier 	else
8549a747e4fSDavid du Colombier 		ttl = 5*Min;
8559a747e4fSDavid du Colombier 
8567dd7cddfSDavid du Colombier 	/* add soa and negative RR to the database */
8576dc4800dSDavid du Colombier 	rrattach(soarr, Authoritative);
8587dd7cddfSDavid du Colombier 
8597dd7cddfSDavid du Colombier 	rp = rralloc(type);
8607dd7cddfSDavid du Colombier 	rp->owner = dp;
8617dd7cddfSDavid du Colombier 	rp->negative = 1;
8627dd7cddfSDavid du Colombier 	rp->negsoaowner = soaowner;
8637dd7cddfSDavid du Colombier 	rp->negrcode = rcode;
8649a747e4fSDavid du Colombier 	rp->ttl = ttl;
8656dc4800dSDavid du Colombier 	rrattach(rp, Authoritative);
8667dd7cddfSDavid du Colombier }
8677dd7cddfSDavid du Colombier 
8684f8f669cSDavid du Colombier static int
8694f8f669cSDavid du Colombier setdestoutns(Dest *p, int n)
8704f8f669cSDavid du Colombier {
8714f8f669cSDavid du Colombier 	uchar *outns = outsidens(n);
8724f8f669cSDavid du Colombier 
8733cbadd90SDavid du Colombier 	destck(p);
8743cbadd90SDavid du Colombier 	destinit(p);
8754f8f669cSDavid du Colombier 	if (outns == nil) {
8764f8f669cSDavid du Colombier 		if (n == 0)
8774f8f669cSDavid du Colombier 			dnslog("[%d] no outside-ns in ndb", getpid());
8784f8f669cSDavid du Colombier 		return -1;
8794f8f669cSDavid du Colombier 	}
8804f8f669cSDavid du Colombier 	memmove(p->a, outns, sizeof p->a);
8814f8f669cSDavid du Colombier 	p->s = dnlookup("outside-ns-ips", Cin, 1);
8824f8f669cSDavid du Colombier 	return 0;
8834f8f669cSDavid du Colombier }
8844f8f669cSDavid du Colombier 
8857dd7cddfSDavid du Colombier /*
8863cbadd90SDavid du Colombier  * issue query via UDP or TCP as appropriate.
8873cbadd90SDavid du Colombier  * for TCP, returns with qp->tcpip set from udppkt header.
8887dd7cddfSDavid du Colombier  */
8897dd7cddfSDavid du Colombier static int
8903cbadd90SDavid du Colombier mydnsquery(Query *qp, int medium, uchar *udppkt, int len)
8917dd7cddfSDavid du Colombier {
892a41547ffSDavid du Colombier 	int rv = -1, nfd;
8933cbadd90SDavid du Colombier 	char *domain;
8943cbadd90SDavid du Colombier 	char conndir[40];
895a41547ffSDavid du Colombier 	uchar belen[2];
8963cbadd90SDavid du Colombier 	NetConnInfo *nci;
8974f8f669cSDavid du Colombier 
8983cbadd90SDavid du Colombier 	queryck(qp);
8994e153993SDavid du Colombier 	domain = smprint("%I", udppkt);
9004e153993SDavid du Colombier 	if (myaddr(domain)) {
9014e153993SDavid du Colombier 		dnslog("mydnsquery: trying to send to myself (%s); bzzzt",
9024e153993SDavid du Colombier 			domain);
9034e153993SDavid du Colombier 		free(domain);
9044e153993SDavid du Colombier 		return rv;
9054e153993SDavid du Colombier 	}
9064e153993SDavid du Colombier 
9073cbadd90SDavid du Colombier 	switch (medium) {
9083cbadd90SDavid du Colombier 	case Udp:
9094e153993SDavid du Colombier 		free(domain);
910a41547ffSDavid du Colombier 		nfd = dup(qp->udpfd, -1);
911a41547ffSDavid du Colombier 		if (nfd < 0) {
912a41547ffSDavid du Colombier 			warning("mydnsquery: qp->udpfd %d: %r", qp->udpfd);
913a41547ffSDavid du Colombier 			close(qp->udpfd);	/* ensure it's closed */
914a41547ffSDavid du Colombier 			qp->udpfd = -1;		/* poison it */
915a41547ffSDavid du Colombier 			return rv;
916a41547ffSDavid du Colombier 		}
917a41547ffSDavid du Colombier 		close(nfd);
918a41547ffSDavid du Colombier 
9194e7b9544SDavid du Colombier 		if (qp->udpfd <= 0)
920a41547ffSDavid du Colombier 			dnslog("mydnsquery: qp->udpfd %d closed", qp->udpfd);
9214e7b9544SDavid du Colombier 		else {
922f27a9a5aSDavid du Colombier 			if (write(qp->udpfd, udppkt, len+Udphdrsize) !=
923f27a9a5aSDavid du Colombier 			    len+Udphdrsize)
924a41547ffSDavid du Colombier 				warning("sending udp msg: %r");
925a41547ffSDavid du Colombier 			else {
926a41547ffSDavid du Colombier 				stats.qsent++;
9273cbadd90SDavid du Colombier 				rv = 0;
9284e7b9544SDavid du Colombier 			}
929a41547ffSDavid du Colombier 		}
9303cbadd90SDavid du Colombier 		break;
9313cbadd90SDavid du Colombier 	case Tcp:
9323cbadd90SDavid du Colombier 		/* send via TCP & keep fd around for reply */
9333cbadd90SDavid du Colombier 		alarm(10*1000);
9343cbadd90SDavid du Colombier 		qp->tcpfd = rv = dial(netmkaddr(domain, "tcp", "dns"), nil,
9353cbadd90SDavid du Colombier 			conndir, &qp->tcpctlfd);
9363cbadd90SDavid du Colombier 		alarm(0);
9373cbadd90SDavid du Colombier 		if (qp->tcpfd < 0) {
9383cbadd90SDavid du Colombier 			dnslog("can't dial tcp!%s!dns: %r", domain);
939a41547ffSDavid du Colombier 			free(domain);
940a41547ffSDavid du Colombier 			break;
941a41547ffSDavid du Colombier 		}
942a41547ffSDavid du Colombier 		free(domain);
9433cbadd90SDavid du Colombier 		nci = getnetconninfo(conndir, qp->tcpfd);
9443cbadd90SDavid du Colombier 		if (nci) {
9453cbadd90SDavid du Colombier 			parseip(qp->tcpip, nci->rsys);
9463cbadd90SDavid du Colombier 			freenetconninfo(nci);
9473cbadd90SDavid du Colombier 		} else
9483cbadd90SDavid du Colombier 			dnslog("mydnsquery: getnetconninfo failed");
9493cbadd90SDavid du Colombier 		qp->tcpset = 1;
9503e12c5d1SDavid du Colombier 
9513cbadd90SDavid du Colombier 		belen[0] = len >> 8;
9523cbadd90SDavid du Colombier 		belen[1] = len;
9533cbadd90SDavid du Colombier 		if (write(qp->tcpfd, belen, 2) != 2 ||
954f27a9a5aSDavid du Colombier 		    write(qp->tcpfd, udppkt + Udphdrsize, len) != len)
955a41547ffSDavid du Colombier 			warning("sending tcp msg: %r");
9563cbadd90SDavid du Colombier 		break;
9573cbadd90SDavid du Colombier 	default:
9583cbadd90SDavid du Colombier 		sysfatal("mydnsquery: bad medium");
9593cbadd90SDavid du Colombier 	}
9603cbadd90SDavid du Colombier 	return rv;
9613cbadd90SDavid du Colombier }
9627dd7cddfSDavid du Colombier 
9633e12c5d1SDavid du Colombier /*
9643cbadd90SDavid du Colombier  * send query to all UDP destinations or one TCP destination,
9653cbadd90SDavid du Colombier  * taken from obuf (udp packet) header
9663e12c5d1SDavid du Colombier  */
9673cbadd90SDavid du Colombier static int
9683cbadd90SDavid du Colombier xmitquery(Query *qp, int medium, int depth, uchar *obuf, int inns, int len)
9693cbadd90SDavid du Colombier {
9703cbadd90SDavid du Colombier 	int j, n;
9713cbadd90SDavid du Colombier 	char buf[32];
9723cbadd90SDavid du Colombier 	Dest *p;
9733e12c5d1SDavid du Colombier 
9743cbadd90SDavid du Colombier 	queryck(qp);
9753cbadd90SDavid du Colombier 	if(time(nil) >= qp->req->aborttime)
9763cbadd90SDavid du Colombier 		return -1;
9773cbadd90SDavid du Colombier 
9783cbadd90SDavid du Colombier 	/*
9793cbadd90SDavid du Colombier 	 * get a nameserver address if we need one.
9803cbadd90SDavid du Colombier 	 * serveraddrs populates qp->dest.
9813cbadd90SDavid du Colombier 	 */
9823cbadd90SDavid du Colombier 	p = qp->dest;
9833cbadd90SDavid du Colombier 	destck(p);
9843cbadd90SDavid du Colombier 	if (qp->ndest < 0 || qp->ndest > Maxdest)
9853cbadd90SDavid du Colombier 		dnslog("qp->ndest %d out of range", qp->ndest);
9863cbadd90SDavid du Colombier 	if (qp->ndest > qp->curdest - p)
9873cbadd90SDavid du Colombier 		qp->curdest = &qp->dest[serveraddrs(qp, qp->curdest - p, depth)];
9883cbadd90SDavid du Colombier 	destck(qp->curdest);
9897dd7cddfSDavid du Colombier 
9907dd7cddfSDavid du Colombier 	/* no servers, punt */
9913cbadd90SDavid du Colombier 	if (qp->curdest == qp->dest)
9924f8f669cSDavid du Colombier 		if (cfg.straddle && cfg.inside) {
9933cbadd90SDavid du Colombier 			/* get ips of "outside-ns-ips" */
9943cbadd90SDavid du Colombier 			p = qp->curdest = qp->dest;
9953cbadd90SDavid du Colombier 			for(n = 0; n < Maxdest; n++, qp->curdest++)
9963cbadd90SDavid du Colombier 				if (setdestoutns(qp->curdest, n) < 0)
9977dd7cddfSDavid du Colombier 					break;
9984f8f669cSDavid du Colombier 		} else {
9994e7b9544SDavid du Colombier 			/* it's probably just a bogus domain, don't log it */
10004e7b9544SDavid du Colombier 			// dnslog("xmitquery: %s: no nameservers", qp->dp->name);
10013cbadd90SDavid du Colombier 			return -1;
10024f8f669cSDavid du Colombier 		}
10037dd7cddfSDavid du Colombier 
10043cbadd90SDavid du Colombier 	/* send to first 'qp->ndest' destinations */
10057dd7cddfSDavid du Colombier 	j = 0;
10063cbadd90SDavid du Colombier 	if (medium == Tcp) {
10073cbadd90SDavid du Colombier 		j++;
10083cbadd90SDavid du Colombier 		queryck(qp);
10093cbadd90SDavid du Colombier 		assert(qp->dp);
10103cbadd90SDavid du Colombier 		procsetname("tcp %sside query for %s %s", (inns? "in": "out"),
10113cbadd90SDavid du Colombier 			qp->dp->name, rrname(qp->type, buf, sizeof buf));
10123cbadd90SDavid du Colombier 		mydnsquery(qp, medium, obuf, len); /* sets qp->tcpip from obuf */
10133cbadd90SDavid du Colombier 		if(debug)
10143cbadd90SDavid du Colombier 			logsend(qp->req->id, depth, qp->tcpip, "", qp->dp->name,
10153cbadd90SDavid du Colombier 				qp->type);
10163cbadd90SDavid du Colombier 	} else
10173cbadd90SDavid du Colombier 		for(; p < &qp->dest[qp->ndest] && p < qp->curdest; p++){
10187dd7cddfSDavid du Colombier 			/* skip destinations we've finished with */
10197dd7cddfSDavid du Colombier 			if(p->nx >= Maxtrans)
10207dd7cddfSDavid du Colombier 				continue;
10217dd7cddfSDavid du Colombier 
10227dd7cddfSDavid du Colombier 			j++;
10237dd7cddfSDavid du Colombier 
10247dd7cddfSDavid du Colombier 			/* exponential backoff of requests */
10253cbadd90SDavid du Colombier 			if((1<<p->nx) > qp->ndest)
10267dd7cddfSDavid du Colombier 				continue;
10277dd7cddfSDavid du Colombier 
10283cbadd90SDavid du Colombier 			procsetname("udp %sside query to %I/%s %s %s",
10293cbadd90SDavid du Colombier 				(inns? "in": "out"), p->a, p->s->name,
10303cbadd90SDavid du Colombier 				qp->dp->name, rrname(qp->type, buf, sizeof buf));
1031219b2ee8SDavid du Colombier 			if(debug)
10323cbadd90SDavid du Colombier 				logsend(qp->req->id, depth, p->a, p->s->name,
10333cbadd90SDavid du Colombier 					qp->dp->name, qp->type);
10344f8f669cSDavid du Colombier 
10353cbadd90SDavid du Colombier 			/* fill in UDP destination addr & send it */
10363cbadd90SDavid du Colombier 			memmove(obuf, p->a, sizeof p->a);
10373cbadd90SDavid du Colombier 			mydnsquery(qp, medium, obuf, len);
10387dd7cddfSDavid du Colombier 			p->nx++;
10393e12c5d1SDavid du Colombier 		}
10403cbadd90SDavid du Colombier 	if(j == 0) {
10413cbadd90SDavid du Colombier 		// dnslog("xmitquery: %s: no destinations left", qp->dp->name);
10423cbadd90SDavid du Colombier 		return -1;
10433cbadd90SDavid du Colombier 	}
10443cbadd90SDavid du Colombier 	return 0;
10453cbadd90SDavid du Colombier }
10463e12c5d1SDavid du Colombier 
1047f46c709fSDavid du Colombier static int lckindex[Maxlcks] = {
1048f46c709fSDavid du Colombier 	0,			/* all others map here */
1049f46c709fSDavid du Colombier 	Ta,
1050f46c709fSDavid du Colombier 	Tns,
1051f46c709fSDavid du Colombier 	Tcname,
1052f46c709fSDavid du Colombier 	Tsoa,
1053f46c709fSDavid du Colombier 	Tptr,
1054f46c709fSDavid du Colombier 	Tmx,
1055f46c709fSDavid du Colombier 	Ttxt,
1056f46c709fSDavid du Colombier 	Taaaa,
1057f46c709fSDavid du Colombier };
1058f46c709fSDavid du Colombier 
1059f46c709fSDavid du Colombier static int
1060f46c709fSDavid du Colombier qtype2lck(int qtype)		/* map query type to querylck index */
1061f46c709fSDavid du Colombier {
1062f46c709fSDavid du Colombier 	int i;
1063f46c709fSDavid du Colombier 
1064f46c709fSDavid du Colombier 	for (i = 1; i < nelem(lckindex); i++)
1065f46c709fSDavid du Colombier 		if (lckindex[i] == qtype)
1066f46c709fSDavid du Colombier 			return i;
1067f46c709fSDavid du Colombier 	return 0;
1068f46c709fSDavid du Colombier }
1069f46c709fSDavid du Colombier 
10700319257bSDavid du Colombier /* is mp a cachable negative response (with Rname set)? */
10710319257bSDavid du Colombier static int
10720319257bSDavid du Colombier isnegrname(DNSmsg *mp)
10730319257bSDavid du Colombier {
10740319257bSDavid du Colombier 	/* TODO: could add || cfg.justforw to RHS of && */
10750319257bSDavid du Colombier 	return mp->an == nil && (mp->flags & Rmask) == Rname;
10760319257bSDavid du Colombier }
10770319257bSDavid du Colombier 
10783cbadd90SDavid du Colombier static int
10793cbadd90SDavid du Colombier procansw(Query *qp, DNSmsg *mp, uchar *srcip, int depth, Dest *p)
10803cbadd90SDavid du Colombier {
10813cbadd90SDavid du Colombier 	int rv;
10824e5f5f32SDavid du Colombier //	int lcktype;
10833cbadd90SDavid du Colombier 	char buf[32];
10843cbadd90SDavid du Colombier 	DN *ndp;
1085c73252aeSDavid du Colombier 	Query *nqp;
10863cbadd90SDavid du Colombier 	RR *tp, *soarr;
10877dd7cddfSDavid du Colombier 
1088f46c709fSDavid du Colombier 	if (mp->an == nil)
1089f46c709fSDavid du Colombier 		stats.negans++;
1090f46c709fSDavid du Colombier 
109159cc4ca5SDavid du Colombier 	/* ignore any error replies */
10923cbadd90SDavid du Colombier 	if((mp->flags & Rmask) == Rserver){
10930319257bSDavid du Colombier 		stats.negserver++;
1094a41547ffSDavid du Colombier 		freeanswers(mp);
10953cbadd90SDavid du Colombier 		if(p != qp->curdest)
10967dd7cddfSDavid du Colombier 			p->code = Rserver;
10973cbadd90SDavid du Colombier 		return -1;
10983e12c5d1SDavid du Colombier 	}
10993e12c5d1SDavid du Colombier 
110059cc4ca5SDavid du Colombier 	/* ignore any bad delegations */
11013cbadd90SDavid du Colombier 	if(mp->ns && baddelegation(mp->ns, qp->nsrp, srcip)){
11020319257bSDavid du Colombier 		stats.negbaddeleg++;
11033cbadd90SDavid du Colombier 		if(mp->an == nil){
11040319257bSDavid du Colombier 			stats.negbdnoans++;
1105a41547ffSDavid du Colombier 			freeanswers(mp);
11063cbadd90SDavid du Colombier 			if(p != qp->curdest)
110759cc4ca5SDavid du Colombier 				p->code = Rserver;
11083cbadd90SDavid du Colombier 			return -1;
110959cc4ca5SDavid du Colombier 		}
1110a41547ffSDavid du Colombier 		rrfreelist(mp->ns);
1111a41547ffSDavid du Colombier 		mp->ns = nil;
111259cc4ca5SDavid du Colombier 	}
111359cc4ca5SDavid du Colombier 
11147dd7cddfSDavid du Colombier 	/* remove any soa's from the authority section */
1115*530fef66SDavid du Colombier 	lock(&dnlock);
11163cbadd90SDavid du Colombier 	soarr = rrremtype(&mp->ns, Tsoa);
11177dd7cddfSDavid du Colombier 
11183e12c5d1SDavid du Colombier 	/* incorporate answers */
11194e153993SDavid du Colombier 	unique(mp->an);
11204e153993SDavid du Colombier 	unique(mp->ns);
11214e153993SDavid du Colombier 	unique(mp->ar);
1122*530fef66SDavid du Colombier 	unlock(&dnlock);
11233cbadd90SDavid du Colombier 	if(mp->an)
11243cbadd90SDavid du Colombier 		rrattach(mp->an, (mp->flags & Fauth) != 0);
11253cbadd90SDavid du Colombier 	if(mp->ar)
11266dc4800dSDavid du Colombier 		rrattach(mp->ar, Notauthoritative);
1127a41547ffSDavid du Colombier 	if(mp->ns && !cfg.justforw){
11283cbadd90SDavid du Colombier 		ndp = mp->ns->owner;
11296dc4800dSDavid du Colombier 		rrattach(mp->ns, Notauthoritative);
11300319257bSDavid du Colombier 	} else {
11314f8f669cSDavid du Colombier 		ndp = nil;
11320319257bSDavid du Colombier 		rrfreelist(mp->ns);
11330319257bSDavid du Colombier 		mp->ns = nil;
11340319257bSDavid du Colombier 	}
11357dd7cddfSDavid du Colombier 
11367dd7cddfSDavid du Colombier 	/* free the question */
1137a41547ffSDavid du Colombier 	if(mp->qd) {
11383cbadd90SDavid du Colombier 		rrfreelist(mp->qd);
1139a41547ffSDavid du Colombier 		mp->qd = nil;
1140a41547ffSDavid du Colombier 	}
11413e12c5d1SDavid du Colombier 
11423e12c5d1SDavid du Colombier 	/*
11433e12c5d1SDavid du Colombier 	 *  Any reply from an authoritative server,
1144a41547ffSDavid du Colombier 	 *  or a positive reply terminates the search.
1145a41547ffSDavid du Colombier 	 *  A negative response now also terminates the search.
11463e12c5d1SDavid du Colombier 	 */
11473cbadd90SDavid du Colombier 	if(mp->an != nil || (mp->flags & Fauth)){
11480319257bSDavid du Colombier 		if(isnegrname(mp))
11493cbadd90SDavid du Colombier 			qp->dp->respcode = Rname;
11507dd7cddfSDavid du Colombier 		else
11513cbadd90SDavid du Colombier 			qp->dp->respcode = 0;
11527dd7cddfSDavid du Colombier 
11537dd7cddfSDavid du Colombier 		/*
1154a41547ffSDavid du Colombier 		 *  cache any negative responses, free soarr.
1155a41547ffSDavid du Colombier 		 *  negative responses need not be authoritative:
1156a41547ffSDavid du Colombier 		 *  they can legitimately come from a cache.
11577dd7cddfSDavid du Colombier 		 */
1158a41547ffSDavid du Colombier 		if( /* (mp->flags & Fauth) && */ mp->an == nil)
11593cbadd90SDavid du Colombier 			cacheneg(qp->dp, qp->type, (mp->flags & Rmask), soarr);
11607dd7cddfSDavid du Colombier 		else
11617dd7cddfSDavid du Colombier 			rrfreelist(soarr);
11623e12c5d1SDavid du Colombier 		return 1;
11630319257bSDavid du Colombier 	} else if (isnegrname(mp)) {
1164a41547ffSDavid du Colombier 		qp->dp->respcode = Rname;
1165a41547ffSDavid du Colombier 		/*
1166a41547ffSDavid du Colombier 		 *  cache negative response.
1167a41547ffSDavid du Colombier 		 *  negative responses need not be authoritative:
1168a41547ffSDavid du Colombier 		 *  they can legitimately come from a cache.
1169a41547ffSDavid du Colombier 		 */
1170a41547ffSDavid du Colombier 		cacheneg(qp->dp, qp->type, (mp->flags & Rmask), soarr);
1171a41547ffSDavid du Colombier 		return 1;
11723e12c5d1SDavid du Colombier 	}
11730319257bSDavid du Colombier 	stats.negnorname++;
11747dd7cddfSDavid du Colombier 	rrfreelist(soarr);
11753e12c5d1SDavid du Colombier 
11763e12c5d1SDavid du Colombier 	/*
1177a41547ffSDavid du Colombier 	 *  if we've been given better name servers, recurse.
1178a41547ffSDavid du Colombier 	 *  if we're a pure resolver, don't recurse, we have
1179a41547ffSDavid du Colombier 	 *  to forward to a fixed set of named servers.
11803e12c5d1SDavid du Colombier 	 */
1181a41547ffSDavid du Colombier 	if(!mp->ns || cfg.resolver && cfg.justforw)
11823cbadd90SDavid du Colombier 		return 0;
11837dd7cddfSDavid du Colombier 	tp = rrlookup(ndp, Tns, NOneg);
11843cbadd90SDavid du Colombier 	if(contains(qp->nsrp, tp)){
11857dd7cddfSDavid du Colombier 		rrfreelist(tp);
11863cbadd90SDavid du Colombier 		return 0;
11873e12c5d1SDavid du Colombier 	}
11883cbadd90SDavid du Colombier 	procsetname("recursive query for %s %s", qp->dp->name,
11893cbadd90SDavid du Colombier 		rrname(qp->type, buf, sizeof buf));
11904e5f5f32SDavid du Colombier 	/*
11914e5f5f32SDavid du Colombier 	 *  we're called from udpquery, called from
11924e5f5f32SDavid du Colombier 	 *  netquery, which current holds qp->dp->querylck,
11934e5f5f32SDavid du Colombier 	 *  so release it now and acquire it upon return.
11944e5f5f32SDavid du Colombier 	 */
11954e5f5f32SDavid du Colombier //	lcktype = qtype2lck(qp->type);
11964e5f5f32SDavid du Colombier //	qunlock(&qp->dp->querylck[lcktype]);
11973cbadd90SDavid du Colombier 
1198c73252aeSDavid du Colombier 	nqp = emalloc(sizeof *nqp);
1199c73252aeSDavid du Colombier 	queryinit(nqp, qp->dp, qp->type, qp->req);
1200c73252aeSDavid du Colombier 	nqp->nsrp = tp;
1201c73252aeSDavid du Colombier 	rv = netquery(nqp, depth+1);
12023cbadd90SDavid du Colombier 
12034e5f5f32SDavid du Colombier //	qlock(&qp->dp->querylck[lcktype]);
1204adb31a62SDavid du Colombier 	rrfreelist(nqp->nsrp);
1205c73252aeSDavid du Colombier 	querydestroy(nqp);
1206c73252aeSDavid du Colombier 	free(nqp);
12073cbadd90SDavid du Colombier 	return rv;
12083cbadd90SDavid du Colombier }
12093cbadd90SDavid du Colombier 
12103cbadd90SDavid du Colombier /*
12113cbadd90SDavid du Colombier  * send a query via tcp to a single address (from ibuf's udp header)
12123cbadd90SDavid du Colombier  * and read the answer(s) into mp->an.
12133cbadd90SDavid du Colombier  */
12143cbadd90SDavid du Colombier static int
12153cbadd90SDavid du Colombier tcpquery(Query *qp, DNSmsg *mp, int depth, uchar *ibuf, uchar *obuf, int len,
12163cbadd90SDavid du Colombier 	int waitsecs, int inns, ushort req)
12173cbadd90SDavid du Colombier {
12184e7b9544SDavid du Colombier 	int rv = 0;
12193cbadd90SDavid du Colombier 	ulong endtime;
12203cbadd90SDavid du Colombier 
12213cbadd90SDavid du Colombier 	endtime = time(nil) + waitsecs;
12223cbadd90SDavid du Colombier 	if(endtime > qp->req->aborttime)
12233cbadd90SDavid du Colombier 		endtime = qp->req->aborttime;
12243cbadd90SDavid du Colombier 
1225a41547ffSDavid du Colombier 	if (0)
12264e7b9544SDavid du Colombier 		dnslog("%s: udp reply truncated; retrying query via tcp to %I",
12274e7b9544SDavid du Colombier 			qp->dp->name, qp->tcpip);
12284e7b9544SDavid du Colombier 
12293cbadd90SDavid du Colombier 	qlock(&qp->tcplock);
12303cbadd90SDavid du Colombier 	memmove(obuf, ibuf, IPaddrlen);		/* send back to respondent */
12313cbadd90SDavid du Colombier 	/* sets qp->tcpip from obuf's udp header */
12324e7b9544SDavid du Colombier 	if (xmitquery(qp, Tcp, depth, obuf, inns, len) < 0 ||
12334e7b9544SDavid du Colombier 	    readreply(qp, Tcp, req, ibuf, mp, endtime) < 0)
12344e7b9544SDavid du Colombier 		rv = -1;
12353cbadd90SDavid du Colombier 	if (qp->tcpfd > 0) {
12363cbadd90SDavid du Colombier 		hangup(qp->tcpctlfd);
12373cbadd90SDavid du Colombier 		close(qp->tcpctlfd);
12383cbadd90SDavid du Colombier 		close(qp->tcpfd);
12393cbadd90SDavid du Colombier 	}
12403cbadd90SDavid du Colombier 	qp->tcpfd = qp->tcpctlfd = -1;
12413cbadd90SDavid du Colombier 	qunlock(&qp->tcplock);
12424e7b9544SDavid du Colombier 	return rv;
12433cbadd90SDavid du Colombier }
12443cbadd90SDavid du Colombier 
12453cbadd90SDavid du Colombier /*
12463cbadd90SDavid du Colombier  *  query name servers.  If the name server returns a pointer to another
12473cbadd90SDavid du Colombier  *  name server, recurse.
12483cbadd90SDavid du Colombier  */
12493cbadd90SDavid du Colombier static int
12504e5f5f32SDavid du Colombier queryns(Query *qp, int depth, uchar *ibuf, uchar *obuf, int waitsecs, int inns)
12513cbadd90SDavid du Colombier {
12523cbadd90SDavid du Colombier 	int ndest, len, replywaits, rv;
12533cbadd90SDavid du Colombier 	ushort req;
12543cbadd90SDavid du Colombier 	ulong endtime;
12553cbadd90SDavid du Colombier 	char buf[12];
12563cbadd90SDavid du Colombier 	uchar srcip[IPaddrlen];
1257a41547ffSDavid du Colombier 	Dest *p, *np, *dest;
1258a41547ffSDavid du Colombier //	Dest dest[Maxdest];
12593cbadd90SDavid du Colombier 
12603cbadd90SDavid du Colombier 	/* pack request into a udp message */
12613cbadd90SDavid du Colombier 	req = rand();
12623cbadd90SDavid du Colombier 	len = mkreq(qp->dp, qp->type, obuf, Frecurse|Oquery, req);
12633cbadd90SDavid du Colombier 
12643cbadd90SDavid du Colombier 	/* no server addresses yet */
12653cbadd90SDavid du Colombier 	queryck(qp);
1266a41547ffSDavid du Colombier 	dest = emalloc(Maxdest * sizeof *dest);	/* dest can't be on stack */
1267a41547ffSDavid du Colombier 	for (p = dest; p < dest + Maxdest; p++)
12683cbadd90SDavid du Colombier 		destinit(p);
12691eb0187fSDavid du Colombier 	/* this dest array is local to this call of queryns() */
12701eb0187fSDavid du Colombier 	free(qp->dest);
12713cbadd90SDavid du Colombier 	qp->curdest = qp->dest = dest;
12723cbadd90SDavid du Colombier 
12733cbadd90SDavid du Colombier 	/*
12743cbadd90SDavid du Colombier 	 *  transmit udp requests and wait for answers.
12753cbadd90SDavid du Colombier 	 *  at most Maxtrans attempts to each address.
12763cbadd90SDavid du Colombier 	 *  each cycle send one more message than the previous.
12773cbadd90SDavid du Colombier 	 *  retry a query via tcp if its response is truncated.
12783cbadd90SDavid du Colombier 	 */
12793cbadd90SDavid du Colombier 	for(ndest = 1; ndest < Maxdest; ndest++){
12803cbadd90SDavid du Colombier 		qp->ndest = ndest;
12813cbadd90SDavid du Colombier 		qp->tcpset = 0;
12823cbadd90SDavid du Colombier 		if (xmitquery(qp, Udp, depth, obuf, inns, len) < 0)
12833cbadd90SDavid du Colombier 			break;
12843cbadd90SDavid du Colombier 
12853cbadd90SDavid du Colombier 		endtime = time(nil) + waitsecs;
12863cbadd90SDavid du Colombier 		if(endtime > qp->req->aborttime)
12873cbadd90SDavid du Colombier 			endtime = qp->req->aborttime;
12883cbadd90SDavid du Colombier 
12893cbadd90SDavid du Colombier 		for(replywaits = 0; replywaits < ndest; replywaits++){
129081730632SDavid du Colombier 			DNSmsg m;
129181730632SDavid du Colombier 
1292f46c709fSDavid du Colombier 			procsetname("reading %sside reply from %I: %s %s from %s",
1293a41547ffSDavid du Colombier 				(inns? "in": "out"), obuf, qp->dp->name,
1294f46c709fSDavid du Colombier 				rrname(qp->type, buf, sizeof buf), qp->req->from);
12953cbadd90SDavid du Colombier 
129681730632SDavid du Colombier 			/* read udp answer into m */
12973cbadd90SDavid du Colombier 			if (readreply(qp, Udp, req, ibuf, &m, endtime) >= 0)
12983cbadd90SDavid du Colombier 				memmove(srcip, ibuf, IPaddrlen);
129981730632SDavid du Colombier 			else if (!(m.flags & Ftrunc)) {
130081730632SDavid du Colombier 				freeanswers(&m);
13013cbadd90SDavid du Colombier 				break;		/* timed out on this dest */
130281730632SDavid du Colombier 			} else {
13033cbadd90SDavid du Colombier 				/* whoops, it was truncated! ask again via tcp */
13046aaebd7dSDavid du Colombier 				freeanswers(&m);
13053cbadd90SDavid du Colombier 				rv = tcpquery(qp, &m, depth, ibuf, obuf, len,
130681730632SDavid du Colombier 					waitsecs, inns, req);  /* answer in m */
130781730632SDavid du Colombier 				if (rv < 0) {
130881730632SDavid du Colombier 					freeanswers(&m);
13093cbadd90SDavid du Colombier 					break;		/* failed via tcp too */
131081730632SDavid du Colombier 				}
13113cbadd90SDavid du Colombier 				memmove(srcip, qp->tcpip, IPaddrlen);
13123cbadd90SDavid du Colombier 			}
13133cbadd90SDavid du Colombier 
13143cbadd90SDavid du Colombier 			/* find responder */
13154e5f5f32SDavid du Colombier 			// dnslog("queryns got reply from %I", srcip);
13163cbadd90SDavid du Colombier 			for(p = qp->dest; p < qp->curdest; p++)
13173cbadd90SDavid du Colombier 				if(memcmp(p->a, srcip, sizeof p->a) == 0)
13183cbadd90SDavid du Colombier 					break;
13193cbadd90SDavid du Colombier 
13203cbadd90SDavid du Colombier 			/* remove all addrs of responding server from list */
13213cbadd90SDavid du Colombier 			for(np = qp->dest; np < qp->curdest; np++)
13223cbadd90SDavid du Colombier 				if(np->s == p->s)
13233cbadd90SDavid du Colombier 					p->nx = Maxtrans;
13243cbadd90SDavid du Colombier 
132581730632SDavid du Colombier 			/* free or incorporate RRs in m */
13263cbadd90SDavid du Colombier 			rv = procansw(qp, &m, srcip, depth, p);
13271eb0187fSDavid du Colombier 			if (rv > 0) {
13281eb0187fSDavid du Colombier 				free(qp->dest);
13291eb0187fSDavid du Colombier 				qp->dest = qp->curdest = nil; /* prevent accidents */
13303cbadd90SDavid du Colombier 				return rv;
13313e12c5d1SDavid du Colombier 			}
13323e12c5d1SDavid du Colombier 		}
13331eb0187fSDavid du Colombier 	}
13347dd7cddfSDavid du Colombier 
13354f8f669cSDavid du Colombier 	/* if all servers returned failure, propagate it */
13363cbadd90SDavid du Colombier 	qp->dp->respcode = Rserver;
13373cbadd90SDavid du Colombier 	for(p = dest; p < qp->curdest; p++) {
13383cbadd90SDavid du Colombier 		destck(p);
13397dd7cddfSDavid du Colombier 		if(p->code != Rserver)
13403cbadd90SDavid du Colombier 			qp->dp->respcode = 0;
13413cbadd90SDavid du Colombier 		p->magic = 0;			/* prevent accidents */
13423cbadd90SDavid du Colombier 	}
13434f8f669cSDavid du Colombier 
13443cbadd90SDavid du Colombier //	if (qp->dp->respcode)
13454e5f5f32SDavid du Colombier //		dnslog("queryns setting Rserver for %s", qp->dp->name);
13467dd7cddfSDavid du Colombier 
1347a41547ffSDavid du Colombier 	free(qp->dest);
13483cbadd90SDavid du Colombier 	qp->dest = qp->curdest = nil;		/* prevent accidents */
13493e12c5d1SDavid du Colombier 	return 0;
13503e12c5d1SDavid du Colombier }
13517dd7cddfSDavid du Colombier 
13524f8f669cSDavid du Colombier /*
13534f8f669cSDavid du Colombier  *  run a command with a supplied fd as standard input
13544f8f669cSDavid du Colombier  */
13554f8f669cSDavid du Colombier char *
13564f8f669cSDavid du Colombier system(int fd, char *cmd)
135728a8a86bSDavid du Colombier {
13584f8f669cSDavid du Colombier 	int pid, p, i;
13594f8f669cSDavid du Colombier 	static Waitmsg msg;
13606b0d5c8bSDavid du Colombier 
13614f8f669cSDavid du Colombier 	if((pid = fork()) == -1)
13624f8f669cSDavid du Colombier 		sysfatal("fork failed: %r");
13634f8f669cSDavid du Colombier 	else if(pid == 0){
13644f8f669cSDavid du Colombier 		dup(fd, 0);
13654f8f669cSDavid du Colombier 		close(fd);
13664f8f669cSDavid du Colombier 		for (i = 3; i < 200; i++)
13674f8f669cSDavid du Colombier 			close(i);		/* don't leak fds */
13684f8f669cSDavid du Colombier 		execl("/bin/rc", "rc", "-c", cmd, nil);
13694f8f669cSDavid du Colombier 		sysfatal("exec rc: %r");
13704f8f669cSDavid du Colombier 	}
13714f8f669cSDavid du Colombier 	for(p = waitpid(); p >= 0; p = waitpid())
13724f8f669cSDavid du Colombier 		if(p == pid)
13734f8f669cSDavid du Colombier 			return msg.msg;
13744f8f669cSDavid du Colombier 	return "lost child";
13754f8f669cSDavid du Colombier }
13764f8f669cSDavid du Colombier 
13770319257bSDavid du Colombier /* compute wait, weighted by probability of success, with minimum */
13780319257bSDavid du Colombier static ulong
13790319257bSDavid du Colombier weight(ulong ms, unsigned pcntprob)
13800319257bSDavid du Colombier {
13810319257bSDavid du Colombier 	ulong wait;
13820319257bSDavid du Colombier 
13830319257bSDavid du Colombier 	wait = (ms * pcntprob) / 100;
13840319257bSDavid du Colombier 	if (wait < 1500)
13850319257bSDavid du Colombier 		wait = 1500;
13860319257bSDavid du Colombier 	return wait;
13870319257bSDavid du Colombier }
13880319257bSDavid du Colombier 
1389a41547ffSDavid du Colombier /*
1390a41547ffSDavid du Colombier  * in principle we could use a single descriptor for a udp port
1391a41547ffSDavid du Colombier  * to send all queries and receive all the answers to them,
1392a41547ffSDavid du Colombier  * but we'd have to sort out the answers by dns-query id.
1393a41547ffSDavid du Colombier  */
13944f8f669cSDavid du Colombier static int
13953cbadd90SDavid du Colombier udpquery(Query *qp, char *mntpt, int depth, int patient, int inns)
13964f8f669cSDavid du Colombier {
13970319257bSDavid du Colombier 	int fd, rv;
13984f8f669cSDavid du Colombier 	long now;
13990319257bSDavid du Colombier 	ulong pcntprob, wait, reqtm;
14004f8f669cSDavid du Colombier 	char *msg;
14014f8f669cSDavid du Colombier 	uchar *obuf, *ibuf;
14024f8f669cSDavid du Colombier 	static QLock mntlck;
14034f8f669cSDavid du Colombier 	static ulong lastmount;
14047dd7cddfSDavid du Colombier 
14057dd7cddfSDavid du Colombier 	/* use alloced buffers rather than ones from the stack */
1406f27a9a5aSDavid du Colombier 	// ibuf = emalloc(Maxudpin+Udphdrsize);
14073cbadd90SDavid du Colombier 	ibuf = emalloc(64*1024);		/* max. tcp reply size */
1408f27a9a5aSDavid du Colombier 	obuf = emalloc(Maxudp+Udphdrsize);
14097dd7cddfSDavid du Colombier 
14104f8f669cSDavid du Colombier 	fd = udpport(mntpt);
14114f8f669cSDavid du Colombier 	while (fd < 0 && cfg.straddle && strcmp(mntpt, "/net.alt") == 0) {
14124f8f669cSDavid du Colombier 		/* HACK: remount /net.alt */
14134f8f669cSDavid du Colombier 		now = time(nil);
14144f8f669cSDavid du Colombier 		if (now < lastmount + Remntretry)
14154f8f669cSDavid du Colombier 			sleep((lastmount + Remntretry - now)*1000);
14164f8f669cSDavid du Colombier 		qlock(&mntlck);
14174f8f669cSDavid du Colombier 		fd = udpport(mntpt);	/* try again under lock */
14184f8f669cSDavid du Colombier 		if (fd < 0) {
14194f8f669cSDavid du Colombier 			dnslog("[%d] remounting /net.alt", getpid());
14204f8f669cSDavid du Colombier 			unmount(nil, "/net.alt");
14214f8f669cSDavid du Colombier 
14224f8f669cSDavid du Colombier 			msg = system(open("/dev/null", ORDWR), "outside");
14234f8f669cSDavid du Colombier 
14244f8f669cSDavid du Colombier 			lastmount = time(nil);
14254f8f669cSDavid du Colombier 			if (msg && *msg) {
14264f8f669cSDavid du Colombier 				dnslog("[%d] can't remount /net.alt: %s",
14274f8f669cSDavid du Colombier 					getpid(), msg);
14284f8f669cSDavid du Colombier 				sleep(10*1000);		/* don't spin wildly */
14294f8f669cSDavid du Colombier 			} else
14304f8f669cSDavid du Colombier 				fd = udpport(mntpt);
14314f8f669cSDavid du Colombier 		}
14324f8f669cSDavid du Colombier 		qunlock(&mntlck);
14334f8f669cSDavid du Colombier 	}
1434a41547ffSDavid du Colombier 	if (fd < 0) {
14354f8f669cSDavid du Colombier 		dnslog("can't get udpport for %s query of name %s: %r",
14363cbadd90SDavid du Colombier 			mntpt, qp->dp->name);
1437a41547ffSDavid du Colombier 		sysfatal("out of udp conversations");	/* we're buggered */
1438a41547ffSDavid du Colombier 	}
1439a41547ffSDavid du Colombier 
1440a41547ffSDavid du Colombier 	/*
14410319257bSDavid du Colombier 	 * Our QIP servers are busted, don't answer AAAA and
14420319257bSDavid du Colombier 	 * take forever to answer CNAME if there isn't one.
14430319257bSDavid du Colombier 	 * They rarely set Rname.
1444a41547ffSDavid du Colombier 	 * make time-to-wait proportional to estimated probability of an
1445a41547ffSDavid du Colombier 	 * RR of that type existing.
1446a41547ffSDavid du Colombier 	 */
1447adb31a62SDavid du Colombier 	if (qp->type >= nelem(likely))
14480319257bSDavid du Colombier 		pcntprob = 35;			/* unpopular query type */
14490319257bSDavid du Colombier 	else
14500319257bSDavid du Colombier 		pcntprob = likely[qp->type];
14510319257bSDavid du Colombier 	reqtm = (patient? 2*Maxreqtm: Maxreqtm);
14520319257bSDavid du Colombier 	/* time for a single outgoing udp query */
14530319257bSDavid du Colombier 	wait = weight(S2MS(reqtm)/3, pcntprob);
14540319257bSDavid du Colombier 	qp->req->aborttime = time(nil) + MS2S(3*wait); /* for all udp queries */
1455a41547ffSDavid du Colombier 
14560319257bSDavid du Colombier 	qp->udpfd = fd;
14574e5f5f32SDavid du Colombier 	rv = queryns(qp, depth, ibuf, obuf, MS2S(wait), inns);
1458a41547ffSDavid du Colombier 	close(fd);
1459a41547ffSDavid du Colombier 	qp->udpfd = -1;
14604f8f669cSDavid du Colombier 
14614f8f669cSDavid du Colombier 	free(obuf);
14624f8f669cSDavid du Colombier 	free(ibuf);
14634f8f669cSDavid du Colombier 	return rv;
14644f8f669cSDavid du Colombier }
14654f8f669cSDavid du Colombier 
14664e5f5f32SDavid du Colombier /* look up (qp->dp->name,qp->type) rr in dns, via *nsrp with results in *reqp */
14674f8f669cSDavid du Colombier static int
14683cbadd90SDavid du Colombier netquery(Query *qp, int depth)
14694f8f669cSDavid du Colombier {
1470410ea80bSDavid du Colombier 	int lock, rv, triedin, inname;
1471225077b0SDavid du Colombier //	char buf[32];
14724f8f669cSDavid du Colombier 	RR *rp;
14734e153993SDavid du Colombier 	DN *dp;
1474225077b0SDavid du Colombier 	Querylck *qlp;
1475225077b0SDavid du Colombier 	static int whined;
14764f8f669cSDavid du Colombier 
1477225077b0SDavid du Colombier 	rv = 0;				/* pessimism */
14784f8f669cSDavid du Colombier 	if(depth > 12)			/* in a recursive loop? */
14794f8f669cSDavid du Colombier 		return 0;
14804f8f669cSDavid du Colombier 
14813cbadd90SDavid du Colombier 	slave(qp->req);
1482d3907fe5SDavid du Colombier 	/*
1483d3907fe5SDavid du Colombier 	 * slave might have forked.  if so, the parent process longjmped to
1484d3907fe5SDavid du Colombier 	 * req->mret; we're usually the child slave, but if there are too
1485d3907fe5SDavid du Colombier 	 * many children already, we're still the same process.
1486d3907fe5SDavid du Colombier 	 */
14874f8f669cSDavid du Colombier 
14884e5f5f32SDavid du Colombier 	/*
14894e5f5f32SDavid du Colombier 	 * don't lock before call to slave so only children can block.
14904e5f5f32SDavid du Colombier 	 * just lock at top-level invocation.
14914e5f5f32SDavid du Colombier 	 */
1492225077b0SDavid du Colombier 	lock = depth <= 1 && qp->req->isslave;
14934e153993SDavid du Colombier 	dp = qp->dp;		/* ensure that it doesn't change underfoot */
1494225077b0SDavid du Colombier 	qlp = nil;
14954e5f5f32SDavid du Colombier 	if(lock) {
1496225077b0SDavid du Colombier //		procsetname("query lock wait: %s %s from %s", dp->name,
1497225077b0SDavid du Colombier //			rrname(qp->type, buf, sizeof buf), qp->req->from);
1498ccf6439bSDavid du Colombier 		/*
1499ccf6439bSDavid du Colombier 		 * don't make concurrent queries for this name.
150081730632SDavid du Colombier 		 * dozens of processes blocking here probably indicates
150181730632SDavid du Colombier 		 * an error in our dns data that causes us to not
150281730632SDavid du Colombier 		 * recognise a zone (area) as one of our own, thus
150381730632SDavid du Colombier 		 * causing us to query other nameservers.
1504ccf6439bSDavid du Colombier 		 */
1505225077b0SDavid du Colombier 		qlp = &dp->querylck[qtype2lck(qp->type)];
1506225077b0SDavid du Colombier 		qlock(qlp);
1507410ea80bSDavid du Colombier 		if (qlp->Ref.ref > 10) {
1508225077b0SDavid du Colombier 			qunlock(qlp);
1509225077b0SDavid du Colombier 			if (!whined) {
1510225077b0SDavid du Colombier 				whined = 1;
1511225077b0SDavid du Colombier 				dnslog("too many outstanding queries for %s;"
151213fec586SDavid du Colombier 					" dropping this one; no further logging"
151313fec586SDavid du Colombier 					" of drops", dp->name);
1514225077b0SDavid du Colombier 			}
1515225077b0SDavid du Colombier 			return 0;
1516225077b0SDavid du Colombier 		}
1517410ea80bSDavid du Colombier 		++qlp->Ref.ref;
1518410ea80bSDavid du Colombier 		qunlock(qlp);
1519225077b0SDavid du Colombier 	}
15204e153993SDavid du Colombier 	procsetname("netquery: %s", dp->name);
15216b0d5c8bSDavid du Colombier 
15226b0d5c8bSDavid du Colombier 	/* prepare server RR's for incremental lookup */
15233cbadd90SDavid du Colombier 	for(rp = qp->nsrp; rp; rp = rp->next)
15246b0d5c8bSDavid du Colombier 		rp->marker = 0;
15256b0d5c8bSDavid du Colombier 
15264f8f669cSDavid du Colombier 	triedin = 0;
1527a41547ffSDavid du Colombier 
15284f8f669cSDavid du Colombier 	/*
15294f8f669cSDavid du Colombier 	 * normal resolvers and servers will just use mntpt for all addresses,
15304f8f669cSDavid du Colombier 	 * even on the outside.  straddling servers will use mntpt (/net)
15314f8f669cSDavid du Colombier 	 * for inside addresses and /net.alt for outside addresses,
15324f8f669cSDavid du Colombier 	 * thus bypassing other inside nameservers.
15334f8f669cSDavid du Colombier 	 */
15344e153993SDavid du Colombier 	inname = insideaddr(dp->name);
15354f8f669cSDavid du Colombier 	if (!cfg.straddle || inname) {
15363cbadd90SDavid du Colombier 		rv = udpquery(qp, mntpt, depth, Hurry, (cfg.inside? Inns: Outns));
15374f8f669cSDavid du Colombier 		triedin = 1;
15384f8f669cSDavid du Colombier 	}
15394f8f669cSDavid du Colombier 
15404f8f669cSDavid du Colombier 	/*
15414f8f669cSDavid du Colombier 	 * if we're still looking, are inside, and have an outside domain,
15424f8f669cSDavid du Colombier 	 * try it on our outside interface, if any.
15434f8f669cSDavid du Colombier 	 */
15444f8f669cSDavid du Colombier 	if (rv == 0 && cfg.inside && !inname) {
15454f8f669cSDavid du Colombier 		if (triedin)
15464f8f669cSDavid du Colombier 			dnslog(
15474f8f669cSDavid du Colombier 	   "[%d] netquery: internal nameservers failed for %s; trying external",
15484e153993SDavid du Colombier 				getpid(), dp->name);
15494f8f669cSDavid du Colombier 
15504f8f669cSDavid du Colombier 		/* prepare server RR's for incremental lookup */
15513cbadd90SDavid du Colombier 		for(rp = qp->nsrp; rp; rp = rp->next)
15524f8f669cSDavid du Colombier 			rp->marker = 0;
15534f8f669cSDavid du Colombier 
15543cbadd90SDavid du Colombier 		rv = udpquery(qp, "/net.alt", depth, Patient, Outns);
15554f8f669cSDavid du Colombier 	}
15563cbadd90SDavid du Colombier //	if (rv == 0)		/* could ask /net.alt/dns directly */
15574e153993SDavid du Colombier //		askoutdns(dp, qp->type);
15584f8f669cSDavid du Colombier 
1559410ea80bSDavid du Colombier 	if(lock && qlp) {
1560410ea80bSDavid du Colombier 		qlock(qlp);
1561410ea80bSDavid du Colombier 		assert(qlp->Ref.ref > 0);
1562410ea80bSDavid du Colombier 		qunlock(qlp);
1563225077b0SDavid du Colombier 		decref(qlp);
1564410ea80bSDavid du Colombier 	}
15657dd7cddfSDavid du Colombier 	return rv;
15667dd7cddfSDavid du Colombier }
15674f8f669cSDavid du Colombier 
15684f8f669cSDavid du Colombier int
15694f8f669cSDavid du Colombier seerootns(void)
15704f8f669cSDavid du Colombier {
15713cbadd90SDavid du Colombier 	int rv;
15724f8f669cSDavid du Colombier 	char root[] = "";
15734f8f669cSDavid du Colombier 	Request req;
1574c73252aeSDavid du Colombier 	Query *qp;
15754f8f669cSDavid du Colombier 
15764f8f669cSDavid du Colombier 	memset(&req, 0, sizeof req);
15774f8f669cSDavid du Colombier 	req.isslave = 1;
1578ccf6439bSDavid du Colombier 	req.aborttime = now + Maxreqtm;
1579f46c709fSDavid du Colombier 	req.from = "internal";
1580c73252aeSDavid du Colombier 	qp = emalloc(sizeof *qp);
1581c73252aeSDavid du Colombier 	queryinit(qp, dnlookup(root, Cin, 1), Tns, &req);
1582c73252aeSDavid du Colombier 
1583c73252aeSDavid du Colombier 	qp->nsrp = dblookup(root, Cin, Tns, 0, 0);
1584c73252aeSDavid du Colombier 	rv = netquery(qp, 0);
1585c73252aeSDavid du Colombier 
1586c73252aeSDavid du Colombier 	rrfreelist(qp->nsrp);
1587c73252aeSDavid du Colombier 	querydestroy(qp);
1588c73252aeSDavid du Colombier 	free(qp);
15893cbadd90SDavid du Colombier 	return rv;
15904f8f669cSDavid du Colombier }
1591