xref: /plan9-contrib/sys/src/cmd/ndb/dnresolve.c (revision 4e7b95445ffebb86c0c2ffea88eb978bd2b5395c)
13cbadd90SDavid du Colombier /*
23cbadd90SDavid du Colombier  * domain name resolvers, see rfcs 1035 and 1123
33cbadd90SDavid du Colombier  */
43e12c5d1SDavid du Colombier #include <u.h>
53e12c5d1SDavid du Colombier #include <libc.h>
64f8f669cSDavid du Colombier #include <ip.h>
74f8f669cSDavid du Colombier #include <bio.h>
84f8f669cSDavid du Colombier #include <ndb.h>
93e12c5d1SDavid du Colombier #include "dns.h"
103e12c5d1SDavid du Colombier 
113cbadd90SDavid du Colombier typedef struct Dest Dest;
123cbadd90SDavid du Colombier typedef struct Ipaddr Ipaddr;
133cbadd90SDavid du Colombier typedef struct Query Query;
143cbadd90SDavid du Colombier typedef struct Sluggards Sluggards;
153cbadd90SDavid du Colombier 
163e12c5d1SDavid du Colombier enum
173e12c5d1SDavid du Colombier {
183cbadd90SDavid 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 */
213cbadd90SDavid du Colombier 	Destmagic=	0xcafebabe,
223cbadd90SDavid du Colombier 	Querymagic=	0xdeadbeef,
233e12c5d1SDavid du Colombier };
243e12c5d1SDavid du Colombier 
253cbadd90SDavid du Colombier struct Ipaddr {
263cbadd90SDavid du Colombier 	Ipaddr *next;
273cbadd90SDavid du Colombier 	uchar	ip[IPaddrlen];
283cbadd90SDavid du Colombier };
293cbadd90SDavid du Colombier 
303cbadd90SDavid du Colombier struct Dest
313cbadd90SDavid du Colombier {
323cbadd90SDavid du Colombier 	uchar	a[IPaddrlen];	/* ip address */
333cbadd90SDavid du Colombier 	DN	*s;		/* name server */
343cbadd90SDavid du Colombier 	int	nx;		/* number of transmissions */
353cbadd90SDavid du Colombier 	int	code;		/* response code; used to clear dp->respcode */
363cbadd90SDavid du Colombier 
373cbadd90SDavid du Colombier 	ulong	magic;
383cbadd90SDavid du Colombier };
393cbadd90SDavid du Colombier 
403cbadd90SDavid du Colombier struct Query {
413cbadd90SDavid du Colombier 	DN	*dp;		/* domain */
423cbadd90SDavid du Colombier 	int	type;		/* and type to look up */
433cbadd90SDavid du Colombier 	Request *req;
443cbadd90SDavid du Colombier 	RR	*nsrp;		/* name servers to consult */
453cbadd90SDavid du Colombier 
463cbadd90SDavid du Colombier 	Dest	*dest;		/* array of destinations */
473cbadd90SDavid du Colombier 	Dest	*curdest;	/* pointer to one of them */
483cbadd90SDavid du Colombier 	int	ndest;
493cbadd90SDavid du Colombier 
503cbadd90SDavid du Colombier 	int	udpfd;		/* can be shared by all udp users */
513cbadd90SDavid du Colombier 
523cbadd90SDavid du Colombier 	QLock	tcplock;	/* only one tcp call at a time per query */
533cbadd90SDavid du Colombier 	int	tcpset;
543cbadd90SDavid du Colombier 	int	tcpfd;		/* if Tcp, read replies from here */
553cbadd90SDavid du Colombier 	int	tcpctlfd;
563cbadd90SDavid du Colombier 	uchar	tcpip[IPaddrlen];
573cbadd90SDavid du Colombier 
583cbadd90SDavid du Colombier 	ulong	magic;
593cbadd90SDavid du Colombier };
603cbadd90SDavid du Colombier 
613cbadd90SDavid du Colombier /* a list of sluggardly name servers */
623cbadd90SDavid du Colombier struct Sluggards {
633cbadd90SDavid du Colombier 	QLock;
643cbadd90SDavid du Colombier 	Ipaddr *head;
653cbadd90SDavid du Colombier 	Ipaddr *tail;
663cbadd90SDavid du Colombier };
673cbadd90SDavid du Colombier 
683cbadd90SDavid du Colombier static Sluggards slugs;
693cbadd90SDavid du Colombier 
707dd7cddfSDavid du Colombier static RR*	dnresolve1(char*, int, int, Request*, int, int);
713cbadd90SDavid du Colombier static int	netquery(Query *, int);
723cbadd90SDavid du Colombier 
733cbadd90SDavid du Colombier static Ipaddr *
743cbadd90SDavid du Colombier newslug(void)
753cbadd90SDavid du Colombier {
763cbadd90SDavid du Colombier 	return emalloc(sizeof(Ipaddr));
773cbadd90SDavid du Colombier }
783cbadd90SDavid du Colombier 
793cbadd90SDavid du Colombier static void
803cbadd90SDavid du Colombier addslug(uchar nsip[])
813cbadd90SDavid du Colombier {
823cbadd90SDavid du Colombier 	Ipaddr *sp;
833cbadd90SDavid du Colombier 	static uchar zip[IPaddrlen];
843cbadd90SDavid du Colombier 
853cbadd90SDavid du Colombier 	if (memcmp(nsip, zip, IPaddrlen) == 0)
863cbadd90SDavid du Colombier 		return;
873cbadd90SDavid du Colombier 
883cbadd90SDavid du Colombier 	qlock(&slugs);
893cbadd90SDavid du Colombier 	for (sp = slugs.head; sp != nil; sp = sp->next)
903cbadd90SDavid du Colombier 		if (memcmp(sp->ip, nsip, IPaddrlen) == 0) {
913cbadd90SDavid du Colombier 			qunlock(&slugs);		/* already know it */
923cbadd90SDavid du Colombier 			return;
933cbadd90SDavid du Colombier 		}
943cbadd90SDavid du Colombier 
953cbadd90SDavid du Colombier 	if (slugs.head == nil)
963cbadd90SDavid du Colombier 		slugs.head = slugs.tail = newslug();
973cbadd90SDavid du Colombier 	else {
983cbadd90SDavid du Colombier 		slugs.tail->next = newslug();
993cbadd90SDavid du Colombier 		slugs.tail = slugs.tail->next;
1003cbadd90SDavid du Colombier 	}
1013cbadd90SDavid du Colombier 	memmove(slugs.tail->ip, nsip, IPaddrlen);
1023cbadd90SDavid du Colombier 	qunlock(&slugs);
1033cbadd90SDavid du Colombier 
1043cbadd90SDavid du Colombier 	dnslog("%I is a slug", nsip);
1053cbadd90SDavid du Colombier }
1063cbadd90SDavid du Colombier 
1073cbadd90SDavid du Colombier int
1083cbadd90SDavid du Colombier isaslug(uchar nsip[])
1093cbadd90SDavid du Colombier {
1103cbadd90SDavid du Colombier 	Ipaddr *sp;
1113cbadd90SDavid du Colombier 
1123cbadd90SDavid du Colombier 	qlock(&slugs);
1133cbadd90SDavid du Colombier 	for (sp = slugs.head; sp != nil; sp = sp->next)
1143cbadd90SDavid du Colombier 		if (memcmp(sp->ip, nsip, IPaddrlen) == 0) {
1153cbadd90SDavid du Colombier 			qunlock(&slugs);
1163cbadd90SDavid du Colombier 			return 1;
1173cbadd90SDavid du Colombier 		}
1183cbadd90SDavid du Colombier 	qunlock(&slugs);
1193cbadd90SDavid du Colombier 	return 0;
1203cbadd90SDavid 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 
2313cbadd90SDavid du Colombier static void
2323cbadd90SDavid du Colombier queryinit(Query *qp, DN *dp, int type, Request *req)
2333cbadd90SDavid du Colombier {
2343cbadd90SDavid du Colombier 	memset(qp, 0, sizeof *qp);
235*4e7b9544SDavid du Colombier 	qp->udpfd = qp->tcpfd = qp->tcpctlfd = -1;
2363cbadd90SDavid du Colombier 	qp->dp = dp;
2373cbadd90SDavid du Colombier 	qp->type = type;
2383cbadd90SDavid du Colombier 	qp->req = req;
2393cbadd90SDavid du Colombier 	qp->nsrp = nil;
2403cbadd90SDavid du Colombier 	qp->dest = qp->curdest = nil;
2413cbadd90SDavid du Colombier 	qp->magic = Querymagic;
2423cbadd90SDavid du Colombier }
2433cbadd90SDavid du Colombier 
2443cbadd90SDavid du Colombier static void
2453cbadd90SDavid du Colombier queryck(Query *qp)
2463cbadd90SDavid du Colombier {
2473cbadd90SDavid du Colombier 	assert(qp);
2483cbadd90SDavid du Colombier 	assert(qp->magic == Querymagic);
2493cbadd90SDavid du Colombier }
2503cbadd90SDavid du Colombier 
2513cbadd90SDavid du Colombier static void
252*4e7b9544SDavid du Colombier querydestroy(Query *qp)
253*4e7b9544SDavid du Colombier {
254*4e7b9544SDavid du Colombier 	queryck(qp);
255*4e7b9544SDavid du Colombier 	if (qp->udpfd > 0)
256*4e7b9544SDavid du Colombier 		close(qp->udpfd);
257*4e7b9544SDavid du Colombier 	if (qp->tcpfd > 0)
258*4e7b9544SDavid du Colombier 		close(qp->tcpfd);
259*4e7b9544SDavid du Colombier 	if (qp->tcpctlfd > 0) {
260*4e7b9544SDavid du Colombier 		hangup(qp->tcpctlfd);
261*4e7b9544SDavid du Colombier 		close(qp->tcpctlfd);
262*4e7b9544SDavid du Colombier 	}
263*4e7b9544SDavid du Colombier 	memset(qp, 0, sizeof *qp);	/* prevent accidents */
264*4e7b9544SDavid du Colombier 	qp->udpfd = qp->tcpfd = qp->tcpctlfd = -1;
265*4e7b9544SDavid du Colombier }
266*4e7b9544SDavid du Colombier 
267*4e7b9544SDavid du Colombier static void
2683cbadd90SDavid du Colombier destinit(Dest *p)
2693cbadd90SDavid du Colombier {
2703cbadd90SDavid du Colombier 	memset(p, 0, sizeof *p);
2713cbadd90SDavid du Colombier 	p->magic = Destmagic;
2723cbadd90SDavid du Colombier }
2733cbadd90SDavid du Colombier 
2743cbadd90SDavid du Colombier static void
2753cbadd90SDavid du Colombier destck(Dest *p)
2763cbadd90SDavid du Colombier {
2773cbadd90SDavid du Colombier 	assert(p);
2783cbadd90SDavid du Colombier 	assert(p->magic == Destmagic);
2793cbadd90SDavid du Colombier }
2803cbadd90SDavid du Colombier 
2813e12c5d1SDavid du Colombier static RR*
2824f8f669cSDavid du Colombier dnresolve1(char *name, int class, int type, Request *req, int depth,
2834f8f669cSDavid du Colombier 	int recurse)
2843e12c5d1SDavid du Colombier {
2853e12c5d1SDavid du Colombier 	DN *dp, *nsdp;
2867dd7cddfSDavid du Colombier 	RR *rp, *nsrp, *dbnsrp;
2873e12c5d1SDavid du Colombier 	char *cp;
2883cbadd90SDavid du Colombier 	Query query;
2893e12c5d1SDavid du Colombier 
2907dd7cddfSDavid du Colombier 	if(debug)
2914f8f669cSDavid du Colombier 		dnslog("[%d] dnresolve1 %s %d %d", getpid(), name, type, class);
2927dd7cddfSDavid du Colombier 
2933e12c5d1SDavid du Colombier 	/* only class Cin implemented so far */
2943e12c5d1SDavid du Colombier 	if(class != Cin)
2954f8f669cSDavid du Colombier 		return nil;
2963e12c5d1SDavid du Colombier 
2973e12c5d1SDavid du Colombier 	dp = dnlookup(name, class, 1);
2983e12c5d1SDavid du Colombier 
2997dd7cddfSDavid du Colombier 	/*
3007dd7cddfSDavid du Colombier 	 *  Try the cache first
3017dd7cddfSDavid du Colombier 	 */
3027dd7cddfSDavid du Colombier 	rp = rrlookup(dp, type, OKneg);
3034f8f669cSDavid du Colombier 	if(rp)
3047dd7cddfSDavid du Colombier 		if(rp->db){
3054f8f669cSDavid du Colombier 			/* unauthoritative db entries are hints */
3067dd7cddfSDavid du Colombier 			if(rp->auth)
3073e12c5d1SDavid du Colombier 				return rp;
3084f8f669cSDavid du Colombier 		} else
3097dd7cddfSDavid du Colombier 			/* cached entry must still be valid */
3104f8f669cSDavid du Colombier 			if(rp->ttl > now)
3117dd7cddfSDavid du Colombier 				/* but Tall entries are special */
3127dd7cddfSDavid du Colombier 				if(type != Tall || rp->query == Tall)
3137dd7cddfSDavid du Colombier 					return rp;
3144f8f669cSDavid du Colombier 
3157dd7cddfSDavid du Colombier 	rrfreelist(rp);
3163e12c5d1SDavid du Colombier 
3177dd7cddfSDavid du Colombier 	/*
3187dd7cddfSDavid du Colombier 	 * try the cache for a canonical name. if found punt
3197dd7cddfSDavid du Colombier 	 * since we'll find it during the canonical name search
3207dd7cddfSDavid du Colombier 	 * in dnresolve().
3217dd7cddfSDavid du Colombier 	 */
3227dd7cddfSDavid du Colombier 	if(type != Tcname){
3237dd7cddfSDavid du Colombier 		rp = rrlookup(dp, Tcname, NOneg);
3247dd7cddfSDavid du Colombier 		rrfreelist(rp);
3253e12c5d1SDavid du Colombier 		if(rp)
3264f8f669cSDavid du Colombier 			return nil;
3273e12c5d1SDavid du Colombier 	}
3283e12c5d1SDavid du Colombier 
3293cbadd90SDavid du Colombier 	queryinit(&query, dp, type, req);
3303cbadd90SDavid du Colombier 
3313e12c5d1SDavid du Colombier 	/*
3324f8f669cSDavid du Colombier 	 *  if we're running as just a resolver, query our
3337dd7cddfSDavid du Colombier 	 *  designated name servers
334219b2ee8SDavid du Colombier 	 */
3354f8f669cSDavid du Colombier 	if(cfg.resolver){
3367dd7cddfSDavid du Colombier 		nsrp = randomize(getdnsservers(class));
3377dd7cddfSDavid du Colombier 		if(nsrp != nil) {
3383cbadd90SDavid du Colombier 			query.nsrp = nsrp;
3393cbadd90SDavid du Colombier 			if(netquery(&query, depth+1)){
3407dd7cddfSDavid du Colombier 				rrfreelist(nsrp);
341*4e7b9544SDavid du Colombier 				querydestroy(&query);
3427dd7cddfSDavid du Colombier 				return rrlookup(dp, type, OKneg);
3437dd7cddfSDavid du Colombier 			}
3447dd7cddfSDavid du Colombier 			rrfreelist(nsrp);
3457dd7cddfSDavid du Colombier 		}
346219b2ee8SDavid du Colombier 	}
347219b2ee8SDavid du Colombier 
348219b2ee8SDavid du Colombier 	/*
3493e12c5d1SDavid du Colombier  	 *  walk up the domain name looking for
3503e12c5d1SDavid du Colombier 	 *  a name server for the domain.
3513e12c5d1SDavid du Colombier 	 */
3523e12c5d1SDavid du Colombier 	for(cp = name; cp; cp = walkup(cp)){
3537dd7cddfSDavid du Colombier 		/*
3547dd7cddfSDavid du Colombier 		 *  if this is a local (served by us) domain,
3557dd7cddfSDavid du Colombier 		 *  return answer
3567dd7cddfSDavid du Colombier 		 */
3577dd7cddfSDavid du Colombier 		dbnsrp = randomize(dblookup(cp, class, Tns, 0, 0));
3587dd7cddfSDavid du Colombier 		if(dbnsrp && dbnsrp->local){
3597dd7cddfSDavid du Colombier 			rp = dblookup(name, class, type, 1, dbnsrp->ttl);
3607dd7cddfSDavid du Colombier 			rrfreelist(dbnsrp);
361*4e7b9544SDavid du Colombier 			querydestroy(&query);
3627dd7cddfSDavid du Colombier 			return rp;
3637dd7cddfSDavid du Colombier 		}
3647dd7cddfSDavid du Colombier 
3657dd7cddfSDavid du Colombier 		/*
3667dd7cddfSDavid du Colombier 		 *  if recursion isn't set, just accept local
3677dd7cddfSDavid du Colombier 		 *  entries
3687dd7cddfSDavid du Colombier 		 */
3697dd7cddfSDavid du Colombier 		if(recurse == Dontrecurse){
3707dd7cddfSDavid du Colombier 			if(dbnsrp)
3717dd7cddfSDavid du Colombier 				rrfreelist(dbnsrp);
3727dd7cddfSDavid du Colombier 			continue;
3737dd7cddfSDavid du Colombier 		}
3747dd7cddfSDavid du Colombier 
3757dd7cddfSDavid du Colombier 		/* look for ns in cache */
3763e12c5d1SDavid du Colombier 		nsdp = dnlookup(cp, class, 0);
3777dd7cddfSDavid du Colombier 		nsrp = nil;
3783e12c5d1SDavid du Colombier 		if(nsdp)
3797dd7cddfSDavid du Colombier 			nsrp = randomize(rrlookup(nsdp, Tns, NOneg));
3807dd7cddfSDavid du Colombier 
3817dd7cddfSDavid du Colombier 		/* if the entry timed out, ignore it */
3827dd7cddfSDavid du Colombier 		if(nsrp && nsrp->ttl < now){
3837dd7cddfSDavid du Colombier 			rrfreelist(nsrp);
3847dd7cddfSDavid du Colombier 			nsrp = nil;
3857dd7cddfSDavid du Colombier 		}
3863e12c5d1SDavid du Colombier 
3873e12c5d1SDavid du Colombier 		if(nsrp){
3887dd7cddfSDavid du Colombier 			rrfreelist(dbnsrp);
3897dd7cddfSDavid du Colombier 
3904f8f669cSDavid du Colombier 			/* query the name servers found in cache */
3913cbadd90SDavid du Colombier 			query.nsrp = nsrp;
3923cbadd90SDavid du Colombier 			if(netquery(&query, depth+1)){
3933e12c5d1SDavid du Colombier 				rrfreelist(nsrp);
394*4e7b9544SDavid du Colombier 				querydestroy(&query);
3957dd7cddfSDavid du Colombier 				return rrlookup(dp, type, OKneg);
3967dd7cddfSDavid du Colombier 			}
3977dd7cddfSDavid du Colombier 			rrfreelist(nsrp);
3987dd7cddfSDavid du Colombier 			continue;
3993e12c5d1SDavid du Colombier 		}
4003e12c5d1SDavid du Colombier 
4017dd7cddfSDavid du Colombier 		/* use ns from db */
4027dd7cddfSDavid du Colombier 		if(dbnsrp){
4037dd7cddfSDavid du Colombier 			/* try the name servers found in db */
4043cbadd90SDavid du Colombier 			query.nsrp = dbnsrp;
4053cbadd90SDavid du Colombier 			if(netquery(&query, depth+1)){
4063e12c5d1SDavid du Colombier 				/* we got an answer */
4077dd7cddfSDavid du Colombier 				rrfreelist(dbnsrp);
408*4e7b9544SDavid du Colombier 				querydestroy(&query);
4097dd7cddfSDavid du Colombier 				return rrlookup(dp, type, NOneg);
4103e12c5d1SDavid du Colombier 			}
4117dd7cddfSDavid du Colombier 			rrfreelist(dbnsrp);
4123e12c5d1SDavid du Colombier 		}
4133e12c5d1SDavid du Colombier 	}
414*4e7b9544SDavid du Colombier 	querydestroy(&query);
4153e12c5d1SDavid du Colombier 
4167dd7cddfSDavid du Colombier 	/* settle for a non-authoritative answer */
4177dd7cddfSDavid du Colombier 	rp = rrlookup(dp, type, OKneg);
4187dd7cddfSDavid du Colombier 	if(rp)
4197dd7cddfSDavid du Colombier 		return rp;
4207dd7cddfSDavid du Colombier 
4217dd7cddfSDavid du Colombier 	/* noone answered.  try the database, we might have a chance. */
4227dd7cddfSDavid du Colombier 	return dblookup(name, class, type, 0, 0);
4233e12c5d1SDavid du Colombier }
4243e12c5d1SDavid du Colombier 
4253e12c5d1SDavid du Colombier /*
4264f8f669cSDavid du Colombier  *  walk a domain name one element to the right.
4274f8f669cSDavid du Colombier  *  return a pointer to that element.
4283e12c5d1SDavid du Colombier  *  in other words, return a pointer to the parent domain name.
4293e12c5d1SDavid du Colombier  */
4303e12c5d1SDavid du Colombier char*
4313e12c5d1SDavid du Colombier walkup(char *name)
4323e12c5d1SDavid du Colombier {
4333e12c5d1SDavid du Colombier 	char *cp;
4343e12c5d1SDavid du Colombier 
4353e12c5d1SDavid du Colombier 	cp = strchr(name, '.');
4363e12c5d1SDavid du Colombier 	if(cp)
4373e12c5d1SDavid du Colombier 		return cp+1;
4383e12c5d1SDavid du Colombier 	else if(*name)
4393e12c5d1SDavid du Colombier 		return "";
4403e12c5d1SDavid du Colombier 	else
4413e12c5d1SDavid du Colombier 		return 0;
4423e12c5d1SDavid du Colombier }
4433e12c5d1SDavid du Colombier 
4443e12c5d1SDavid du Colombier /*
4453e12c5d1SDavid du Colombier  *  Get a udpport for requests and replies.  Put the port
4463e12c5d1SDavid du Colombier  *  into "headers" mode.
4473e12c5d1SDavid du Colombier  */
4483e12c5d1SDavid du Colombier static char *hmsg = "headers";
4491e8349ebSDavid du Colombier static char *ohmsg = "oldheaders";
4503e12c5d1SDavid du Colombier 
451dc5a79c1SDavid du Colombier int
4524f8f669cSDavid du Colombier udpport(char *mtpt)
4533e12c5d1SDavid du Colombier {
4543e12c5d1SDavid du Colombier 	int fd, ctl;
4554f8f669cSDavid du Colombier 	char ds[64], adir[64];
4563e12c5d1SDavid du Colombier 
4573e12c5d1SDavid du Colombier 	/* get a udp port */
4584f8f669cSDavid du Colombier 	snprint(ds, sizeof ds, "%s/udp!*!0", (mtpt? mtpt: "/net"));
4597dd7cddfSDavid du Colombier 	ctl = announce(ds, adir);
4607dd7cddfSDavid du Colombier 	if(ctl < 0){
4617dd7cddfSDavid du Colombier 		/* warning("can't get udp port"); */
462bd389b36SDavid du Colombier 		return -1;
463bd389b36SDavid du Colombier 	}
4643e12c5d1SDavid du Colombier 
4653e12c5d1SDavid du Colombier 	/* turn on header style interface */
466bd389b36SDavid du Colombier 	if(write(ctl, hmsg, strlen(hmsg)) , 0){
4677dd7cddfSDavid du Colombier 		close(ctl);
468bd389b36SDavid du Colombier 		warning(hmsg);
469bd389b36SDavid du Colombier 		return -1;
470bd389b36SDavid du Colombier 	}
4711e8349ebSDavid du Colombier 	write(ctl, ohmsg, strlen(ohmsg));
4723e12c5d1SDavid du Colombier 
4737dd7cddfSDavid du Colombier 	/* grab the data file */
4744f8f669cSDavid du Colombier 	snprint(ds, sizeof ds, "%s/data", adir);
4757dd7cddfSDavid du Colombier 	fd = open(ds, ORDWR);
4763e12c5d1SDavid du Colombier 	close(ctl);
4774f8f669cSDavid du Colombier 	if(fd < 0)
4784f8f669cSDavid du Colombier 		warning("can't open udp port %s: %r", ds);
4793e12c5d1SDavid du Colombier 	return fd;
4803e12c5d1SDavid du Colombier }
4813e12c5d1SDavid du Colombier 
482d6d99297SDavid du Colombier /* generate a DNS UDP query packet */
483dc5a79c1SDavid du Colombier int
484dc5a79c1SDavid du Colombier mkreq(DN *dp, int type, uchar *buf, int flags, ushort reqno)
4853e12c5d1SDavid du Colombier {
4863e12c5d1SDavid du Colombier 	DNSmsg m;
4873e12c5d1SDavid du Colombier 	int len;
488d9dc5dd1SDavid du Colombier 	OUdphdr *uh = (OUdphdr*)buf;
4893e12c5d1SDavid du Colombier 
4903e12c5d1SDavid du Colombier 	/* stuff port number into output buffer */
4913cbadd90SDavid du Colombier 	memset(uh, 0, sizeof *uh);
4927dd7cddfSDavid du Colombier 	hnputs(uh->rport, 53);
4933e12c5d1SDavid du Colombier 
4943e12c5d1SDavid du Colombier 	/* make request and convert it to output format */
4953cbadd90SDavid du Colombier 	memset(&m, 0, sizeof m);
496dc5a79c1SDavid du Colombier 	m.flags = flags;
4973e12c5d1SDavid du Colombier 	m.id = reqno;
4983e12c5d1SDavid du Colombier 	m.qd = rralloc(type);
4993e12c5d1SDavid du Colombier 	m.qd->owner = dp;
5003e12c5d1SDavid du Colombier 	m.qd->type = type;
501d9dc5dd1SDavid du Colombier 	len = convDNS2M(&m, &buf[OUdphdrsize], Maxudp);
5027dd7cddfSDavid du Colombier 	rrfree(m.qd);
5033e12c5d1SDavid du Colombier 	return len;
5043e12c5d1SDavid du Colombier }
5053e12c5d1SDavid du Colombier 
5067dd7cddfSDavid du Colombier /* for alarms in readreply */
5073e12c5d1SDavid du Colombier static void
5083e12c5d1SDavid du Colombier ding(void *x, char *msg)
5093e12c5d1SDavid du Colombier {
5103e12c5d1SDavid du Colombier 	USED(x);
5113e12c5d1SDavid du Colombier 	if(strcmp(msg, "alarm") == 0)
5123e12c5d1SDavid du Colombier 		noted(NCONT);
5133e12c5d1SDavid du Colombier 	else
5143e12c5d1SDavid du Colombier 		noted(NDFLT);
5153e12c5d1SDavid du Colombier }
5167dd7cddfSDavid du Colombier 
5177dd7cddfSDavid du Colombier static void
5187dd7cddfSDavid du Colombier freeanswers(DNSmsg *mp)
5193e12c5d1SDavid du Colombier {
5207dd7cddfSDavid du Colombier 	rrfreelist(mp->qd);
5217dd7cddfSDavid du Colombier 	rrfreelist(mp->an);
5227dd7cddfSDavid du Colombier 	rrfreelist(mp->ns);
5237dd7cddfSDavid du Colombier 	rrfreelist(mp->ar);
5244f8f669cSDavid du Colombier 	mp->qd = mp->an = mp->ns = mp->ar = nil;
5257dd7cddfSDavid du Colombier }
5267dd7cddfSDavid du Colombier 
5273cbadd90SDavid du Colombier /* sets srcip */
5283cbadd90SDavid du Colombier static int
5293cbadd90SDavid du Colombier readnet(Query *qp, int medium, uchar *ibuf, ulong endtime, uchar **replyp,
5303cbadd90SDavid du Colombier 	uchar *srcip)
5313cbadd90SDavid du Colombier {
5323cbadd90SDavid du Colombier 	int len, fd;
5333cbadd90SDavid du Colombier 	uchar *reply;
5343cbadd90SDavid du Colombier 	uchar lenbuf[2];
5353cbadd90SDavid du Colombier 
5363cbadd90SDavid du Colombier 	/* timed read of reply */
5373cbadd90SDavid du Colombier 	alarm((endtime - time(nil)) * 1000);
5383cbadd90SDavid du Colombier 	reply = ibuf;
5393cbadd90SDavid du Colombier 	len = -1;			/* pessimism */
540*4e7b9544SDavid du Colombier 	memset(srcip, 0, IPaddrlen);
541*4e7b9544SDavid du Colombier 	if (medium == Udp) {
542*4e7b9544SDavid du Colombier 		if (qp->udpfd <= 0)
543*4e7b9544SDavid du Colombier 			dnslog("readnet: qp->udpfd closed");
544*4e7b9544SDavid du Colombier 		else {
545*4e7b9544SDavid du Colombier 			len = read(qp->udpfd, ibuf, OUdphdrsize+Maxudpin);
546*4e7b9544SDavid du Colombier 			if (len >= IPaddrlen)
547*4e7b9544SDavid du Colombier 				memmove(srcip, ibuf, IPaddrlen);
548*4e7b9544SDavid du Colombier 			if (len >= OUdphdrsize) {
549*4e7b9544SDavid du Colombier 				len   -= OUdphdrsize;
550*4e7b9544SDavid du Colombier 				reply += OUdphdrsize;
551*4e7b9544SDavid du Colombier 			}
552*4e7b9544SDavid du Colombier 		}
553*4e7b9544SDavid du Colombier 	} else {
5543cbadd90SDavid du Colombier 		if (!qp->tcpset)
5553cbadd90SDavid du Colombier 			dnslog("readnet: tcp params not set");
5563cbadd90SDavid du Colombier 		fd = qp->tcpfd;
5573cbadd90SDavid du Colombier 		if (fd <= 0)
5583cbadd90SDavid du Colombier 			dnslog("readnet: %s: tcp fd unset for dest %I",
5593cbadd90SDavid du Colombier 				qp->dp->name, qp->tcpip);
5603cbadd90SDavid du Colombier 		else if (readn(fd, lenbuf, 2) != 2) {
5613cbadd90SDavid du Colombier 			dnslog("readnet: short read of tcp size from %I",
5623cbadd90SDavid du Colombier 				qp->tcpip);
563ccf6439bSDavid du Colombier 			/*
564ccf6439bSDavid du Colombier 			 * probably a time-out; demote the ns.
565ccf6439bSDavid du Colombier 			 * actually, the problem may be the query, not the ns.
566ccf6439bSDavid du Colombier 			 */
5673cbadd90SDavid du Colombier 			addslug(qp->tcpip);
5683cbadd90SDavid du Colombier 		} else {
5693cbadd90SDavid du Colombier 			len = lenbuf[0]<<8 | lenbuf[1];
5703cbadd90SDavid du Colombier 			if (readn(fd, ibuf, len) != len) {
5713cbadd90SDavid du Colombier 				dnslog("readnet: short read of tcp data from %I",
5723cbadd90SDavid du Colombier 					qp->tcpip);
5733cbadd90SDavid du Colombier 				/* probably a time-out; demote the ns */
5743cbadd90SDavid du Colombier 				addslug(qp->tcpip);
5753cbadd90SDavid du Colombier 				len = -1;
5763cbadd90SDavid du Colombier 			}
5773cbadd90SDavid du Colombier 		}
5783cbadd90SDavid du Colombier 		memmove(srcip, qp->tcpip, IPaddrlen);
5793cbadd90SDavid du Colombier 	}
5803cbadd90SDavid du Colombier 	alarm(0);
5813cbadd90SDavid du Colombier 	*replyp = reply;
5823cbadd90SDavid du Colombier 	return len;
5833cbadd90SDavid du Colombier }
5843cbadd90SDavid du Colombier 
5857dd7cddfSDavid du Colombier /*
5863cbadd90SDavid du Colombier  *  read replies to a request and remember the rrs in the answer(s).
5873cbadd90SDavid du Colombier  *  ignore any of the wrong type.
5884f8f669cSDavid du Colombier  *  wait at most until endtime.
5897dd7cddfSDavid du Colombier  */
5907dd7cddfSDavid du Colombier static int
5913cbadd90SDavid du Colombier readreply(Query *qp, int medium, ushort req, uchar *ibuf, DNSmsg *mp,
5923cbadd90SDavid du Colombier 	ulong endtime)
5937dd7cddfSDavid du Colombier {
5943cbadd90SDavid du Colombier 	int len = -1, rv;
5957dd7cddfSDavid du Colombier 	char *err;
5963cbadd90SDavid du Colombier 	uchar *reply;
5973cbadd90SDavid du Colombier 	uchar srcip[IPaddrlen];
5983e12c5d1SDavid du Colombier 	RR *rp;
5997dd7cddfSDavid du Colombier 
6007dd7cddfSDavid du Colombier 	notify(ding);
6017dd7cddfSDavid du Colombier 
6023cbadd90SDavid du Colombier 	queryck(qp);
6033cbadd90SDavid du Colombier 	rv = 0;
6043cbadd90SDavid du Colombier 	memset(mp, 0, sizeof *mp);
6053cbadd90SDavid du Colombier 	if (time(nil) >= endtime)
6063cbadd90SDavid du Colombier 		return -1;		/* timed out before we started */
6077dd7cddfSDavid du Colombier 
6083cbadd90SDavid du Colombier 	for (; time(nil) < endtime &&
6093cbadd90SDavid du Colombier 	    (len = readnet(qp, medium, ibuf, endtime, &reply, srcip)) >= 0;
6103cbadd90SDavid du Colombier 	    freeanswers(mp)){
6117dd7cddfSDavid du Colombier 		/* convert into internal format  */
6123cbadd90SDavid du Colombier 		memset(mp, 0, sizeof *mp);
6133cbadd90SDavid du Colombier 		err = convM2DNS(reply, len, mp, nil);
614d6d99297SDavid du Colombier 		if (mp->flags & Ftrunc) {
6153cbadd90SDavid du Colombier //			dnslog("readreply: %s: truncated reply, len %d from %I",
6163cbadd90SDavid du Colombier //				qp->dp->name, len, srcip);
6173cbadd90SDavid du Colombier 			/* notify the caller to retry the query via tcp. */
6183cbadd90SDavid du Colombier 			return -1;
6193cbadd90SDavid du Colombier 		} else if(err){
6203cbadd90SDavid du Colombier 			dnslog("readreply: %s: input err, len %d: %s: %I",
6213cbadd90SDavid du Colombier 				qp->dp->name, len, err, srcip);
6223cbadd90SDavid du Colombier 			free(err);
6237dd7cddfSDavid du Colombier 			continue;
6247dd7cddfSDavid du Colombier 		}
6253cbadd90SDavid du Colombier 		if (err)
6263cbadd90SDavid du Colombier 			free(err);
6277dd7cddfSDavid du Colombier 		if(debug)
6283cbadd90SDavid du Colombier 			logreply(qp->req->id, srcip, mp);
6297dd7cddfSDavid du Colombier 
6307dd7cddfSDavid du Colombier 		/* answering the right question? */
6313cbadd90SDavid du Colombier 		if(mp->id != req)
6323cbadd90SDavid du Colombier 			dnslog("%d: id %d instead of %d: %I", qp->req->id,
6333cbadd90SDavid du Colombier 				mp->id, req, srcip);
6343cbadd90SDavid du Colombier 		else if(mp->qd == 0)
6353cbadd90SDavid du Colombier 			dnslog("%d: no question RR: %I", qp->req->id, srcip);
6363cbadd90SDavid du Colombier 		else if(mp->qd->owner != qp->dp)
6373cbadd90SDavid du Colombier 			dnslog("%d: owner %s instead of %s: %I", qp->req->id,
6383cbadd90SDavid du Colombier 				mp->qd->owner->name, qp->dp->name, srcip);
6393cbadd90SDavid du Colombier 		else if(mp->qd->type != qp->type)
6403cbadd90SDavid du Colombier 			dnslog("%d: qp->type %d instead of %d: %I",
6413cbadd90SDavid du Colombier 				qp->req->id, mp->qd->type, qp->type, srcip);
6423cbadd90SDavid du Colombier 		else {
6437dd7cddfSDavid du Colombier 			/* remember what request this is in answer to */
6447dd7cddfSDavid du Colombier 			for(rp = mp->an; rp; rp = rp->next)
6453cbadd90SDavid du Colombier 				rp->query = qp->type;
6463cbadd90SDavid du Colombier 			return rv;
6477dd7cddfSDavid du Colombier 		}
6487dd7cddfSDavid du Colombier 	}
6493cbadd90SDavid du Colombier 	if (time(nil) >= endtime)
6503cbadd90SDavid du Colombier 		addslug(srcip);
6513cbadd90SDavid du Colombier 	else
6523cbadd90SDavid du Colombier 		dnslog("readreply: %s: %I read error or eof (returned %d)",
6533cbadd90SDavid du Colombier 			qp->dp->name, srcip, len);
6543cbadd90SDavid du Colombier 	return -1;
6553cbadd90SDavid du Colombier }
6567dd7cddfSDavid du Colombier 
6577dd7cddfSDavid du Colombier /*
6587dd7cddfSDavid du Colombier  *	return non-0 if first list includes second list
6597dd7cddfSDavid du Colombier  */
6607dd7cddfSDavid du Colombier int
6617dd7cddfSDavid du Colombier contains(RR *rp1, RR *rp2)
6627dd7cddfSDavid du Colombier {
6637dd7cddfSDavid du Colombier 	RR *trp1, *trp2;
6647dd7cddfSDavid du Colombier 
6657dd7cddfSDavid du Colombier 	for(trp2 = rp2; trp2; trp2 = trp2->next){
6663cbadd90SDavid du Colombier 		for(trp1 = rp1; trp1; trp1 = trp1->next)
6677dd7cddfSDavid du Colombier 			if(trp1->type == trp2->type)
6687dd7cddfSDavid du Colombier 			if(trp1->host == trp2->host)
6697dd7cddfSDavid du Colombier 			if(trp1->owner == trp2->owner)
6707dd7cddfSDavid du Colombier 				break;
6714f8f669cSDavid du Colombier 		if(trp1 == nil)
6727dd7cddfSDavid du Colombier 			return 0;
6737dd7cddfSDavid du Colombier 	}
6747dd7cddfSDavid du Colombier 	return 1;
6757dd7cddfSDavid du Colombier }
6767dd7cddfSDavid du Colombier 
6777dd7cddfSDavid du Colombier 
6786b6b9ac8SDavid du Colombier /*
6796b6b9ac8SDavid du Colombier  *  return multicast version if any
6806b6b9ac8SDavid du Colombier  */
6816b6b9ac8SDavid du Colombier int
6826b6b9ac8SDavid du Colombier ipisbm(uchar *ip)
6836b6b9ac8SDavid du Colombier {
6846b6b9ac8SDavid du Colombier 	if(isv4(ip)){
6854f8f669cSDavid du Colombier 		if (ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0 ||
6864f8f669cSDavid du Colombier 		    ipcmp(ip, IPv4bcast) == 0)
6876b6b9ac8SDavid du Colombier 			return 4;
6884f8f669cSDavid du Colombier 	} else
6896b6b9ac8SDavid du Colombier 		if(ip[0] == 0xff)
6906b6b9ac8SDavid du Colombier 			return 6;
69128a8a86bSDavid du Colombier 	return 0;
69228a8a86bSDavid du Colombier }
69328a8a86bSDavid du Colombier 
6947dd7cddfSDavid du Colombier /*
6957dd7cddfSDavid du Colombier  *  Get next server address
6967dd7cddfSDavid du Colombier  */
6977dd7cddfSDavid du Colombier static int
6983cbadd90SDavid du Colombier serveraddrs(Query *qp, int nd, int depth)
6997dd7cddfSDavid du Colombier {
7007dd7cddfSDavid du Colombier 	RR *rp, *arp, *trp;
7017dd7cddfSDavid du Colombier 	Dest *cur;
7027dd7cddfSDavid du Colombier 
7037dd7cddfSDavid du Colombier 	if(nd >= Maxdest)
7047dd7cddfSDavid du Colombier 		return 0;
7057dd7cddfSDavid du Colombier 
7067dd7cddfSDavid du Colombier 	/*
7077dd7cddfSDavid du Colombier 	 *  look for a server whose address we already know.
7087dd7cddfSDavid du Colombier 	 *  if we find one, mark it so we ignore this on
7097dd7cddfSDavid du Colombier 	 *  subsequent passes.
7107dd7cddfSDavid du Colombier 	 */
7117dd7cddfSDavid du Colombier 	arp = 0;
7123cbadd90SDavid du Colombier 	for(rp = qp->nsrp; rp; rp = rp->next){
71334f77ae3SDavid du Colombier 		assert(rp->magic == RRmagic);
7147dd7cddfSDavid du Colombier 		if(rp->marker)
7157dd7cddfSDavid du Colombier 			continue;
7167dd7cddfSDavid du Colombier 		arp = rrlookup(rp->host, Ta, NOneg);
7177dd7cddfSDavid du Colombier 		if(arp){
7187dd7cddfSDavid du Colombier 			rp->marker = 1;
7197dd7cddfSDavid du Colombier 			break;
7207dd7cddfSDavid du Colombier 		}
7217dd7cddfSDavid du Colombier 		arp = dblookup(rp->host->name, Cin, Ta, 0, 0);
7227dd7cddfSDavid du Colombier 		if(arp){
7237dd7cddfSDavid du Colombier 			rp->marker = 1;
7247dd7cddfSDavid du Colombier 			break;
7257dd7cddfSDavid du Colombier 		}
7267dd7cddfSDavid du Colombier 	}
7277dd7cddfSDavid du Colombier 
7287dd7cddfSDavid du Colombier 	/*
7297dd7cddfSDavid du Colombier 	 *  if the cache and database lookup didn't find any new
7307dd7cddfSDavid du Colombier 	 *  server addresses, try resolving one via the network.
7317dd7cddfSDavid du Colombier 	 *  Mark any we try to resolve so we don't try a second time.
7327dd7cddfSDavid du Colombier 	 */
7334f8f669cSDavid du Colombier 	if(arp == 0)
7343cbadd90SDavid du Colombier 		for(rp = qp->nsrp; rp; rp = rp->next){
7357dd7cddfSDavid du Colombier 			if(rp->marker)
7367dd7cddfSDavid du Colombier 				continue;
7377dd7cddfSDavid du Colombier 			rp->marker = 1;
7387dd7cddfSDavid du Colombier 
7397dd7cddfSDavid du Colombier 			/*
7407dd7cddfSDavid du Colombier 			 *  avoid loops looking up a server under itself
7417dd7cddfSDavid du Colombier 			 */
7427dd7cddfSDavid du Colombier 			if(subsume(rp->owner->name, rp->host->name))
7437dd7cddfSDavid du Colombier 				continue;
7447dd7cddfSDavid du Colombier 
7453cbadd90SDavid du Colombier 			arp = dnresolve(rp->host->name, Cin, Ta, qp->req, 0,
7464f8f669cSDavid du Colombier 				depth+1, Recurse, 1, 0);
7477dd7cddfSDavid du Colombier 			rrfreelist(rrremneg(&arp));
7487dd7cddfSDavid du Colombier 			if(arp)
7497dd7cddfSDavid du Colombier 				break;
7507dd7cddfSDavid du Colombier 		}
7517dd7cddfSDavid du Colombier 
7527dd7cddfSDavid du Colombier 	/* use any addresses that we found */
7533cbadd90SDavid du Colombier 	for(trp = arp; trp && nd < Maxdest; trp = trp->next){
7543cbadd90SDavid du Colombier 		cur = &qp->dest[nd];
7557dd7cddfSDavid du Colombier 		parseip(cur->a, trp->ip->name);
7564f8f669cSDavid du Colombier 		/*
7574f8f669cSDavid du Colombier 		 * straddling servers can reject all nameservers if they are all
7584f8f669cSDavid du Colombier 		 * inside, so be sure to list at least one outside ns at
7594f8f669cSDavid du Colombier 		 * the end of the ns list in /lib/ndb for `dom='.
7604f8f669cSDavid du Colombier 		 */
7614f8f669cSDavid du Colombier 		if (ipisbm(cur->a) ||
7623cbadd90SDavid du Colombier 		    cfg.straddle && !insideaddr(qp->dp->name) && insidens(cur->a))
7636b6b9ac8SDavid du Colombier 			continue;
7647dd7cddfSDavid du Colombier 		cur->nx = 0;
7657dd7cddfSDavid du Colombier 		cur->s = trp->owner;
7667dd7cddfSDavid du Colombier 		cur->code = Rtimeout;
7676b6b9ac8SDavid du Colombier 		nd++;
7687dd7cddfSDavid du Colombier 	}
7697dd7cddfSDavid du Colombier 	rrfreelist(arp);
7707dd7cddfSDavid du Colombier 	return nd;
7717dd7cddfSDavid du Colombier }
7727dd7cddfSDavid du Colombier 
7737dd7cddfSDavid du Colombier /*
7747dd7cddfSDavid du Colombier  *  cache negative responses
7757dd7cddfSDavid du Colombier  */
7767dd7cddfSDavid du Colombier static void
7777dd7cddfSDavid du Colombier cacheneg(DN *dp, int type, int rcode, RR *soarr)
7787dd7cddfSDavid du Colombier {
7797dd7cddfSDavid du Colombier 	RR *rp;
7807dd7cddfSDavid du Colombier 	DN *soaowner;
7819a747e4fSDavid du Colombier 	ulong ttl;
7827dd7cddfSDavid du Colombier 
7834f8f669cSDavid du Colombier 	/* no cache time specified, don't make anything up */
7847dd7cddfSDavid du Colombier 	if(soarr != nil){
7857dd7cddfSDavid du Colombier 		if(soarr->next != nil){
7867dd7cddfSDavid du Colombier 			rrfreelist(soarr->next);
7877dd7cddfSDavid du Colombier 			soarr->next = nil;
7887dd7cddfSDavid du Colombier 		}
7897dd7cddfSDavid du Colombier 		soaowner = soarr->owner;
7907dd7cddfSDavid du Colombier 	} else
7917dd7cddfSDavid du Colombier 		soaowner = nil;
7927dd7cddfSDavid du Colombier 
7939a747e4fSDavid du Colombier 	/* the attach can cause soarr to be freed so mine it now */
7949a747e4fSDavid du Colombier 	if(soarr != nil && soarr->soa != nil)
7959a747e4fSDavid du Colombier 		ttl = soarr->soa->minttl+now;
7969a747e4fSDavid du Colombier 	else
7979a747e4fSDavid du Colombier 		ttl = 5*Min;
7989a747e4fSDavid du Colombier 
7997dd7cddfSDavid du Colombier 	/* add soa and negative RR to the database */
8007dd7cddfSDavid du Colombier 	rrattach(soarr, 1);
8017dd7cddfSDavid du Colombier 
8027dd7cddfSDavid du Colombier 	rp = rralloc(type);
8037dd7cddfSDavid du Colombier 	rp->owner = dp;
8047dd7cddfSDavid du Colombier 	rp->negative = 1;
8057dd7cddfSDavid du Colombier 	rp->negsoaowner = soaowner;
8067dd7cddfSDavid du Colombier 	rp->negrcode = rcode;
8079a747e4fSDavid du Colombier 	rp->ttl = ttl;
8087dd7cddfSDavid du Colombier 	rrattach(rp, 1);
8097dd7cddfSDavid du Colombier }
8107dd7cddfSDavid du Colombier 
8114f8f669cSDavid du Colombier static int
8124f8f669cSDavid du Colombier setdestoutns(Dest *p, int n)
8134f8f669cSDavid du Colombier {
8144f8f669cSDavid du Colombier 	uchar *outns = outsidens(n);
8154f8f669cSDavid du Colombier 
8163cbadd90SDavid du Colombier 	destck(p);
8173cbadd90SDavid du Colombier 	destinit(p);
8184f8f669cSDavid du Colombier 	if (outns == nil) {
8194f8f669cSDavid du Colombier 		if (n == 0)
8204f8f669cSDavid du Colombier 			dnslog("[%d] no outside-ns in ndb", getpid());
8214f8f669cSDavid du Colombier 		return -1;
8224f8f669cSDavid du Colombier 	}
8234f8f669cSDavid du Colombier 	memmove(p->a, outns, sizeof p->a);
8244f8f669cSDavid du Colombier 	p->s = dnlookup("outside-ns-ips", Cin, 1);
8254f8f669cSDavid du Colombier 	return 0;
8264f8f669cSDavid du Colombier }
8274f8f669cSDavid du Colombier 
8287dd7cddfSDavid du Colombier /*
8293cbadd90SDavid du Colombier  * issue query via UDP or TCP as appropriate.
8303cbadd90SDavid du Colombier  * for TCP, returns with qp->tcpip set from udppkt header.
8317dd7cddfSDavid du Colombier  */
8327dd7cddfSDavid du Colombier static int
8333cbadd90SDavid du Colombier mydnsquery(Query *qp, int medium, uchar *udppkt, int len)
8347dd7cddfSDavid du Colombier {
8353cbadd90SDavid du Colombier 	int rv = -1;
8363cbadd90SDavid du Colombier 	char *domain;
8373cbadd90SDavid du Colombier 	char conndir[40];
8383cbadd90SDavid du Colombier 	NetConnInfo *nci;
8394f8f669cSDavid du Colombier 
8403cbadd90SDavid du Colombier 	queryck(qp);
8413cbadd90SDavid du Colombier 	switch (medium) {
8423cbadd90SDavid du Colombier 	case Udp:
843*4e7b9544SDavid du Colombier 		if (qp->udpfd <= 0)
844*4e7b9544SDavid du Colombier 			dnslog("mydnsquery: qp->udpfd closed");
845*4e7b9544SDavid du Colombier 		else {
846*4e7b9544SDavid du Colombier 			if (write(qp->udpfd, udppkt, len+OUdphdrsize) !=
847*4e7b9544SDavid du Colombier 			    len+OUdphdrsize)
8483cbadd90SDavid du Colombier 				warning("sending udp msg %r");
8493cbadd90SDavid du Colombier 			rv = 0;
850*4e7b9544SDavid du Colombier 		}
8513cbadd90SDavid du Colombier 		break;
8523cbadd90SDavid du Colombier 	case Tcp:
8533cbadd90SDavid du Colombier 		/* send via TCP & keep fd around for reply */
8543cbadd90SDavid du Colombier 		domain = smprint("%I", udppkt);
8553cbadd90SDavid du Colombier 		alarm(10*1000);
8563cbadd90SDavid du Colombier 		qp->tcpfd = rv = dial(netmkaddr(domain, "tcp", "dns"), nil,
8573cbadd90SDavid du Colombier 			conndir, &qp->tcpctlfd);
8583cbadd90SDavid du Colombier 		alarm(0);
8593cbadd90SDavid du Colombier 		if (qp->tcpfd < 0) {
8603cbadd90SDavid du Colombier 			dnslog("can't dial tcp!%s!dns: %r", domain);
8613cbadd90SDavid du Colombier 			addslug(udppkt);
8623cbadd90SDavid du Colombier 		} else {
8633cbadd90SDavid du Colombier 			uchar belen[2];
8643e12c5d1SDavid du Colombier 
8653cbadd90SDavid du Colombier 			nci = getnetconninfo(conndir, qp->tcpfd);
8663cbadd90SDavid du Colombier 			if (nci) {
8673cbadd90SDavid du Colombier 				parseip(qp->tcpip, nci->rsys);
8683cbadd90SDavid du Colombier 				freenetconninfo(nci);
8693cbadd90SDavid du Colombier 			} else
8703cbadd90SDavid du Colombier 				dnslog("mydnsquery: getnetconninfo failed");
8713cbadd90SDavid du Colombier 			qp->tcpset = 1;
8723e12c5d1SDavid du Colombier 
8733cbadd90SDavid du Colombier 			belen[0] = len >> 8;
8743cbadd90SDavid du Colombier 			belen[1] = len;
8753cbadd90SDavid du Colombier 			if (write(qp->tcpfd, belen, 2) != 2 ||
8763cbadd90SDavid du Colombier 			    write(qp->tcpfd, udppkt + OUdphdrsize, len) != len)
8773cbadd90SDavid du Colombier 				warning("sending tcp msg %r");
8783cbadd90SDavid du Colombier 		}
8793cbadd90SDavid du Colombier 		free(domain);
8803cbadd90SDavid du Colombier 		break;
8813cbadd90SDavid du Colombier 	default:
8823cbadd90SDavid du Colombier 		sysfatal("mydnsquery: bad medium");
8833cbadd90SDavid du Colombier 	}
8843cbadd90SDavid du Colombier 	return rv;
8853cbadd90SDavid du Colombier }
8867dd7cddfSDavid du Colombier 
8873e12c5d1SDavid du Colombier /*
8883cbadd90SDavid du Colombier  * send query to all UDP destinations or one TCP destination,
8893cbadd90SDavid du Colombier  * taken from obuf (udp packet) header
8903e12c5d1SDavid du Colombier  */
8913cbadd90SDavid du Colombier static int
8923cbadd90SDavid du Colombier xmitquery(Query *qp, int medium, int depth, uchar *obuf, int inns, int len)
8933cbadd90SDavid du Colombier {
8943cbadd90SDavid du Colombier 	int j, n;
8953cbadd90SDavid du Colombier 	char buf[32];
8963cbadd90SDavid du Colombier 	Dest *p;
8973e12c5d1SDavid du Colombier 
8983cbadd90SDavid du Colombier 	queryck(qp);
8993cbadd90SDavid du Colombier 	if(time(nil) >= qp->req->aborttime)
9003cbadd90SDavid du Colombier 		return -1;
9013cbadd90SDavid du Colombier 
9023cbadd90SDavid du Colombier 	/*
9033cbadd90SDavid du Colombier 	 * get a nameserver address if we need one.
9043cbadd90SDavid du Colombier 	 * serveraddrs populates qp->dest.
9053cbadd90SDavid du Colombier 	 */
9063cbadd90SDavid du Colombier 	p = qp->dest;
9073cbadd90SDavid du Colombier 	destck(p);
9083cbadd90SDavid du Colombier 	if (qp->ndest < 0 || qp->ndest > Maxdest)
9093cbadd90SDavid du Colombier 		dnslog("qp->ndest %d out of range", qp->ndest);
9103cbadd90SDavid du Colombier 	if (qp->ndest > qp->curdest - p)
9113cbadd90SDavid du Colombier 		qp->curdest = &qp->dest[serveraddrs(qp, qp->curdest - p, depth)];
9123cbadd90SDavid du Colombier 	destck(qp->curdest);
9137dd7cddfSDavid du Colombier 
9147dd7cddfSDavid du Colombier 	/* no servers, punt */
9153cbadd90SDavid du Colombier 	if (qp->curdest == qp->dest)
9164f8f669cSDavid du Colombier 		if (cfg.straddle && cfg.inside) {
9173cbadd90SDavid du Colombier 			/* get ips of "outside-ns-ips" */
9183cbadd90SDavid du Colombier 			p = qp->curdest = qp->dest;
9193cbadd90SDavid du Colombier 			for(n = 0; n < Maxdest; n++, qp->curdest++)
9203cbadd90SDavid du Colombier 				if (setdestoutns(qp->curdest, n) < 0)
9217dd7cddfSDavid du Colombier 					break;
9224f8f669cSDavid du Colombier 		} else {
923*4e7b9544SDavid du Colombier 			/* it's probably just a bogus domain, don't log it */
924*4e7b9544SDavid du Colombier 			// dnslog("xmitquery: %s: no nameservers", qp->dp->name);
9253cbadd90SDavid du Colombier 			return -1;
9264f8f669cSDavid du Colombier 		}
9277dd7cddfSDavid du Colombier 
9283cbadd90SDavid du Colombier 	/* send to first 'qp->ndest' destinations */
9297dd7cddfSDavid du Colombier 	j = 0;
9303cbadd90SDavid du Colombier 	if (medium == Tcp) {
9313cbadd90SDavid du Colombier 		j++;
9323cbadd90SDavid du Colombier 		queryck(qp);
9333cbadd90SDavid du Colombier 		assert(qp->dp);
9343cbadd90SDavid du Colombier 		procsetname("tcp %sside query for %s %s", (inns? "in": "out"),
9353cbadd90SDavid du Colombier 			qp->dp->name, rrname(qp->type, buf, sizeof buf));
9363cbadd90SDavid du Colombier 		mydnsquery(qp, medium, obuf, len); /* sets qp->tcpip from obuf */
9373cbadd90SDavid du Colombier 		if(debug)
9383cbadd90SDavid du Colombier 			logsend(qp->req->id, depth, qp->tcpip, "", qp->dp->name,
9393cbadd90SDavid du Colombier 				qp->type);
9403cbadd90SDavid du Colombier 	} else
9413cbadd90SDavid du Colombier 		for(; p < &qp->dest[qp->ndest] && p < qp->curdest; p++){
9427dd7cddfSDavid du Colombier 			/* skip destinations we've finished with */
9437dd7cddfSDavid du Colombier 			if(p->nx >= Maxtrans)
9447dd7cddfSDavid du Colombier 				continue;
9457dd7cddfSDavid du Colombier 
9467dd7cddfSDavid du Colombier 			j++;
9477dd7cddfSDavid du Colombier 
9487dd7cddfSDavid du Colombier 			/* exponential backoff of requests */
9493cbadd90SDavid du Colombier 			if((1<<p->nx) > qp->ndest)
9507dd7cddfSDavid du Colombier 				continue;
9517dd7cddfSDavid du Colombier 
9523cbadd90SDavid du Colombier 			procsetname("udp %sside query to %I/%s %s %s",
9533cbadd90SDavid du Colombier 				(inns? "in": "out"), p->a, p->s->name,
9543cbadd90SDavid du Colombier 				qp->dp->name, rrname(qp->type, buf, sizeof buf));
955219b2ee8SDavid du Colombier 			if(debug)
9563cbadd90SDavid du Colombier 				logsend(qp->req->id, depth, p->a, p->s->name,
9573cbadd90SDavid du Colombier 					qp->dp->name, qp->type);
9584f8f669cSDavid du Colombier 
9593cbadd90SDavid du Colombier 			/* fill in UDP destination addr & send it */
9603cbadd90SDavid du Colombier 			memmove(obuf, p->a, sizeof p->a);
9613cbadd90SDavid du Colombier 			mydnsquery(qp, medium, obuf, len);
9627dd7cddfSDavid du Colombier 			p->nx++;
9633e12c5d1SDavid du Colombier 		}
9643cbadd90SDavid du Colombier 	if(j == 0) {
9653cbadd90SDavid du Colombier 		// dnslog("xmitquery: %s: no destinations left", qp->dp->name);
9663cbadd90SDavid du Colombier 		return -1;
9673cbadd90SDavid du Colombier 	}
9683cbadd90SDavid du Colombier 	return 0;
9693cbadd90SDavid du Colombier }
9703e12c5d1SDavid du Colombier 
9713cbadd90SDavid du Colombier static int
9723cbadd90SDavid du Colombier procansw(Query *qp, DNSmsg *mp, uchar *srcip, int depth, Dest *p)
9733cbadd90SDavid du Colombier {
9743cbadd90SDavid du Colombier 	int rv;
9753cbadd90SDavid du Colombier 	char buf[32];
9763cbadd90SDavid du Colombier 	DN *ndp;
9773cbadd90SDavid du Colombier 	Query nquery;
9783cbadd90SDavid du Colombier 	RR *tp, *soarr;
9797dd7cddfSDavid du Colombier 
98059cc4ca5SDavid du Colombier 	/* ignore any error replies */
9813cbadd90SDavid du Colombier 	if((mp->flags & Rmask) == Rserver){
9823cbadd90SDavid du Colombier 		rrfreelist(mp->qd);
9833cbadd90SDavid du Colombier 		rrfreelist(mp->an);
9843cbadd90SDavid du Colombier 		rrfreelist(mp->ar);
9853cbadd90SDavid du Colombier 		rrfreelist(mp->ns);
9863cbadd90SDavid du Colombier 		if(p != qp->curdest)
9877dd7cddfSDavid du Colombier 			p->code = Rserver;
9883cbadd90SDavid du Colombier 		return -1;
9893e12c5d1SDavid du Colombier 	}
9903e12c5d1SDavid du Colombier 
99159cc4ca5SDavid du Colombier 	/* ignore any bad delegations */
9923cbadd90SDavid du Colombier 	if(mp->ns && baddelegation(mp->ns, qp->nsrp, srcip)){
9933cbadd90SDavid du Colombier 		rrfreelist(mp->ns);
9943cbadd90SDavid du Colombier 		mp->ns = nil;
9953cbadd90SDavid du Colombier 		if(mp->an == nil){
9963cbadd90SDavid du Colombier 			rrfreelist(mp->qd);
9973cbadd90SDavid du Colombier 			rrfreelist(mp->ar);
9983cbadd90SDavid du Colombier 			if(p != qp->curdest)
99959cc4ca5SDavid du Colombier 				p->code = Rserver;
10003cbadd90SDavid du Colombier 			return -1;
100159cc4ca5SDavid du Colombier 		}
100259cc4ca5SDavid du Colombier 	}
100359cc4ca5SDavid du Colombier 
10047dd7cddfSDavid du Colombier 	/* remove any soa's from the authority section */
10053cbadd90SDavid du Colombier 	soarr = rrremtype(&mp->ns, Tsoa);
10067dd7cddfSDavid du Colombier 
10073e12c5d1SDavid du Colombier 	/* incorporate answers */
10083cbadd90SDavid du Colombier 	if(mp->an)
10093cbadd90SDavid du Colombier 		rrattach(mp->an, (mp->flags & Fauth) != 0);
10103cbadd90SDavid du Colombier 	if(mp->ar)
10113cbadd90SDavid du Colombier 		rrattach(mp->ar, 0);
10123cbadd90SDavid du Colombier 	if(mp->ns){
10133cbadd90SDavid du Colombier 		ndp = mp->ns->owner;
10143cbadd90SDavid du Colombier 		rrattach(mp->ns, 0);
10157dd7cddfSDavid du Colombier 	} else
10164f8f669cSDavid du Colombier 		ndp = nil;
10177dd7cddfSDavid du Colombier 
10187dd7cddfSDavid du Colombier 	/* free the question */
10193cbadd90SDavid du Colombier 	if(mp->qd)
10203cbadd90SDavid du Colombier 		rrfreelist(mp->qd);
10213e12c5d1SDavid du Colombier 
10223e12c5d1SDavid du Colombier 	/*
10233e12c5d1SDavid du Colombier 	 *  Any reply from an authoritative server,
10243e12c5d1SDavid du Colombier 	 *  or a positive reply terminates the search
10253e12c5d1SDavid du Colombier 	 */
10263cbadd90SDavid du Colombier 	if(mp->an != nil || (mp->flags & Fauth)){
10273cbadd90SDavid du Colombier 		if(mp->an == nil && (mp->flags & Rmask) == Rname)
10283cbadd90SDavid du Colombier 			qp->dp->respcode = Rname;
10297dd7cddfSDavid du Colombier 		else
10303cbadd90SDavid du Colombier 			qp->dp->respcode = 0;
10317dd7cddfSDavid du Colombier 
10327dd7cddfSDavid du Colombier 		/*
10337dd7cddfSDavid du Colombier 		 *  cache any negative responses, free soarr
10347dd7cddfSDavid du Colombier 		 */
10353cbadd90SDavid du Colombier 		if((mp->flags & Fauth) && mp->an == nil)
10363cbadd90SDavid du Colombier 			cacheneg(qp->dp, qp->type, (mp->flags & Rmask), soarr);
10377dd7cddfSDavid du Colombier 		else
10387dd7cddfSDavid du Colombier 			rrfreelist(soarr);
10393e12c5d1SDavid du Colombier 		return 1;
10403e12c5d1SDavid du Colombier 	}
10417dd7cddfSDavid du Colombier 	rrfreelist(soarr);
10423e12c5d1SDavid du Colombier 
10433e12c5d1SDavid du Colombier 	/*
10444f8f669cSDavid du Colombier 	 *  if we've been given better name servers,
10454f8f669cSDavid du Colombier 	 *  recurse.  we're called from udpquery, called from
10463cbadd90SDavid du Colombier 	 *  netquery, which current holds qp->dp->querylck,
10474f8f669cSDavid du Colombier 	 *  so release it now and acquire it upon return.
10483e12c5d1SDavid du Colombier 	 */
10493cbadd90SDavid du Colombier 	if(!mp->ns)
10503cbadd90SDavid du Colombier 		return 0;
10517dd7cddfSDavid du Colombier 	tp = rrlookup(ndp, Tns, NOneg);
10523cbadd90SDavid du Colombier 	if(contains(qp->nsrp, tp)){
10537dd7cddfSDavid du Colombier 		rrfreelist(tp);
10543cbadd90SDavid du Colombier 		return 0;
10553e12c5d1SDavid du Colombier 	}
10563cbadd90SDavid du Colombier 	procsetname("recursive query for %s %s", qp->dp->name,
10573cbadd90SDavid du Colombier 		rrname(qp->type, buf, sizeof buf));
1058ccf6439bSDavid du Colombier //	qunlock(&qp->dp->querylck);
10593cbadd90SDavid du Colombier 
10603cbadd90SDavid du Colombier 	queryinit(&nquery, qp->dp, qp->type, qp->req);
10613cbadd90SDavid du Colombier 	nquery.nsrp = tp;
10623cbadd90SDavid du Colombier 	rv = netquery(&nquery, depth+1);
10633cbadd90SDavid du Colombier 
1064ccf6439bSDavid du Colombier //	qlock(&qp->dp->querylck);
10653cbadd90SDavid du Colombier 	rrfreelist(tp);
1066*4e7b9544SDavid du Colombier 	querydestroy(&nquery);
10673cbadd90SDavid du Colombier 	return rv;
10683cbadd90SDavid du Colombier }
10693cbadd90SDavid du Colombier 
10703cbadd90SDavid du Colombier /*
10713cbadd90SDavid du Colombier  * send a query via tcp to a single address (from ibuf's udp header)
10723cbadd90SDavid du Colombier  * and read the answer(s) into mp->an.
10733cbadd90SDavid du Colombier  */
10743cbadd90SDavid du Colombier static int
10753cbadd90SDavid du Colombier tcpquery(Query *qp, DNSmsg *mp, int depth, uchar *ibuf, uchar *obuf, int len,
10763cbadd90SDavid du Colombier 	int waitsecs, int inns, ushort req)
10773cbadd90SDavid du Colombier {
1078*4e7b9544SDavid du Colombier 	int rv = 0;
10793cbadd90SDavid du Colombier 	ulong endtime;
10803cbadd90SDavid du Colombier 
10813cbadd90SDavid du Colombier 	endtime = time(nil) + waitsecs;
10823cbadd90SDavid du Colombier 	if(endtime > qp->req->aborttime)
10833cbadd90SDavid du Colombier 		endtime = qp->req->aborttime;
10843cbadd90SDavid du Colombier 
1085*4e7b9544SDavid du Colombier 	dnslog("%s: udp reply truncated; retrying query via tcp to %I",
1086*4e7b9544SDavid du Colombier 		qp->dp->name, qp->tcpip);
1087*4e7b9544SDavid du Colombier 
10883cbadd90SDavid du Colombier 	qlock(&qp->tcplock);
10893cbadd90SDavid du Colombier 	memmove(obuf, ibuf, IPaddrlen);		/* send back to respondent */
10903cbadd90SDavid du Colombier 	/* sets qp->tcpip from obuf's udp header */
1091*4e7b9544SDavid du Colombier 	if (xmitquery(qp, Tcp, depth, obuf, inns, len) < 0 ||
1092*4e7b9544SDavid du Colombier 	    readreply(qp, Tcp, req, ibuf, mp, endtime) < 0)
1093*4e7b9544SDavid du Colombier 		rv = -1;
10943cbadd90SDavid du Colombier 	if (qp->tcpfd > 0) {
10953cbadd90SDavid du Colombier 		hangup(qp->tcpctlfd);
10963cbadd90SDavid du Colombier 		close(qp->tcpctlfd);
10973cbadd90SDavid du Colombier 		close(qp->tcpfd);
10983cbadd90SDavid du Colombier 	}
10993cbadd90SDavid du Colombier 	qp->tcpfd = qp->tcpctlfd = -1;
11003cbadd90SDavid du Colombier 	qunlock(&qp->tcplock);
1101*4e7b9544SDavid du Colombier 	return rv;
11023cbadd90SDavid du Colombier }
11033cbadd90SDavid du Colombier 
11043cbadd90SDavid du Colombier /*
11053cbadd90SDavid du Colombier  *  query name servers.  If the name server returns a pointer to another
11063cbadd90SDavid du Colombier  *  name server, recurse.
11073cbadd90SDavid du Colombier  */
11083cbadd90SDavid du Colombier static int
11093cbadd90SDavid du Colombier netquery1(Query *qp, int depth, uchar *ibuf, uchar *obuf, int waitsecs, int inns)
11103cbadd90SDavid du Colombier {
11113cbadd90SDavid du Colombier 	int ndest, len, replywaits, rv;
11123cbadd90SDavid du Colombier 	ushort req;
11133cbadd90SDavid du Colombier 	ulong endtime;
11143cbadd90SDavid du Colombier 	char buf[12];
11153cbadd90SDavid du Colombier 	uchar srcip[IPaddrlen];
11163cbadd90SDavid du Colombier 	DNSmsg m;
11173cbadd90SDavid du Colombier 	Dest *p, *np;
11183cbadd90SDavid du Colombier 	Dest dest[Maxdest];
11193cbadd90SDavid du Colombier 
11203cbadd90SDavid du Colombier 	/* pack request into a udp message */
11213cbadd90SDavid du Colombier 	req = rand();
11223cbadd90SDavid du Colombier 	len = mkreq(qp->dp, qp->type, obuf, Frecurse|Oquery, req);
11233cbadd90SDavid du Colombier 
11243cbadd90SDavid du Colombier 	/* no server addresses yet */
11253cbadd90SDavid du Colombier 	queryck(qp);
11263cbadd90SDavid du Colombier 	for (p = dest; p < dest + nelem(dest); p++)
11273cbadd90SDavid du Colombier 		destinit(p);
11283cbadd90SDavid du Colombier 	qp->curdest = qp->dest = dest;
11293cbadd90SDavid du Colombier 
11303cbadd90SDavid du Colombier 	/*
11313cbadd90SDavid du Colombier 	 *  transmit udp requests and wait for answers.
11323cbadd90SDavid du Colombier 	 *  at most Maxtrans attempts to each address.
11333cbadd90SDavid du Colombier 	 *  each cycle send one more message than the previous.
11343cbadd90SDavid du Colombier 	 *  retry a query via tcp if its response is truncated.
11353cbadd90SDavid du Colombier 	 */
11363cbadd90SDavid du Colombier 	for(ndest = 1; ndest < Maxdest; ndest++){
11373cbadd90SDavid du Colombier 		qp->ndest = ndest;
11383cbadd90SDavid du Colombier 		qp->tcpset = 0;
11393cbadd90SDavid du Colombier 		if (xmitquery(qp, Udp, depth, obuf, inns, len) < 0)
11403cbadd90SDavid du Colombier 			break;
11413cbadd90SDavid du Colombier 
11423cbadd90SDavid du Colombier 		endtime = time(nil) + waitsecs;
11433cbadd90SDavid du Colombier 		if(endtime > qp->req->aborttime)
11443cbadd90SDavid du Colombier 			endtime = qp->req->aborttime;
11453cbadd90SDavid du Colombier 
11463cbadd90SDavid du Colombier 		for(replywaits = 0; replywaits < ndest; replywaits++){
11473cbadd90SDavid du Colombier 			procsetname("reading %sside reply from %s%I for %s %s",
11483cbadd90SDavid du Colombier 				(inns? "in": "out"),
11493cbadd90SDavid du Colombier 				(isaslug(qp->tcpip)? "sluggard ": ""), obuf,
11503cbadd90SDavid du Colombier 				qp->dp->name, rrname(qp->type, buf, sizeof buf));
11513cbadd90SDavid du Colombier 
11523cbadd90SDavid du Colombier 			/* read udp answer */
11533cbadd90SDavid du Colombier 			if (readreply(qp, Udp, req, ibuf, &m, endtime) >= 0)
11543cbadd90SDavid du Colombier 				memmove(srcip, ibuf, IPaddrlen);
11553cbadd90SDavid du Colombier 			else if (!(m.flags & Ftrunc)) {
11563cbadd90SDavid du Colombier 				addslug(ibuf);
11573cbadd90SDavid du Colombier 				break;		/* timed out on this dest */
11583cbadd90SDavid du Colombier 			} else {
11593cbadd90SDavid du Colombier 				/* whoops, it was truncated! ask again via tcp */
11603cbadd90SDavid du Colombier 				rv = tcpquery(qp, &m, depth, ibuf, obuf, len,
11613cbadd90SDavid du Colombier 					waitsecs, inns, req);
11623cbadd90SDavid du Colombier 				if (rv < 0)
11633cbadd90SDavid du Colombier 					break;		/* failed via tcp too */
11643cbadd90SDavid du Colombier 				memmove(srcip, qp->tcpip, IPaddrlen);
11653cbadd90SDavid du Colombier 			}
11663cbadd90SDavid du Colombier 
11673cbadd90SDavid du Colombier 			/* find responder */
11683cbadd90SDavid du Colombier 			// dnslog("netquery1 got reply from %I", srcip);
11693cbadd90SDavid du Colombier 			for(p = qp->dest; p < qp->curdest; p++)
11703cbadd90SDavid du Colombier 				if(memcmp(p->a, srcip, sizeof p->a) == 0)
11713cbadd90SDavid du Colombier 					break;
11723cbadd90SDavid du Colombier 
11733cbadd90SDavid du Colombier 			/* remove all addrs of responding server from list */
11743cbadd90SDavid du Colombier 			for(np = qp->dest; np < qp->curdest; np++)
11753cbadd90SDavid du Colombier 				if(np->s == p->s)
11763cbadd90SDavid du Colombier 					p->nx = Maxtrans;
11773cbadd90SDavid du Colombier 
11783cbadd90SDavid du Colombier 			rv = procansw(qp, &m, srcip, depth, p);
11793cbadd90SDavid du Colombier 			if (rv > 0)
11803cbadd90SDavid du Colombier 				return rv;
11813e12c5d1SDavid du Colombier 		}
11823e12c5d1SDavid du Colombier 	}
11837dd7cddfSDavid du Colombier 
11844f8f669cSDavid du Colombier 	/* if all servers returned failure, propagate it */
11853cbadd90SDavid du Colombier 	qp->dp->respcode = Rserver;
11863cbadd90SDavid du Colombier 	for(p = dest; p < qp->curdest; p++) {
11873cbadd90SDavid du Colombier 		destck(p);
11887dd7cddfSDavid du Colombier 		if(p->code != Rserver)
11893cbadd90SDavid du Colombier 			qp->dp->respcode = 0;
11903cbadd90SDavid du Colombier 		p->magic = 0;			/* prevent accidents */
11913cbadd90SDavid du Colombier 	}
11924f8f669cSDavid du Colombier 
11933cbadd90SDavid du Colombier //	if (qp->dp->respcode)
11943cbadd90SDavid du Colombier //		dnslog("netquery1 setting Rserver for %s", qp->dp->name);
11957dd7cddfSDavid du Colombier 
11963cbadd90SDavid du Colombier 	qp->dest = qp->curdest = nil;		/* prevent accidents */
11973e12c5d1SDavid du Colombier 	return 0;
11983e12c5d1SDavid du Colombier }
11997dd7cddfSDavid du Colombier 
12004f8f669cSDavid du Colombier /*
12014f8f669cSDavid du Colombier  *  run a command with a supplied fd as standard input
12024f8f669cSDavid du Colombier  */
12034f8f669cSDavid du Colombier char *
12044f8f669cSDavid du Colombier system(int fd, char *cmd)
120528a8a86bSDavid du Colombier {
12064f8f669cSDavid du Colombier 	int pid, p, i;
12074f8f669cSDavid du Colombier 	static Waitmsg msg;
12086b0d5c8bSDavid du Colombier 
12094f8f669cSDavid du Colombier 	if((pid = fork()) == -1)
12104f8f669cSDavid du Colombier 		sysfatal("fork failed: %r");
12114f8f669cSDavid du Colombier 	else if(pid == 0){
12124f8f669cSDavid du Colombier 		dup(fd, 0);
12134f8f669cSDavid du Colombier 		close(fd);
12144f8f669cSDavid du Colombier 		for (i = 3; i < 200; i++)
12154f8f669cSDavid du Colombier 			close(i);		/* don't leak fds */
12164f8f669cSDavid du Colombier 		execl("/bin/rc", "rc", "-c", cmd, nil);
12174f8f669cSDavid du Colombier 		sysfatal("exec rc: %r");
12184f8f669cSDavid du Colombier 	}
12194f8f669cSDavid du Colombier 	for(p = waitpid(); p >= 0; p = waitpid())
12204f8f669cSDavid du Colombier 		if(p == pid)
12214f8f669cSDavid du Colombier 			return msg.msg;
12224f8f669cSDavid du Colombier 	return "lost child";
12234f8f669cSDavid du Colombier }
12244f8f669cSDavid du Colombier 
12254f8f669cSDavid du Colombier enum { Hurry, Patient, };
12264f8f669cSDavid du Colombier enum { Outns, Inns, };
12274f8f669cSDavid du Colombier enum { Remntretry = 15, };	/* min. sec.s between remount attempts */
12284f8f669cSDavid du Colombier 
12294f8f669cSDavid du Colombier static int
12303cbadd90SDavid du Colombier udpquery(Query *qp, char *mntpt, int depth, int patient, int inns)
12314f8f669cSDavid du Colombier {
12324f8f669cSDavid du Colombier 	int fd, rv = 0;
12334f8f669cSDavid du Colombier 	long now;
12344f8f669cSDavid du Colombier 	char *msg;
12354f8f669cSDavid du Colombier 	uchar *obuf, *ibuf;
12364f8f669cSDavid du Colombier 	static QLock mntlck;
12374f8f669cSDavid du Colombier 	static ulong lastmount;
12387dd7cddfSDavid du Colombier 
12397dd7cddfSDavid du Colombier 	/* use alloced buffers rather than ones from the stack */
12403cbadd90SDavid du Colombier 	// ibuf = emalloc(Maxudpin+OUdphdrsize);
12413cbadd90SDavid du Colombier 	ibuf = emalloc(64*1024);		/* max. tcp reply size */
1242d9dc5dd1SDavid du Colombier 	obuf = emalloc(Maxudp+OUdphdrsize);
12437dd7cddfSDavid du Colombier 
12444f8f669cSDavid du Colombier 	fd = udpport(mntpt);
12454f8f669cSDavid du Colombier 	while (fd < 0 && cfg.straddle && strcmp(mntpt, "/net.alt") == 0) {
12464f8f669cSDavid du Colombier 		/* HACK: remount /net.alt */
12474f8f669cSDavid du Colombier 		now = time(nil);
12484f8f669cSDavid du Colombier 		if (now < lastmount + Remntretry)
12494f8f669cSDavid du Colombier 			sleep((lastmount + Remntretry - now)*1000);
12504f8f669cSDavid du Colombier 		qlock(&mntlck);
12514f8f669cSDavid du Colombier 		fd = udpport(mntpt);	/* try again under lock */
12524f8f669cSDavid du Colombier 		if (fd < 0) {
12534f8f669cSDavid du Colombier 			dnslog("[%d] remounting /net.alt", getpid());
12544f8f669cSDavid du Colombier 			unmount(nil, "/net.alt");
12554f8f669cSDavid du Colombier 
12564f8f669cSDavid du Colombier 			msg = system(open("/dev/null", ORDWR), "outside");
12574f8f669cSDavid du Colombier 
12584f8f669cSDavid du Colombier 			lastmount = time(nil);
12594f8f669cSDavid du Colombier 			if (msg && *msg) {
12604f8f669cSDavid du Colombier 				dnslog("[%d] can't remount /net.alt: %s",
12614f8f669cSDavid du Colombier 					getpid(), msg);
12624f8f669cSDavid du Colombier 				sleep(10*1000);		/* don't spin wildly */
12634f8f669cSDavid du Colombier 			} else
12644f8f669cSDavid du Colombier 				fd = udpport(mntpt);
12654f8f669cSDavid du Colombier 		}
12664f8f669cSDavid du Colombier 		qunlock(&mntlck);
12674f8f669cSDavid du Colombier 	}
12684f8f669cSDavid du Colombier 	if(fd >= 0) {
12693cbadd90SDavid du Colombier 		qp->req->aborttime = time(nil) + (patient? Maxreqtm: Maxreqtm/2);
12703cbadd90SDavid du Colombier 		qp->udpfd = fd;
1271ccf6439bSDavid du Colombier 		/* tune; was (patient? 15: 10) */
12723cbadd90SDavid du Colombier 		rv = netquery1(qp, depth, ibuf, obuf, (patient? 10: 5), inns);
12734f8f669cSDavid du Colombier 		close(fd);
12744f8f669cSDavid du Colombier 	} else
12754f8f669cSDavid du Colombier 		dnslog("can't get udpport for %s query of name %s: %r",
12763cbadd90SDavid du Colombier 			mntpt, qp->dp->name);
12774f8f669cSDavid du Colombier 
12784f8f669cSDavid du Colombier 	free(obuf);
12794f8f669cSDavid du Colombier 	free(ibuf);
12804f8f669cSDavid du Colombier 	return rv;
12814f8f669cSDavid du Colombier }
12824f8f669cSDavid du Colombier 
12834f8f669cSDavid du Colombier /* look up (dp->name,type) via *nsrp with results in *reqp */
12844f8f669cSDavid du Colombier static int
12853cbadd90SDavid du Colombier netquery(Query *qp, int depth)
12864f8f669cSDavid du Colombier {
12874f8f669cSDavid du Colombier 	int lock, rv, triedin, inname;
12884f8f669cSDavid du Colombier 	RR *rp;
12894f8f669cSDavid du Colombier 
12904f8f669cSDavid du Colombier 	if(depth > 12)			/* in a recursive loop? */
12914f8f669cSDavid du Colombier 		return 0;
12924f8f669cSDavid du Colombier 
12933cbadd90SDavid du Colombier 	slave(qp->req);
1294d3907fe5SDavid du Colombier 	/*
1295d3907fe5SDavid du Colombier 	 * slave might have forked.  if so, the parent process longjmped to
1296d3907fe5SDavid du Colombier 	 * req->mret; we're usually the child slave, but if there are too
1297d3907fe5SDavid du Colombier 	 * many children already, we're still the same process.
1298d3907fe5SDavid du Colombier 	 */
12994f8f669cSDavid du Colombier 
13004f8f669cSDavid du Colombier 	/* don't lock before call to slave so only children can block */
1301ccf6439bSDavid du Colombier 	if (0)
13023cbadd90SDavid du Colombier 		lock = qp->req->isslave != 0;
1303ccf6439bSDavid du Colombier 	if(0 && lock) {
13043cbadd90SDavid du Colombier 		procsetname("query lock wait for %s", qp->dp->name);
1305ccf6439bSDavid du Colombier 		/*
1306ccf6439bSDavid du Colombier 		 * don't make concurrent queries for this name.
1307ccf6439bSDavid du Colombier 		 *
1308ccf6439bSDavid du Colombier 		 * this seemed like a good idea, to avoid swamping
1309ccf6439bSDavid du Colombier 		 * an overloaded ns, but in practice, dns processes
1310ccf6439bSDavid du Colombier 		 * pile up quickly and dns becomes unresponsive for a while.
1311ccf6439bSDavid du Colombier 		 */
13123cbadd90SDavid du Colombier 		qlock(&qp->dp->querylck);
13134f8f669cSDavid du Colombier 	}
1314ccf6439bSDavid du Colombier 	procsetname("netquery: %s", qp->dp->name);
13156b0d5c8bSDavid du Colombier 
13166b0d5c8bSDavid du Colombier 	/* prepare server RR's for incremental lookup */
13173cbadd90SDavid du Colombier 	for(rp = qp->nsrp; rp; rp = rp->next)
13186b0d5c8bSDavid du Colombier 		rp->marker = 0;
13196b0d5c8bSDavid du Colombier 
13204f8f669cSDavid du Colombier 	rv = 0;				/* pessimism */
13214f8f669cSDavid du Colombier 	triedin = 0;
13223cbadd90SDavid du Colombier 	qp->nsrp = qp->nsrp;
13234f8f669cSDavid du Colombier 	/*
13244f8f669cSDavid du Colombier 	 * normal resolvers and servers will just use mntpt for all addresses,
13254f8f669cSDavid du Colombier 	 * even on the outside.  straddling servers will use mntpt (/net)
13264f8f669cSDavid du Colombier 	 * for inside addresses and /net.alt for outside addresses,
13274f8f669cSDavid du Colombier 	 * thus bypassing other inside nameservers.
13284f8f669cSDavid du Colombier 	 */
13293cbadd90SDavid du Colombier 	inname = insideaddr(qp->dp->name);
13304f8f669cSDavid du Colombier 	if (!cfg.straddle || inname) {
13313cbadd90SDavid du Colombier 		rv = udpquery(qp, mntpt, depth, Hurry, (cfg.inside? Inns: Outns));
13324f8f669cSDavid du Colombier 		triedin = 1;
13334f8f669cSDavid du Colombier 	}
13344f8f669cSDavid du Colombier 
13354f8f669cSDavid du Colombier 	/*
13364f8f669cSDavid du Colombier 	 * if we're still looking, are inside, and have an outside domain,
13374f8f669cSDavid du Colombier 	 * try it on our outside interface, if any.
13384f8f669cSDavid du Colombier 	 */
13394f8f669cSDavid du Colombier 	if (rv == 0 && cfg.inside && !inname) {
13404f8f669cSDavid du Colombier 		if (triedin)
13414f8f669cSDavid du Colombier 			dnslog(
13424f8f669cSDavid du Colombier 	   "[%d] netquery: internal nameservers failed for %s; trying external",
13433cbadd90SDavid du Colombier 				getpid(), qp->dp->name);
13444f8f669cSDavid du Colombier 
13454f8f669cSDavid du Colombier 		/* prepare server RR's for incremental lookup */
13463cbadd90SDavid du Colombier 		for(rp = qp->nsrp; rp; rp = rp->next)
13474f8f669cSDavid du Colombier 			rp->marker = 0;
13484f8f669cSDavid du Colombier 
13493cbadd90SDavid du Colombier 		rv = udpquery(qp, "/net.alt", depth, Patient, Outns);
13504f8f669cSDavid du Colombier 	}
13513cbadd90SDavid du Colombier //	if (rv == 0)		/* could ask /net.alt/dns directly */
13523cbadd90SDavid du Colombier //		askoutdns(qp->dp, qp->type);
13534f8f669cSDavid du Colombier 
1354ccf6439bSDavid du Colombier 	if(0 && lock)
13553cbadd90SDavid du Colombier 		qunlock(&qp->dp->querylck);
13567dd7cddfSDavid du Colombier 	return rv;
13577dd7cddfSDavid du Colombier }
13584f8f669cSDavid du Colombier 
13594f8f669cSDavid du Colombier int
13604f8f669cSDavid du Colombier seerootns(void)
13614f8f669cSDavid du Colombier {
13623cbadd90SDavid du Colombier 	int rv;
13634f8f669cSDavid du Colombier 	char root[] = "";
13644f8f669cSDavid du Colombier 	Request req;
13653cbadd90SDavid du Colombier 	Query query;
13664f8f669cSDavid du Colombier 
13674f8f669cSDavid du Colombier 	memset(&req, 0, sizeof req);
13684f8f669cSDavid du Colombier 	req.isslave = 1;
1369ccf6439bSDavid du Colombier 	req.aborttime = now + Maxreqtm;
13703cbadd90SDavid du Colombier 	queryinit(&query, dnlookup(root, Cin, 1), Tns, &req);
13713cbadd90SDavid du Colombier 	query.nsrp = dblookup(root, Cin, Tns, 0, 0);
13723cbadd90SDavid du Colombier 	rv = netquery(&query, 0);
1373*4e7b9544SDavid du Colombier 	querydestroy(&query);
13743cbadd90SDavid du Colombier 	return rv;
13754f8f669cSDavid du Colombier }
1376