xref: /plan9/sys/src/cmd/ndb/dnresolve.c (revision 3cbadd901a4736d4ea7140103b449a87d1bf0d5b)
1*3cbadd90SDavid du Colombier /*
2*3cbadd90SDavid du Colombier  * domain name resolvers, see rfcs 1035 and 1123
3*3cbadd90SDavid 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 
11*3cbadd90SDavid du Colombier typedef struct Dest Dest;
12*3cbadd90SDavid du Colombier typedef struct Ipaddr Ipaddr;
13*3cbadd90SDavid du Colombier typedef struct Query Query;
14*3cbadd90SDavid du Colombier typedef struct Sluggards Sluggards;
15*3cbadd90SDavid du Colombier 
163e12c5d1SDavid du Colombier enum
173e12c5d1SDavid du Colombier {
18*3cbadd90SDavid du Colombier 	Udp, Tcp,
197dd7cddfSDavid du Colombier 	Maxdest=	24,	/* maximum destinations for a request message */
203e12c5d1SDavid du Colombier 	Maxtrans=	3,	/* maximum transmissions to a server */
21*3cbadd90SDavid du Colombier 	Destmagic=	0xcafebabe,
22*3cbadd90SDavid du Colombier 	Querymagic=	0xdeadbeef,
233e12c5d1SDavid du Colombier };
243e12c5d1SDavid du Colombier 
25*3cbadd90SDavid du Colombier struct Ipaddr {
26*3cbadd90SDavid du Colombier 	Ipaddr *next;
27*3cbadd90SDavid du Colombier 	uchar	ip[IPaddrlen];
28*3cbadd90SDavid du Colombier };
29*3cbadd90SDavid du Colombier 
30*3cbadd90SDavid du Colombier struct Dest
31*3cbadd90SDavid du Colombier {
32*3cbadd90SDavid du Colombier 	uchar	a[IPaddrlen];	/* ip address */
33*3cbadd90SDavid du Colombier 	DN	*s;		/* name server */
34*3cbadd90SDavid du Colombier 	int	nx;		/* number of transmissions */
35*3cbadd90SDavid du Colombier 	int	code;		/* response code; used to clear dp->respcode */
36*3cbadd90SDavid du Colombier 
37*3cbadd90SDavid du Colombier 	ulong	magic;
38*3cbadd90SDavid du Colombier };
39*3cbadd90SDavid du Colombier 
40*3cbadd90SDavid du Colombier struct Query {
41*3cbadd90SDavid du Colombier 	DN	*dp;		/* domain */
42*3cbadd90SDavid du Colombier 	int	type;		/* and type to look up */
43*3cbadd90SDavid du Colombier 	Request *req;
44*3cbadd90SDavid du Colombier 	RR	*nsrp;		/* name servers to consult */
45*3cbadd90SDavid du Colombier 
46*3cbadd90SDavid du Colombier 	Dest	*dest;		/* array of destinations */
47*3cbadd90SDavid du Colombier 	Dest	*curdest;	/* pointer to one of them */
48*3cbadd90SDavid du Colombier 	int	ndest;
49*3cbadd90SDavid du Colombier 
50*3cbadd90SDavid du Colombier 	int	udpfd;		/* can be shared by all udp users */
51*3cbadd90SDavid du Colombier 
52*3cbadd90SDavid du Colombier 	QLock	tcplock;	/* only one tcp call at a time per query */
53*3cbadd90SDavid du Colombier 	int	tcpset;
54*3cbadd90SDavid du Colombier 	int	tcpfd;		/* if Tcp, read replies from here */
55*3cbadd90SDavid du Colombier 	int	tcpctlfd;
56*3cbadd90SDavid du Colombier 	uchar	tcpip[IPaddrlen];
57*3cbadd90SDavid du Colombier 
58*3cbadd90SDavid du Colombier 	ulong	magic;
59*3cbadd90SDavid du Colombier };
60*3cbadd90SDavid du Colombier 
61*3cbadd90SDavid du Colombier /* a list of sluggardly name servers */
62*3cbadd90SDavid du Colombier struct Sluggards {
63*3cbadd90SDavid du Colombier 	QLock;
64*3cbadd90SDavid du Colombier 	Ipaddr *head;
65*3cbadd90SDavid du Colombier 	Ipaddr *tail;
66*3cbadd90SDavid du Colombier };
67*3cbadd90SDavid du Colombier 
68*3cbadd90SDavid du Colombier static Sluggards slugs;
69*3cbadd90SDavid du Colombier 
707dd7cddfSDavid du Colombier static RR*	dnresolve1(char*, int, int, Request*, int, int);
71*3cbadd90SDavid du Colombier static int	netquery(Query *, int);
72*3cbadd90SDavid du Colombier 
73*3cbadd90SDavid du Colombier static Ipaddr *
74*3cbadd90SDavid du Colombier newslug(void)
75*3cbadd90SDavid du Colombier {
76*3cbadd90SDavid du Colombier 	return emalloc(sizeof(Ipaddr));
77*3cbadd90SDavid du Colombier }
78*3cbadd90SDavid du Colombier 
79*3cbadd90SDavid du Colombier static void
80*3cbadd90SDavid du Colombier addslug(uchar nsip[])
81*3cbadd90SDavid du Colombier {
82*3cbadd90SDavid du Colombier 	Ipaddr *sp;
83*3cbadd90SDavid du Colombier 	static uchar zip[IPaddrlen];
84*3cbadd90SDavid du Colombier 
85*3cbadd90SDavid du Colombier 	if (memcmp(nsip, zip, IPaddrlen) == 0)
86*3cbadd90SDavid du Colombier 		return;
87*3cbadd90SDavid du Colombier 
88*3cbadd90SDavid du Colombier 	qlock(&slugs);
89*3cbadd90SDavid du Colombier 	for (sp = slugs.head; sp != nil; sp = sp->next)
90*3cbadd90SDavid du Colombier 		if (memcmp(sp->ip, nsip, IPaddrlen) == 0) {
91*3cbadd90SDavid du Colombier 			qunlock(&slugs);		/* already know it */
92*3cbadd90SDavid du Colombier 			return;
93*3cbadd90SDavid du Colombier 		}
94*3cbadd90SDavid du Colombier 
95*3cbadd90SDavid du Colombier 	if (slugs.head == nil)
96*3cbadd90SDavid du Colombier 		slugs.head = slugs.tail = newslug();
97*3cbadd90SDavid du Colombier 	else {
98*3cbadd90SDavid du Colombier 		slugs.tail->next = newslug();
99*3cbadd90SDavid du Colombier 		slugs.tail = slugs.tail->next;
100*3cbadd90SDavid du Colombier 	}
101*3cbadd90SDavid du Colombier 	memmove(slugs.tail->ip, nsip, IPaddrlen);
102*3cbadd90SDavid du Colombier 	qunlock(&slugs);
103*3cbadd90SDavid du Colombier 
104*3cbadd90SDavid du Colombier 	dnslog("%I is a slug", nsip);
105*3cbadd90SDavid du Colombier }
106*3cbadd90SDavid du Colombier 
107*3cbadd90SDavid du Colombier int
108*3cbadd90SDavid du Colombier isaslug(uchar nsip[])
109*3cbadd90SDavid du Colombier {
110*3cbadd90SDavid du Colombier 	Ipaddr *sp;
111*3cbadd90SDavid du Colombier 
112*3cbadd90SDavid du Colombier 	qlock(&slugs);
113*3cbadd90SDavid du Colombier 	for (sp = slugs.head; sp != nil; sp = sp->next)
114*3cbadd90SDavid du Colombier 		if (memcmp(sp->ip, nsip, IPaddrlen) == 0) {
115*3cbadd90SDavid du Colombier 			qunlock(&slugs);
116*3cbadd90SDavid du Colombier 			return 1;
117*3cbadd90SDavid du Colombier 		}
118*3cbadd90SDavid du Colombier 	qunlock(&slugs);
119*3cbadd90SDavid du Colombier 	return 0;
120*3cbadd90SDavid du Colombier }
1213e12c5d1SDavid du Colombier 
1224f8f669cSDavid du Colombier /*
1234f8f669cSDavid du Colombier  * reading /proc/pid/args yields either "name" or "name [display args]",
1244f8f669cSDavid du Colombier  * so return only display args, if any.
1254f8f669cSDavid du Colombier  */
1264f8f669cSDavid du Colombier static char *
1274f8f669cSDavid du Colombier procgetname(void)
1284f8f669cSDavid du Colombier {
1294f8f669cSDavid du Colombier 	int fd, n;
1304f8f669cSDavid du Colombier 	char *lp, *rp;
1314f8f669cSDavid du Colombier 	char buf[256];
1324f8f669cSDavid du Colombier 
1334f8f669cSDavid du Colombier 	snprint(buf, sizeof buf, "#p/%d/args", getpid());
1344f8f669cSDavid du Colombier 	if((fd = open(buf, OREAD)) < 0)
1354f8f669cSDavid du Colombier 		return strdup("");
1364f8f669cSDavid du Colombier 	*buf = '\0';
1374f8f669cSDavid du Colombier 	n = read(fd, buf, sizeof buf-1);
1384f8f669cSDavid du Colombier 	close(fd);
1394f8f669cSDavid du Colombier 	if (n >= 0)
1404f8f669cSDavid du Colombier 		buf[n] = '\0';
1414f8f669cSDavid du Colombier 	if ((lp = strchr(buf, '[')) == nil ||
1424f8f669cSDavid du Colombier 	    (rp = strrchr(buf, ']')) == nil)
1434f8f669cSDavid du Colombier 		return strdup("");
1444f8f669cSDavid du Colombier 	*rp = '\0';
1454f8f669cSDavid du Colombier 	return strdup(lp+1);
1464f8f669cSDavid du Colombier }
1473e12c5d1SDavid du Colombier 
1483e12c5d1SDavid du Colombier /*
1493e12c5d1SDavid du Colombier  *  lookup 'type' info for domain name 'name'.  If it doesn't exist, try
1503e12c5d1SDavid du Colombier  *  looking it up as a canonical name.
1513e12c5d1SDavid du Colombier  */
1523e12c5d1SDavid du Colombier RR*
1534f8f669cSDavid du Colombier dnresolve(char *name, int class, int type, Request *req, RR **cn, int depth,
1544f8f669cSDavid du Colombier 	int recurse, int rooted, int *status)
1553e12c5d1SDavid du Colombier {
1567dd7cddfSDavid du Colombier 	RR *rp, *nrp, *drp;
1573e12c5d1SDavid du Colombier 	DN *dp;
1587dd7cddfSDavid du Colombier 	int loops;
1594f8f669cSDavid du Colombier 	char *procname;
1607dd7cddfSDavid du Colombier 	char nname[Domlen];
1613e12c5d1SDavid du Colombier 
1627dd7cddfSDavid du Colombier 	if(status)
1637dd7cddfSDavid du Colombier 		*status = 0;
1647dd7cddfSDavid du Colombier 
1654f8f669cSDavid du Colombier 	procname = procgetname();
1667dd7cddfSDavid du Colombier 	/*
1677dd7cddfSDavid du Colombier 	 *  hack for systems that don't have resolve search
1687dd7cddfSDavid du Colombier 	 *  lists.  Just look up the simple name in the database.
1697dd7cddfSDavid du Colombier 	 */
1707dd7cddfSDavid du Colombier 	if(!rooted && strchr(name, '.') == 0){
1717dd7cddfSDavid du Colombier 		rp = nil;
1727dd7cddfSDavid du Colombier 		drp = domainlist(class);
1737dd7cddfSDavid du Colombier 		for(nrp = drp; nrp != nil; nrp = nrp->next){
1744f8f669cSDavid du Colombier 			snprint(nname, sizeof nname, "%s.%s", name,
1754f8f669cSDavid du Colombier 				nrp->ptr->name);
1764f8f669cSDavid du Colombier 			rp = dnresolve(nname, class, type, req, cn, depth,
1774f8f669cSDavid du Colombier 				recurse, rooted, status);
178271b8d73SDavid du Colombier 			rrfreelist(rrremneg(&rp));
1797dd7cddfSDavid du Colombier 			if(rp != nil)
1807dd7cddfSDavid du Colombier 				break;
1817dd7cddfSDavid du Colombier 		}
1827dd7cddfSDavid du Colombier 		if(drp != nil)
1837dd7cddfSDavid du Colombier 			rrfree(drp);
1844f8f669cSDavid du Colombier 		procsetname(procname);
1854f8f669cSDavid du Colombier 		free(procname);
1863e12c5d1SDavid du Colombier 		return rp;
1877dd7cddfSDavid du Colombier 	}
1883e12c5d1SDavid du Colombier 
1897dd7cddfSDavid du Colombier 	/*
1907dd7cddfSDavid du Colombier 	 *  try the name directly
1917dd7cddfSDavid du Colombier 	 */
1927dd7cddfSDavid du Colombier 	rp = dnresolve1(name, class, type, req, depth, recurse);
1934f8f669cSDavid du Colombier 	if(rp) {
1944f8f669cSDavid du Colombier 		procsetname(procname);
1954f8f669cSDavid du Colombier 		free(procname);
1967dd7cddfSDavid du Colombier 		return randomize(rp);
1974f8f669cSDavid du Colombier 	}
1987dd7cddfSDavid du Colombier 
1997dd7cddfSDavid du Colombier 	/* try it as a canonical name if we weren't told the name didn't exist */
2007dd7cddfSDavid du Colombier 	dp = dnlookup(name, class, 0);
2014f8f669cSDavid du Colombier 	if(type != Tptr && dp->respcode != Rname)
2027dd7cddfSDavid du Colombier 		for(loops = 0; rp == nil && loops < 32; loops++){
2037dd7cddfSDavid du Colombier 			rp = dnresolve1(name, class, Tcname, req, depth, recurse);
2047dd7cddfSDavid du Colombier 			if(rp == nil)
2057dd7cddfSDavid du Colombier 				break;
20680ee5cbfSDavid du Colombier 
20780ee5cbfSDavid du Colombier 			if(rp->negative){
20880ee5cbfSDavid du Colombier 				rrfreelist(rp);
20980ee5cbfSDavid du Colombier 				rp = nil;
21080ee5cbfSDavid du Colombier 				break;
21180ee5cbfSDavid du Colombier 			}
2127dd7cddfSDavid du Colombier 
2137dd7cddfSDavid du Colombier 			name = rp->host->name;
2147dd7cddfSDavid du Colombier 			if(cn)
2157dd7cddfSDavid du Colombier 				rrcat(cn, rp);
2167dd7cddfSDavid du Colombier 			else
2177dd7cddfSDavid du Colombier 				rrfreelist(rp);
2187dd7cddfSDavid du Colombier 
2197dd7cddfSDavid du Colombier 			rp = dnresolve1(name, class, type, req, depth, recurse);
2207dd7cddfSDavid du Colombier 		}
2217dd7cddfSDavid du Colombier 
2227dd7cddfSDavid du Colombier 	/* distinction between not found and not good */
2234f8f669cSDavid du Colombier 	if(rp == nil && status != nil && dp->respcode != 0)
2244f8f669cSDavid du Colombier 		*status = dp->respcode;
2257dd7cddfSDavid du Colombier 
2264f8f669cSDavid du Colombier 	procsetname(procname);
2274f8f669cSDavid du Colombier 	free(procname);
2287dd7cddfSDavid du Colombier 	return randomize(rp);
2293e12c5d1SDavid du Colombier }
2303e12c5d1SDavid du Colombier 
231*3cbadd90SDavid du Colombier static void
232*3cbadd90SDavid du Colombier queryinit(Query *qp, DN *dp, int type, Request *req)
233*3cbadd90SDavid du Colombier {
234*3cbadd90SDavid du Colombier 	memset(qp, 0, sizeof *qp);
235*3cbadd90SDavid du Colombier 	qp->udpfd = -1;
236*3cbadd90SDavid du Colombier 	qp->dp = dp;
237*3cbadd90SDavid du Colombier 	qp->type = type;
238*3cbadd90SDavid du Colombier 	qp->req = req;
239*3cbadd90SDavid du Colombier 	qp->nsrp = nil;
240*3cbadd90SDavid du Colombier 	qp->dest = qp->curdest = nil;
241*3cbadd90SDavid du Colombier 	qp->magic = Querymagic;
242*3cbadd90SDavid du Colombier }
243*3cbadd90SDavid du Colombier 
244*3cbadd90SDavid du Colombier static void
245*3cbadd90SDavid du Colombier queryck(Query *qp)
246*3cbadd90SDavid du Colombier {
247*3cbadd90SDavid du Colombier 	assert(qp);
248*3cbadd90SDavid du Colombier 	assert(qp->magic == Querymagic);
249*3cbadd90SDavid du Colombier }
250*3cbadd90SDavid du Colombier 
251*3cbadd90SDavid du Colombier static void
252*3cbadd90SDavid du Colombier destinit(Dest *p)
253*3cbadd90SDavid du Colombier {
254*3cbadd90SDavid du Colombier 	memset(p, 0, sizeof *p);
255*3cbadd90SDavid du Colombier 	p->magic = Destmagic;
256*3cbadd90SDavid du Colombier }
257*3cbadd90SDavid du Colombier 
258*3cbadd90SDavid du Colombier static void
259*3cbadd90SDavid du Colombier destck(Dest *p)
260*3cbadd90SDavid du Colombier {
261*3cbadd90SDavid du Colombier 	assert(p);
262*3cbadd90SDavid du Colombier 	assert(p->magic == Destmagic);
263*3cbadd90SDavid du Colombier }
264*3cbadd90SDavid du Colombier 
2653e12c5d1SDavid du Colombier static RR*
2664f8f669cSDavid du Colombier dnresolve1(char *name, int class, int type, Request *req, int depth,
2674f8f669cSDavid du Colombier 	int recurse)
2683e12c5d1SDavid du Colombier {
2693e12c5d1SDavid du Colombier 	DN *dp, *nsdp;
2707dd7cddfSDavid du Colombier 	RR *rp, *nsrp, *dbnsrp;
2713e12c5d1SDavid du Colombier 	char *cp;
272*3cbadd90SDavid du Colombier 	Query query;
2733e12c5d1SDavid du Colombier 
2747dd7cddfSDavid du Colombier 	if(debug)
2754f8f669cSDavid du Colombier 		dnslog("[%d] dnresolve1 %s %d %d", getpid(), name, type, class);
2767dd7cddfSDavid du Colombier 
2773e12c5d1SDavid du Colombier 	/* only class Cin implemented so far */
2783e12c5d1SDavid du Colombier 	if(class != Cin)
2794f8f669cSDavid du Colombier 		return nil;
2803e12c5d1SDavid du Colombier 
2813e12c5d1SDavid du Colombier 	dp = dnlookup(name, class, 1);
2823e12c5d1SDavid du Colombier 
2837dd7cddfSDavid du Colombier 	/*
2847dd7cddfSDavid du Colombier 	 *  Try the cache first
2857dd7cddfSDavid du Colombier 	 */
2867dd7cddfSDavid du Colombier 	rp = rrlookup(dp, type, OKneg);
2874f8f669cSDavid du Colombier 	if(rp)
2887dd7cddfSDavid du Colombier 		if(rp->db){
2894f8f669cSDavid du Colombier 			/* unauthoritative db entries are hints */
2907dd7cddfSDavid du Colombier 			if(rp->auth)
2913e12c5d1SDavid du Colombier 				return rp;
2924f8f669cSDavid du Colombier 		} else
2937dd7cddfSDavid du Colombier 			/* cached entry must still be valid */
2944f8f669cSDavid du Colombier 			if(rp->ttl > now)
2957dd7cddfSDavid du Colombier 				/* but Tall entries are special */
2967dd7cddfSDavid du Colombier 				if(type != Tall || rp->query == Tall)
2977dd7cddfSDavid du Colombier 					return rp;
2984f8f669cSDavid du Colombier 
2997dd7cddfSDavid du Colombier 	rrfreelist(rp);
3003e12c5d1SDavid du Colombier 
3017dd7cddfSDavid du Colombier 	/*
3027dd7cddfSDavid du Colombier 	 * try the cache for a canonical name. if found punt
3037dd7cddfSDavid du Colombier 	 * since we'll find it during the canonical name search
3047dd7cddfSDavid du Colombier 	 * in dnresolve().
3057dd7cddfSDavid du Colombier 	 */
3067dd7cddfSDavid du Colombier 	if(type != Tcname){
3077dd7cddfSDavid du Colombier 		rp = rrlookup(dp, Tcname, NOneg);
3087dd7cddfSDavid du Colombier 		rrfreelist(rp);
3093e12c5d1SDavid du Colombier 		if(rp)
3104f8f669cSDavid du Colombier 			return nil;
3113e12c5d1SDavid du Colombier 	}
3123e12c5d1SDavid du Colombier 
313*3cbadd90SDavid du Colombier 	queryinit(&query, dp, type, req);
314*3cbadd90SDavid du Colombier 
3153e12c5d1SDavid du Colombier 	/*
3164f8f669cSDavid du Colombier 	 *  if we're running as just a resolver, query our
3177dd7cddfSDavid du Colombier 	 *  designated name servers
318219b2ee8SDavid du Colombier 	 */
3194f8f669cSDavid du Colombier 	if(cfg.resolver){
3207dd7cddfSDavid du Colombier 		nsrp = randomize(getdnsservers(class));
3217dd7cddfSDavid du Colombier 		if(nsrp != nil) {
322*3cbadd90SDavid du Colombier 			query.nsrp = nsrp;
323*3cbadd90SDavid du Colombier 			if(netquery(&query, depth+1)){
3247dd7cddfSDavid du Colombier 				rrfreelist(nsrp);
325*3cbadd90SDavid du Colombier 				/* prevent accidents */
326*3cbadd90SDavid du Colombier 				memset(&query, 0, sizeof query);
3277dd7cddfSDavid du Colombier 				return rrlookup(dp, type, OKneg);
3287dd7cddfSDavid du Colombier 			}
3297dd7cddfSDavid du Colombier 			rrfreelist(nsrp);
3307dd7cddfSDavid du Colombier 		}
331219b2ee8SDavid du Colombier 	}
332219b2ee8SDavid du Colombier 
333219b2ee8SDavid du Colombier 	/*
3343e12c5d1SDavid du Colombier  	 *  walk up the domain name looking for
3353e12c5d1SDavid du Colombier 	 *  a name server for the domain.
3363e12c5d1SDavid du Colombier 	 */
3373e12c5d1SDavid du Colombier 	for(cp = name; cp; cp = walkup(cp)){
3387dd7cddfSDavid du Colombier 		/*
3397dd7cddfSDavid du Colombier 		 *  if this is a local (served by us) domain,
3407dd7cddfSDavid du Colombier 		 *  return answer
3417dd7cddfSDavid du Colombier 		 */
3427dd7cddfSDavid du Colombier 		dbnsrp = randomize(dblookup(cp, class, Tns, 0, 0));
3437dd7cddfSDavid du Colombier 		if(dbnsrp && dbnsrp->local){
3447dd7cddfSDavid du Colombier 			rp = dblookup(name, class, type, 1, dbnsrp->ttl);
3457dd7cddfSDavid du Colombier 			rrfreelist(dbnsrp);
346*3cbadd90SDavid du Colombier 			/* prevent accidents */
347*3cbadd90SDavid du Colombier 			memset(&query, 0, sizeof query);
3487dd7cddfSDavid du Colombier 			return rp;
3497dd7cddfSDavid du Colombier 		}
3507dd7cddfSDavid du Colombier 
3517dd7cddfSDavid du Colombier 		/*
3527dd7cddfSDavid du Colombier 		 *  if recursion isn't set, just accept local
3537dd7cddfSDavid du Colombier 		 *  entries
3547dd7cddfSDavid du Colombier 		 */
3557dd7cddfSDavid du Colombier 		if(recurse == Dontrecurse){
3567dd7cddfSDavid du Colombier 			if(dbnsrp)
3577dd7cddfSDavid du Colombier 				rrfreelist(dbnsrp);
3587dd7cddfSDavid du Colombier 			continue;
3597dd7cddfSDavid du Colombier 		}
3607dd7cddfSDavid du Colombier 
3617dd7cddfSDavid du Colombier 		/* look for ns in cache */
3623e12c5d1SDavid du Colombier 		nsdp = dnlookup(cp, class, 0);
3637dd7cddfSDavid du Colombier 		nsrp = nil;
3643e12c5d1SDavid du Colombier 		if(nsdp)
3657dd7cddfSDavid du Colombier 			nsrp = randomize(rrlookup(nsdp, Tns, NOneg));
3667dd7cddfSDavid du Colombier 
3677dd7cddfSDavid du Colombier 		/* if the entry timed out, ignore it */
3687dd7cddfSDavid du Colombier 		if(nsrp && nsrp->ttl < now){
3697dd7cddfSDavid du Colombier 			rrfreelist(nsrp);
3707dd7cddfSDavid du Colombier 			nsrp = nil;
3717dd7cddfSDavid du Colombier 		}
3723e12c5d1SDavid du Colombier 
3733e12c5d1SDavid du Colombier 		if(nsrp){
3747dd7cddfSDavid du Colombier 			rrfreelist(dbnsrp);
3757dd7cddfSDavid du Colombier 
3764f8f669cSDavid du Colombier 			/* query the name servers found in cache */
377*3cbadd90SDavid du Colombier 			query.nsrp = nsrp;
378*3cbadd90SDavid du Colombier 			if(netquery(&query, depth+1)){
3793e12c5d1SDavid du Colombier 				rrfreelist(nsrp);
380*3cbadd90SDavid du Colombier 				/* prevent accidents */
381*3cbadd90SDavid du Colombier 				memset(&query, 0, sizeof query);
3827dd7cddfSDavid du Colombier 				return rrlookup(dp, type, OKneg);
3837dd7cddfSDavid du Colombier 			}
3847dd7cddfSDavid du Colombier 			rrfreelist(nsrp);
3857dd7cddfSDavid du Colombier 			continue;
3863e12c5d1SDavid du Colombier 		}
3873e12c5d1SDavid du Colombier 
3887dd7cddfSDavid du Colombier 		/* use ns from db */
3897dd7cddfSDavid du Colombier 		if(dbnsrp){
3907dd7cddfSDavid du Colombier 			/* try the name servers found in db */
391*3cbadd90SDavid du Colombier 			query.nsrp = dbnsrp;
392*3cbadd90SDavid du Colombier 			if(netquery(&query, depth+1)){
3933e12c5d1SDavid du Colombier 				/* we got an answer */
3947dd7cddfSDavid du Colombier 				rrfreelist(dbnsrp);
395*3cbadd90SDavid du Colombier 				/* prevent accidents */
396*3cbadd90SDavid du Colombier 				memset(&query, 0, sizeof query);
3977dd7cddfSDavid du Colombier 				return rrlookup(dp, type, NOneg);
3983e12c5d1SDavid du Colombier 			}
3997dd7cddfSDavid du Colombier 			rrfreelist(dbnsrp);
4003e12c5d1SDavid du Colombier 		}
4013e12c5d1SDavid du Colombier 	}
402*3cbadd90SDavid du Colombier 	memset(&query, 0, sizeof query);	/* prevent accidents */
4033e12c5d1SDavid du Colombier 
4047dd7cddfSDavid du Colombier 	/* settle for a non-authoritative answer */
4057dd7cddfSDavid du Colombier 	rp = rrlookup(dp, type, OKneg);
4067dd7cddfSDavid du Colombier 	if(rp)
4077dd7cddfSDavid du Colombier 		return rp;
4087dd7cddfSDavid du Colombier 
4097dd7cddfSDavid du Colombier 	/* noone answered.  try the database, we might have a chance. */
4107dd7cddfSDavid du Colombier 	return dblookup(name, class, type, 0, 0);
4113e12c5d1SDavid du Colombier }
4123e12c5d1SDavid du Colombier 
4133e12c5d1SDavid du Colombier /*
4144f8f669cSDavid du Colombier  *  walk a domain name one element to the right.
4154f8f669cSDavid du Colombier  *  return a pointer to that element.
4163e12c5d1SDavid du Colombier  *  in other words, return a pointer to the parent domain name.
4173e12c5d1SDavid du Colombier  */
4183e12c5d1SDavid du Colombier char*
4193e12c5d1SDavid du Colombier walkup(char *name)
4203e12c5d1SDavid du Colombier {
4213e12c5d1SDavid du Colombier 	char *cp;
4223e12c5d1SDavid du Colombier 
4233e12c5d1SDavid du Colombier 	cp = strchr(name, '.');
4243e12c5d1SDavid du Colombier 	if(cp)
4253e12c5d1SDavid du Colombier 		return cp+1;
4263e12c5d1SDavid du Colombier 	else if(*name)
4273e12c5d1SDavid du Colombier 		return "";
4283e12c5d1SDavid du Colombier 	else
4293e12c5d1SDavid du Colombier 		return 0;
4303e12c5d1SDavid du Colombier }
4313e12c5d1SDavid du Colombier 
4323e12c5d1SDavid du Colombier /*
4333e12c5d1SDavid du Colombier  *  Get a udpport for requests and replies.  Put the port
4343e12c5d1SDavid du Colombier  *  into "headers" mode.
4353e12c5d1SDavid du Colombier  */
4363e12c5d1SDavid du Colombier static char *hmsg = "headers";
4371e8349ebSDavid du Colombier static char *ohmsg = "oldheaders";
4383e12c5d1SDavid du Colombier 
439dc5a79c1SDavid du Colombier int
4404f8f669cSDavid du Colombier udpport(char *mtpt)
4413e12c5d1SDavid du Colombier {
4423e12c5d1SDavid du Colombier 	int fd, ctl;
4434f8f669cSDavid du Colombier 	char ds[64], adir[64];
4443e12c5d1SDavid du Colombier 
4453e12c5d1SDavid du Colombier 	/* get a udp port */
4464f8f669cSDavid du Colombier 	snprint(ds, sizeof ds, "%s/udp!*!0", (mtpt? mtpt: "/net"));
4477dd7cddfSDavid du Colombier 	ctl = announce(ds, adir);
4487dd7cddfSDavid du Colombier 	if(ctl < 0){
4497dd7cddfSDavid du Colombier 		/* warning("can't get udp port"); */
450bd389b36SDavid du Colombier 		return -1;
451bd389b36SDavid du Colombier 	}
4523e12c5d1SDavid du Colombier 
4533e12c5d1SDavid du Colombier 	/* turn on header style interface */
454bd389b36SDavid du Colombier 	if(write(ctl, hmsg, strlen(hmsg)) , 0){
4557dd7cddfSDavid du Colombier 		close(ctl);
456bd389b36SDavid du Colombier 		warning(hmsg);
457bd389b36SDavid du Colombier 		return -1;
458bd389b36SDavid du Colombier 	}
4591e8349ebSDavid du Colombier 	write(ctl, ohmsg, strlen(ohmsg));
4603e12c5d1SDavid du Colombier 
4617dd7cddfSDavid du Colombier 	/* grab the data file */
4624f8f669cSDavid du Colombier 	snprint(ds, sizeof ds, "%s/data", adir);
4637dd7cddfSDavid du Colombier 	fd = open(ds, ORDWR);
4643e12c5d1SDavid du Colombier 	close(ctl);
4654f8f669cSDavid du Colombier 	if(fd < 0)
4664f8f669cSDavid du Colombier 		warning("can't open udp port %s: %r", ds);
4673e12c5d1SDavid du Colombier 	return fd;
4683e12c5d1SDavid du Colombier }
4693e12c5d1SDavid du Colombier 
470d6d99297SDavid du Colombier /* generate a DNS UDP query packet */
471dc5a79c1SDavid du Colombier int
472dc5a79c1SDavid du Colombier mkreq(DN *dp, int type, uchar *buf, int flags, ushort reqno)
4733e12c5d1SDavid du Colombier {
4743e12c5d1SDavid du Colombier 	DNSmsg m;
4753e12c5d1SDavid du Colombier 	int len;
476d9dc5dd1SDavid du Colombier 	OUdphdr *uh = (OUdphdr*)buf;
4773e12c5d1SDavid du Colombier 
4783e12c5d1SDavid du Colombier 	/* stuff port number into output buffer */
479*3cbadd90SDavid du Colombier 	memset(uh, 0, sizeof *uh);
4807dd7cddfSDavid du Colombier 	hnputs(uh->rport, 53);
4813e12c5d1SDavid du Colombier 
4823e12c5d1SDavid du Colombier 	/* make request and convert it to output format */
483*3cbadd90SDavid du Colombier 	memset(&m, 0, sizeof m);
484dc5a79c1SDavid du Colombier 	m.flags = flags;
4853e12c5d1SDavid du Colombier 	m.id = reqno;
4863e12c5d1SDavid du Colombier 	m.qd = rralloc(type);
4873e12c5d1SDavid du Colombier 	m.qd->owner = dp;
4883e12c5d1SDavid du Colombier 	m.qd->type = type;
489d9dc5dd1SDavid du Colombier 	len = convDNS2M(&m, &buf[OUdphdrsize], Maxudp);
4907dd7cddfSDavid du Colombier 	rrfree(m.qd);
4913e12c5d1SDavid du Colombier 	return len;
4923e12c5d1SDavid du Colombier }
4933e12c5d1SDavid du Colombier 
4947dd7cddfSDavid du Colombier /* for alarms in readreply */
4953e12c5d1SDavid du Colombier static void
4963e12c5d1SDavid du Colombier ding(void *x, char *msg)
4973e12c5d1SDavid du Colombier {
4983e12c5d1SDavid du Colombier 	USED(x);
4993e12c5d1SDavid du Colombier 	if(strcmp(msg, "alarm") == 0)
5003e12c5d1SDavid du Colombier 		noted(NCONT);
5013e12c5d1SDavid du Colombier 	else
5023e12c5d1SDavid du Colombier 		noted(NDFLT);
5033e12c5d1SDavid du Colombier }
5047dd7cddfSDavid du Colombier 
5057dd7cddfSDavid du Colombier static void
5067dd7cddfSDavid du Colombier freeanswers(DNSmsg *mp)
5073e12c5d1SDavid du Colombier {
5087dd7cddfSDavid du Colombier 	rrfreelist(mp->qd);
5097dd7cddfSDavid du Colombier 	rrfreelist(mp->an);
5107dd7cddfSDavid du Colombier 	rrfreelist(mp->ns);
5117dd7cddfSDavid du Colombier 	rrfreelist(mp->ar);
5124f8f669cSDavid du Colombier 	mp->qd = mp->an = mp->ns = mp->ar = nil;
5137dd7cddfSDavid du Colombier }
5147dd7cddfSDavid du Colombier 
515*3cbadd90SDavid du Colombier /* sets srcip */
516*3cbadd90SDavid du Colombier static int
517*3cbadd90SDavid du Colombier readnet(Query *qp, int medium, uchar *ibuf, ulong endtime, uchar **replyp,
518*3cbadd90SDavid du Colombier 	uchar *srcip)
519*3cbadd90SDavid du Colombier {
520*3cbadd90SDavid du Colombier 	int len, fd;
521*3cbadd90SDavid du Colombier 	uchar *reply;
522*3cbadd90SDavid du Colombier 	uchar lenbuf[2];
523*3cbadd90SDavid du Colombier 
524*3cbadd90SDavid du Colombier 	/* timed read of reply */
525*3cbadd90SDavid du Colombier 	alarm((endtime - time(nil)) * 1000);
526*3cbadd90SDavid du Colombier 	reply = ibuf;
527*3cbadd90SDavid du Colombier 	if (medium == Udp) {
528*3cbadd90SDavid du Colombier 		len = read(qp->udpfd, ibuf, OUdphdrsize+Maxudpin);
529*3cbadd90SDavid du Colombier 		len -= OUdphdrsize;
530*3cbadd90SDavid du Colombier 		memmove(srcip, ibuf, IPaddrlen);
531*3cbadd90SDavid du Colombier 		reply += OUdphdrsize;
532*3cbadd90SDavid du Colombier 	} else {
533*3cbadd90SDavid du Colombier 		len = -1;			/* pessimism */
534*3cbadd90SDavid du Colombier 		if (!qp->tcpset)
535*3cbadd90SDavid du Colombier 			dnslog("readnet: tcp params not set");
536*3cbadd90SDavid du Colombier 		fd = qp->tcpfd;
537*3cbadd90SDavid du Colombier 		if (fd <= 0)
538*3cbadd90SDavid du Colombier 			dnslog("readnet: %s: tcp fd unset for dest %I",
539*3cbadd90SDavid du Colombier 				qp->dp->name, qp->tcpip);
540*3cbadd90SDavid du Colombier 		else if (readn(fd, lenbuf, 2) != 2) {
541*3cbadd90SDavid du Colombier 			dnslog("readnet: short read of tcp size from %I",
542*3cbadd90SDavid du Colombier 				qp->tcpip);
543*3cbadd90SDavid du Colombier 			/* probably a time-out; demote the ns */
544*3cbadd90SDavid du Colombier 			addslug(qp->tcpip);
545*3cbadd90SDavid du Colombier 		} else {
546*3cbadd90SDavid du Colombier 			len = lenbuf[0]<<8 | lenbuf[1];
547*3cbadd90SDavid du Colombier 			if (readn(fd, ibuf, len) != len) {
548*3cbadd90SDavid du Colombier 				dnslog("readnet: short read of tcp data from %I",
549*3cbadd90SDavid du Colombier 					qp->tcpip);
550*3cbadd90SDavid du Colombier 				/* probably a time-out; demote the ns */
551*3cbadd90SDavid du Colombier 				addslug(qp->tcpip);
552*3cbadd90SDavid du Colombier 				len = -1;
553*3cbadd90SDavid du Colombier 			}
554*3cbadd90SDavid du Colombier 		}
555*3cbadd90SDavid du Colombier 		memmove(srcip, qp->tcpip, IPaddrlen);
556*3cbadd90SDavid du Colombier 	}
557*3cbadd90SDavid du Colombier 	alarm(0);
558*3cbadd90SDavid du Colombier 	*replyp = reply;
559*3cbadd90SDavid du Colombier 	return len;
560*3cbadd90SDavid du Colombier }
561*3cbadd90SDavid du Colombier 
5627dd7cddfSDavid du Colombier /*
563*3cbadd90SDavid du Colombier  *  read replies to a request and remember the rrs in the answer(s).
564*3cbadd90SDavid du Colombier  *  ignore any of the wrong type.
5654f8f669cSDavid du Colombier  *  wait at most until endtime.
5667dd7cddfSDavid du Colombier  */
5677dd7cddfSDavid du Colombier static int
568*3cbadd90SDavid du Colombier readreply(Query *qp, int medium, ushort req, uchar *ibuf, DNSmsg *mp,
569*3cbadd90SDavid du Colombier 	ulong endtime)
5707dd7cddfSDavid du Colombier {
571*3cbadd90SDavid du Colombier 	int len = -1, rv;
5727dd7cddfSDavid du Colombier 	char *err;
573*3cbadd90SDavid du Colombier 	uchar *reply;
574*3cbadd90SDavid du Colombier 	uchar srcip[IPaddrlen];
5753e12c5d1SDavid du Colombier 	RR *rp;
5767dd7cddfSDavid du Colombier 
5777dd7cddfSDavid du Colombier 	notify(ding);
5787dd7cddfSDavid du Colombier 
579*3cbadd90SDavid du Colombier 	queryck(qp);
580*3cbadd90SDavid du Colombier 	rv = 0;
581*3cbadd90SDavid du Colombier 	memset(mp, 0, sizeof *mp);
582*3cbadd90SDavid du Colombier 	if (time(nil) >= endtime)
583*3cbadd90SDavid du Colombier 		return -1;		/* timed out before we started */
5847dd7cddfSDavid du Colombier 
585*3cbadd90SDavid du Colombier 	for (; time(nil) < endtime &&
586*3cbadd90SDavid du Colombier 	    (len = readnet(qp, medium, ibuf, endtime, &reply, srcip)) >= 0;
587*3cbadd90SDavid du Colombier 	    freeanswers(mp)){
5887dd7cddfSDavid du Colombier 		/* convert into internal format  */
589*3cbadd90SDavid du Colombier 		memset(mp, 0, sizeof *mp);
590*3cbadd90SDavid du Colombier 		err = convM2DNS(reply, len, mp, nil);
591d6d99297SDavid du Colombier 		if (mp->flags & Ftrunc) {
592*3cbadd90SDavid du Colombier //			dnslog("readreply: %s: truncated reply, len %d from %I",
593*3cbadd90SDavid du Colombier //				qp->dp->name, len, srcip);
594*3cbadd90SDavid du Colombier 			/* notify the caller to retry the query via tcp. */
595*3cbadd90SDavid du Colombier 			return -1;
596*3cbadd90SDavid du Colombier 		} else if(err){
597*3cbadd90SDavid du Colombier 			dnslog("readreply: %s: input err, len %d: %s: %I",
598*3cbadd90SDavid du Colombier 				qp->dp->name, len, err, srcip);
599*3cbadd90SDavid du Colombier 			free(err);
6007dd7cddfSDavid du Colombier 			continue;
6017dd7cddfSDavid du Colombier 		}
602*3cbadd90SDavid du Colombier 		if (err)
603*3cbadd90SDavid du Colombier 			free(err);
6047dd7cddfSDavid du Colombier 		if(debug)
605*3cbadd90SDavid du Colombier 			logreply(qp->req->id, srcip, mp);
6067dd7cddfSDavid du Colombier 
6077dd7cddfSDavid du Colombier 		/* answering the right question? */
608*3cbadd90SDavid du Colombier 		if(mp->id != req)
609*3cbadd90SDavid du Colombier 			dnslog("%d: id %d instead of %d: %I", qp->req->id,
610*3cbadd90SDavid du Colombier 				mp->id, req, srcip);
611*3cbadd90SDavid du Colombier 		else if(mp->qd == 0)
612*3cbadd90SDavid du Colombier 			dnslog("%d: no question RR: %I", qp->req->id, srcip);
613*3cbadd90SDavid du Colombier 		else if(mp->qd->owner != qp->dp)
614*3cbadd90SDavid du Colombier 			dnslog("%d: owner %s instead of %s: %I", qp->req->id,
615*3cbadd90SDavid du Colombier 				mp->qd->owner->name, qp->dp->name, srcip);
616*3cbadd90SDavid du Colombier 		else if(mp->qd->type != qp->type)
617*3cbadd90SDavid du Colombier 			dnslog("%d: qp->type %d instead of %d: %I",
618*3cbadd90SDavid du Colombier 				qp->req->id, mp->qd->type, qp->type, srcip);
619*3cbadd90SDavid du Colombier 		else {
6207dd7cddfSDavid du Colombier 			/* remember what request this is in answer to */
6217dd7cddfSDavid du Colombier 			for(rp = mp->an; rp; rp = rp->next)
622*3cbadd90SDavid du Colombier 				rp->query = qp->type;
623*3cbadd90SDavid du Colombier 			return rv;
6247dd7cddfSDavid du Colombier 		}
6257dd7cddfSDavid du Colombier 	}
626*3cbadd90SDavid du Colombier 	if (time(nil) >= endtime)
627*3cbadd90SDavid du Colombier 		addslug(srcip);
628*3cbadd90SDavid du Colombier 	else
629*3cbadd90SDavid du Colombier 		dnslog("readreply: %s: %I read error or eof (returned %d)",
630*3cbadd90SDavid du Colombier 			qp->dp->name, srcip, len);
631*3cbadd90SDavid du Colombier 	return -1;
632*3cbadd90SDavid du Colombier }
6337dd7cddfSDavid du Colombier 
6347dd7cddfSDavid du Colombier /*
6357dd7cddfSDavid du Colombier  *	return non-0 if first list includes second list
6367dd7cddfSDavid du Colombier  */
6377dd7cddfSDavid du Colombier int
6387dd7cddfSDavid du Colombier contains(RR *rp1, RR *rp2)
6397dd7cddfSDavid du Colombier {
6407dd7cddfSDavid du Colombier 	RR *trp1, *trp2;
6417dd7cddfSDavid du Colombier 
6427dd7cddfSDavid du Colombier 	for(trp2 = rp2; trp2; trp2 = trp2->next){
643*3cbadd90SDavid du Colombier 		for(trp1 = rp1; trp1; trp1 = trp1->next)
6447dd7cddfSDavid du Colombier 			if(trp1->type == trp2->type)
6457dd7cddfSDavid du Colombier 			if(trp1->host == trp2->host)
6467dd7cddfSDavid du Colombier 			if(trp1->owner == trp2->owner)
6477dd7cddfSDavid du Colombier 				break;
6484f8f669cSDavid du Colombier 		if(trp1 == nil)
6497dd7cddfSDavid du Colombier 			return 0;
6507dd7cddfSDavid du Colombier 	}
6517dd7cddfSDavid du Colombier 	return 1;
6527dd7cddfSDavid du Colombier }
6537dd7cddfSDavid du Colombier 
6547dd7cddfSDavid du Colombier 
6556b6b9ac8SDavid du Colombier /*
6566b6b9ac8SDavid du Colombier  *  return multicast version if any
6576b6b9ac8SDavid du Colombier  */
6586b6b9ac8SDavid du Colombier int
6596b6b9ac8SDavid du Colombier ipisbm(uchar *ip)
6606b6b9ac8SDavid du Colombier {
6616b6b9ac8SDavid du Colombier 	if(isv4(ip)){
6624f8f669cSDavid du Colombier 		if (ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0 ||
6634f8f669cSDavid du Colombier 		    ipcmp(ip, IPv4bcast) == 0)
6646b6b9ac8SDavid du Colombier 			return 4;
6654f8f669cSDavid du Colombier 	} else
6666b6b9ac8SDavid du Colombier 		if(ip[0] == 0xff)
6676b6b9ac8SDavid du Colombier 			return 6;
66828a8a86bSDavid du Colombier 	return 0;
66928a8a86bSDavid du Colombier }
67028a8a86bSDavid du Colombier 
6717dd7cddfSDavid du Colombier /*
6727dd7cddfSDavid du Colombier  *  Get next server address
6737dd7cddfSDavid du Colombier  */
6747dd7cddfSDavid du Colombier static int
675*3cbadd90SDavid du Colombier serveraddrs(Query *qp, int nd, int depth)
6767dd7cddfSDavid du Colombier {
6777dd7cddfSDavid du Colombier 	RR *rp, *arp, *trp;
6787dd7cddfSDavid du Colombier 	Dest *cur;
6797dd7cddfSDavid du Colombier 
6807dd7cddfSDavid du Colombier 	if(nd >= Maxdest)
6817dd7cddfSDavid du Colombier 		return 0;
6827dd7cddfSDavid du Colombier 
6837dd7cddfSDavid du Colombier 	/*
6847dd7cddfSDavid du Colombier 	 *  look for a server whose address we already know.
6857dd7cddfSDavid du Colombier 	 *  if we find one, mark it so we ignore this on
6867dd7cddfSDavid du Colombier 	 *  subsequent passes.
6877dd7cddfSDavid du Colombier 	 */
6887dd7cddfSDavid du Colombier 	arp = 0;
689*3cbadd90SDavid du Colombier 	for(rp = qp->nsrp; rp; rp = rp->next){
69034f77ae3SDavid du Colombier 		assert(rp->magic == RRmagic);
6917dd7cddfSDavid du Colombier 		if(rp->marker)
6927dd7cddfSDavid du Colombier 			continue;
6937dd7cddfSDavid du Colombier 		arp = rrlookup(rp->host, Ta, NOneg);
6947dd7cddfSDavid du Colombier 		if(arp){
6957dd7cddfSDavid du Colombier 			rp->marker = 1;
6967dd7cddfSDavid du Colombier 			break;
6977dd7cddfSDavid du Colombier 		}
6987dd7cddfSDavid du Colombier 		arp = dblookup(rp->host->name, Cin, Ta, 0, 0);
6997dd7cddfSDavid du Colombier 		if(arp){
7007dd7cddfSDavid du Colombier 			rp->marker = 1;
7017dd7cddfSDavid du Colombier 			break;
7027dd7cddfSDavid du Colombier 		}
7037dd7cddfSDavid du Colombier 	}
7047dd7cddfSDavid du Colombier 
7057dd7cddfSDavid du Colombier 	/*
7067dd7cddfSDavid du Colombier 	 *  if the cache and database lookup didn't find any new
7077dd7cddfSDavid du Colombier 	 *  server addresses, try resolving one via the network.
7087dd7cddfSDavid du Colombier 	 *  Mark any we try to resolve so we don't try a second time.
7097dd7cddfSDavid du Colombier 	 */
7104f8f669cSDavid du Colombier 	if(arp == 0)
711*3cbadd90SDavid du Colombier 		for(rp = qp->nsrp; rp; rp = rp->next){
7127dd7cddfSDavid du Colombier 			if(rp->marker)
7137dd7cddfSDavid du Colombier 				continue;
7147dd7cddfSDavid du Colombier 			rp->marker = 1;
7157dd7cddfSDavid du Colombier 
7167dd7cddfSDavid du Colombier 			/*
7177dd7cddfSDavid du Colombier 			 *  avoid loops looking up a server under itself
7187dd7cddfSDavid du Colombier 			 */
7197dd7cddfSDavid du Colombier 			if(subsume(rp->owner->name, rp->host->name))
7207dd7cddfSDavid du Colombier 				continue;
7217dd7cddfSDavid du Colombier 
722*3cbadd90SDavid du Colombier 			arp = dnresolve(rp->host->name, Cin, Ta, qp->req, 0,
7234f8f669cSDavid du Colombier 				depth+1, Recurse, 1, 0);
7247dd7cddfSDavid du Colombier 			rrfreelist(rrremneg(&arp));
7257dd7cddfSDavid du Colombier 			if(arp)
7267dd7cddfSDavid du Colombier 				break;
7277dd7cddfSDavid du Colombier 		}
7287dd7cddfSDavid du Colombier 
7297dd7cddfSDavid du Colombier 	/* use any addresses that we found */
730*3cbadd90SDavid du Colombier 	for(trp = arp; trp && nd < Maxdest; trp = trp->next){
731*3cbadd90SDavid du Colombier 		cur = &qp->dest[nd];
7327dd7cddfSDavid du Colombier 		parseip(cur->a, trp->ip->name);
7334f8f669cSDavid du Colombier 		/*
7344f8f669cSDavid du Colombier 		 * straddling servers can reject all nameservers if they are all
7354f8f669cSDavid du Colombier 		 * inside, so be sure to list at least one outside ns at
7364f8f669cSDavid du Colombier 		 * the end of the ns list in /lib/ndb for `dom='.
7374f8f669cSDavid du Colombier 		 */
7384f8f669cSDavid du Colombier 		if (ipisbm(cur->a) ||
739*3cbadd90SDavid du Colombier 		    cfg.straddle && !insideaddr(qp->dp->name) && insidens(cur->a))
7406b6b9ac8SDavid du Colombier 			continue;
7417dd7cddfSDavid du Colombier 		cur->nx = 0;
7427dd7cddfSDavid du Colombier 		cur->s = trp->owner;
7437dd7cddfSDavid du Colombier 		cur->code = Rtimeout;
7446b6b9ac8SDavid du Colombier 		nd++;
7457dd7cddfSDavid du Colombier 	}
7467dd7cddfSDavid du Colombier 	rrfreelist(arp);
7477dd7cddfSDavid du Colombier 	return nd;
7487dd7cddfSDavid du Colombier }
7497dd7cddfSDavid du Colombier 
7507dd7cddfSDavid du Colombier /*
7517dd7cddfSDavid du Colombier  *  cache negative responses
7527dd7cddfSDavid du Colombier  */
7537dd7cddfSDavid du Colombier static void
7547dd7cddfSDavid du Colombier cacheneg(DN *dp, int type, int rcode, RR *soarr)
7557dd7cddfSDavid du Colombier {
7567dd7cddfSDavid du Colombier 	RR *rp;
7577dd7cddfSDavid du Colombier 	DN *soaowner;
7589a747e4fSDavid du Colombier 	ulong ttl;
7597dd7cddfSDavid du Colombier 
7604f8f669cSDavid du Colombier 	/* no cache time specified, don't make anything up */
7617dd7cddfSDavid du Colombier 	if(soarr != nil){
7627dd7cddfSDavid du Colombier 		if(soarr->next != nil){
7637dd7cddfSDavid du Colombier 			rrfreelist(soarr->next);
7647dd7cddfSDavid du Colombier 			soarr->next = nil;
7657dd7cddfSDavid du Colombier 		}
7667dd7cddfSDavid du Colombier 		soaowner = soarr->owner;
7677dd7cddfSDavid du Colombier 	} else
7687dd7cddfSDavid du Colombier 		soaowner = nil;
7697dd7cddfSDavid du Colombier 
7709a747e4fSDavid du Colombier 	/* the attach can cause soarr to be freed so mine it now */
7719a747e4fSDavid du Colombier 	if(soarr != nil && soarr->soa != nil)
7729a747e4fSDavid du Colombier 		ttl = soarr->soa->minttl+now;
7739a747e4fSDavid du Colombier 	else
7749a747e4fSDavid du Colombier 		ttl = 5*Min;
7759a747e4fSDavid du Colombier 
7767dd7cddfSDavid du Colombier 	/* add soa and negative RR to the database */
7777dd7cddfSDavid du Colombier 	rrattach(soarr, 1);
7787dd7cddfSDavid du Colombier 
7797dd7cddfSDavid du Colombier 	rp = rralloc(type);
7807dd7cddfSDavid du Colombier 	rp->owner = dp;
7817dd7cddfSDavid du Colombier 	rp->negative = 1;
7827dd7cddfSDavid du Colombier 	rp->negsoaowner = soaowner;
7837dd7cddfSDavid du Colombier 	rp->negrcode = rcode;
7849a747e4fSDavid du Colombier 	rp->ttl = ttl;
7857dd7cddfSDavid du Colombier 	rrattach(rp, 1);
7867dd7cddfSDavid du Colombier }
7877dd7cddfSDavid du Colombier 
7884f8f669cSDavid du Colombier static int
7894f8f669cSDavid du Colombier setdestoutns(Dest *p, int n)
7904f8f669cSDavid du Colombier {
7914f8f669cSDavid du Colombier 	uchar *outns = outsidens(n);
7924f8f669cSDavid du Colombier 
793*3cbadd90SDavid du Colombier 	destck(p);
794*3cbadd90SDavid du Colombier 	destinit(p);
7954f8f669cSDavid du Colombier 	if (outns == nil) {
7964f8f669cSDavid du Colombier 		if (n == 0)
7974f8f669cSDavid du Colombier 			dnslog("[%d] no outside-ns in ndb", getpid());
7984f8f669cSDavid du Colombier 		return -1;
7994f8f669cSDavid du Colombier 	}
8004f8f669cSDavid du Colombier 	memmove(p->a, outns, sizeof p->a);
8014f8f669cSDavid du Colombier 	p->s = dnlookup("outside-ns-ips", Cin, 1);
8024f8f669cSDavid du Colombier 	return 0;
8034f8f669cSDavid du Colombier }
8044f8f669cSDavid du Colombier 
8057dd7cddfSDavid du Colombier /*
806*3cbadd90SDavid du Colombier  * issue query via UDP or TCP as appropriate.
807*3cbadd90SDavid du Colombier  * for TCP, returns with qp->tcpip set from udppkt header.
8087dd7cddfSDavid du Colombier  */
8097dd7cddfSDavid du Colombier static int
810*3cbadd90SDavid du Colombier mydnsquery(Query *qp, int medium, uchar *udppkt, int len)
8117dd7cddfSDavid du Colombier {
812*3cbadd90SDavid du Colombier 	int rv = -1;
813*3cbadd90SDavid du Colombier 	char *domain;
814*3cbadd90SDavid du Colombier 	char conndir[40];
815*3cbadd90SDavid du Colombier 	NetConnInfo *nci;
8164f8f669cSDavid du Colombier 
817*3cbadd90SDavid du Colombier 	queryck(qp);
818*3cbadd90SDavid du Colombier 	switch (medium) {
819*3cbadd90SDavid du Colombier 	case Udp:
820*3cbadd90SDavid du Colombier 		if(write(qp->udpfd, udppkt, len+OUdphdrsize) != len+OUdphdrsize)
821*3cbadd90SDavid du Colombier 			warning("sending udp msg %r");
822*3cbadd90SDavid du Colombier 		rv = 0;
823*3cbadd90SDavid du Colombier 		break;
824*3cbadd90SDavid du Colombier 	case Tcp:
825*3cbadd90SDavid du Colombier 		/* send via TCP & keep fd around for reply */
826*3cbadd90SDavid du Colombier 		domain = smprint("%I", udppkt);
827*3cbadd90SDavid du Colombier 		alarm(10*1000);
828*3cbadd90SDavid du Colombier 		qp->tcpfd = rv = dial(netmkaddr(domain, "tcp", "dns"), nil,
829*3cbadd90SDavid du Colombier 			conndir, &qp->tcpctlfd);
830*3cbadd90SDavid du Colombier 		alarm(0);
831*3cbadd90SDavid du Colombier 		if (qp->tcpfd < 0) {
832*3cbadd90SDavid du Colombier 			dnslog("can't dial tcp!%s!dns: %r", domain);
833*3cbadd90SDavid du Colombier 			addslug(udppkt);
834*3cbadd90SDavid du Colombier 		} else {
835*3cbadd90SDavid du Colombier 			uchar belen[2];
8363e12c5d1SDavid du Colombier 
837*3cbadd90SDavid du Colombier 			nci = getnetconninfo(conndir, qp->tcpfd);
838*3cbadd90SDavid du Colombier 			if (nci) {
839*3cbadd90SDavid du Colombier 				parseip(qp->tcpip, nci->rsys);
840*3cbadd90SDavid du Colombier 				freenetconninfo(nci);
841*3cbadd90SDavid du Colombier 			} else
842*3cbadd90SDavid du Colombier 				dnslog("mydnsquery: getnetconninfo failed");
843*3cbadd90SDavid du Colombier 			qp->tcpset = 1;
8443e12c5d1SDavid du Colombier 
845*3cbadd90SDavid du Colombier 			belen[0] = len >> 8;
846*3cbadd90SDavid du Colombier 			belen[1] = len;
847*3cbadd90SDavid du Colombier 			if (write(qp->tcpfd, belen, 2) != 2 ||
848*3cbadd90SDavid du Colombier 			    write(qp->tcpfd, udppkt + OUdphdrsize, len) != len)
849*3cbadd90SDavid du Colombier 				warning("sending tcp msg %r");
850*3cbadd90SDavid du Colombier 		}
851*3cbadd90SDavid du Colombier 		free(domain);
852*3cbadd90SDavid du Colombier 		break;
853*3cbadd90SDavid du Colombier 	default:
854*3cbadd90SDavid du Colombier 		sysfatal("mydnsquery: bad medium");
855*3cbadd90SDavid du Colombier 	}
856*3cbadd90SDavid du Colombier 	return rv;
857*3cbadd90SDavid du Colombier }
8587dd7cddfSDavid du Colombier 
8593e12c5d1SDavid du Colombier /*
860*3cbadd90SDavid du Colombier  * send query to all UDP destinations or one TCP destination,
861*3cbadd90SDavid du Colombier  * taken from obuf (udp packet) header
8623e12c5d1SDavid du Colombier  */
863*3cbadd90SDavid du Colombier static int
864*3cbadd90SDavid du Colombier xmitquery(Query *qp, int medium, int depth, uchar *obuf, int inns, int len)
865*3cbadd90SDavid du Colombier {
866*3cbadd90SDavid du Colombier 	int j, n;
867*3cbadd90SDavid du Colombier 	char buf[32];
868*3cbadd90SDavid du Colombier 	Dest *p;
8693e12c5d1SDavid du Colombier 
870*3cbadd90SDavid du Colombier 	queryck(qp);
871*3cbadd90SDavid du Colombier //	dnslog("xmitquery xmit loop: now %ld aborttime %ld", time(nil),
872*3cbadd90SDavid du Colombier //		qp->req->aborttime);
873*3cbadd90SDavid du Colombier 	if(time(nil) >= qp->req->aborttime)
874*3cbadd90SDavid du Colombier 		return -1;
875*3cbadd90SDavid du Colombier 
876*3cbadd90SDavid du Colombier 	/*
877*3cbadd90SDavid du Colombier 	 * get a nameserver address if we need one.
878*3cbadd90SDavid du Colombier 	 * serveraddrs populates qp->dest.
879*3cbadd90SDavid du Colombier 	 */
880*3cbadd90SDavid du Colombier 	p = qp->dest;
881*3cbadd90SDavid du Colombier 	destck(p);
882*3cbadd90SDavid du Colombier 	if (qp->ndest < 0 || qp->ndest > Maxdest)
883*3cbadd90SDavid du Colombier 		dnslog("qp->ndest %d out of range", qp->ndest);
884*3cbadd90SDavid du Colombier 	if (qp->ndest > qp->curdest - p)
885*3cbadd90SDavid du Colombier 		qp->curdest = &qp->dest[serveraddrs(qp, qp->curdest - p, depth)];
886*3cbadd90SDavid du Colombier 	destck(qp->curdest);
8877dd7cddfSDavid du Colombier 
8887dd7cddfSDavid du Colombier 	/* no servers, punt */
889*3cbadd90SDavid du Colombier 	if (qp->curdest == qp->dest)
8904f8f669cSDavid du Colombier 		if (cfg.straddle && cfg.inside) {
891*3cbadd90SDavid du Colombier 			/* get ips of "outside-ns-ips" */
892*3cbadd90SDavid du Colombier 			p = qp->curdest = qp->dest;
893*3cbadd90SDavid du Colombier 			for(n = 0; n < Maxdest; n++, qp->curdest++)
894*3cbadd90SDavid du Colombier 				if (setdestoutns(qp->curdest, n) < 0)
8957dd7cddfSDavid du Colombier 					break;
8964f8f669cSDavid du Colombier 		} else {
897*3cbadd90SDavid du Colombier 			dnslog("xmitquery: %s: no nameservers", qp->dp->name);
898*3cbadd90SDavid du Colombier 			return -1;
8994f8f669cSDavid du Colombier 		}
9007dd7cddfSDavid du Colombier 
901*3cbadd90SDavid du Colombier 	/* send to first 'qp->ndest' destinations */
9027dd7cddfSDavid du Colombier 	j = 0;
903*3cbadd90SDavid du Colombier 	if (medium == Tcp) {
904*3cbadd90SDavid du Colombier 		j++;
905*3cbadd90SDavid du Colombier 		queryck(qp);
906*3cbadd90SDavid du Colombier 		assert(qp->dp);
907*3cbadd90SDavid du Colombier 		procsetname("tcp %sside query for %s %s", (inns? "in": "out"),
908*3cbadd90SDavid du Colombier 			qp->dp->name, rrname(qp->type, buf, sizeof buf));
909*3cbadd90SDavid du Colombier 		mydnsquery(qp, medium, obuf, len); /* sets qp->tcpip from obuf */
910*3cbadd90SDavid du Colombier 		if(debug)
911*3cbadd90SDavid du Colombier 			logsend(qp->req->id, depth, qp->tcpip, "", qp->dp->name,
912*3cbadd90SDavid du Colombier 				qp->type);
913*3cbadd90SDavid du Colombier 	} else
914*3cbadd90SDavid du Colombier 		for(; p < &qp->dest[qp->ndest] && p < qp->curdest; p++){
9157dd7cddfSDavid du Colombier 			/* skip destinations we've finished with */
9167dd7cddfSDavid du Colombier 			if(p->nx >= Maxtrans)
9177dd7cddfSDavid du Colombier 				continue;
9187dd7cddfSDavid du Colombier 
9197dd7cddfSDavid du Colombier 			j++;
9207dd7cddfSDavid du Colombier 
9217dd7cddfSDavid du Colombier 			/* exponential backoff of requests */
922*3cbadd90SDavid du Colombier 			if((1<<p->nx) > qp->ndest)
9237dd7cddfSDavid du Colombier 				continue;
9247dd7cddfSDavid du Colombier 
925*3cbadd90SDavid du Colombier 			procsetname("udp %sside query to %I/%s %s %s",
926*3cbadd90SDavid du Colombier 				(inns? "in": "out"), p->a, p->s->name,
927*3cbadd90SDavid du Colombier 				qp->dp->name, rrname(qp->type, buf, sizeof buf));
928219b2ee8SDavid du Colombier 			if(debug)
929*3cbadd90SDavid du Colombier 				logsend(qp->req->id, depth, p->a, p->s->name,
930*3cbadd90SDavid du Colombier 					qp->dp->name, qp->type);
9314f8f669cSDavid du Colombier 
932*3cbadd90SDavid du Colombier 			/* fill in UDP destination addr & send it */
933*3cbadd90SDavid du Colombier 			memmove(obuf, p->a, sizeof p->a);
934*3cbadd90SDavid du Colombier 			mydnsquery(qp, medium, obuf, len);
9357dd7cddfSDavid du Colombier 			p->nx++;
9363e12c5d1SDavid du Colombier 		}
937*3cbadd90SDavid du Colombier 	if(j == 0) {
938*3cbadd90SDavid du Colombier 		// dnslog("xmitquery: %s: no destinations left", qp->dp->name);
939*3cbadd90SDavid du Colombier 		return -1;
940*3cbadd90SDavid du Colombier 	}
941*3cbadd90SDavid du Colombier 	return 0;
942*3cbadd90SDavid du Colombier }
9433e12c5d1SDavid du Colombier 
944*3cbadd90SDavid du Colombier static int
945*3cbadd90SDavid du Colombier procansw(Query *qp, DNSmsg *mp, uchar *srcip, int depth, Dest *p)
946*3cbadd90SDavid du Colombier {
947*3cbadd90SDavid du Colombier 	int rv;
948*3cbadd90SDavid du Colombier 	char buf[32];
949*3cbadd90SDavid du Colombier 	DN *ndp;
950*3cbadd90SDavid du Colombier 	Query nquery;
951*3cbadd90SDavid du Colombier 	RR *tp, *soarr;
9527dd7cddfSDavid du Colombier 
95359cc4ca5SDavid du Colombier 	/* ignore any error replies */
954*3cbadd90SDavid du Colombier 	if((mp->flags & Rmask) == Rserver){
955*3cbadd90SDavid du Colombier 		rrfreelist(mp->qd);
956*3cbadd90SDavid du Colombier 		rrfreelist(mp->an);
957*3cbadd90SDavid du Colombier 		rrfreelist(mp->ar);
958*3cbadd90SDavid du Colombier 		rrfreelist(mp->ns);
959*3cbadd90SDavid du Colombier 		if(p != qp->curdest)
9607dd7cddfSDavid du Colombier 			p->code = Rserver;
961*3cbadd90SDavid du Colombier 		return -1;
9623e12c5d1SDavid du Colombier 	}
9633e12c5d1SDavid du Colombier 
96459cc4ca5SDavid du Colombier 	/* ignore any bad delegations */
965*3cbadd90SDavid du Colombier 	if(mp->ns && baddelegation(mp->ns, qp->nsrp, srcip)){
966*3cbadd90SDavid du Colombier 		rrfreelist(mp->ns);
967*3cbadd90SDavid du Colombier 		mp->ns = nil;
968*3cbadd90SDavid du Colombier 		if(mp->an == nil){
969*3cbadd90SDavid du Colombier 			rrfreelist(mp->qd);
970*3cbadd90SDavid du Colombier 			rrfreelist(mp->ar);
971*3cbadd90SDavid du Colombier 			if(p != qp->curdest)
97259cc4ca5SDavid du Colombier 				p->code = Rserver;
973*3cbadd90SDavid du Colombier 			return -1;
97459cc4ca5SDavid du Colombier 		}
97559cc4ca5SDavid du Colombier 	}
97659cc4ca5SDavid du Colombier 
9777dd7cddfSDavid du Colombier 	/* remove any soa's from the authority section */
978*3cbadd90SDavid du Colombier 	soarr = rrremtype(&mp->ns, Tsoa);
9797dd7cddfSDavid du Colombier 
9803e12c5d1SDavid du Colombier 	/* incorporate answers */
981*3cbadd90SDavid du Colombier 	if(mp->an)
982*3cbadd90SDavid du Colombier 		rrattach(mp->an, (mp->flags & Fauth) != 0);
983*3cbadd90SDavid du Colombier 	if(mp->ar)
984*3cbadd90SDavid du Colombier 		rrattach(mp->ar, 0);
985*3cbadd90SDavid du Colombier 	if(mp->ns){
986*3cbadd90SDavid du Colombier 		ndp = mp->ns->owner;
987*3cbadd90SDavid du Colombier 		rrattach(mp->ns, 0);
9887dd7cddfSDavid du Colombier 	} else
9894f8f669cSDavid du Colombier 		ndp = nil;
9907dd7cddfSDavid du Colombier 
9917dd7cddfSDavid du Colombier 	/* free the question */
992*3cbadd90SDavid du Colombier 	if(mp->qd)
993*3cbadd90SDavid du Colombier 		rrfreelist(mp->qd);
9943e12c5d1SDavid du Colombier 
9953e12c5d1SDavid du Colombier 	/*
9963e12c5d1SDavid du Colombier 	 *  Any reply from an authoritative server,
9973e12c5d1SDavid du Colombier 	 *  or a positive reply terminates the search
9983e12c5d1SDavid du Colombier 	 */
999*3cbadd90SDavid du Colombier 	if(mp->an != nil || (mp->flags & Fauth)){
1000*3cbadd90SDavid du Colombier 		if(mp->an == nil && (mp->flags & Rmask) == Rname)
1001*3cbadd90SDavid du Colombier 			qp->dp->respcode = Rname;
10027dd7cddfSDavid du Colombier 		else
1003*3cbadd90SDavid du Colombier 			qp->dp->respcode = 0;
10047dd7cddfSDavid du Colombier 
10057dd7cddfSDavid du Colombier 		/*
10067dd7cddfSDavid du Colombier 		 *  cache any negative responses, free soarr
10077dd7cddfSDavid du Colombier 		 */
1008*3cbadd90SDavid du Colombier 		if((mp->flags & Fauth) && mp->an == nil)
1009*3cbadd90SDavid du Colombier 			cacheneg(qp->dp, qp->type, (mp->flags & Rmask), soarr);
10107dd7cddfSDavid du Colombier 		else
10117dd7cddfSDavid du Colombier 			rrfreelist(soarr);
10123e12c5d1SDavid du Colombier 		return 1;
10133e12c5d1SDavid du Colombier 	}
10147dd7cddfSDavid du Colombier 	rrfreelist(soarr);
10153e12c5d1SDavid du Colombier 
10163e12c5d1SDavid du Colombier 	/*
10174f8f669cSDavid du Colombier 	 *  if we've been given better name servers,
10184f8f669cSDavid du Colombier 	 *  recurse.  we're called from udpquery, called from
1019*3cbadd90SDavid du Colombier 	 *  netquery, which current holds qp->dp->querylck,
10204f8f669cSDavid du Colombier 	 *  so release it now and acquire it upon return.
10213e12c5d1SDavid du Colombier 	 */
1022*3cbadd90SDavid du Colombier 	if(!mp->ns)
1023*3cbadd90SDavid du Colombier 		return 0;
10247dd7cddfSDavid du Colombier 	tp = rrlookup(ndp, Tns, NOneg);
1025*3cbadd90SDavid du Colombier 	if(contains(qp->nsrp, tp)){
10267dd7cddfSDavid du Colombier 		rrfreelist(tp);
1027*3cbadd90SDavid du Colombier 		return 0;
10283e12c5d1SDavid du Colombier 	}
1029*3cbadd90SDavid du Colombier 	procsetname("recursive query for %s %s", qp->dp->name,
1030*3cbadd90SDavid du Colombier 		rrname(qp->type, buf, sizeof buf));
1031*3cbadd90SDavid du Colombier 	qunlock(&qp->dp->querylck);
1032*3cbadd90SDavid du Colombier 
1033*3cbadd90SDavid du Colombier 	queryinit(&nquery, qp->dp, qp->type, qp->req);
1034*3cbadd90SDavid du Colombier 	nquery.nsrp = tp;
1035*3cbadd90SDavid du Colombier 	rv = netquery(&nquery, depth+1);
1036*3cbadd90SDavid du Colombier 
1037*3cbadd90SDavid du Colombier 	qlock(&qp->dp->querylck);
1038*3cbadd90SDavid du Colombier 	rrfreelist(tp);
1039*3cbadd90SDavid du Colombier 	memset(&nquery, 0, sizeof nquery); /* prevent accidents */
1040*3cbadd90SDavid du Colombier 	return rv;
1041*3cbadd90SDavid du Colombier }
1042*3cbadd90SDavid du Colombier 
1043*3cbadd90SDavid du Colombier /*
1044*3cbadd90SDavid du Colombier  * send a query via tcp to a single address (from ibuf's udp header)
1045*3cbadd90SDavid du Colombier  * and read the answer(s) into mp->an.
1046*3cbadd90SDavid du Colombier  */
1047*3cbadd90SDavid du Colombier static int
1048*3cbadd90SDavid du Colombier tcpquery(Query *qp, DNSmsg *mp, int depth, uchar *ibuf, uchar *obuf, int len,
1049*3cbadd90SDavid du Colombier 	int waitsecs, int inns, ushort req)
1050*3cbadd90SDavid du Colombier {
1051*3cbadd90SDavid du Colombier 	ulong endtime;
1052*3cbadd90SDavid du Colombier 
1053*3cbadd90SDavid du Colombier 	endtime = time(nil) + waitsecs;
1054*3cbadd90SDavid du Colombier 	if(endtime > qp->req->aborttime)
1055*3cbadd90SDavid du Colombier 		endtime = qp->req->aborttime;
1056*3cbadd90SDavid du Colombier 
1057*3cbadd90SDavid du Colombier 	qlock(&qp->tcplock);
1058*3cbadd90SDavid du Colombier 	memmove(obuf, ibuf, IPaddrlen);		/* send back to respondent */
1059*3cbadd90SDavid du Colombier 	/* sets qp->tcpip from obuf's udp header */
1060*3cbadd90SDavid du Colombier 	if (xmitquery(qp, Tcp, depth, obuf, inns, len) < 0) {
1061*3cbadd90SDavid du Colombier 		qunlock(&qp->tcplock);
1062*3cbadd90SDavid du Colombier 		return -1;
1063*3cbadd90SDavid du Colombier 	}
1064*3cbadd90SDavid du Colombier 	dnslog("%s: udp reply truncated; retrying query via tcp to %I",
1065*3cbadd90SDavid du Colombier 		qp->dp->name, qp->tcpip);
1066*3cbadd90SDavid du Colombier 	if (readreply(qp, Tcp, req, ibuf, mp, endtime) < 0) {
1067*3cbadd90SDavid du Colombier 		qunlock(&qp->tcplock);
1068*3cbadd90SDavid du Colombier 		return -1;
1069*3cbadd90SDavid du Colombier 	}
1070*3cbadd90SDavid du Colombier 	if (qp->tcpfd > 0) {
1071*3cbadd90SDavid du Colombier 		hangup(qp->tcpctlfd);
1072*3cbadd90SDavid du Colombier 		close(qp->tcpctlfd);
1073*3cbadd90SDavid du Colombier 		close(qp->tcpfd);
1074*3cbadd90SDavid du Colombier 	}
1075*3cbadd90SDavid du Colombier 	qp->tcpfd = qp->tcpctlfd = -1;
1076*3cbadd90SDavid du Colombier 	qunlock(&qp->tcplock);
1077*3cbadd90SDavid du Colombier 
1078*3cbadd90SDavid du Colombier //	dnslog("%s: %s answer by tcp", qp->dp->name,
1079*3cbadd90SDavid du Colombier //		(mp->an? "got": "didn't get"));
1080*3cbadd90SDavid du Colombier 	return 0;
1081*3cbadd90SDavid du Colombier }
1082*3cbadd90SDavid du Colombier 
1083*3cbadd90SDavid du Colombier /*
1084*3cbadd90SDavid du Colombier  *  query name servers.  If the name server returns a pointer to another
1085*3cbadd90SDavid du Colombier  *  name server, recurse.
1086*3cbadd90SDavid du Colombier  */
1087*3cbadd90SDavid du Colombier static int
1088*3cbadd90SDavid du Colombier netquery1(Query *qp, int depth, uchar *ibuf, uchar *obuf, int waitsecs, int inns)
1089*3cbadd90SDavid du Colombier {
1090*3cbadd90SDavid du Colombier 	int ndest, len, replywaits, rv;
1091*3cbadd90SDavid du Colombier 	ushort req;
1092*3cbadd90SDavid du Colombier 	ulong endtime;
1093*3cbadd90SDavid du Colombier 	char buf[12];
1094*3cbadd90SDavid du Colombier 	uchar srcip[IPaddrlen];
1095*3cbadd90SDavid du Colombier 	DNSmsg m;
1096*3cbadd90SDavid du Colombier 	Dest *p, *np;
1097*3cbadd90SDavid du Colombier 	Dest dest[Maxdest];
1098*3cbadd90SDavid du Colombier 
1099*3cbadd90SDavid du Colombier 	/* pack request into a udp message */
1100*3cbadd90SDavid du Colombier 	req = rand();
1101*3cbadd90SDavid du Colombier 	len = mkreq(qp->dp, qp->type, obuf, Frecurse|Oquery, req);
1102*3cbadd90SDavid du Colombier 
1103*3cbadd90SDavid du Colombier 	/* no server addresses yet */
1104*3cbadd90SDavid du Colombier 	queryck(qp);
1105*3cbadd90SDavid du Colombier 	for (p = dest; p < dest + nelem(dest); p++)
1106*3cbadd90SDavid du Colombier 		destinit(p);
1107*3cbadd90SDavid du Colombier 	qp->curdest = qp->dest = dest;
1108*3cbadd90SDavid du Colombier 
1109*3cbadd90SDavid du Colombier 	/*
1110*3cbadd90SDavid du Colombier 	 *  transmit udp requests and wait for answers.
1111*3cbadd90SDavid du Colombier 	 *  at most Maxtrans attempts to each address.
1112*3cbadd90SDavid du Colombier 	 *  each cycle send one more message than the previous.
1113*3cbadd90SDavid du Colombier 	 *  retry a query via tcp if its response is truncated.
1114*3cbadd90SDavid du Colombier 	 */
1115*3cbadd90SDavid du Colombier 	for(ndest = 1; ndest < Maxdest; ndest++){
1116*3cbadd90SDavid du Colombier 		qp->ndest = ndest;
1117*3cbadd90SDavid du Colombier 		qp->tcpset = 0;
1118*3cbadd90SDavid du Colombier 		if (xmitquery(qp, Udp, depth, obuf, inns, len) < 0)
1119*3cbadd90SDavid du Colombier 			break;
1120*3cbadd90SDavid du Colombier 
1121*3cbadd90SDavid du Colombier 		endtime = time(nil) + waitsecs;
1122*3cbadd90SDavid du Colombier 		if(endtime > qp->req->aborttime)
1123*3cbadd90SDavid du Colombier 			endtime = qp->req->aborttime;
1124*3cbadd90SDavid du Colombier 
1125*3cbadd90SDavid du Colombier 		for(replywaits = 0; replywaits < ndest; replywaits++){
1126*3cbadd90SDavid du Colombier 			procsetname("reading %sside reply from %s%I for %s %s",
1127*3cbadd90SDavid du Colombier 				(inns? "in": "out"),
1128*3cbadd90SDavid du Colombier 				(isaslug(qp->tcpip)? "sluggard ": ""), obuf,
1129*3cbadd90SDavid du Colombier 				qp->dp->name, rrname(qp->type, buf, sizeof buf));
1130*3cbadd90SDavid du Colombier 
1131*3cbadd90SDavid du Colombier 			/* read udp answer */
1132*3cbadd90SDavid du Colombier 			if (readreply(qp, Udp, req, ibuf, &m, endtime) >= 0)
1133*3cbadd90SDavid du Colombier 				memmove(srcip, ibuf, IPaddrlen);
1134*3cbadd90SDavid du Colombier 			else if (!(m.flags & Ftrunc)) {
1135*3cbadd90SDavid du Colombier 				addslug(ibuf);
1136*3cbadd90SDavid du Colombier 				break;		/* timed out on this dest */
1137*3cbadd90SDavid du Colombier 			} else {
1138*3cbadd90SDavid du Colombier 				/* whoops, it was truncated! ask again via tcp */
1139*3cbadd90SDavid du Colombier 				rv = tcpquery(qp, &m, depth, ibuf, obuf, len,
1140*3cbadd90SDavid du Colombier 					waitsecs, inns, req);
1141*3cbadd90SDavid du Colombier 				if (rv < 0)
1142*3cbadd90SDavid du Colombier 					break;		/* failed via tcp too */
1143*3cbadd90SDavid du Colombier 				memmove(srcip, qp->tcpip, IPaddrlen);
1144*3cbadd90SDavid du Colombier 			}
1145*3cbadd90SDavid du Colombier 
1146*3cbadd90SDavid du Colombier 			/* find responder */
1147*3cbadd90SDavid du Colombier 			// dnslog("netquery1 got reply from %I", srcip);
1148*3cbadd90SDavid du Colombier 			for(p = qp->dest; p < qp->curdest; p++)
1149*3cbadd90SDavid du Colombier 				if(memcmp(p->a, srcip, sizeof p->a) == 0)
1150*3cbadd90SDavid du Colombier 					break;
1151*3cbadd90SDavid du Colombier 
1152*3cbadd90SDavid du Colombier 			/* remove all addrs of responding server from list */
1153*3cbadd90SDavid du Colombier 			for(np = qp->dest; np < qp->curdest; np++)
1154*3cbadd90SDavid du Colombier 				if(np->s == p->s)
1155*3cbadd90SDavid du Colombier 					p->nx = Maxtrans;
1156*3cbadd90SDavid du Colombier 
1157*3cbadd90SDavid du Colombier 			rv = procansw(qp, &m, srcip, depth, p);
1158*3cbadd90SDavid du Colombier 			if (rv > 0)
1159*3cbadd90SDavid du Colombier 				return rv;
11603e12c5d1SDavid du Colombier 		}
11613e12c5d1SDavid du Colombier 	}
11627dd7cddfSDavid du Colombier 
11634f8f669cSDavid du Colombier 	/* if all servers returned failure, propagate it */
1164*3cbadd90SDavid du Colombier 	qp->dp->respcode = Rserver;
1165*3cbadd90SDavid du Colombier 	for(p = dest; p < qp->curdest; p++) {
1166*3cbadd90SDavid du Colombier 		destck(p);
11677dd7cddfSDavid du Colombier 		if(p->code != Rserver)
1168*3cbadd90SDavid du Colombier 			qp->dp->respcode = 0;
1169*3cbadd90SDavid du Colombier 		p->magic = 0;			/* prevent accidents */
1170*3cbadd90SDavid du Colombier 	}
11714f8f669cSDavid du Colombier 
1172*3cbadd90SDavid du Colombier //	if (qp->dp->respcode)
1173*3cbadd90SDavid du Colombier //		dnslog("netquery1 setting Rserver for %s", qp->dp->name);
11747dd7cddfSDavid du Colombier 
1175*3cbadd90SDavid du Colombier 	qp->dest = qp->curdest = nil;		/* prevent accidents */
11763e12c5d1SDavid du Colombier 	return 0;
11773e12c5d1SDavid du Colombier }
11787dd7cddfSDavid du Colombier 
11794f8f669cSDavid du Colombier /*
11804f8f669cSDavid du Colombier  *  run a command with a supplied fd as standard input
11814f8f669cSDavid du Colombier  */
11824f8f669cSDavid du Colombier char *
11834f8f669cSDavid du Colombier system(int fd, char *cmd)
118428a8a86bSDavid du Colombier {
11854f8f669cSDavid du Colombier 	int pid, p, i;
11864f8f669cSDavid du Colombier 	static Waitmsg msg;
11876b0d5c8bSDavid du Colombier 
11884f8f669cSDavid du Colombier 	if((pid = fork()) == -1)
11894f8f669cSDavid du Colombier 		sysfatal("fork failed: %r");
11904f8f669cSDavid du Colombier 	else if(pid == 0){
11914f8f669cSDavid du Colombier 		dup(fd, 0);
11924f8f669cSDavid du Colombier 		close(fd);
11934f8f669cSDavid du Colombier 		for (i = 3; i < 200; i++)
11944f8f669cSDavid du Colombier 			close(i);		/* don't leak fds */
11954f8f669cSDavid du Colombier 		execl("/bin/rc", "rc", "-c", cmd, nil);
11964f8f669cSDavid du Colombier 		sysfatal("exec rc: %r");
11974f8f669cSDavid du Colombier 	}
11984f8f669cSDavid du Colombier 	for(p = waitpid(); p >= 0; p = waitpid())
11994f8f669cSDavid du Colombier 		if(p == pid)
12004f8f669cSDavid du Colombier 			return msg.msg;
12014f8f669cSDavid du Colombier 	return "lost child";
12024f8f669cSDavid du Colombier }
12034f8f669cSDavid du Colombier 
12044f8f669cSDavid du Colombier enum { Hurry, Patient, };
12054f8f669cSDavid du Colombier enum { Outns, Inns, };
12064f8f669cSDavid du Colombier enum { Remntretry = 15, };	/* min. sec.s between remount attempts */
12074f8f669cSDavid du Colombier 
12084f8f669cSDavid du Colombier static int
1209*3cbadd90SDavid du Colombier udpquery(Query *qp, char *mntpt, int depth, int patient, int inns)
12104f8f669cSDavid du Colombier {
12114f8f669cSDavid du Colombier 	int fd, rv = 0;
12124f8f669cSDavid du Colombier 	long now;
12134f8f669cSDavid du Colombier 	char *msg;
12144f8f669cSDavid du Colombier 	uchar *obuf, *ibuf;
12154f8f669cSDavid du Colombier 	static QLock mntlck;
12164f8f669cSDavid du Colombier 	static ulong lastmount;
12177dd7cddfSDavid du Colombier 
12187dd7cddfSDavid du Colombier 	/* use alloced buffers rather than ones from the stack */
1219*3cbadd90SDavid du Colombier 	// ibuf = emalloc(Maxudpin+OUdphdrsize);
1220*3cbadd90SDavid du Colombier 	ibuf = emalloc(64*1024);		/* max. tcp reply size */
1221d9dc5dd1SDavid du Colombier 	obuf = emalloc(Maxudp+OUdphdrsize);
12227dd7cddfSDavid du Colombier 
12234f8f669cSDavid du Colombier 	fd = udpport(mntpt);
12244f8f669cSDavid du Colombier 	while (fd < 0 && cfg.straddle && strcmp(mntpt, "/net.alt") == 0) {
12254f8f669cSDavid du Colombier 		/* HACK: remount /net.alt */
12264f8f669cSDavid du Colombier 		now = time(nil);
12274f8f669cSDavid du Colombier 		if (now < lastmount + Remntretry)
12284f8f669cSDavid du Colombier 			sleep((lastmount + Remntretry - now)*1000);
12294f8f669cSDavid du Colombier 		qlock(&mntlck);
12304f8f669cSDavid du Colombier 		fd = udpport(mntpt);	/* try again under lock */
12314f8f669cSDavid du Colombier 		if (fd < 0) {
12324f8f669cSDavid du Colombier 			dnslog("[%d] remounting /net.alt", getpid());
12334f8f669cSDavid du Colombier 			unmount(nil, "/net.alt");
12344f8f669cSDavid du Colombier 
12354f8f669cSDavid du Colombier 			msg = system(open("/dev/null", ORDWR), "outside");
12364f8f669cSDavid du Colombier 
12374f8f669cSDavid du Colombier 			lastmount = time(nil);
12384f8f669cSDavid du Colombier 			if (msg && *msg) {
12394f8f669cSDavid du Colombier 				dnslog("[%d] can't remount /net.alt: %s",
12404f8f669cSDavid du Colombier 					getpid(), msg);
12414f8f669cSDavid du Colombier 				sleep(10*1000);		/* don't spin wildly */
12424f8f669cSDavid du Colombier 			} else
12434f8f669cSDavid du Colombier 				fd = udpport(mntpt);
12444f8f669cSDavid du Colombier 		}
12454f8f669cSDavid du Colombier 		qunlock(&mntlck);
12464f8f669cSDavid du Colombier 	}
12474f8f669cSDavid du Colombier 	if(fd >= 0) {
1248*3cbadd90SDavid du Colombier 		qp->req->aborttime = time(nil) + (patient? Maxreqtm: Maxreqtm/2);
1249d6d99297SDavid du Colombier 		/* tune; was (patient? 15: 10) */
1250*3cbadd90SDavid du Colombier 		qp->udpfd = fd;
1251*3cbadd90SDavid du Colombier 		rv = netquery1(qp, depth, ibuf, obuf, (patient? 10: 5), inns);
12524f8f669cSDavid du Colombier 		close(fd);
12534f8f669cSDavid du Colombier 	} else
12544f8f669cSDavid du Colombier 		dnslog("can't get udpport for %s query of name %s: %r",
1255*3cbadd90SDavid du Colombier 			mntpt, qp->dp->name);
12564f8f669cSDavid du Colombier 
12574f8f669cSDavid du Colombier 	free(obuf);
12584f8f669cSDavid du Colombier 	free(ibuf);
12594f8f669cSDavid du Colombier 	return rv;
12604f8f669cSDavid du Colombier }
12614f8f669cSDavid du Colombier 
12624f8f669cSDavid du Colombier /* look up (dp->name,type) via *nsrp with results in *reqp */
12634f8f669cSDavid du Colombier static int
1264*3cbadd90SDavid du Colombier netquery(Query *qp, int depth)
12654f8f669cSDavid du Colombier {
12664f8f669cSDavid du Colombier 	int lock, rv, triedin, inname;
12674f8f669cSDavid du Colombier 	RR *rp;
12684f8f669cSDavid du Colombier 
12694f8f669cSDavid du Colombier 	if(depth > 12)			/* in a recursive loop? */
12704f8f669cSDavid du Colombier 		return 0;
12714f8f669cSDavid du Colombier 
1272*3cbadd90SDavid du Colombier 	slave(qp->req);
1273d3907fe5SDavid du Colombier 	/*
1274d3907fe5SDavid du Colombier 	 * slave might have forked.  if so, the parent process longjmped to
1275d3907fe5SDavid du Colombier 	 * req->mret; we're usually the child slave, but if there are too
1276d3907fe5SDavid du Colombier 	 * many children already, we're still the same process.
1277d3907fe5SDavid du Colombier 	 */
12784f8f669cSDavid du Colombier 
12794f8f669cSDavid du Colombier 	/* don't lock before call to slave so only children can block */
1280*3cbadd90SDavid du Colombier 	lock = qp->req->isslave != 0;
12814f8f669cSDavid du Colombier 	if(lock) {
1282*3cbadd90SDavid du Colombier 		procsetname("query lock wait for %s", qp->dp->name);
12834f8f669cSDavid du Colombier 		/* don't make concurrent queries for this name */
1284*3cbadd90SDavid du Colombier 		qlock(&qp->dp->querylck);
1285*3cbadd90SDavid du Colombier 		procsetname("netquery: %s", qp->dp->name);
12864f8f669cSDavid du Colombier 	}
12876b0d5c8bSDavid du Colombier 
12886b0d5c8bSDavid du Colombier 	/* prepare server RR's for incremental lookup */
1289*3cbadd90SDavid du Colombier 	for(rp = qp->nsrp; rp; rp = rp->next)
12906b0d5c8bSDavid du Colombier 		rp->marker = 0;
12916b0d5c8bSDavid du Colombier 
12924f8f669cSDavid du Colombier 	rv = 0;				/* pessimism */
12934f8f669cSDavid du Colombier 	triedin = 0;
1294*3cbadd90SDavid du Colombier 	qp->nsrp = qp->nsrp;
12954f8f669cSDavid du Colombier 	/*
12964f8f669cSDavid du Colombier 	 * normal resolvers and servers will just use mntpt for all addresses,
12974f8f669cSDavid du Colombier 	 * even on the outside.  straddling servers will use mntpt (/net)
12984f8f669cSDavid du Colombier 	 * for inside addresses and /net.alt for outside addresses,
12994f8f669cSDavid du Colombier 	 * thus bypassing other inside nameservers.
13004f8f669cSDavid du Colombier 	 */
1301*3cbadd90SDavid du Colombier 	inname = insideaddr(qp->dp->name);
13024f8f669cSDavid du Colombier 	if (!cfg.straddle || inname) {
1303*3cbadd90SDavid du Colombier 		rv = udpquery(qp, mntpt, depth, Hurry, (cfg.inside? Inns: Outns));
13044f8f669cSDavid du Colombier 		triedin = 1;
13054f8f669cSDavid du Colombier 	}
13064f8f669cSDavid du Colombier 
13074f8f669cSDavid du Colombier 	/*
13084f8f669cSDavid du Colombier 	 * if we're still looking, are inside, and have an outside domain,
13094f8f669cSDavid du Colombier 	 * try it on our outside interface, if any.
13104f8f669cSDavid du Colombier 	 */
13114f8f669cSDavid du Colombier 	if (rv == 0 && cfg.inside && !inname) {
13124f8f669cSDavid du Colombier 		if (triedin)
13134f8f669cSDavid du Colombier 			dnslog(
13144f8f669cSDavid du Colombier 	   "[%d] netquery: internal nameservers failed for %s; trying external",
1315*3cbadd90SDavid du Colombier 				getpid(), qp->dp->name);
13164f8f669cSDavid du Colombier 
13174f8f669cSDavid du Colombier 		/* prepare server RR's for incremental lookup */
1318*3cbadd90SDavid du Colombier 		for(rp = qp->nsrp; rp; rp = rp->next)
13194f8f669cSDavid du Colombier 			rp->marker = 0;
13204f8f669cSDavid du Colombier 
1321*3cbadd90SDavid du Colombier 		rv = udpquery(qp, "/net.alt", depth, Patient, Outns);
13224f8f669cSDavid du Colombier 	}
1323*3cbadd90SDavid du Colombier //	if (rv == 0)		/* could ask /net.alt/dns directly */
1324*3cbadd90SDavid du Colombier //		askoutdns(qp->dp, qp->type);
13254f8f669cSDavid du Colombier 
1326*3cbadd90SDavid du Colombier 	if(lock) {
1327*3cbadd90SDavid du Colombier 		qunlock(&qp->dp->querylck);
1328*3cbadd90SDavid du Colombier 	}
13297dd7cddfSDavid du Colombier 	return rv;
13307dd7cddfSDavid du Colombier }
13314f8f669cSDavid du Colombier 
13324f8f669cSDavid du Colombier int
13334f8f669cSDavid du Colombier seerootns(void)
13344f8f669cSDavid du Colombier {
1335*3cbadd90SDavid du Colombier 	int rv;
13364f8f669cSDavid du Colombier 	char root[] = "";
13374f8f669cSDavid du Colombier 	Request req;
1338*3cbadd90SDavid du Colombier 	Query query;
13394f8f669cSDavid du Colombier 
13404f8f669cSDavid du Colombier 	memset(&req, 0, sizeof req);
13414f8f669cSDavid du Colombier 	req.isslave = 1;
13424f8f669cSDavid du Colombier 	req.aborttime = now + Maxreqtm*2;	/* be patient */
1343*3cbadd90SDavid du Colombier 	queryinit(&query, dnlookup(root, Cin, 1), Tns, &req);
1344*3cbadd90SDavid du Colombier 	query.nsrp = dblookup(root, Cin, Tns, 0, 0);
1345*3cbadd90SDavid du Colombier 	rv = netquery(&query, 0);
1346*3cbadd90SDavid du Colombier 	memset(&query, 0, sizeof query);	/* prevent accidents */
1347*3cbadd90SDavid du Colombier 	return rv;
13484f8f669cSDavid du Colombier }
1349