xref: /plan9/sys/src/cmd/ndb/dnresolve.c (revision adb31a6221c09bb6f8b2c527bf9100e20ffac78f)
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 */
53*adb31a62SDavid 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 /*
924f8f669cSDavid du Colombier  * reading /proc/pid/args yields either "name" 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);
150271b8d73SDavid du Colombier 			rrfreelist(rrremneg(&rp));
1517dd7cddfSDavid du Colombier 		}
1527dd7cddfSDavid du Colombier 		if(drp != nil)
1530319257bSDavid du Colombier 			rrfreelist(drp);	/* was rrfree */
1544f8f669cSDavid du Colombier 		procsetname(procname);
1554f8f669cSDavid du Colombier 		free(procname);
1563e12c5d1SDavid du Colombier 		return rp;
1577dd7cddfSDavid du Colombier 	}
1583e12c5d1SDavid du Colombier 
1597dd7cddfSDavid du Colombier 	/*
1607dd7cddfSDavid du Colombier 	 *  try the name directly
1617dd7cddfSDavid du Colombier 	 */
1627dd7cddfSDavid du Colombier 	rp = dnresolve1(name, class, type, req, depth, recurse);
163225077b0SDavid du Colombier 	if(rp == nil) {
164225077b0SDavid du Colombier 		/*
165225077b0SDavid du Colombier 		 * try it as a canonical name if we weren't told
166225077b0SDavid du Colombier 		 * that the name didn't exist
167225077b0SDavid du Colombier 		 */
1687dd7cddfSDavid du Colombier 		dp = dnlookup(name, class, 0);
1694f8f669cSDavid du Colombier 		if(type != Tptr && dp->respcode != Rname)
1707dd7cddfSDavid du Colombier 			for(loops = 0; rp == nil && loops < 32; loops++){
171225077b0SDavid du Colombier 				rp = dnresolve1(name, class, Tcname, req,
172225077b0SDavid du Colombier 					depth, recurse);
1737dd7cddfSDavid du Colombier 				if(rp == nil)
1747dd7cddfSDavid du Colombier 					break;
17580ee5cbfSDavid du Colombier 
176c73252aeSDavid du Colombier 				/* rp->host == nil shouldn't happen, but does */
177c73252aeSDavid du Colombier 				if(rp->negative || rp->host == nil){
17880ee5cbfSDavid du Colombier 					rrfreelist(rp);
17980ee5cbfSDavid du Colombier 					rp = nil;
18080ee5cbfSDavid du Colombier 					break;
18180ee5cbfSDavid du Colombier 				}
1827dd7cddfSDavid du Colombier 
1837dd7cddfSDavid du Colombier 				name = rp->host->name;
1847dd7cddfSDavid du Colombier 				if(cn)
1857dd7cddfSDavid du Colombier 					rrcat(cn, rp);
1867dd7cddfSDavid du Colombier 				else
1877dd7cddfSDavid du Colombier 					rrfreelist(rp);
1887dd7cddfSDavid du Colombier 
189225077b0SDavid du Colombier 				rp = dnresolve1(name, class, type, req,
190225077b0SDavid du Colombier 					depth, recurse);
1917dd7cddfSDavid du Colombier 			}
1927dd7cddfSDavid du Colombier 
1937dd7cddfSDavid du Colombier 		/* distinction between not found and not good */
1944f8f669cSDavid du Colombier 		if(rp == nil && status != nil && dp->respcode != 0)
1954f8f669cSDavid du Colombier 			*status = dp->respcode;
196225077b0SDavid du Colombier 	}
1974f8f669cSDavid du Colombier 	procsetname(procname);
1984f8f669cSDavid du Colombier 	free(procname);
1997dd7cddfSDavid du Colombier 	return randomize(rp);
2003e12c5d1SDavid du Colombier }
2013e12c5d1SDavid du Colombier 
2023cbadd90SDavid du Colombier static void
2033cbadd90SDavid du Colombier queryinit(Query *qp, DN *dp, int type, Request *req)
2043cbadd90SDavid du Colombier {
2053cbadd90SDavid du Colombier 	memset(qp, 0, sizeof *qp);
2064e7b9544SDavid du Colombier 	qp->udpfd = qp->tcpfd = qp->tcpctlfd = -1;
2073cbadd90SDavid du Colombier 	qp->dp = dp;
2083cbadd90SDavid du Colombier 	qp->type = type;
209*adb31a62SDavid du Colombier 	if (qp->type != type)
210*adb31a62SDavid du Colombier 		dnslog("queryinit: bogus type %d", type);
2113cbadd90SDavid du Colombier 	qp->req = req;
2123cbadd90SDavid du Colombier 	qp->nsrp = nil;
2133cbadd90SDavid du Colombier 	qp->dest = qp->curdest = nil;
2143cbadd90SDavid du Colombier 	qp->magic = Querymagic;
2153cbadd90SDavid du Colombier }
2163cbadd90SDavid du Colombier 
2173cbadd90SDavid du Colombier static void
2183cbadd90SDavid du Colombier queryck(Query *qp)
2193cbadd90SDavid du Colombier {
2203cbadd90SDavid du Colombier 	assert(qp);
2213cbadd90SDavid du Colombier 	assert(qp->magic == Querymagic);
2223cbadd90SDavid du Colombier }
2233cbadd90SDavid du Colombier 
2243cbadd90SDavid du Colombier static void
2254e7b9544SDavid du Colombier querydestroy(Query *qp)
2264e7b9544SDavid du Colombier {
2274e7b9544SDavid du Colombier 	queryck(qp);
228a41547ffSDavid du Colombier 	/* leave udpfd alone */
2294e7b9544SDavid du Colombier 	if (qp->tcpfd > 0)
2304e7b9544SDavid du Colombier 		close(qp->tcpfd);
2314e7b9544SDavid du Colombier 	if (qp->tcpctlfd > 0) {
2324e7b9544SDavid du Colombier 		hangup(qp->tcpctlfd);
2334e7b9544SDavid du Colombier 		close(qp->tcpctlfd);
2344e7b9544SDavid du Colombier 	}
235a41547ffSDavid du Colombier 	free(qp->dest);
2364e7b9544SDavid du Colombier 	memset(qp, 0, sizeof *qp);	/* prevent accidents */
2374e7b9544SDavid du Colombier 	qp->udpfd = qp->tcpfd = qp->tcpctlfd = -1;
2384e7b9544SDavid du Colombier }
2394e7b9544SDavid du Colombier 
2404e7b9544SDavid du Colombier static void
2413cbadd90SDavid du Colombier destinit(Dest *p)
2423cbadd90SDavid du Colombier {
2433cbadd90SDavid du Colombier 	memset(p, 0, sizeof *p);
2443cbadd90SDavid du Colombier 	p->magic = Destmagic;
2453cbadd90SDavid du Colombier }
2463cbadd90SDavid du Colombier 
2473cbadd90SDavid du Colombier static void
2483cbadd90SDavid du Colombier destck(Dest *p)
2493cbadd90SDavid du Colombier {
2503cbadd90SDavid du Colombier 	assert(p);
2513cbadd90SDavid du Colombier 	assert(p->magic == Destmagic);
2523cbadd90SDavid du Colombier }
2533cbadd90SDavid du Colombier 
254a41547ffSDavid du Colombier static void
255a41547ffSDavid du Colombier destdestroy(Dest *p)
256a41547ffSDavid du Colombier {
257a41547ffSDavid du Colombier 	USED(p);
258a41547ffSDavid du Colombier }
259a41547ffSDavid du Colombier 
260a41547ffSDavid du Colombier /*
261a41547ffSDavid du Colombier  * if the response to a query hasn't arrived within 100 ms.,
262a41547ffSDavid du Colombier  * it's unlikely to arrive at all.  after 1 s., it's really unlikely.
263a41547ffSDavid du Colombier  * queries for missing RRs are likely to produce time-outs rather than
264a41547ffSDavid du Colombier  * negative responses, so cname and aaaa queries are likely to time out,
265a41547ffSDavid du Colombier  * thus we don't wait very long for them.
266a41547ffSDavid du Colombier  */
267a41547ffSDavid du Colombier static void
268a41547ffSDavid du Colombier notestats(vlong start, int tmout, int type)
269a41547ffSDavid du Colombier {
270a41547ffSDavid du Colombier 	qlock(&stats);
271a41547ffSDavid du Colombier 	if (tmout) {
272a41547ffSDavid du Colombier 		stats.tmout++;
273a41547ffSDavid du Colombier 		if (type == Taaaa)
274a41547ffSDavid du Colombier 			stats.tmoutv6++;
275a41547ffSDavid du Colombier 		else if (type == Tcname)
276a41547ffSDavid du Colombier 			stats.tmoutcname++;
277a41547ffSDavid du Colombier 	} else {
278a41547ffSDavid du Colombier 		long wait10ths = NS2MS(nsec() - start) / 100;
279a41547ffSDavid du Colombier 
28081730632SDavid du Colombier 		if (wait10ths <= 0)
281a41547ffSDavid du Colombier 			stats.under10ths[0]++;
282a41547ffSDavid du Colombier 		else if (wait10ths >= nelem(stats.under10ths))
283a41547ffSDavid du Colombier 			stats.under10ths[nelem(stats.under10ths) - 1]++;
284a41547ffSDavid du Colombier 		else
285a41547ffSDavid du Colombier 			stats.under10ths[wait10ths]++;
286a41547ffSDavid du Colombier 	}
287a41547ffSDavid du Colombier 	qunlock(&stats);
288a41547ffSDavid du Colombier }
289a41547ffSDavid du Colombier 
290a41547ffSDavid du Colombier static void
291a41547ffSDavid du Colombier noteinmem(void)
292a41547ffSDavid du Colombier {
293a41547ffSDavid du Colombier 	qlock(&stats);
294a41547ffSDavid du Colombier 	stats.answinmem++;
295a41547ffSDavid du Colombier 	qunlock(&stats);
296a41547ffSDavid du Colombier }
297a41547ffSDavid du Colombier 
2983e12c5d1SDavid du Colombier static RR*
299c73252aeSDavid du Colombier issuequery(Query *qp, RR *rp, char *name, int class, int depth, int recurse)
3003e12c5d1SDavid du Colombier {
301f46c709fSDavid du Colombier 	char *cp;
302c73252aeSDavid du Colombier 	DN *nsdp;
303c73252aeSDavid du Colombier 	RR *nsrp, *dbnsrp;
3043cbadd90SDavid du Colombier 
3053e12c5d1SDavid du Colombier 	/*
3064f8f669cSDavid du Colombier 	 *  if we're running as just a resolver, query our
3077dd7cddfSDavid du Colombier 	 *  designated name servers
308219b2ee8SDavid du Colombier 	 */
3094f8f669cSDavid du Colombier 	if(cfg.resolver){
3107dd7cddfSDavid du Colombier 		nsrp = randomize(getdnsservers(class));
3117dd7cddfSDavid du Colombier 		if(nsrp != nil) {
312c73252aeSDavid du Colombier 			qp->nsrp = nsrp;
313c73252aeSDavid du Colombier 			if(netquery(qp, depth+1)){
3147dd7cddfSDavid du Colombier 				rrfreelist(nsrp);
315c73252aeSDavid du Colombier 				return rrlookup(qp->dp, qp->type, OKneg);
3167dd7cddfSDavid du Colombier 			}
3177dd7cddfSDavid du Colombier 			rrfreelist(nsrp);
3187dd7cddfSDavid du Colombier 		}
319219b2ee8SDavid du Colombier 	}
320219b2ee8SDavid du Colombier 
321219b2ee8SDavid du Colombier 	/*
3223e12c5d1SDavid du Colombier  	 *  walk up the domain name looking for
3233e12c5d1SDavid du Colombier 	 *  a name server for the domain.
3243e12c5d1SDavid du Colombier 	 */
3253e12c5d1SDavid du Colombier 	for(cp = name; cp; cp = walkup(cp)){
3267dd7cddfSDavid du Colombier 		/*
3277dd7cddfSDavid du Colombier 		 *  if this is a local (served by us) domain,
3287dd7cddfSDavid du Colombier 		 *  return answer
3297dd7cddfSDavid du Colombier 		 */
3307dd7cddfSDavid du Colombier 		dbnsrp = randomize(dblookup(cp, class, Tns, 0, 0));
3317dd7cddfSDavid du Colombier 		if(dbnsrp && dbnsrp->local){
332c73252aeSDavid du Colombier 			rp = dblookup(name, class, qp->type, 1, dbnsrp->ttl);
3337dd7cddfSDavid du Colombier 			rrfreelist(dbnsrp);
3347dd7cddfSDavid du Colombier 			return rp;
3357dd7cddfSDavid du Colombier 		}
3367dd7cddfSDavid du Colombier 
3377dd7cddfSDavid du Colombier 		/*
3387dd7cddfSDavid du Colombier 		 *  if recursion isn't set, just accept local
3397dd7cddfSDavid du Colombier 		 *  entries
3407dd7cddfSDavid du Colombier 		 */
3417dd7cddfSDavid du Colombier 		if(recurse == Dontrecurse){
3427dd7cddfSDavid du Colombier 			if(dbnsrp)
3437dd7cddfSDavid du Colombier 				rrfreelist(dbnsrp);
3447dd7cddfSDavid du Colombier 			continue;
3457dd7cddfSDavid du Colombier 		}
3467dd7cddfSDavid du Colombier 
3477dd7cddfSDavid du Colombier 		/* look for ns in cache */
3483e12c5d1SDavid du Colombier 		nsdp = dnlookup(cp, class, 0);
3497dd7cddfSDavid du Colombier 		nsrp = nil;
3503e12c5d1SDavid du Colombier 		if(nsdp)
3517dd7cddfSDavid du Colombier 			nsrp = randomize(rrlookup(nsdp, Tns, NOneg));
3527dd7cddfSDavid du Colombier 
3537dd7cddfSDavid du Colombier 		/* if the entry timed out, ignore it */
3547dd7cddfSDavid du Colombier 		if(nsrp && nsrp->ttl < now){
3557dd7cddfSDavid du Colombier 			rrfreelist(nsrp);
3567dd7cddfSDavid du Colombier 			nsrp = nil;
3577dd7cddfSDavid du Colombier 		}
3583e12c5d1SDavid du Colombier 
3593e12c5d1SDavid du Colombier 		if(nsrp){
3607dd7cddfSDavid du Colombier 			rrfreelist(dbnsrp);
3617dd7cddfSDavid du Colombier 
3624f8f669cSDavid du Colombier 			/* query the name servers found in cache */
363c73252aeSDavid du Colombier 			qp->nsrp = nsrp;
364c73252aeSDavid du Colombier 			if(netquery(qp, depth+1)){
3653e12c5d1SDavid du Colombier 				rrfreelist(nsrp);
366c73252aeSDavid du Colombier 				return rrlookup(qp->dp, qp->type, OKneg);
3677dd7cddfSDavid du Colombier 			}
3687dd7cddfSDavid du Colombier 			rrfreelist(nsrp);
3697dd7cddfSDavid du Colombier 			continue;
3703e12c5d1SDavid du Colombier 		}
3713e12c5d1SDavid du Colombier 
3727dd7cddfSDavid du Colombier 		/* use ns from db */
3737dd7cddfSDavid du Colombier 		if(dbnsrp){
3747dd7cddfSDavid du Colombier 			/* try the name servers found in db */
375c73252aeSDavid du Colombier 			qp->nsrp = dbnsrp;
376c73252aeSDavid du Colombier 			if(netquery(qp, depth+1)){
3773e12c5d1SDavid du Colombier 				/* we got an answer */
3787dd7cddfSDavid du Colombier 				rrfreelist(dbnsrp);
379c73252aeSDavid du Colombier 				return rrlookup(qp->dp, qp->type, NOneg);
3803e12c5d1SDavid du Colombier 			}
3817dd7cddfSDavid du Colombier 			rrfreelist(dbnsrp);
3823e12c5d1SDavid du Colombier 		}
3833e12c5d1SDavid du Colombier 	}
384c73252aeSDavid du Colombier 	return rp;
385c73252aeSDavid du Colombier }
386c73252aeSDavid du Colombier 
387c73252aeSDavid du Colombier static RR*
388c73252aeSDavid du Colombier dnresolve1(char *name, int class, int type, Request *req, int depth,
389c73252aeSDavid du Colombier 	int recurse)
390c73252aeSDavid du Colombier {
391c73252aeSDavid du Colombier 	Area *area;
392c73252aeSDavid du Colombier 	DN *dp;
393c73252aeSDavid du Colombier 	RR *rp;
394c73252aeSDavid du Colombier 	Query *qp;
395c73252aeSDavid du Colombier 
396c73252aeSDavid du Colombier 	if(debug)
397c73252aeSDavid du Colombier 		dnslog("[%d] dnresolve1 %s %d %d", getpid(), name, type, class);
398c73252aeSDavid du Colombier 
399c73252aeSDavid du Colombier 	/* only class Cin implemented so far */
400c73252aeSDavid du Colombier 	if(class != Cin)
401c73252aeSDavid du Colombier 		return nil;
402c73252aeSDavid du Colombier 
403c73252aeSDavid du Colombier 	dp = dnlookup(name, class, 1);
404c73252aeSDavid du Colombier 
405c73252aeSDavid du Colombier 	/*
406c73252aeSDavid du Colombier 	 *  Try the cache first
407c73252aeSDavid du Colombier 	 */
408c73252aeSDavid du Colombier 	rp = rrlookup(dp, type, OKneg);
409c73252aeSDavid du Colombier 	if(rp)
410c73252aeSDavid du Colombier 		if(rp->db){
411c73252aeSDavid du Colombier 			/* unauthoritative db entries are hints */
412c73252aeSDavid du Colombier 			if(rp->auth) {
413c73252aeSDavid du Colombier 				noteinmem();
414c73252aeSDavid du Colombier 				return rp;
415c73252aeSDavid du Colombier 			}
416c73252aeSDavid du Colombier 		} else
417c73252aeSDavid du Colombier 			/* cached entry must still be valid */
418c73252aeSDavid du Colombier 			if(rp->ttl > now)
419c73252aeSDavid du Colombier 				/* but Tall entries are special */
420c73252aeSDavid du Colombier 				if(type != Tall || rp->query == Tall) {
421c73252aeSDavid du Colombier 					noteinmem();
422c73252aeSDavid du Colombier 					return rp;
423c73252aeSDavid du Colombier 				}
424c73252aeSDavid du Colombier 	rrfreelist(rp);
425*adb31a62SDavid du Colombier 	rp = nil;
426c73252aeSDavid du Colombier 
427c73252aeSDavid du Colombier 	/*
428c73252aeSDavid du Colombier 	 * try the cache for a canonical name. if found punt
429c73252aeSDavid du Colombier 	 * since we'll find it during the canonical name search
430c73252aeSDavid du Colombier 	 * in dnresolve().
431c73252aeSDavid du Colombier 	 */
432c73252aeSDavid du Colombier 	if(type != Tcname){
433c73252aeSDavid du Colombier 		rp = rrlookup(dp, Tcname, NOneg);
434c73252aeSDavid du Colombier 		rrfreelist(rp);
435c73252aeSDavid du Colombier 		if(rp)
436c73252aeSDavid du Colombier 			return nil;
437c73252aeSDavid du Colombier 	}
438c73252aeSDavid du Colombier 
439c73252aeSDavid du Colombier 	/*
440c73252aeSDavid du Colombier 	 * if the domain name is within an area of ours,
441c73252aeSDavid du Colombier 	 * we should have found its data in memory by now.
442c73252aeSDavid du Colombier 	 */
443c73252aeSDavid du Colombier 	area = inmyarea(dp->name);
444c73252aeSDavid du Colombier 	if (area || strncmp(dp->name, "local#", 6) == 0) {
445c73252aeSDavid du Colombier //		char buf[32];
446c73252aeSDavid du Colombier 
447c73252aeSDavid du Colombier //		dnslog("%s %s: no data in area %s", dp->name,
448c73252aeSDavid du Colombier //			rrname(type, buf, sizeof buf), area->soarr->owner->name);
449c73252aeSDavid du Colombier 		return nil;
450c73252aeSDavid du Colombier 	}
451c73252aeSDavid du Colombier 
452c73252aeSDavid du Colombier 	qp = emalloc(sizeof *qp);
453c73252aeSDavid du Colombier 	queryinit(qp, dp, type, req);
454c73252aeSDavid du Colombier 	rp = issuequery(qp, rp, name, class, depth, recurse);
455c73252aeSDavid du Colombier 	querydestroy(qp);
456c73252aeSDavid du Colombier 	free(qp);
457c73252aeSDavid du Colombier 	if(rp)
458c73252aeSDavid du Colombier 		return rp;
4593e12c5d1SDavid du Colombier 
4607dd7cddfSDavid du Colombier 	/* settle for a non-authoritative answer */
4617dd7cddfSDavid du Colombier 	rp = rrlookup(dp, type, OKneg);
4627dd7cddfSDavid du Colombier 	if(rp)
4637dd7cddfSDavid du Colombier 		return rp;
4647dd7cddfSDavid du Colombier 
4657dd7cddfSDavid du Colombier 	/* noone answered.  try the database, we might have a chance. */
4667dd7cddfSDavid du Colombier 	return dblookup(name, class, type, 0, 0);
4673e12c5d1SDavid du Colombier }
4683e12c5d1SDavid du Colombier 
4693e12c5d1SDavid du Colombier /*
4704f8f669cSDavid du Colombier  *  walk a domain name one element to the right.
4714f8f669cSDavid du Colombier  *  return a pointer to that element.
4723e12c5d1SDavid du Colombier  *  in other words, return a pointer to the parent domain name.
4733e12c5d1SDavid du Colombier  */
4743e12c5d1SDavid du Colombier char*
4753e12c5d1SDavid du Colombier walkup(char *name)
4763e12c5d1SDavid du Colombier {
4773e12c5d1SDavid du Colombier 	char *cp;
4783e12c5d1SDavid du Colombier 
4793e12c5d1SDavid du Colombier 	cp = strchr(name, '.');
4803e12c5d1SDavid du Colombier 	if(cp)
4813e12c5d1SDavid du Colombier 		return cp+1;
4823e12c5d1SDavid du Colombier 	else if(*name)
4833e12c5d1SDavid du Colombier 		return "";
4843e12c5d1SDavid du Colombier 	else
4853e12c5d1SDavid du Colombier 		return 0;
4863e12c5d1SDavid du Colombier }
4873e12c5d1SDavid du Colombier 
4883e12c5d1SDavid du Colombier /*
489a41547ffSDavid du Colombier  *  Get a udp port for sending requests and reading replies.  Put the port
4903e12c5d1SDavid du Colombier  *  into "headers" mode.
4913e12c5d1SDavid du Colombier  */
4923e12c5d1SDavid du Colombier static char *hmsg = "headers";
4933e12c5d1SDavid du Colombier 
494dc5a79c1SDavid du Colombier int
4954f8f669cSDavid du Colombier udpport(char *mtpt)
4963e12c5d1SDavid du Colombier {
4973e12c5d1SDavid du Colombier 	int fd, ctl;
4984f8f669cSDavid du Colombier 	char ds[64], adir[64];
4993e12c5d1SDavid du Colombier 
5003e12c5d1SDavid du Colombier 	/* get a udp port */
5014f8f669cSDavid du Colombier 	snprint(ds, sizeof ds, "%s/udp!*!0", (mtpt? mtpt: "/net"));
5027dd7cddfSDavid du Colombier 	ctl = announce(ds, adir);
5037dd7cddfSDavid du Colombier 	if(ctl < 0){
5047dd7cddfSDavid du Colombier 		/* warning("can't get udp port"); */
505bd389b36SDavid du Colombier 		return -1;
506bd389b36SDavid du Colombier 	}
5073e12c5d1SDavid du Colombier 
5083e12c5d1SDavid du Colombier 	/* turn on header style interface */
509bd389b36SDavid du Colombier 	if(write(ctl, hmsg, strlen(hmsg)) , 0){
5107dd7cddfSDavid du Colombier 		close(ctl);
511bd389b36SDavid du Colombier 		warning(hmsg);
512bd389b36SDavid du Colombier 		return -1;
513bd389b36SDavid du Colombier 	}
5143e12c5d1SDavid du Colombier 
5157dd7cddfSDavid du Colombier 	/* grab the data file */
5164f8f669cSDavid du Colombier 	snprint(ds, sizeof ds, "%s/data", adir);
5177dd7cddfSDavid du Colombier 	fd = open(ds, ORDWR);
5183e12c5d1SDavid du Colombier 	close(ctl);
5194f8f669cSDavid du Colombier 	if(fd < 0)
5204f8f669cSDavid du Colombier 		warning("can't open udp port %s: %r", ds);
5213e12c5d1SDavid du Colombier 	return fd;
5223e12c5d1SDavid du Colombier }
5233e12c5d1SDavid du Colombier 
524d6d99297SDavid du Colombier /* generate a DNS UDP query packet */
525dc5a79c1SDavid du Colombier int
526dc5a79c1SDavid du Colombier mkreq(DN *dp, int type, uchar *buf, int flags, ushort reqno)
5273e12c5d1SDavid du Colombier {
5283e12c5d1SDavid du Colombier 	DNSmsg m;
5293e12c5d1SDavid du Colombier 	int len;
530f27a9a5aSDavid du Colombier 	Udphdr *uh = (Udphdr*)buf;
5313e12c5d1SDavid du Colombier 
5323e12c5d1SDavid du Colombier 	/* stuff port number into output buffer */
5333cbadd90SDavid du Colombier 	memset(uh, 0, sizeof *uh);
5347dd7cddfSDavid du Colombier 	hnputs(uh->rport, 53);
5353e12c5d1SDavid du Colombier 
5363e12c5d1SDavid du Colombier 	/* make request and convert it to output format */
5373cbadd90SDavid du Colombier 	memset(&m, 0, sizeof m);
538dc5a79c1SDavid du Colombier 	m.flags = flags;
5393e12c5d1SDavid du Colombier 	m.id = reqno;
5403e12c5d1SDavid du Colombier 	m.qd = rralloc(type);
5413e12c5d1SDavid du Colombier 	m.qd->owner = dp;
5423e12c5d1SDavid du Colombier 	m.qd->type = type;
543*adb31a62SDavid du Colombier 	if (m.qd->type != type)
544*adb31a62SDavid du Colombier 		dnslog("mkreq: bogus type %d", type);
545f27a9a5aSDavid du Colombier 	len = convDNS2M(&m, &buf[Udphdrsize], Maxudp);
5467dd7cddfSDavid du Colombier 	rrfree(m.qd);
5473e12c5d1SDavid du Colombier 	return len;
5483e12c5d1SDavid du Colombier }
5493e12c5d1SDavid du Colombier 
5507dd7cddfSDavid du Colombier /* for alarms in readreply */
5513e12c5d1SDavid du Colombier static void
552c73252aeSDavid du Colombier ding(void*, char *msg)
5533e12c5d1SDavid du Colombier {
554c73252aeSDavid du Colombier 	if(strstr(msg, "alarm") != nil)
555c73252aeSDavid du Colombier 		noted(NCONT);		/* resume with system call error */
5563e12c5d1SDavid du Colombier 	else
557c73252aeSDavid du Colombier 		noted(NDFLT);		/* die */
5583e12c5d1SDavid du Colombier }
5597dd7cddfSDavid du Colombier 
560a41547ffSDavid du Colombier void
5617dd7cddfSDavid du Colombier freeanswers(DNSmsg *mp)
5623e12c5d1SDavid du Colombier {
5637dd7cddfSDavid du Colombier 	rrfreelist(mp->qd);
5647dd7cddfSDavid du Colombier 	rrfreelist(mp->an);
5657dd7cddfSDavid du Colombier 	rrfreelist(mp->ns);
5667dd7cddfSDavid du Colombier 	rrfreelist(mp->ar);
5674f8f669cSDavid du Colombier 	mp->qd = mp->an = mp->ns = mp->ar = nil;
5687dd7cddfSDavid du Colombier }
5697dd7cddfSDavid du Colombier 
5703cbadd90SDavid du Colombier /* sets srcip */
5713cbadd90SDavid du Colombier static int
5723cbadd90SDavid du Colombier readnet(Query *qp, int medium, uchar *ibuf, ulong endtime, uchar **replyp,
5733cbadd90SDavid du Colombier 	uchar *srcip)
5743cbadd90SDavid du Colombier {
5753cbadd90SDavid du Colombier 	int len, fd;
576a41547ffSDavid du Colombier 	long ms;
577a41547ffSDavid du Colombier 	vlong startns = nsec();
5783cbadd90SDavid du Colombier 	uchar *reply;
5793cbadd90SDavid du Colombier 	uchar lenbuf[2];
5803cbadd90SDavid du Colombier 
5813cbadd90SDavid du Colombier 	/* timed read of reply */
582a41547ffSDavid du Colombier 	ms = S2MS(endtime) - NS2MS(startns);
583a41547ffSDavid du Colombier 	if (ms < 2000)
584a41547ffSDavid du Colombier 		ms = 2000;	/* give the remote ns a fighting chance */
5853cbadd90SDavid du Colombier 	reply = ibuf;
5863cbadd90SDavid du Colombier 	len = -1;			/* pessimism */
5874e7b9544SDavid du Colombier 	memset(srcip, 0, IPaddrlen);
588a41547ffSDavid du Colombier 	if (medium == Udp)
5894e7b9544SDavid du Colombier 		if (qp->udpfd <= 0)
5904e7b9544SDavid du Colombier 			dnslog("readnet: qp->udpfd closed");
5914e7b9544SDavid du Colombier 		else {
592a41547ffSDavid du Colombier 			alarm(ms);
593f27a9a5aSDavid du Colombier 			len = read(qp->udpfd, ibuf, Udphdrsize+Maxudpin);
594a41547ffSDavid du Colombier 			alarm(0);
595a41547ffSDavid du Colombier 			notestats(startns, len < 0, qp->type);
5964e7b9544SDavid du Colombier 			if (len >= IPaddrlen)
5974e7b9544SDavid du Colombier 				memmove(srcip, ibuf, IPaddrlen);
598f27a9a5aSDavid du Colombier 			if (len >= Udphdrsize) {
599f27a9a5aSDavid du Colombier 				len   -= Udphdrsize;
600f27a9a5aSDavid du Colombier 				reply += Udphdrsize;
6014e7b9544SDavid du Colombier 			}
6024e7b9544SDavid du Colombier 		}
603a41547ffSDavid du Colombier 	else {
6043cbadd90SDavid du Colombier 		if (!qp->tcpset)
6053cbadd90SDavid du Colombier 			dnslog("readnet: tcp params not set");
606a41547ffSDavid du Colombier 		alarm(ms);
6073cbadd90SDavid du Colombier 		fd = qp->tcpfd;
6083cbadd90SDavid du Colombier 		if (fd <= 0)
6093cbadd90SDavid du Colombier 			dnslog("readnet: %s: tcp fd unset for dest %I",
6103cbadd90SDavid du Colombier 				qp->dp->name, qp->tcpip);
6113cbadd90SDavid du Colombier 		else if (readn(fd, lenbuf, 2) != 2) {
6123cbadd90SDavid du Colombier 			dnslog("readnet: short read of tcp size from %I",
6133cbadd90SDavid du Colombier 				qp->tcpip);
614a41547ffSDavid du Colombier 			/* probably a time-out */
615a41547ffSDavid du Colombier 			notestats(startns, 1, qp->type);
6163cbadd90SDavid du Colombier 		} else {
6173cbadd90SDavid du Colombier 			len = lenbuf[0]<<8 | lenbuf[1];
6183cbadd90SDavid du Colombier 			if (readn(fd, ibuf, len) != len) {
6193cbadd90SDavid du Colombier 				dnslog("readnet: short read of tcp data from %I",
6203cbadd90SDavid du Colombier 					qp->tcpip);
621a41547ffSDavid du Colombier 				/* probably a time-out */
622a41547ffSDavid du Colombier 				notestats(startns, 1, qp->type);
6233cbadd90SDavid du Colombier 				len = -1;
6243cbadd90SDavid du Colombier 			}
6253cbadd90SDavid du Colombier 		}
626a41547ffSDavid du Colombier 		alarm(0);
6273cbadd90SDavid du Colombier 		memmove(srcip, qp->tcpip, IPaddrlen);
6283cbadd90SDavid du Colombier 	}
6293cbadd90SDavid du Colombier 	*replyp = reply;
6303cbadd90SDavid du Colombier 	return len;
6313cbadd90SDavid du Colombier }
6323cbadd90SDavid du Colombier 
6337dd7cddfSDavid du Colombier /*
6343cbadd90SDavid du Colombier  *  read replies to a request and remember the rrs in the answer(s).
6353cbadd90SDavid du Colombier  *  ignore any of the wrong type.
6364f8f669cSDavid du Colombier  *  wait at most until endtime.
6377dd7cddfSDavid du Colombier  */
6387dd7cddfSDavid du Colombier static int
6393cbadd90SDavid du Colombier readreply(Query *qp, int medium, ushort req, uchar *ibuf, DNSmsg *mp,
6403cbadd90SDavid du Colombier 	ulong endtime)
6417dd7cddfSDavid du Colombier {
642a41547ffSDavid du Colombier 	int len, rv;
6437dd7cddfSDavid du Colombier 	char *err;
644a41547ffSDavid du Colombier 	char tbuf[32];
6453cbadd90SDavid du Colombier 	uchar *reply;
6463cbadd90SDavid du Colombier 	uchar srcip[IPaddrlen];
6473e12c5d1SDavid du Colombier 	RR *rp;
6487dd7cddfSDavid du Colombier 
6497dd7cddfSDavid du Colombier 	notify(ding);
6507dd7cddfSDavid du Colombier 
6513cbadd90SDavid du Colombier 	queryck(qp);
6523cbadd90SDavid du Colombier 	rv = 0;
6533cbadd90SDavid du Colombier 	memset(mp, 0, sizeof *mp);
6543cbadd90SDavid du Colombier 	if (time(nil) >= endtime)
6553cbadd90SDavid du Colombier 		return -1;		/* timed out before we started */
6567dd7cddfSDavid du Colombier 
657a41547ffSDavid du Colombier 	memset(srcip, 0, sizeof srcip);
658a41547ffSDavid du Colombier 	if (0)
659a41547ffSDavid du Colombier 		len = -1;
6603cbadd90SDavid du Colombier 	for (; time(nil) < endtime &&
6613cbadd90SDavid du Colombier 	    (len = readnet(qp, medium, ibuf, endtime, &reply, srcip)) >= 0;
6623cbadd90SDavid du Colombier 	    freeanswers(mp)){
6637dd7cddfSDavid du Colombier 		/* convert into internal format  */
6643cbadd90SDavid du Colombier 		memset(mp, 0, sizeof *mp);
6653cbadd90SDavid du Colombier 		err = convM2DNS(reply, len, mp, nil);
666d6d99297SDavid du Colombier 		if (mp->flags & Ftrunc) {
667a41547ffSDavid du Colombier 			free(err);
668a41547ffSDavid du Colombier 			freeanswers(mp);
669a41547ffSDavid du Colombier 			/* notify our caller to retry the query via tcp. */
6703cbadd90SDavid du Colombier 			return -1;
6713cbadd90SDavid du Colombier 		} else if(err){
6723cbadd90SDavid du Colombier 			dnslog("readreply: %s: input err, len %d: %s: %I",
6733cbadd90SDavid du Colombier 				qp->dp->name, len, err, srcip);
6743cbadd90SDavid du Colombier 			free(err);
6757dd7cddfSDavid du Colombier 			continue;
6767dd7cddfSDavid du Colombier 		}
6777dd7cddfSDavid du Colombier 		if(debug)
6783cbadd90SDavid du Colombier 			logreply(qp->req->id, srcip, mp);
6797dd7cddfSDavid du Colombier 
6807dd7cddfSDavid du Colombier 		/* answering the right question? */
6813cbadd90SDavid du Colombier 		if(mp->id != req)
6823cbadd90SDavid du Colombier 			dnslog("%d: id %d instead of %d: %I", qp->req->id,
6833cbadd90SDavid du Colombier 				mp->id, req, srcip);
6843cbadd90SDavid du Colombier 		else if(mp->qd == 0)
6853cbadd90SDavid du Colombier 			dnslog("%d: no question RR: %I", qp->req->id, srcip);
6863cbadd90SDavid du Colombier 		else if(mp->qd->owner != qp->dp)
6873cbadd90SDavid du Colombier 			dnslog("%d: owner %s instead of %s: %I", qp->req->id,
6883cbadd90SDavid du Colombier 				mp->qd->owner->name, qp->dp->name, srcip);
6893cbadd90SDavid du Colombier 		else if(mp->qd->type != qp->type)
6903cbadd90SDavid du Colombier 			dnslog("%d: qp->type %d instead of %d: %I",
6913cbadd90SDavid du Colombier 				qp->req->id, mp->qd->type, qp->type, srcip);
6923cbadd90SDavid du Colombier 		else {
6937dd7cddfSDavid du Colombier 			/* remember what request this is in answer to */
6947dd7cddfSDavid du Colombier 			for(rp = mp->an; rp; rp = rp->next)
6953cbadd90SDavid du Colombier 				rp->query = qp->type;
6963cbadd90SDavid du Colombier 			return rv;
6977dd7cddfSDavid du Colombier 		}
6987dd7cddfSDavid du Colombier 	}
699a41547ffSDavid du Colombier 	if (time(nil) >= endtime) {
700a41547ffSDavid du Colombier 		;				/* query expired */
701a41547ffSDavid du Colombier 	} else if (0) {
702a41547ffSDavid du Colombier 		/* this happens routinely when a read times out */
703a41547ffSDavid du Colombier 		dnslog("readreply: %s type %s: ns %I read error or eof "
704a41547ffSDavid du Colombier 			"(returned %d): %r", qp->dp->name, rrname(qp->type,
705a41547ffSDavid du Colombier 			tbuf, sizeof tbuf), srcip, len);
706a41547ffSDavid du Colombier 		if (medium == Udp)
707a41547ffSDavid du Colombier 			for (rp = qp->nsrp; rp != nil; rp = rp->next)
708a41547ffSDavid du Colombier 				if (rp->type == Tns)
709a41547ffSDavid du Colombier 					dnslog("readreply: %s: query sent to "
710a41547ffSDavid du Colombier 						"ns %s", qp->dp->name,
711a41547ffSDavid du Colombier 						rp->host->name);
712a41547ffSDavid du Colombier 	}
7133cbadd90SDavid du Colombier 	return -1;
7143cbadd90SDavid du Colombier }
7157dd7cddfSDavid du Colombier 
7167dd7cddfSDavid du Colombier /*
7177dd7cddfSDavid du Colombier  *	return non-0 if first list includes second list
7187dd7cddfSDavid du Colombier  */
7197dd7cddfSDavid du Colombier int
7207dd7cddfSDavid du Colombier contains(RR *rp1, RR *rp2)
7217dd7cddfSDavid du Colombier {
7227dd7cddfSDavid du Colombier 	RR *trp1, *trp2;
7237dd7cddfSDavid du Colombier 
7247dd7cddfSDavid du Colombier 	for(trp2 = rp2; trp2; trp2 = trp2->next){
7253cbadd90SDavid du Colombier 		for(trp1 = rp1; trp1; trp1 = trp1->next)
7267dd7cddfSDavid du Colombier 			if(trp1->type == trp2->type)
7277dd7cddfSDavid du Colombier 			if(trp1->host == trp2->host)
7287dd7cddfSDavid du Colombier 			if(trp1->owner == trp2->owner)
7297dd7cddfSDavid du Colombier 				break;
7304f8f669cSDavid du Colombier 		if(trp1 == nil)
7317dd7cddfSDavid du Colombier 			return 0;
7327dd7cddfSDavid du Colombier 	}
7337dd7cddfSDavid du Colombier 	return 1;
7347dd7cddfSDavid du Colombier }
7357dd7cddfSDavid du Colombier 
7367dd7cddfSDavid du Colombier 
7376b6b9ac8SDavid du Colombier /*
7386b6b9ac8SDavid du Colombier  *  return multicast version if any
7396b6b9ac8SDavid du Colombier  */
7406b6b9ac8SDavid du Colombier int
7416b6b9ac8SDavid du Colombier ipisbm(uchar *ip)
7426b6b9ac8SDavid du Colombier {
7436b6b9ac8SDavid du Colombier 	if(isv4(ip)){
7444f8f669cSDavid du Colombier 		if (ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0 ||
7454f8f669cSDavid du Colombier 		    ipcmp(ip, IPv4bcast) == 0)
7466b6b9ac8SDavid du Colombier 			return 4;
7474f8f669cSDavid du Colombier 	} else
7486b6b9ac8SDavid du Colombier 		if(ip[0] == 0xff)
7496b6b9ac8SDavid du Colombier 			return 6;
75028a8a86bSDavid du Colombier 	return 0;
75128a8a86bSDavid du Colombier }
75228a8a86bSDavid du Colombier 
7537dd7cddfSDavid du Colombier /*
7547dd7cddfSDavid du Colombier  *  Get next server address
7557dd7cddfSDavid du Colombier  */
7567dd7cddfSDavid du Colombier static int
7573cbadd90SDavid du Colombier serveraddrs(Query *qp, int nd, int depth)
7587dd7cddfSDavid du Colombier {
7597dd7cddfSDavid du Colombier 	RR *rp, *arp, *trp;
7607dd7cddfSDavid du Colombier 	Dest *cur;
7617dd7cddfSDavid du Colombier 
7627dd7cddfSDavid du Colombier 	if(nd >= Maxdest)
7637dd7cddfSDavid du Colombier 		return 0;
7647dd7cddfSDavid du Colombier 
7657dd7cddfSDavid du Colombier 	/*
7667dd7cddfSDavid du Colombier 	 *  look for a server whose address we already know.
7677dd7cddfSDavid du Colombier 	 *  if we find one, mark it so we ignore this on
7687dd7cddfSDavid du Colombier 	 *  subsequent passes.
7697dd7cddfSDavid du Colombier 	 */
7707dd7cddfSDavid du Colombier 	arp = 0;
7713cbadd90SDavid du Colombier 	for(rp = qp->nsrp; rp; rp = rp->next){
77234f77ae3SDavid du Colombier 		assert(rp->magic == RRmagic);
7737dd7cddfSDavid du Colombier 		if(rp->marker)
7747dd7cddfSDavid du Colombier 			continue;
7757dd7cddfSDavid du Colombier 		arp = rrlookup(rp->host, Ta, NOneg);
7767dd7cddfSDavid du Colombier 		if(arp){
7777dd7cddfSDavid du Colombier 			rp->marker = 1;
7787dd7cddfSDavid du Colombier 			break;
7797dd7cddfSDavid du Colombier 		}
7807dd7cddfSDavid du Colombier 		arp = dblookup(rp->host->name, Cin, Ta, 0, 0);
7817dd7cddfSDavid du Colombier 		if(arp){
7827dd7cddfSDavid du Colombier 			rp->marker = 1;
7837dd7cddfSDavid du Colombier 			break;
7847dd7cddfSDavid du Colombier 		}
7857dd7cddfSDavid du Colombier 	}
7867dd7cddfSDavid du Colombier 
7877dd7cddfSDavid du Colombier 	/*
7887dd7cddfSDavid du Colombier 	 *  if the cache and database lookup didn't find any new
7897dd7cddfSDavid du Colombier 	 *  server addresses, try resolving one via the network.
7907dd7cddfSDavid du Colombier 	 *  Mark any we try to resolve so we don't try a second time.
7917dd7cddfSDavid du Colombier 	 */
7924f8f669cSDavid du Colombier 	if(arp == 0)
7933cbadd90SDavid du Colombier 		for(rp = qp->nsrp; rp; rp = rp->next){
7947dd7cddfSDavid du Colombier 			if(rp->marker)
7957dd7cddfSDavid du Colombier 				continue;
7967dd7cddfSDavid du Colombier 			rp->marker = 1;
7977dd7cddfSDavid du Colombier 
7987dd7cddfSDavid du Colombier 			/*
7997dd7cddfSDavid du Colombier 			 *  avoid loops looking up a server under itself
8007dd7cddfSDavid du Colombier 			 */
8017dd7cddfSDavid du Colombier 			if(subsume(rp->owner->name, rp->host->name))
8027dd7cddfSDavid du Colombier 				continue;
8037dd7cddfSDavid du Colombier 
8043cbadd90SDavid du Colombier 			arp = dnresolve(rp->host->name, Cin, Ta, qp->req, 0,
8054f8f669cSDavid du Colombier 				depth+1, Recurse, 1, 0);
8067dd7cddfSDavid du Colombier 			rrfreelist(rrremneg(&arp));
8077dd7cddfSDavid du Colombier 			if(arp)
8087dd7cddfSDavid du Colombier 				break;
8097dd7cddfSDavid du Colombier 		}
8107dd7cddfSDavid du Colombier 
8117dd7cddfSDavid du Colombier 	/* use any addresses that we found */
8123cbadd90SDavid du Colombier 	for(trp = arp; trp && nd < Maxdest; trp = trp->next){
8133cbadd90SDavid du Colombier 		cur = &qp->dest[nd];
8147dd7cddfSDavid du Colombier 		parseip(cur->a, trp->ip->name);
8154f8f669cSDavid du Colombier 		/*
8164f8f669cSDavid du Colombier 		 * straddling servers can reject all nameservers if they are all
8174f8f669cSDavid du Colombier 		 * inside, so be sure to list at least one outside ns at
8184f8f669cSDavid du Colombier 		 * the end of the ns list in /lib/ndb for `dom='.
8194f8f669cSDavid du Colombier 		 */
8204f8f669cSDavid du Colombier 		if (ipisbm(cur->a) ||
8213cbadd90SDavid du Colombier 		    cfg.straddle && !insideaddr(qp->dp->name) && insidens(cur->a))
8226b6b9ac8SDavid du Colombier 			continue;
8237dd7cddfSDavid du Colombier 		cur->nx = 0;
8247dd7cddfSDavid du Colombier 		cur->s = trp->owner;
8257dd7cddfSDavid du Colombier 		cur->code = Rtimeout;
8266b6b9ac8SDavid du Colombier 		nd++;
8277dd7cddfSDavid du Colombier 	}
8287dd7cddfSDavid du Colombier 	rrfreelist(arp);
8297dd7cddfSDavid du Colombier 	return nd;
8307dd7cddfSDavid du Colombier }
8317dd7cddfSDavid du Colombier 
8327dd7cddfSDavid du Colombier /*
8337dd7cddfSDavid du Colombier  *  cache negative responses
8347dd7cddfSDavid du Colombier  */
8357dd7cddfSDavid du Colombier static void
8367dd7cddfSDavid du Colombier cacheneg(DN *dp, int type, int rcode, RR *soarr)
8377dd7cddfSDavid du Colombier {
8387dd7cddfSDavid du Colombier 	RR *rp;
8397dd7cddfSDavid du Colombier 	DN *soaowner;
8409a747e4fSDavid du Colombier 	ulong ttl;
8417dd7cddfSDavid du Colombier 
842f46c709fSDavid du Colombier 	stats.negcached++;
843f46c709fSDavid du Colombier 
8444f8f669cSDavid du Colombier 	/* no cache time specified, don't make anything up */
8457dd7cddfSDavid du Colombier 	if(soarr != nil){
8467dd7cddfSDavid du Colombier 		if(soarr->next != nil){
8477dd7cddfSDavid du Colombier 			rrfreelist(soarr->next);
8487dd7cddfSDavid du Colombier 			soarr->next = nil;
8497dd7cddfSDavid du Colombier 		}
8507dd7cddfSDavid du Colombier 		soaowner = soarr->owner;
8517dd7cddfSDavid du Colombier 	} else
8527dd7cddfSDavid du Colombier 		soaowner = nil;
8537dd7cddfSDavid du Colombier 
8549a747e4fSDavid du Colombier 	/* the attach can cause soarr to be freed so mine it now */
8559a747e4fSDavid du Colombier 	if(soarr != nil && soarr->soa != nil)
8569a747e4fSDavid du Colombier 		ttl = soarr->soa->minttl+now;
8579a747e4fSDavid du Colombier 	else
8589a747e4fSDavid du Colombier 		ttl = 5*Min;
8599a747e4fSDavid du Colombier 
8607dd7cddfSDavid du Colombier 	/* add soa and negative RR to the database */
8616dc4800dSDavid du Colombier 	rrattach(soarr, Authoritative);
8627dd7cddfSDavid du Colombier 
8637dd7cddfSDavid du Colombier 	rp = rralloc(type);
8647dd7cddfSDavid du Colombier 	rp->owner = dp;
8657dd7cddfSDavid du Colombier 	rp->negative = 1;
8667dd7cddfSDavid du Colombier 	rp->negsoaowner = soaowner;
8677dd7cddfSDavid du Colombier 	rp->negrcode = rcode;
8689a747e4fSDavid du Colombier 	rp->ttl = ttl;
8696dc4800dSDavid du Colombier 	rrattach(rp, Authoritative);
8707dd7cddfSDavid du Colombier }
8717dd7cddfSDavid du Colombier 
8724f8f669cSDavid du Colombier static int
8734f8f669cSDavid du Colombier setdestoutns(Dest *p, int n)
8744f8f669cSDavid du Colombier {
8754f8f669cSDavid du Colombier 	uchar *outns = outsidens(n);
8764f8f669cSDavid du Colombier 
8773cbadd90SDavid du Colombier 	destck(p);
8783cbadd90SDavid du Colombier 	destinit(p);
8794f8f669cSDavid du Colombier 	if (outns == nil) {
8804f8f669cSDavid du Colombier 		if (n == 0)
8814f8f669cSDavid du Colombier 			dnslog("[%d] no outside-ns in ndb", getpid());
8824f8f669cSDavid du Colombier 		return -1;
8834f8f669cSDavid du Colombier 	}
8844f8f669cSDavid du Colombier 	memmove(p->a, outns, sizeof p->a);
8854f8f669cSDavid du Colombier 	p->s = dnlookup("outside-ns-ips", Cin, 1);
8864f8f669cSDavid du Colombier 	return 0;
8874f8f669cSDavid du Colombier }
8884f8f669cSDavid du Colombier 
8897dd7cddfSDavid du Colombier /*
8903cbadd90SDavid du Colombier  * issue query via UDP or TCP as appropriate.
8913cbadd90SDavid du Colombier  * for TCP, returns with qp->tcpip set from udppkt header.
8927dd7cddfSDavid du Colombier  */
8937dd7cddfSDavid du Colombier static int
8943cbadd90SDavid du Colombier mydnsquery(Query *qp, int medium, uchar *udppkt, int len)
8957dd7cddfSDavid du Colombier {
896a41547ffSDavid du Colombier 	int rv = -1, nfd;
8973cbadd90SDavid du Colombier 	char *domain;
8983cbadd90SDavid du Colombier 	char conndir[40];
899a41547ffSDavid du Colombier 	uchar belen[2];
9003cbadd90SDavid du Colombier 	NetConnInfo *nci;
9014f8f669cSDavid du Colombier 
9023cbadd90SDavid du Colombier 	queryck(qp);
9034e153993SDavid du Colombier 	domain = smprint("%I", udppkt);
9044e153993SDavid du Colombier 	if (myaddr(domain)) {
9054e153993SDavid du Colombier 		dnslog("mydnsquery: trying to send to myself (%s); bzzzt",
9064e153993SDavid du Colombier 			domain);
9074e153993SDavid du Colombier 		free(domain);
9084e153993SDavid du Colombier 		return rv;
9094e153993SDavid du Colombier 	}
9104e153993SDavid du Colombier 
9113cbadd90SDavid du Colombier 	switch (medium) {
9123cbadd90SDavid du Colombier 	case Udp:
9134e153993SDavid du Colombier 		free(domain);
914a41547ffSDavid du Colombier 		nfd = dup(qp->udpfd, -1);
915a41547ffSDavid du Colombier 		if (nfd < 0) {
916a41547ffSDavid du Colombier 			warning("mydnsquery: qp->udpfd %d: %r", qp->udpfd);
917a41547ffSDavid du Colombier 			close(qp->udpfd);	/* ensure it's closed */
918a41547ffSDavid du Colombier 			qp->udpfd = -1;		/* poison it */
919a41547ffSDavid du Colombier 			return rv;
920a41547ffSDavid du Colombier 		}
921a41547ffSDavid du Colombier 		close(nfd);
922a41547ffSDavid du Colombier 
9234e7b9544SDavid du Colombier 		if (qp->udpfd <= 0)
924a41547ffSDavid du Colombier 			dnslog("mydnsquery: qp->udpfd %d closed", qp->udpfd);
9254e7b9544SDavid du Colombier 		else {
926f27a9a5aSDavid du Colombier 			if (write(qp->udpfd, udppkt, len+Udphdrsize) !=
927f27a9a5aSDavid du Colombier 			    len+Udphdrsize)
928a41547ffSDavid du Colombier 				warning("sending udp msg: %r");
929a41547ffSDavid du Colombier 			else {
930a41547ffSDavid du Colombier 				stats.qsent++;
9313cbadd90SDavid du Colombier 				rv = 0;
9324e7b9544SDavid du Colombier 			}
933a41547ffSDavid du Colombier 		}
9343cbadd90SDavid du Colombier 		break;
9353cbadd90SDavid du Colombier 	case Tcp:
9363cbadd90SDavid du Colombier 		/* send via TCP & keep fd around for reply */
9373cbadd90SDavid du Colombier 		alarm(10*1000);
9383cbadd90SDavid du Colombier 		qp->tcpfd = rv = dial(netmkaddr(domain, "tcp", "dns"), nil,
9393cbadd90SDavid du Colombier 			conndir, &qp->tcpctlfd);
9403cbadd90SDavid du Colombier 		alarm(0);
9413cbadd90SDavid du Colombier 		if (qp->tcpfd < 0) {
9423cbadd90SDavid du Colombier 			dnslog("can't dial tcp!%s!dns: %r", domain);
943a41547ffSDavid du Colombier 			free(domain);
944a41547ffSDavid du Colombier 			break;
945a41547ffSDavid du Colombier 		}
946a41547ffSDavid du Colombier 		free(domain);
9473cbadd90SDavid du Colombier 		nci = getnetconninfo(conndir, qp->tcpfd);
9483cbadd90SDavid du Colombier 		if (nci) {
9493cbadd90SDavid du Colombier 			parseip(qp->tcpip, nci->rsys);
9503cbadd90SDavid du Colombier 			freenetconninfo(nci);
9513cbadd90SDavid du Colombier 		} else
9523cbadd90SDavid du Colombier 			dnslog("mydnsquery: getnetconninfo failed");
9533cbadd90SDavid du Colombier 		qp->tcpset = 1;
9543e12c5d1SDavid du Colombier 
9553cbadd90SDavid du Colombier 		belen[0] = len >> 8;
9563cbadd90SDavid du Colombier 		belen[1] = len;
9573cbadd90SDavid du Colombier 		if (write(qp->tcpfd, belen, 2) != 2 ||
958f27a9a5aSDavid du Colombier 		    write(qp->tcpfd, udppkt + Udphdrsize, len) != len)
959a41547ffSDavid du Colombier 			warning("sending tcp msg: %r");
9603cbadd90SDavid du Colombier 		break;
9613cbadd90SDavid du Colombier 	default:
9623cbadd90SDavid du Colombier 		sysfatal("mydnsquery: bad medium");
9633cbadd90SDavid du Colombier 	}
9643cbadd90SDavid du Colombier 	return rv;
9653cbadd90SDavid du Colombier }
9667dd7cddfSDavid du Colombier 
9673e12c5d1SDavid du Colombier /*
9683cbadd90SDavid du Colombier  * send query to all UDP destinations or one TCP destination,
9693cbadd90SDavid du Colombier  * taken from obuf (udp packet) header
9703e12c5d1SDavid du Colombier  */
9713cbadd90SDavid du Colombier static int
9723cbadd90SDavid du Colombier xmitquery(Query *qp, int medium, int depth, uchar *obuf, int inns, int len)
9733cbadd90SDavid du Colombier {
9743cbadd90SDavid du Colombier 	int j, n;
9753cbadd90SDavid du Colombier 	char buf[32];
9763cbadd90SDavid du Colombier 	Dest *p;
9773e12c5d1SDavid du Colombier 
9783cbadd90SDavid du Colombier 	queryck(qp);
9793cbadd90SDavid du Colombier 	if(time(nil) >= qp->req->aborttime)
9803cbadd90SDavid du Colombier 		return -1;
9813cbadd90SDavid du Colombier 
9823cbadd90SDavid du Colombier 	/*
9833cbadd90SDavid du Colombier 	 * get a nameserver address if we need one.
9843cbadd90SDavid du Colombier 	 * serveraddrs populates qp->dest.
9853cbadd90SDavid du Colombier 	 */
9863cbadd90SDavid du Colombier 	p = qp->dest;
9873cbadd90SDavid du Colombier 	destck(p);
9883cbadd90SDavid du Colombier 	if (qp->ndest < 0 || qp->ndest > Maxdest)
9893cbadd90SDavid du Colombier 		dnslog("qp->ndest %d out of range", qp->ndest);
9903cbadd90SDavid du Colombier 	if (qp->ndest > qp->curdest - p)
9913cbadd90SDavid du Colombier 		qp->curdest = &qp->dest[serveraddrs(qp, qp->curdest - p, depth)];
9923cbadd90SDavid du Colombier 	destck(qp->curdest);
9937dd7cddfSDavid du Colombier 
9947dd7cddfSDavid du Colombier 	/* no servers, punt */
9953cbadd90SDavid du Colombier 	if (qp->curdest == qp->dest)
9964f8f669cSDavid du Colombier 		if (cfg.straddle && cfg.inside) {
9973cbadd90SDavid du Colombier 			/* get ips of "outside-ns-ips" */
9983cbadd90SDavid du Colombier 			p = qp->curdest = qp->dest;
9993cbadd90SDavid du Colombier 			for(n = 0; n < Maxdest; n++, qp->curdest++)
10003cbadd90SDavid du Colombier 				if (setdestoutns(qp->curdest, n) < 0)
10017dd7cddfSDavid du Colombier 					break;
10024f8f669cSDavid du Colombier 		} else {
10034e7b9544SDavid du Colombier 			/* it's probably just a bogus domain, don't log it */
10044e7b9544SDavid du Colombier 			// dnslog("xmitquery: %s: no nameservers", qp->dp->name);
10053cbadd90SDavid du Colombier 			return -1;
10064f8f669cSDavid du Colombier 		}
10077dd7cddfSDavid du Colombier 
10083cbadd90SDavid du Colombier 	/* send to first 'qp->ndest' destinations */
10097dd7cddfSDavid du Colombier 	j = 0;
10103cbadd90SDavid du Colombier 	if (medium == Tcp) {
10113cbadd90SDavid du Colombier 		j++;
10123cbadd90SDavid du Colombier 		queryck(qp);
10133cbadd90SDavid du Colombier 		assert(qp->dp);
10143cbadd90SDavid du Colombier 		procsetname("tcp %sside query for %s %s", (inns? "in": "out"),
10153cbadd90SDavid du Colombier 			qp->dp->name, rrname(qp->type, buf, sizeof buf));
10163cbadd90SDavid du Colombier 		mydnsquery(qp, medium, obuf, len); /* sets qp->tcpip from obuf */
10173cbadd90SDavid du Colombier 		if(debug)
10183cbadd90SDavid du Colombier 			logsend(qp->req->id, depth, qp->tcpip, "", qp->dp->name,
10193cbadd90SDavid du Colombier 				qp->type);
10203cbadd90SDavid du Colombier 	} else
10213cbadd90SDavid du Colombier 		for(; p < &qp->dest[qp->ndest] && p < qp->curdest; p++){
10227dd7cddfSDavid du Colombier 			/* skip destinations we've finished with */
10237dd7cddfSDavid du Colombier 			if(p->nx >= Maxtrans)
10247dd7cddfSDavid du Colombier 				continue;
10257dd7cddfSDavid du Colombier 
10267dd7cddfSDavid du Colombier 			j++;
10277dd7cddfSDavid du Colombier 
10287dd7cddfSDavid du Colombier 			/* exponential backoff of requests */
10293cbadd90SDavid du Colombier 			if((1<<p->nx) > qp->ndest)
10307dd7cddfSDavid du Colombier 				continue;
10317dd7cddfSDavid du Colombier 
10323cbadd90SDavid du Colombier 			procsetname("udp %sside query to %I/%s %s %s",
10333cbadd90SDavid du Colombier 				(inns? "in": "out"), p->a, p->s->name,
10343cbadd90SDavid du Colombier 				qp->dp->name, rrname(qp->type, buf, sizeof buf));
1035219b2ee8SDavid du Colombier 			if(debug)
10363cbadd90SDavid du Colombier 				logsend(qp->req->id, depth, p->a, p->s->name,
10373cbadd90SDavid du Colombier 					qp->dp->name, qp->type);
10384f8f669cSDavid du Colombier 
10393cbadd90SDavid du Colombier 			/* fill in UDP destination addr & send it */
10403cbadd90SDavid du Colombier 			memmove(obuf, p->a, sizeof p->a);
10413cbadd90SDavid du Colombier 			mydnsquery(qp, medium, obuf, len);
10427dd7cddfSDavid du Colombier 			p->nx++;
10433e12c5d1SDavid du Colombier 		}
10443cbadd90SDavid du Colombier 	if(j == 0) {
10453cbadd90SDavid du Colombier 		// dnslog("xmitquery: %s: no destinations left", qp->dp->name);
10463cbadd90SDavid du Colombier 		return -1;
10473cbadd90SDavid du Colombier 	}
10483cbadd90SDavid du Colombier 	return 0;
10493cbadd90SDavid du Colombier }
10503e12c5d1SDavid du Colombier 
1051f46c709fSDavid du Colombier static int lckindex[Maxlcks] = {
1052f46c709fSDavid du Colombier 	0,			/* all others map here */
1053f46c709fSDavid du Colombier 	Ta,
1054f46c709fSDavid du Colombier 	Tns,
1055f46c709fSDavid du Colombier 	Tcname,
1056f46c709fSDavid du Colombier 	Tsoa,
1057f46c709fSDavid du Colombier 	Tptr,
1058f46c709fSDavid du Colombier 	Tmx,
1059f46c709fSDavid du Colombier 	Ttxt,
1060f46c709fSDavid du Colombier 	Taaaa,
1061f46c709fSDavid du Colombier };
1062f46c709fSDavid du Colombier 
1063f46c709fSDavid du Colombier static int
1064f46c709fSDavid du Colombier qtype2lck(int qtype)		/* map query type to querylck index */
1065f46c709fSDavid du Colombier {
1066f46c709fSDavid du Colombier 	int i;
1067f46c709fSDavid du Colombier 
1068f46c709fSDavid du Colombier 	for (i = 1; i < nelem(lckindex); i++)
1069f46c709fSDavid du Colombier 		if (lckindex[i] == qtype)
1070f46c709fSDavid du Colombier 			return i;
1071f46c709fSDavid du Colombier 	return 0;
1072f46c709fSDavid du Colombier }
1073f46c709fSDavid du Colombier 
10740319257bSDavid du Colombier /* is mp a cachable negative response (with Rname set)? */
10750319257bSDavid du Colombier static int
10760319257bSDavid du Colombier isnegrname(DNSmsg *mp)
10770319257bSDavid du Colombier {
10780319257bSDavid du Colombier 	/* TODO: could add || cfg.justforw to RHS of && */
10790319257bSDavid du Colombier 	return mp->an == nil && (mp->flags & Rmask) == Rname;
10800319257bSDavid du Colombier }
10810319257bSDavid du Colombier 
10823cbadd90SDavid du Colombier static int
10833cbadd90SDavid du Colombier procansw(Query *qp, DNSmsg *mp, uchar *srcip, int depth, Dest *p)
10843cbadd90SDavid du Colombier {
10853cbadd90SDavid du Colombier 	int rv;
10864e5f5f32SDavid du Colombier //	int lcktype;
10873cbadd90SDavid du Colombier 	char buf[32];
10883cbadd90SDavid du Colombier 	DN *ndp;
1089c73252aeSDavid du Colombier 	Query *nqp;
10903cbadd90SDavid du Colombier 	RR *tp, *soarr;
10917dd7cddfSDavid du Colombier 
1092f46c709fSDavid du Colombier 	if (mp->an == nil)
1093f46c709fSDavid du Colombier 		stats.negans++;
1094f46c709fSDavid du Colombier 
109559cc4ca5SDavid du Colombier 	/* ignore any error replies */
10963cbadd90SDavid du Colombier 	if((mp->flags & Rmask) == Rserver){
10970319257bSDavid du Colombier 		stats.negserver++;
1098a41547ffSDavid du Colombier 		freeanswers(mp);
10993cbadd90SDavid du Colombier 		if(p != qp->curdest)
11007dd7cddfSDavid du Colombier 			p->code = Rserver;
11013cbadd90SDavid du Colombier 		return -1;
11023e12c5d1SDavid du Colombier 	}
11033e12c5d1SDavid du Colombier 
110459cc4ca5SDavid du Colombier 	/* ignore any bad delegations */
11053cbadd90SDavid du Colombier 	if(mp->ns && baddelegation(mp->ns, qp->nsrp, srcip)){
11060319257bSDavid du Colombier 		stats.negbaddeleg++;
11073cbadd90SDavid du Colombier 		if(mp->an == nil){
11080319257bSDavid du Colombier 			stats.negbdnoans++;
1109a41547ffSDavid du Colombier 			freeanswers(mp);
11103cbadd90SDavid du Colombier 			if(p != qp->curdest)
111159cc4ca5SDavid du Colombier 				p->code = Rserver;
11123cbadd90SDavid du Colombier 			return -1;
111359cc4ca5SDavid du Colombier 		}
1114a41547ffSDavid du Colombier 		rrfreelist(mp->ns);
1115a41547ffSDavid du Colombier 		mp->ns = nil;
111659cc4ca5SDavid du Colombier 	}
111759cc4ca5SDavid du Colombier 
11187dd7cddfSDavid du Colombier 	/* remove any soa's from the authority section */
11193cbadd90SDavid du Colombier 	soarr = rrremtype(&mp->ns, Tsoa);
11207dd7cddfSDavid du Colombier 
11213e12c5d1SDavid du Colombier 	/* incorporate answers */
11224e153993SDavid du Colombier 	unique(mp->an);
11234e153993SDavid du Colombier 	unique(mp->ns);
11244e153993SDavid du Colombier 	unique(mp->ar);
11253cbadd90SDavid du Colombier 	if(mp->an)
11263cbadd90SDavid du Colombier 		rrattach(mp->an, (mp->flags & Fauth) != 0);
11273cbadd90SDavid du Colombier 	if(mp->ar)
11286dc4800dSDavid du Colombier 		rrattach(mp->ar, Notauthoritative);
1129a41547ffSDavid du Colombier 	if(mp->ns && !cfg.justforw){
11303cbadd90SDavid du Colombier 		ndp = mp->ns->owner;
11316dc4800dSDavid du Colombier 		rrattach(mp->ns, Notauthoritative);
11320319257bSDavid du Colombier 	} else {
11334f8f669cSDavid du Colombier 		ndp = nil;
11340319257bSDavid du Colombier 		rrfreelist(mp->ns);
11350319257bSDavid du Colombier 		mp->ns = nil;
11360319257bSDavid du Colombier 	}
11377dd7cddfSDavid du Colombier 
11387dd7cddfSDavid du Colombier 	/* free the question */
1139a41547ffSDavid du Colombier 	if(mp->qd) {
11403cbadd90SDavid du Colombier 		rrfreelist(mp->qd);
1141a41547ffSDavid du Colombier 		mp->qd = nil;
1142a41547ffSDavid du Colombier 	}
11433e12c5d1SDavid du Colombier 
11443e12c5d1SDavid du Colombier 	/*
11453e12c5d1SDavid du Colombier 	 *  Any reply from an authoritative server,
1146a41547ffSDavid du Colombier 	 *  or a positive reply terminates the search.
1147a41547ffSDavid du Colombier 	 *  A negative response now also terminates the search.
11483e12c5d1SDavid du Colombier 	 */
11493cbadd90SDavid du Colombier 	if(mp->an != nil || (mp->flags & Fauth)){
11500319257bSDavid du Colombier 		if(isnegrname(mp))
11513cbadd90SDavid du Colombier 			qp->dp->respcode = Rname;
11527dd7cddfSDavid du Colombier 		else
11533cbadd90SDavid du Colombier 			qp->dp->respcode = 0;
11547dd7cddfSDavid du Colombier 
11557dd7cddfSDavid du Colombier 		/*
1156a41547ffSDavid du Colombier 		 *  cache any negative responses, free soarr.
1157a41547ffSDavid du Colombier 		 *  negative responses need not be authoritative:
1158a41547ffSDavid du Colombier 		 *  they can legitimately come from a cache.
11597dd7cddfSDavid du Colombier 		 */
1160a41547ffSDavid du Colombier 		if( /* (mp->flags & Fauth) && */ mp->an == nil)
11613cbadd90SDavid du Colombier 			cacheneg(qp->dp, qp->type, (mp->flags & Rmask), soarr);
11627dd7cddfSDavid du Colombier 		else
11637dd7cddfSDavid du Colombier 			rrfreelist(soarr);
11643e12c5d1SDavid du Colombier 		return 1;
11650319257bSDavid du Colombier 	} else if (isnegrname(mp)) {
1166a41547ffSDavid du Colombier 		qp->dp->respcode = Rname;
1167a41547ffSDavid du Colombier 		/*
1168a41547ffSDavid du Colombier 		 *  cache negative response.
1169a41547ffSDavid du Colombier 		 *  negative responses need not be authoritative:
1170a41547ffSDavid du Colombier 		 *  they can legitimately come from a cache.
1171a41547ffSDavid du Colombier 		 */
1172a41547ffSDavid du Colombier 		cacheneg(qp->dp, qp->type, (mp->flags & Rmask), soarr);
1173a41547ffSDavid du Colombier 		return 1;
11743e12c5d1SDavid du Colombier 	}
11750319257bSDavid du Colombier 	stats.negnorname++;
11767dd7cddfSDavid du Colombier 	rrfreelist(soarr);
11773e12c5d1SDavid du Colombier 
11783e12c5d1SDavid du Colombier 	/*
1179a41547ffSDavid du Colombier 	 *  if we've been given better name servers, recurse.
1180a41547ffSDavid du Colombier 	 *  if we're a pure resolver, don't recurse, we have
1181a41547ffSDavid du Colombier 	 *  to forward to a fixed set of named servers.
11823e12c5d1SDavid du Colombier 	 */
1183a41547ffSDavid du Colombier 	if(!mp->ns || cfg.resolver && cfg.justforw)
11843cbadd90SDavid du Colombier 		return 0;
11857dd7cddfSDavid du Colombier 	tp = rrlookup(ndp, Tns, NOneg);
11863cbadd90SDavid du Colombier 	if(contains(qp->nsrp, tp)){
11877dd7cddfSDavid du Colombier 		rrfreelist(tp);
11883cbadd90SDavid du Colombier 		return 0;
11893e12c5d1SDavid du Colombier 	}
11903cbadd90SDavid du Colombier 	procsetname("recursive query for %s %s", qp->dp->name,
11913cbadd90SDavid du Colombier 		rrname(qp->type, buf, sizeof buf));
11924e5f5f32SDavid du Colombier 	/*
11934e5f5f32SDavid du Colombier 	 *  we're called from udpquery, called from
11944e5f5f32SDavid du Colombier 	 *  netquery, which current holds qp->dp->querylck,
11954e5f5f32SDavid du Colombier 	 *  so release it now and acquire it upon return.
11964e5f5f32SDavid du Colombier 	 */
11974e5f5f32SDavid du Colombier //	lcktype = qtype2lck(qp->type);
11984e5f5f32SDavid du Colombier //	qunlock(&qp->dp->querylck[lcktype]);
11993cbadd90SDavid du Colombier 
1200c73252aeSDavid du Colombier 	nqp = emalloc(sizeof *nqp);
1201c73252aeSDavid du Colombier 	queryinit(nqp, qp->dp, qp->type, qp->req);
1202c73252aeSDavid du Colombier 	nqp->nsrp = tp;
1203c73252aeSDavid du Colombier 	rv = netquery(nqp, depth+1);
12043cbadd90SDavid du Colombier 
12054e5f5f32SDavid du Colombier //	qlock(&qp->dp->querylck[lcktype]);
1206*adb31a62SDavid du Colombier 	rrfreelist(nqp->nsrp);
1207c73252aeSDavid du Colombier 	querydestroy(nqp);
1208c73252aeSDavid du Colombier 	free(nqp);
12093cbadd90SDavid du Colombier 	return rv;
12103cbadd90SDavid du Colombier }
12113cbadd90SDavid du Colombier 
12123cbadd90SDavid du Colombier /*
12133cbadd90SDavid du Colombier  * send a query via tcp to a single address (from ibuf's udp header)
12143cbadd90SDavid du Colombier  * and read the answer(s) into mp->an.
12153cbadd90SDavid du Colombier  */
12163cbadd90SDavid du Colombier static int
12173cbadd90SDavid du Colombier tcpquery(Query *qp, DNSmsg *mp, int depth, uchar *ibuf, uchar *obuf, int len,
12183cbadd90SDavid du Colombier 	int waitsecs, int inns, ushort req)
12193cbadd90SDavid du Colombier {
12204e7b9544SDavid du Colombier 	int rv = 0;
12213cbadd90SDavid du Colombier 	ulong endtime;
12223cbadd90SDavid du Colombier 
12233cbadd90SDavid du Colombier 	endtime = time(nil) + waitsecs;
12243cbadd90SDavid du Colombier 	if(endtime > qp->req->aborttime)
12253cbadd90SDavid du Colombier 		endtime = qp->req->aborttime;
12263cbadd90SDavid du Colombier 
1227a41547ffSDavid du Colombier 	if (0)
12284e7b9544SDavid du Colombier 		dnslog("%s: udp reply truncated; retrying query via tcp to %I",
12294e7b9544SDavid du Colombier 			qp->dp->name, qp->tcpip);
12304e7b9544SDavid du Colombier 
12313cbadd90SDavid du Colombier 	qlock(&qp->tcplock);
12323cbadd90SDavid du Colombier 	memmove(obuf, ibuf, IPaddrlen);		/* send back to respondent */
12333cbadd90SDavid du Colombier 	/* sets qp->tcpip from obuf's udp header */
12344e7b9544SDavid du Colombier 	if (xmitquery(qp, Tcp, depth, obuf, inns, len) < 0 ||
12354e7b9544SDavid du Colombier 	    readreply(qp, Tcp, req, ibuf, mp, endtime) < 0)
12364e7b9544SDavid du Colombier 		rv = -1;
12373cbadd90SDavid du Colombier 	if (qp->tcpfd > 0) {
12383cbadd90SDavid du Colombier 		hangup(qp->tcpctlfd);
12393cbadd90SDavid du Colombier 		close(qp->tcpctlfd);
12403cbadd90SDavid du Colombier 		close(qp->tcpfd);
12413cbadd90SDavid du Colombier 	}
12423cbadd90SDavid du Colombier 	qp->tcpfd = qp->tcpctlfd = -1;
12433cbadd90SDavid du Colombier 	qunlock(&qp->tcplock);
12444e7b9544SDavid du Colombier 	return rv;
12453cbadd90SDavid du Colombier }
12463cbadd90SDavid du Colombier 
12473cbadd90SDavid du Colombier /*
12483cbadd90SDavid du Colombier  *  query name servers.  If the name server returns a pointer to another
12493cbadd90SDavid du Colombier  *  name server, recurse.
12503cbadd90SDavid du Colombier  */
12513cbadd90SDavid du Colombier static int
12524e5f5f32SDavid du Colombier queryns(Query *qp, int depth, uchar *ibuf, uchar *obuf, int waitsecs, int inns)
12533cbadd90SDavid du Colombier {
12543cbadd90SDavid du Colombier 	int ndest, len, replywaits, rv;
12553cbadd90SDavid du Colombier 	ushort req;
12563cbadd90SDavid du Colombier 	ulong endtime;
12573cbadd90SDavid du Colombier 	char buf[12];
12583cbadd90SDavid du Colombier 	uchar srcip[IPaddrlen];
1259a41547ffSDavid du Colombier 	Dest *p, *np, *dest;
1260a41547ffSDavid du Colombier //	Dest dest[Maxdest];
12613cbadd90SDavid du Colombier 
12623cbadd90SDavid du Colombier 	/* pack request into a udp message */
12633cbadd90SDavid du Colombier 	req = rand();
12643cbadd90SDavid du Colombier 	len = mkreq(qp->dp, qp->type, obuf, Frecurse|Oquery, req);
12653cbadd90SDavid du Colombier 
12663cbadd90SDavid du Colombier 	/* no server addresses yet */
12673cbadd90SDavid du Colombier 	queryck(qp);
1268a41547ffSDavid du Colombier 	dest = emalloc(Maxdest * sizeof *dest);	/* dest can't be on stack */
1269a41547ffSDavid du Colombier 	for (p = dest; p < dest + Maxdest; p++)
12703cbadd90SDavid du Colombier 		destinit(p);
12711eb0187fSDavid du Colombier 	/* this dest array is local to this call of queryns() */
12721eb0187fSDavid du Colombier 	free(qp->dest);
12733cbadd90SDavid du Colombier 	qp->curdest = qp->dest = dest;
12743cbadd90SDavid du Colombier 
12753cbadd90SDavid du Colombier 	/*
12763cbadd90SDavid du Colombier 	 *  transmit udp requests and wait for answers.
12773cbadd90SDavid du Colombier 	 *  at most Maxtrans attempts to each address.
12783cbadd90SDavid du Colombier 	 *  each cycle send one more message than the previous.
12793cbadd90SDavid du Colombier 	 *  retry a query via tcp if its response is truncated.
12803cbadd90SDavid du Colombier 	 */
12813cbadd90SDavid du Colombier 	for(ndest = 1; ndest < Maxdest; ndest++){
12823cbadd90SDavid du Colombier 		qp->ndest = ndest;
12833cbadd90SDavid du Colombier 		qp->tcpset = 0;
12843cbadd90SDavid du Colombier 		if (xmitquery(qp, Udp, depth, obuf, inns, len) < 0)
12853cbadd90SDavid du Colombier 			break;
12863cbadd90SDavid du Colombier 
12873cbadd90SDavid du Colombier 		endtime = time(nil) + waitsecs;
12883cbadd90SDavid du Colombier 		if(endtime > qp->req->aborttime)
12893cbadd90SDavid du Colombier 			endtime = qp->req->aborttime;
12903cbadd90SDavid du Colombier 
12913cbadd90SDavid du Colombier 		for(replywaits = 0; replywaits < ndest; replywaits++){
129281730632SDavid du Colombier 			DNSmsg m;
129381730632SDavid du Colombier 
1294f46c709fSDavid du Colombier 			procsetname("reading %sside reply from %I: %s %s from %s",
1295a41547ffSDavid du Colombier 				(inns? "in": "out"), obuf, qp->dp->name,
1296f46c709fSDavid du Colombier 				rrname(qp->type, buf, sizeof buf), qp->req->from);
12973cbadd90SDavid du Colombier 
129881730632SDavid du Colombier 			/* read udp answer into m */
12993cbadd90SDavid du Colombier 			if (readreply(qp, Udp, req, ibuf, &m, endtime) >= 0)
13003cbadd90SDavid du Colombier 				memmove(srcip, ibuf, IPaddrlen);
130181730632SDavid du Colombier 			else if (!(m.flags & Ftrunc)) {
130281730632SDavid du Colombier 				freeanswers(&m);
13033cbadd90SDavid du Colombier 				break;		/* timed out on this dest */
130481730632SDavid du Colombier 			} else {
13053cbadd90SDavid du Colombier 				/* whoops, it was truncated! ask again via tcp */
13063cbadd90SDavid du Colombier 				rv = tcpquery(qp, &m, depth, ibuf, obuf, len,
130781730632SDavid du Colombier 					waitsecs, inns, req);  /* answer in m */
130881730632SDavid du Colombier 				if (rv < 0) {
130981730632SDavid du Colombier 					freeanswers(&m);
13103cbadd90SDavid du Colombier 					break;		/* failed via tcp too */
131181730632SDavid du Colombier 				}
13123cbadd90SDavid du Colombier 				memmove(srcip, qp->tcpip, IPaddrlen);
13133cbadd90SDavid du Colombier 			}
13143cbadd90SDavid du Colombier 
13153cbadd90SDavid du Colombier 			/* find responder */
13164e5f5f32SDavid du Colombier 			// dnslog("queryns got reply from %I", srcip);
13173cbadd90SDavid du Colombier 			for(p = qp->dest; p < qp->curdest; p++)
13183cbadd90SDavid du Colombier 				if(memcmp(p->a, srcip, sizeof p->a) == 0)
13193cbadd90SDavid du Colombier 					break;
13203cbadd90SDavid du Colombier 
13213cbadd90SDavid du Colombier 			/* remove all addrs of responding server from list */
13223cbadd90SDavid du Colombier 			for(np = qp->dest; np < qp->curdest; np++)
13233cbadd90SDavid du Colombier 				if(np->s == p->s)
13243cbadd90SDavid du Colombier 					p->nx = Maxtrans;
13253cbadd90SDavid du Colombier 
132681730632SDavid du Colombier 			/* free or incorporate RRs in m */
13273cbadd90SDavid du Colombier 			rv = procansw(qp, &m, srcip, depth, p);
13281eb0187fSDavid du Colombier 			if (rv > 0) {
13291eb0187fSDavid du Colombier 				free(qp->dest);
13301eb0187fSDavid du Colombier 				qp->dest = qp->curdest = nil; /* prevent accidents */
13313cbadd90SDavid du Colombier 				return rv;
13323e12c5d1SDavid du Colombier 			}
13333e12c5d1SDavid du Colombier 		}
13341eb0187fSDavid du Colombier 	}
13357dd7cddfSDavid du Colombier 
13364f8f669cSDavid du Colombier 	/* if all servers returned failure, propagate it */
13373cbadd90SDavid du Colombier 	qp->dp->respcode = Rserver;
13383cbadd90SDavid du Colombier 	for(p = dest; p < qp->curdest; p++) {
13393cbadd90SDavid du Colombier 		destck(p);
13407dd7cddfSDavid du Colombier 		if(p->code != Rserver)
13413cbadd90SDavid du Colombier 			qp->dp->respcode = 0;
13423cbadd90SDavid du Colombier 		p->magic = 0;			/* prevent accidents */
13433cbadd90SDavid du Colombier 	}
13444f8f669cSDavid du Colombier 
13453cbadd90SDavid du Colombier //	if (qp->dp->respcode)
13464e5f5f32SDavid du Colombier //		dnslog("queryns setting Rserver for %s", qp->dp->name);
13477dd7cddfSDavid du Colombier 
1348a41547ffSDavid du Colombier 	free(qp->dest);
13493cbadd90SDavid du Colombier 	qp->dest = qp->curdest = nil;		/* prevent accidents */
13503e12c5d1SDavid du Colombier 	return 0;
13513e12c5d1SDavid du Colombier }
13527dd7cddfSDavid du Colombier 
13534f8f669cSDavid du Colombier /*
13544f8f669cSDavid du Colombier  *  run a command with a supplied fd as standard input
13554f8f669cSDavid du Colombier  */
13564f8f669cSDavid du Colombier char *
13574f8f669cSDavid du Colombier system(int fd, char *cmd)
135828a8a86bSDavid du Colombier {
13594f8f669cSDavid du Colombier 	int pid, p, i;
13604f8f669cSDavid du Colombier 	static Waitmsg msg;
13616b0d5c8bSDavid du Colombier 
13624f8f669cSDavid du Colombier 	if((pid = fork()) == -1)
13634f8f669cSDavid du Colombier 		sysfatal("fork failed: %r");
13644f8f669cSDavid du Colombier 	else if(pid == 0){
13654f8f669cSDavid du Colombier 		dup(fd, 0);
13664f8f669cSDavid du Colombier 		close(fd);
13674f8f669cSDavid du Colombier 		for (i = 3; i < 200; i++)
13684f8f669cSDavid du Colombier 			close(i);		/* don't leak fds */
13694f8f669cSDavid du Colombier 		execl("/bin/rc", "rc", "-c", cmd, nil);
13704f8f669cSDavid du Colombier 		sysfatal("exec rc: %r");
13714f8f669cSDavid du Colombier 	}
13724f8f669cSDavid du Colombier 	for(p = waitpid(); p >= 0; p = waitpid())
13734f8f669cSDavid du Colombier 		if(p == pid)
13744f8f669cSDavid du Colombier 			return msg.msg;
13754f8f669cSDavid du Colombier 	return "lost child";
13764f8f669cSDavid du Colombier }
13774f8f669cSDavid du Colombier 
13780319257bSDavid du Colombier /* compute wait, weighted by probability of success, with minimum */
13790319257bSDavid du Colombier static ulong
13800319257bSDavid du Colombier weight(ulong ms, unsigned pcntprob)
13810319257bSDavid du Colombier {
13820319257bSDavid du Colombier 	ulong wait;
13830319257bSDavid du Colombier 
13840319257bSDavid du Colombier 	wait = (ms * pcntprob) / 100;
13850319257bSDavid du Colombier 	if (wait < 1500)
13860319257bSDavid du Colombier 		wait = 1500;
13870319257bSDavid du Colombier 	return wait;
13880319257bSDavid du Colombier }
13890319257bSDavid du Colombier 
1390a41547ffSDavid du Colombier /*
1391a41547ffSDavid du Colombier  * in principle we could use a single descriptor for a udp port
1392a41547ffSDavid du Colombier  * to send all queries and receive all the answers to them,
1393a41547ffSDavid du Colombier  * but we'd have to sort out the answers by dns-query id.
1394a41547ffSDavid du Colombier  */
13954f8f669cSDavid du Colombier static int
13963cbadd90SDavid du Colombier udpquery(Query *qp, char *mntpt, int depth, int patient, int inns)
13974f8f669cSDavid du Colombier {
13980319257bSDavid du Colombier 	int fd, rv;
13994f8f669cSDavid du Colombier 	long now;
14000319257bSDavid du Colombier 	ulong pcntprob, wait, reqtm;
14014f8f669cSDavid du Colombier 	char *msg;
14024f8f669cSDavid du Colombier 	uchar *obuf, *ibuf;
14034f8f669cSDavid du Colombier 	static QLock mntlck;
14044f8f669cSDavid du Colombier 	static ulong lastmount;
14057dd7cddfSDavid du Colombier 
14067dd7cddfSDavid du Colombier 	/* use alloced buffers rather than ones from the stack */
1407f27a9a5aSDavid du Colombier 	// ibuf = emalloc(Maxudpin+Udphdrsize);
14083cbadd90SDavid du Colombier 	ibuf = emalloc(64*1024);		/* max. tcp reply size */
1409f27a9a5aSDavid du Colombier 	obuf = emalloc(Maxudp+Udphdrsize);
14107dd7cddfSDavid du Colombier 
14114f8f669cSDavid du Colombier 	fd = udpport(mntpt);
14124f8f669cSDavid du Colombier 	while (fd < 0 && cfg.straddle && strcmp(mntpt, "/net.alt") == 0) {
14134f8f669cSDavid du Colombier 		/* HACK: remount /net.alt */
14144f8f669cSDavid du Colombier 		now = time(nil);
14154f8f669cSDavid du Colombier 		if (now < lastmount + Remntretry)
14164f8f669cSDavid du Colombier 			sleep((lastmount + Remntretry - now)*1000);
14174f8f669cSDavid du Colombier 		qlock(&mntlck);
14184f8f669cSDavid du Colombier 		fd = udpport(mntpt);	/* try again under lock */
14194f8f669cSDavid du Colombier 		if (fd < 0) {
14204f8f669cSDavid du Colombier 			dnslog("[%d] remounting /net.alt", getpid());
14214f8f669cSDavid du Colombier 			unmount(nil, "/net.alt");
14224f8f669cSDavid du Colombier 
14234f8f669cSDavid du Colombier 			msg = system(open("/dev/null", ORDWR), "outside");
14244f8f669cSDavid du Colombier 
14254f8f669cSDavid du Colombier 			lastmount = time(nil);
14264f8f669cSDavid du Colombier 			if (msg && *msg) {
14274f8f669cSDavid du Colombier 				dnslog("[%d] can't remount /net.alt: %s",
14284f8f669cSDavid du Colombier 					getpid(), msg);
14294f8f669cSDavid du Colombier 				sleep(10*1000);		/* don't spin wildly */
14304f8f669cSDavid du Colombier 			} else
14314f8f669cSDavid du Colombier 				fd = udpport(mntpt);
14324f8f669cSDavid du Colombier 		}
14334f8f669cSDavid du Colombier 		qunlock(&mntlck);
14344f8f669cSDavid du Colombier 	}
1435a41547ffSDavid du Colombier 	if (fd < 0) {
14364f8f669cSDavid du Colombier 		dnslog("can't get udpport for %s query of name %s: %r",
14373cbadd90SDavid du Colombier 			mntpt, qp->dp->name);
1438a41547ffSDavid du Colombier 		sysfatal("out of udp conversations");	/* we're buggered */
1439a41547ffSDavid du Colombier 	}
1440a41547ffSDavid du Colombier 
1441a41547ffSDavid du Colombier 	/*
14420319257bSDavid du Colombier 	 * Our QIP servers are busted, don't answer AAAA and
14430319257bSDavid du Colombier 	 * take forever to answer CNAME if there isn't one.
14440319257bSDavid du Colombier 	 * They rarely set Rname.
1445a41547ffSDavid du Colombier 	 * make time-to-wait proportional to estimated probability of an
1446a41547ffSDavid du Colombier 	 * RR of that type existing.
1447a41547ffSDavid du Colombier 	 */
1448*adb31a62SDavid du Colombier 	if (qp->type >= nelem(likely))
14490319257bSDavid du Colombier 		pcntprob = 35;			/* unpopular query type */
14500319257bSDavid du Colombier 	else
14510319257bSDavid du Colombier 		pcntprob = likely[qp->type];
14520319257bSDavid du Colombier 	reqtm = (patient? 2*Maxreqtm: Maxreqtm);
14530319257bSDavid du Colombier 	/* time for a single outgoing udp query */
14540319257bSDavid du Colombier 	wait = weight(S2MS(reqtm)/3, pcntprob);
14550319257bSDavid du Colombier 	qp->req->aborttime = time(nil) + MS2S(3*wait); /* for all udp queries */
1456a41547ffSDavid du Colombier 
14570319257bSDavid du Colombier 	qp->udpfd = fd;
14584e5f5f32SDavid du Colombier 	rv = queryns(qp, depth, ibuf, obuf, MS2S(wait), inns);
1459a41547ffSDavid du Colombier 	close(fd);
1460a41547ffSDavid du Colombier 	qp->udpfd = -1;
14614f8f669cSDavid du Colombier 
14624f8f669cSDavid du Colombier 	free(obuf);
14634f8f669cSDavid du Colombier 	free(ibuf);
14644f8f669cSDavid du Colombier 	return rv;
14654f8f669cSDavid du Colombier }
14664f8f669cSDavid du Colombier 
14674e5f5f32SDavid du Colombier /* look up (qp->dp->name,qp->type) rr in dns, via *nsrp with results in *reqp */
14684f8f669cSDavid du Colombier static int
14693cbadd90SDavid du Colombier netquery(Query *qp, int depth)
14704f8f669cSDavid du Colombier {
1471225077b0SDavid du Colombier 	int lock, rv, triedin, inname, cnt;
1472225077b0SDavid du Colombier //	char buf[32];
14734f8f669cSDavid du Colombier 	RR *rp;
14744e153993SDavid du Colombier 	DN *dp;
1475225077b0SDavid du Colombier 	Querylck *qlp;
1476225077b0SDavid du Colombier 	static int whined;
14774f8f669cSDavid du Colombier 
1478225077b0SDavid du Colombier 	rv = 0;				/* pessimism */
14794f8f669cSDavid du Colombier 	if(depth > 12)			/* in a recursive loop? */
14804f8f669cSDavid du Colombier 		return 0;
14814f8f669cSDavid du Colombier 
14823cbadd90SDavid du Colombier 	slave(qp->req);
1483d3907fe5SDavid du Colombier 	/*
1484d3907fe5SDavid du Colombier 	 * slave might have forked.  if so, the parent process longjmped to
1485d3907fe5SDavid du Colombier 	 * req->mret; we're usually the child slave, but if there are too
1486d3907fe5SDavid du Colombier 	 * many children already, we're still the same process.
1487d3907fe5SDavid du Colombier 	 */
14884f8f669cSDavid du Colombier 
14894e5f5f32SDavid du Colombier 	/*
14904e5f5f32SDavid du Colombier 	 * don't lock before call to slave so only children can block.
14914e5f5f32SDavid du Colombier 	 * just lock at top-level invocation.
14924e5f5f32SDavid du Colombier 	 */
1493225077b0SDavid du Colombier 	lock = depth <= 1 && qp->req->isslave;
14944e153993SDavid du Colombier 	dp = qp->dp;		/* ensure that it doesn't change underfoot */
1495225077b0SDavid du Colombier 	qlp = nil;
14964e5f5f32SDavid du Colombier 	if(lock) {
1497225077b0SDavid du Colombier //		procsetname("query lock wait: %s %s from %s", dp->name,
1498225077b0SDavid du Colombier //			rrname(qp->type, buf, sizeof buf), qp->req->from);
1499ccf6439bSDavid du Colombier 		/*
1500ccf6439bSDavid du Colombier 		 * don't make concurrent queries for this name.
150181730632SDavid du Colombier 		 * dozens of processes blocking here probably indicates
150281730632SDavid du Colombier 		 * an error in our dns data that causes us to not
150381730632SDavid du Colombier 		 * recognise a zone (area) as one of our own, thus
150481730632SDavid du Colombier 		 * causing us to query other nameservers.
1505ccf6439bSDavid du Colombier 		 */
1506225077b0SDavid du Colombier 		qlp = &dp->querylck[qtype2lck(qp->type)];
1507225077b0SDavid du Colombier 		incref(qlp);
1508225077b0SDavid du Colombier 		qlock(qlp);
1509225077b0SDavid du Colombier 		cnt = qlp->Ref.ref;
1510225077b0SDavid du Colombier 		qunlock(qlp);
1511225077b0SDavid du Colombier 		if (cnt > 10) {
1512225077b0SDavid du Colombier 			decref(qlp);
1513225077b0SDavid du Colombier 			if (!whined) {
1514225077b0SDavid du Colombier 				whined = 1;
1515225077b0SDavid du Colombier 				dnslog("too many outstanding queries for %s; "
1516225077b0SDavid du Colombier 					"dropping this one; "
1517225077b0SDavid du Colombier 					"no further logging of drops",
1518225077b0SDavid du Colombier 					dp->name);
1519225077b0SDavid du Colombier 			}
1520225077b0SDavid du Colombier 			return 0;
1521225077b0SDavid du Colombier 		}
1522225077b0SDavid du Colombier 	}
15234e153993SDavid du Colombier 	procsetname("netquery: %s", dp->name);
15246b0d5c8bSDavid du Colombier 
15256b0d5c8bSDavid du Colombier 	/* prepare server RR's for incremental lookup */
15263cbadd90SDavid du Colombier 	for(rp = qp->nsrp; rp; rp = rp->next)
15276b0d5c8bSDavid du Colombier 		rp->marker = 0;
15286b0d5c8bSDavid du Colombier 
15294f8f669cSDavid du Colombier 	triedin = 0;
1530a41547ffSDavid du Colombier 
15314f8f669cSDavid du Colombier 	/*
15324f8f669cSDavid du Colombier 	 * normal resolvers and servers will just use mntpt for all addresses,
15334f8f669cSDavid du Colombier 	 * even on the outside.  straddling servers will use mntpt (/net)
15344f8f669cSDavid du Colombier 	 * for inside addresses and /net.alt for outside addresses,
15354f8f669cSDavid du Colombier 	 * thus bypassing other inside nameservers.
15364f8f669cSDavid du Colombier 	 */
15374e153993SDavid du Colombier 	inname = insideaddr(dp->name);
15384f8f669cSDavid du Colombier 	if (!cfg.straddle || inname) {
15393cbadd90SDavid du Colombier 		rv = udpquery(qp, mntpt, depth, Hurry, (cfg.inside? Inns: Outns));
15404f8f669cSDavid du Colombier 		triedin = 1;
15414f8f669cSDavid du Colombier 	}
15424f8f669cSDavid du Colombier 
15434f8f669cSDavid du Colombier 	/*
15444f8f669cSDavid du Colombier 	 * if we're still looking, are inside, and have an outside domain,
15454f8f669cSDavid du Colombier 	 * try it on our outside interface, if any.
15464f8f669cSDavid du Colombier 	 */
15474f8f669cSDavid du Colombier 	if (rv == 0 && cfg.inside && !inname) {
15484f8f669cSDavid du Colombier 		if (triedin)
15494f8f669cSDavid du Colombier 			dnslog(
15504f8f669cSDavid du Colombier 	   "[%d] netquery: internal nameservers failed for %s; trying external",
15514e153993SDavid du Colombier 				getpid(), dp->name);
15524f8f669cSDavid du Colombier 
15534f8f669cSDavid du Colombier 		/* prepare server RR's for incremental lookup */
15543cbadd90SDavid du Colombier 		for(rp = qp->nsrp; rp; rp = rp->next)
15554f8f669cSDavid du Colombier 			rp->marker = 0;
15564f8f669cSDavid du Colombier 
15573cbadd90SDavid du Colombier 		rv = udpquery(qp, "/net.alt", depth, Patient, Outns);
15584f8f669cSDavid du Colombier 	}
15593cbadd90SDavid du Colombier //	if (rv == 0)		/* could ask /net.alt/dns directly */
15604e153993SDavid du Colombier //		askoutdns(dp, qp->type);
15614f8f669cSDavid du Colombier 
1562225077b0SDavid du Colombier 	if(lock && qlp)
1563225077b0SDavid du Colombier 		decref(qlp);
15647dd7cddfSDavid du Colombier 	return rv;
15657dd7cddfSDavid du Colombier }
15664f8f669cSDavid du Colombier 
15674f8f669cSDavid du Colombier int
15684f8f669cSDavid du Colombier seerootns(void)
15694f8f669cSDavid du Colombier {
15703cbadd90SDavid du Colombier 	int rv;
15714f8f669cSDavid du Colombier 	char root[] = "";
15724f8f669cSDavid du Colombier 	Request req;
1573c73252aeSDavid du Colombier 	Query *qp;
15744f8f669cSDavid du Colombier 
15754f8f669cSDavid du Colombier 	memset(&req, 0, sizeof req);
15764f8f669cSDavid du Colombier 	req.isslave = 1;
1577ccf6439bSDavid du Colombier 	req.aborttime = now + Maxreqtm;
1578f46c709fSDavid du Colombier 	req.from = "internal";
1579c73252aeSDavid du Colombier 	qp = emalloc(sizeof *qp);
1580c73252aeSDavid du Colombier 	queryinit(qp, dnlookup(root, Cin, 1), Tns, &req);
1581c73252aeSDavid du Colombier 
1582c73252aeSDavid du Colombier 	qp->nsrp = dblookup(root, Cin, Tns, 0, 0);
1583c73252aeSDavid du Colombier 	rv = netquery(qp, 0);
1584c73252aeSDavid du Colombier 
1585c73252aeSDavid du Colombier 	rrfreelist(qp->nsrp);
1586c73252aeSDavid du Colombier 	querydestroy(qp);
1587c73252aeSDavid du Colombier 	free(qp);
15883cbadd90SDavid du Colombier 	return rv;
15894f8f669cSDavid du Colombier }
1590