xref: /plan9/sys/src/cmd/ndb/dnresolve.c (revision 28a8a86bb9fb9a6d8ffb576390c3148c8a004bf5)
13e12c5d1SDavid du Colombier #include <u.h>
23e12c5d1SDavid du Colombier #include <libc.h>
3*28a8a86bSDavid du Colombier #include <ip.h>
4*28a8a86bSDavid du Colombier #include <bio.h>
5*28a8a86bSDavid du Colombier #include <ndb.h>
63e12c5d1SDavid du Colombier #include "dns.h"
73e12c5d1SDavid du Colombier 
83e12c5d1SDavid du Colombier enum
93e12c5d1SDavid du Colombier {
107dd7cddfSDavid du Colombier 	Maxdest=	24,	/* maximum destinations for a request message */
113e12c5d1SDavid du Colombier 	Maxtrans=	3,	/* maximum transmissions to a server */
123e12c5d1SDavid du Colombier };
133e12c5d1SDavid du Colombier 
147dd7cddfSDavid du Colombier static int	netquery(DN*, int, RR*, Request*, int);
157dd7cddfSDavid du Colombier static RR*	dnresolve1(char*, int, int, Request*, int, int);
163e12c5d1SDavid du Colombier 
177dd7cddfSDavid du Colombier char *LOG = "dns";
183e12c5d1SDavid du Colombier 
193e12c5d1SDavid du Colombier /*
205aa528faSDavid du Colombier  * reading /proc/pid/args yields either "name" or "name [display args]",
215aa528faSDavid du Colombier  * so return only display args, if any.
225aa528faSDavid du Colombier  */
235aa528faSDavid du Colombier static char *
245aa528faSDavid du Colombier procgetname(void)
255aa528faSDavid du Colombier {
265aa528faSDavid du Colombier 	int fd, n;
275aa528faSDavid du Colombier 	char *lp, *rp;
285aa528faSDavid du Colombier 	char buf[256];
295aa528faSDavid du Colombier 
305aa528faSDavid du Colombier 	snprint(buf, sizeof buf, "#p/%d/args", getpid());
315aa528faSDavid du Colombier 	if((fd = open(buf, OREAD)) < 0)
325aa528faSDavid du Colombier 		return strdup("");
335aa528faSDavid du Colombier 	*buf = '\0';
345aa528faSDavid du Colombier 	n = read(fd, buf, sizeof buf-1);
355aa528faSDavid du Colombier 	close(fd);
365aa528faSDavid du Colombier 	if (n >= 0)
375aa528faSDavid du Colombier 		buf[n] = '\0';
385aa528faSDavid du Colombier 	if ((lp = strchr(buf, '[')) == nil ||
395aa528faSDavid du Colombier 	    (rp = strrchr(buf, ']')) == nil)
405aa528faSDavid du Colombier 		return strdup("");
415aa528faSDavid du Colombier 	*rp = '\0';
425aa528faSDavid du Colombier 	return strdup(lp+1);
435aa528faSDavid du Colombier }
445aa528faSDavid du Colombier 
455aa528faSDavid du Colombier /*
463e12c5d1SDavid du Colombier  *  lookup 'type' info for domain name 'name'.  If it doesn't exist, try
473e12c5d1SDavid du Colombier  *  looking it up as a canonical name.
483e12c5d1SDavid du Colombier  */
493e12c5d1SDavid du Colombier RR*
505aa528faSDavid du Colombier dnresolve(char *name, int class, int type, Request *req, RR **cn, int depth,
515aa528faSDavid du Colombier 	int recurse, int rooted, int *status)
523e12c5d1SDavid du Colombier {
537dd7cddfSDavid du Colombier 	RR *rp, *nrp, *drp;
543e12c5d1SDavid du Colombier 	DN *dp;
557dd7cddfSDavid du Colombier 	int loops;
565aa528faSDavid du Colombier 	char *procname;
577dd7cddfSDavid du Colombier 	char nname[Domlen];
583e12c5d1SDavid du Colombier 
597dd7cddfSDavid du Colombier 	if(status)
607dd7cddfSDavid du Colombier 		*status = 0;
617dd7cddfSDavid du Colombier 
625aa528faSDavid du Colombier 	procname = procgetname();
637dd7cddfSDavid du Colombier 	/*
647dd7cddfSDavid du Colombier 	 *  hack for systems that don't have resolve search
657dd7cddfSDavid du Colombier 	 *  lists.  Just look up the simple name in the database.
667dd7cddfSDavid du Colombier 	 */
677dd7cddfSDavid du Colombier 	if(!rooted && strchr(name, '.') == 0){
687dd7cddfSDavid du Colombier 		rp = nil;
697dd7cddfSDavid du Colombier 		drp = domainlist(class);
707dd7cddfSDavid du Colombier 		for(nrp = drp; nrp != nil; nrp = nrp->next){
715aa528faSDavid du Colombier 			snprint(nname, sizeof nname, "%s.%s", name,
725aa528faSDavid du Colombier 				nrp->ptr->name);
735aa528faSDavid du Colombier 			rp = dnresolve(nname, class, type, req, cn, depth,
745aa528faSDavid du Colombier 				recurse, rooted, status);
75271b8d73SDavid du Colombier 			rrfreelist(rrremneg(&rp));
767dd7cddfSDavid du Colombier 			if(rp != nil)
777dd7cddfSDavid du Colombier 				break;
787dd7cddfSDavid du Colombier 		}
797dd7cddfSDavid du Colombier 		if(drp != nil)
807dd7cddfSDavid du Colombier 			rrfree(drp);
815aa528faSDavid du Colombier 		procsetname(procname);
825aa528faSDavid du Colombier 		free(procname);
833e12c5d1SDavid du Colombier 		return rp;
847dd7cddfSDavid du Colombier 	}
853e12c5d1SDavid du Colombier 
867dd7cddfSDavid du Colombier 	/*
877dd7cddfSDavid du Colombier 	 *  try the name directly
887dd7cddfSDavid du Colombier 	 */
897dd7cddfSDavid du Colombier 	rp = dnresolve1(name, class, type, req, depth, recurse);
905aa528faSDavid du Colombier 	if(rp) {
915aa528faSDavid du Colombier 		procsetname(procname);
925aa528faSDavid du Colombier 		free(procname);
937dd7cddfSDavid du Colombier 		return randomize(rp);
945aa528faSDavid du Colombier 	}
957dd7cddfSDavid du Colombier 
967dd7cddfSDavid du Colombier 	/* try it as a canonical name if we weren't told the name didn't exist */
977dd7cddfSDavid du Colombier 	dp = dnlookup(name, class, 0);
985aa528faSDavid du Colombier 	if(type != Tptr && dp->nonexistent != Rname)
997dd7cddfSDavid du Colombier 		for(loops=0; rp == nil && loops < 32; loops++){
1007dd7cddfSDavid du Colombier 			rp = dnresolve1(name, class, Tcname, req, depth, recurse);
1017dd7cddfSDavid du Colombier 			if(rp == nil)
1027dd7cddfSDavid du Colombier 				break;
10380ee5cbfSDavid du Colombier 
10480ee5cbfSDavid du Colombier 			if(rp->negative){
10580ee5cbfSDavid du Colombier 				rrfreelist(rp);
10680ee5cbfSDavid du Colombier 				rp = nil;
10780ee5cbfSDavid du Colombier 				break;
10880ee5cbfSDavid du Colombier 			}
1097dd7cddfSDavid du Colombier 
1107dd7cddfSDavid du Colombier 			name = rp->host->name;
1117dd7cddfSDavid du Colombier 			if(cn)
1127dd7cddfSDavid du Colombier 				rrcat(cn, rp);
1137dd7cddfSDavid du Colombier 			else
1147dd7cddfSDavid du Colombier 				rrfreelist(rp);
1157dd7cddfSDavid du Colombier 
1167dd7cddfSDavid du Colombier 			rp = dnresolve1(name, class, type, req, depth, recurse);
1177dd7cddfSDavid du Colombier 		}
1187dd7cddfSDavid du Colombier 
1197dd7cddfSDavid du Colombier 	/* distinction between not found and not good */
1205aa528faSDavid du Colombier 	if(rp == nil && status != nil && dp->nonexistent != 0)
1217dd7cddfSDavid du Colombier 		*status = dp->nonexistent;
1227dd7cddfSDavid du Colombier 
1235aa528faSDavid du Colombier 	procsetname(procname);
1245aa528faSDavid du Colombier 	free(procname);
1257dd7cddfSDavid du Colombier 	return randomize(rp);
1263e12c5d1SDavid du Colombier }
1273e12c5d1SDavid du Colombier 
1283e12c5d1SDavid du Colombier static RR*
1295aa528faSDavid du Colombier dnresolve1(char *name, int class, int type, Request *req, int depth,
1305aa528faSDavid du Colombier 	int recurse)
1313e12c5d1SDavid du Colombier {
1323e12c5d1SDavid du Colombier 	DN *dp, *nsdp;
1337dd7cddfSDavid du Colombier 	RR *rp, *nsrp, *dbnsrp;
1343e12c5d1SDavid du Colombier 	char *cp;
1353e12c5d1SDavid du Colombier 
1367dd7cddfSDavid du Colombier 	if(debug)
1375aa528faSDavid du Colombier 		syslog(0, LOG, "[%d] dnresolve1 %s %d %d",
1385aa528faSDavid du Colombier 			getpid(), name, type, class);
1397dd7cddfSDavid du Colombier 
1403e12c5d1SDavid du Colombier 	/* only class Cin implemented so far */
1413e12c5d1SDavid du Colombier 	if(class != Cin)
1425aa528faSDavid du Colombier 		return nil;
1433e12c5d1SDavid du Colombier 
1443e12c5d1SDavid du Colombier 	dp = dnlookup(name, class, 1);
1453e12c5d1SDavid du Colombier 
1467dd7cddfSDavid du Colombier 	/*
1477dd7cddfSDavid du Colombier 	 *  Try the cache first
1487dd7cddfSDavid du Colombier 	 */
1497dd7cddfSDavid du Colombier 	rp = rrlookup(dp, type, OKneg);
1505aa528faSDavid du Colombier 	if(rp)
1517dd7cddfSDavid du Colombier 		if(rp->db){
1527dd7cddfSDavid du Colombier 			/* unauthenticated db entries are hints */
1537dd7cddfSDavid du Colombier 			if(rp->auth)
1543e12c5d1SDavid du Colombier 				return rp;
1555aa528faSDavid du Colombier 		} else
1567dd7cddfSDavid du Colombier 			/* cached entry must still be valid */
1575aa528faSDavid du Colombier 			if(rp->ttl > now)
1587dd7cddfSDavid du Colombier 				/* but Tall entries are special */
1597dd7cddfSDavid du Colombier 				if(type != Tall || rp->query == Tall)
1607dd7cddfSDavid du Colombier 					return rp;
1615aa528faSDavid du Colombier 
1627dd7cddfSDavid du Colombier 	rrfreelist(rp);
1633e12c5d1SDavid du Colombier 
1647dd7cddfSDavid du Colombier 	/*
1657dd7cddfSDavid du Colombier 	 * try the cache for a canonical name. if found punt
1667dd7cddfSDavid du Colombier 	 * since we'll find it during the canonical name search
1677dd7cddfSDavid du Colombier 	 * in dnresolve().
1687dd7cddfSDavid du Colombier 	 */
1697dd7cddfSDavid du Colombier 	if(type != Tcname){
1707dd7cddfSDavid du Colombier 		rp = rrlookup(dp, Tcname, NOneg);
1717dd7cddfSDavid du Colombier 		rrfreelist(rp);
1723e12c5d1SDavid du Colombier 		if(rp)
1735aa528faSDavid du Colombier 			return nil;
1743e12c5d1SDavid du Colombier 	}
1753e12c5d1SDavid du Colombier 
1763e12c5d1SDavid du Colombier 	/*
1777dd7cddfSDavid du Colombier 	 *  if we're running as just a resolver, go to our
1787dd7cddfSDavid du Colombier 	 *  designated name servers
179219b2ee8SDavid du Colombier 	 */
1807dd7cddfSDavid du Colombier 	if(resolver){
1817dd7cddfSDavid du Colombier 		nsrp = randomize(getdnsservers(class));
1827dd7cddfSDavid du Colombier 		if(nsrp != nil) {
1837dd7cddfSDavid du Colombier 			if(netquery(dp, type, nsrp, req, depth+1)){
1847dd7cddfSDavid du Colombier 				rrfreelist(nsrp);
1857dd7cddfSDavid du Colombier 				return rrlookup(dp, type, OKneg);
1867dd7cddfSDavid du Colombier 			}
1877dd7cddfSDavid du Colombier 			rrfreelist(nsrp);
1887dd7cddfSDavid du Colombier 		}
189219b2ee8SDavid du Colombier 	}
190219b2ee8SDavid du Colombier 
191219b2ee8SDavid du Colombier 	/*
1923e12c5d1SDavid du Colombier  	 *  walk up the domain name looking for
1933e12c5d1SDavid du Colombier 	 *  a name server for the domain.
1943e12c5d1SDavid du Colombier 	 */
1953e12c5d1SDavid du Colombier 	for(cp = name; cp; cp = walkup(cp)){
1967dd7cddfSDavid du Colombier 		/*
1977dd7cddfSDavid du Colombier 		 *  if this is a local (served by us) domain,
1987dd7cddfSDavid du Colombier 		 *  return answer
1997dd7cddfSDavid du Colombier 		 */
2007dd7cddfSDavid du Colombier 		dbnsrp = randomize(dblookup(cp, class, Tns, 0, 0));
2017dd7cddfSDavid du Colombier 		if(dbnsrp && dbnsrp->local){
2027dd7cddfSDavid du Colombier 			rp = dblookup(name, class, type, 1, dbnsrp->ttl);
2037dd7cddfSDavid du Colombier 			rrfreelist(dbnsrp);
2047dd7cddfSDavid du Colombier 			return rp;
2057dd7cddfSDavid du Colombier 		}
2067dd7cddfSDavid du Colombier 
2077dd7cddfSDavid du Colombier 		/*
2087dd7cddfSDavid du Colombier 		 *  if recursion isn't set, just accept local
2097dd7cddfSDavid du Colombier 		 *  entries
2107dd7cddfSDavid du Colombier 		 */
2117dd7cddfSDavid du Colombier 		if(recurse == Dontrecurse){
2127dd7cddfSDavid du Colombier 			if(dbnsrp)
2137dd7cddfSDavid du Colombier 				rrfreelist(dbnsrp);
2147dd7cddfSDavid du Colombier 			continue;
2157dd7cddfSDavid du Colombier 		}
2167dd7cddfSDavid du Colombier 
2177dd7cddfSDavid du Colombier 		/* look for ns in cache */
2183e12c5d1SDavid du Colombier 		nsdp = dnlookup(cp, class, 0);
2197dd7cddfSDavid du Colombier 		nsrp = nil;
2203e12c5d1SDavid du Colombier 		if(nsdp)
2217dd7cddfSDavid du Colombier 			nsrp = randomize(rrlookup(nsdp, Tns, NOneg));
2227dd7cddfSDavid du Colombier 
2237dd7cddfSDavid du Colombier 		/* if the entry timed out, ignore it */
2247dd7cddfSDavid du Colombier 		if(nsrp && nsrp->ttl < now){
2257dd7cddfSDavid du Colombier 			rrfreelist(nsrp);
2267dd7cddfSDavid du Colombier 			nsrp = nil;
2277dd7cddfSDavid du Colombier 		}
2283e12c5d1SDavid du Colombier 
2293e12c5d1SDavid du Colombier 		if(nsrp){
2307dd7cddfSDavid du Colombier 			rrfreelist(dbnsrp);
2317dd7cddfSDavid du Colombier 
2327dd7cddfSDavid du Colombier 			/* try the name servers found in cache */
2337dd7cddfSDavid du Colombier 			if(netquery(dp, type, nsrp, req, depth+1)){
2343e12c5d1SDavid du Colombier 				rrfreelist(nsrp);
2357dd7cddfSDavid du Colombier 				return rrlookup(dp, type, OKneg);
2367dd7cddfSDavid du Colombier 			}
2377dd7cddfSDavid du Colombier 			rrfreelist(nsrp);
2387dd7cddfSDavid du Colombier 			continue;
2393e12c5d1SDavid du Colombier 		}
2403e12c5d1SDavid du Colombier 
2417dd7cddfSDavid du Colombier 		/* use ns from db */
2427dd7cddfSDavid du Colombier 		if(dbnsrp){
2437dd7cddfSDavid du Colombier 			/* try the name servers found in db */
2447dd7cddfSDavid du Colombier 			if(netquery(dp, type, dbnsrp, req, depth+1)){
2453e12c5d1SDavid du Colombier 				/* we got an answer */
2467dd7cddfSDavid du Colombier 				rrfreelist(dbnsrp);
2477dd7cddfSDavid du Colombier 				return rrlookup(dp, type, NOneg);
2483e12c5d1SDavid du Colombier 			}
2497dd7cddfSDavid du Colombier 			rrfreelist(dbnsrp);
2503e12c5d1SDavid du Colombier 		}
2513e12c5d1SDavid du Colombier 	}
2523e12c5d1SDavid du Colombier 
2537dd7cddfSDavid du Colombier 	/* settle for a non-authoritative answer */
2547dd7cddfSDavid du Colombier 	rp = rrlookup(dp, type, OKneg);
2557dd7cddfSDavid du Colombier 	if(rp)
2567dd7cddfSDavid du Colombier 		return rp;
2577dd7cddfSDavid du Colombier 
2587dd7cddfSDavid du Colombier 	/* noone answered.  try the database, we might have a chance. */
2597dd7cddfSDavid du Colombier 	return dblookup(name, class, type, 0, 0);
2603e12c5d1SDavid du Colombier }
2613e12c5d1SDavid du Colombier 
2623e12c5d1SDavid du Colombier /*
2635aa528faSDavid du Colombier  *  walk a domain name one element to the right.
2645aa528faSDavid du Colombier  *  return a pointer to that element.
2653e12c5d1SDavid du Colombier  *  in other words, return a pointer to the parent domain name.
2663e12c5d1SDavid du Colombier  */
2673e12c5d1SDavid du Colombier char*
2683e12c5d1SDavid du Colombier walkup(char *name)
2693e12c5d1SDavid du Colombier {
2703e12c5d1SDavid du Colombier 	char *cp;
2713e12c5d1SDavid du Colombier 
2723e12c5d1SDavid du Colombier 	cp = strchr(name, '.');
2733e12c5d1SDavid du Colombier 	if(cp)
2743e12c5d1SDavid du Colombier 		return cp+1;
2753e12c5d1SDavid du Colombier 	else if(*name)
2763e12c5d1SDavid du Colombier 		return "";
2773e12c5d1SDavid du Colombier 	else
2783e12c5d1SDavid du Colombier 		return 0;
2793e12c5d1SDavid du Colombier }
2803e12c5d1SDavid du Colombier 
2813e12c5d1SDavid du Colombier /*
2823e12c5d1SDavid du Colombier  *  Get a udpport for requests and replies.  Put the port
2833e12c5d1SDavid du Colombier  *  into "headers" mode.
2843e12c5d1SDavid du Colombier  */
2853e12c5d1SDavid du Colombier static char *hmsg = "headers";
2861e8349ebSDavid du Colombier static char *ohmsg = "oldheaders";
2873e12c5d1SDavid du Colombier 
288dc5a79c1SDavid du Colombier int
289*28a8a86bSDavid du Colombier udpport(char *mtpt)
2903e12c5d1SDavid du Colombier {
2913e12c5d1SDavid du Colombier 	int fd, ctl;
2925aa528faSDavid du Colombier 	char ds[64], adir[64];
2933e12c5d1SDavid du Colombier 
2943e12c5d1SDavid du Colombier 	/* get a udp port */
295*28a8a86bSDavid du Colombier 	snprint(ds, sizeof ds, "%s/udp!*!0", (mtpt? mtpt: "/net"));
2967dd7cddfSDavid du Colombier 	ctl = announce(ds, adir);
2977dd7cddfSDavid du Colombier 	if(ctl < 0){
2987dd7cddfSDavid du Colombier 		/* warning("can't get udp port"); */
299bd389b36SDavid du Colombier 		return -1;
300bd389b36SDavid du Colombier 	}
3013e12c5d1SDavid du Colombier 
3023e12c5d1SDavid du Colombier 	/* turn on header style interface */
303bd389b36SDavid du Colombier 	if(write(ctl, hmsg, strlen(hmsg)) , 0){
3047dd7cddfSDavid du Colombier 		close(ctl);
305bd389b36SDavid du Colombier 		warning(hmsg);
306bd389b36SDavid du Colombier 		return -1;
307bd389b36SDavid du Colombier 	}
3081e8349ebSDavid du Colombier 	write(ctl, ohmsg, strlen(ohmsg));
3093e12c5d1SDavid du Colombier 
3107dd7cddfSDavid du Colombier 	/* grab the data file */
311*28a8a86bSDavid du Colombier 	snprint(ds, sizeof ds, "%s/data", adir);
3127dd7cddfSDavid du Colombier 	fd = open(ds, ORDWR);
3133e12c5d1SDavid du Colombier 	close(ctl);
3145aa528faSDavid du Colombier 	if(fd < 0)
315*28a8a86bSDavid du Colombier 		warning("can't open udp port %s: %r", ds);
3163e12c5d1SDavid du Colombier 	return fd;
3173e12c5d1SDavid du Colombier }
3183e12c5d1SDavid du Colombier 
319dc5a79c1SDavid du Colombier int
320dc5a79c1SDavid du Colombier mkreq(DN *dp, int type, uchar *buf, int flags, ushort reqno)
3213e12c5d1SDavid du Colombier {
3223e12c5d1SDavid du Colombier 	DNSmsg m;
3233e12c5d1SDavid du Colombier 	int len;
324d9dc5dd1SDavid du Colombier 	OUdphdr *uh = (OUdphdr*)buf;
3253e12c5d1SDavid du Colombier 
3263e12c5d1SDavid du Colombier 	/* stuff port number into output buffer */
3277dd7cddfSDavid du Colombier 	memset(uh, 0, sizeof(*uh));
3287dd7cddfSDavid du Colombier 	hnputs(uh->rport, 53);
3293e12c5d1SDavid du Colombier 
3303e12c5d1SDavid du Colombier 	/* make request and convert it to output format */
3313e12c5d1SDavid du Colombier 	memset(&m, 0, sizeof(m));
332dc5a79c1SDavid du Colombier 	m.flags = flags;
3333e12c5d1SDavid du Colombier 	m.id = reqno;
3343e12c5d1SDavid du Colombier 	m.qd = rralloc(type);
3353e12c5d1SDavid du Colombier 	m.qd->owner = dp;
3363e12c5d1SDavid du Colombier 	m.qd->type = type;
337d9dc5dd1SDavid du Colombier 	len = convDNS2M(&m, &buf[OUdphdrsize], Maxudp);
3383e12c5d1SDavid du Colombier 	if(len < 0)
3395aa528faSDavid du Colombier 		abort();	/* "can't convert" */
3407dd7cddfSDavid du Colombier 	rrfree(m.qd);
3413e12c5d1SDavid du Colombier 	return len;
3423e12c5d1SDavid du Colombier }
3433e12c5d1SDavid du Colombier 
3447dd7cddfSDavid du Colombier /* for alarms in readreply */
3453e12c5d1SDavid du Colombier static void
3463e12c5d1SDavid du Colombier ding(void *x, char *msg)
3473e12c5d1SDavid du Colombier {
3483e12c5d1SDavid du Colombier 	USED(x);
3493e12c5d1SDavid du Colombier 	if(strcmp(msg, "alarm") == 0)
3503e12c5d1SDavid du Colombier 		noted(NCONT);
3513e12c5d1SDavid du Colombier 	else
3523e12c5d1SDavid du Colombier 		noted(NDFLT);
3533e12c5d1SDavid du Colombier }
3547dd7cddfSDavid du Colombier 
3557dd7cddfSDavid du Colombier static void
3567dd7cddfSDavid du Colombier freeanswers(DNSmsg *mp)
3573e12c5d1SDavid du Colombier {
3587dd7cddfSDavid du Colombier 	rrfreelist(mp->qd);
3597dd7cddfSDavid du Colombier 	rrfreelist(mp->an);
3607dd7cddfSDavid du Colombier 	rrfreelist(mp->ns);
3617dd7cddfSDavid du Colombier 	rrfreelist(mp->ar);
3625aa528faSDavid du Colombier 	mp->qd = mp->an = mp->ns = mp->ar = nil;
3637dd7cddfSDavid du Colombier }
3647dd7cddfSDavid du Colombier 
3657dd7cddfSDavid du Colombier /*
3665aa528faSDavid du Colombier  *  read replies to a request.  ignore any of the wrong type.
3675aa528faSDavid du Colombier  *  wait at most 5 seconds.
3687dd7cddfSDavid du Colombier  */
3697dd7cddfSDavid du Colombier static int
3707dd7cddfSDavid du Colombier readreply(int fd, DN *dp, int type, ushort req,
3717dd7cddfSDavid du Colombier 	  uchar *ibuf, DNSmsg *mp, ulong endtime, Request *reqp)
3727dd7cddfSDavid du Colombier {
3737dd7cddfSDavid du Colombier 	char *err;
3747dd7cddfSDavid du Colombier 	int len;
3757dd7cddfSDavid du Colombier 	ulong now;
3763e12c5d1SDavid du Colombier 	RR *rp;
3777dd7cddfSDavid du Colombier 
3787dd7cddfSDavid du Colombier 	notify(ding);
3797dd7cddfSDavid du Colombier 
3807dd7cddfSDavid du Colombier 	for(; ; freeanswers(mp)){
3817dd7cddfSDavid du Colombier 		now = time(0);
3827dd7cddfSDavid du Colombier 		if(now >= endtime)
3837dd7cddfSDavid du Colombier 			return -1;	/* timed out */
3847dd7cddfSDavid du Colombier 
3857dd7cddfSDavid du Colombier 		/* timed read */
38659cc4ca5SDavid du Colombier 		alarm((endtime - now) * 1000);
387d9dc5dd1SDavid du Colombier 		len = read(fd, ibuf, OUdphdrsize+Maxudpin);
3887dd7cddfSDavid du Colombier 		alarm(0);
389d9dc5dd1SDavid du Colombier 		len -= OUdphdrsize;
3907dd7cddfSDavid du Colombier 		if(len < 0)
3917dd7cddfSDavid du Colombier 			return -1;	/* timed out */
3927dd7cddfSDavid du Colombier 
3937dd7cddfSDavid du Colombier 		/* convert into internal format  */
3947dd7cddfSDavid du Colombier 		memset(mp, 0, sizeof(*mp));
3955aa528faSDavid du Colombier 		err = convM2DNS(&ibuf[OUdphdrsize], len, mp, nil);
3967dd7cddfSDavid du Colombier 		if(err){
3975aa528faSDavid du Colombier 			syslog(0, LOG, "input err: %s: %I", err, ibuf);
3987dd7cddfSDavid du Colombier 			continue;
3997dd7cddfSDavid du Colombier 		}
4007dd7cddfSDavid du Colombier 		if(debug)
4017dd7cddfSDavid du Colombier 			logreply(reqp->id, ibuf, mp);
4027dd7cddfSDavid du Colombier 
4037dd7cddfSDavid du Colombier 		/* answering the right question? */
4047dd7cddfSDavid du Colombier 		if(mp->id != req){
4057dd7cddfSDavid du Colombier 			syslog(0, LOG, "%d: id %d instead of %d: %I", reqp->id,
4067dd7cddfSDavid du Colombier 					mp->id, req, ibuf);
4077dd7cddfSDavid du Colombier 			continue;
4087dd7cddfSDavid du Colombier 		}
4097dd7cddfSDavid du Colombier 		if(mp->qd == 0){
4107dd7cddfSDavid du Colombier 			syslog(0, LOG, "%d: no question RR: %I", reqp->id, ibuf);
4117dd7cddfSDavid du Colombier 			continue;
4127dd7cddfSDavid du Colombier 		}
4137dd7cddfSDavid du Colombier 		if(mp->qd->owner != dp){
4145aa528faSDavid du Colombier 			syslog(0, LOG, "%d: owner %s instead of %s: %I",
4155aa528faSDavid du Colombier 				reqp->id, mp->qd->owner->name, dp->name, ibuf);
4167dd7cddfSDavid du Colombier 			continue;
4177dd7cddfSDavid du Colombier 		}
4187dd7cddfSDavid du Colombier 		if(mp->qd->type != type){
4195aa528faSDavid du Colombier 			syslog(0, LOG, "%d: type %d instead of %d: %I",
4205aa528faSDavid du Colombier 				reqp->id, mp->qd->type, type, ibuf);
4217dd7cddfSDavid du Colombier 			continue;
4227dd7cddfSDavid du Colombier 		}
4237dd7cddfSDavid du Colombier 
4247dd7cddfSDavid du Colombier 		/* remember what request this is in answer to */
4257dd7cddfSDavid du Colombier 		for(rp = mp->an; rp; rp = rp->next)
4267dd7cddfSDavid du Colombier 			rp->query = type;
4277dd7cddfSDavid du Colombier 
4287dd7cddfSDavid du Colombier 		return 0;
4297dd7cddfSDavid du Colombier 	}
4307dd7cddfSDavid du Colombier }
4317dd7cddfSDavid du Colombier 
4327dd7cddfSDavid du Colombier /*
4337dd7cddfSDavid du Colombier  *	return non-0 if first list includes second list
4347dd7cddfSDavid du Colombier  */
4357dd7cddfSDavid du Colombier int
4367dd7cddfSDavid du Colombier contains(RR *rp1, RR *rp2)
4377dd7cddfSDavid du Colombier {
4387dd7cddfSDavid du Colombier 	RR *trp1, *trp2;
4397dd7cddfSDavid du Colombier 
4407dd7cddfSDavid du Colombier 	for(trp2 = rp2; trp2; trp2 = trp2->next){
4417dd7cddfSDavid du Colombier 		for(trp1 = rp1; trp1; trp1 = trp1->next){
4427dd7cddfSDavid du Colombier 			if(trp1->type == trp2->type)
4437dd7cddfSDavid du Colombier 			if(trp1->host == trp2->host)
4447dd7cddfSDavid du Colombier 			if(trp1->owner == trp2->owner)
4457dd7cddfSDavid du Colombier 				break;
4467dd7cddfSDavid du Colombier 		}
4475aa528faSDavid du Colombier 		if(trp1 == nil)
4487dd7cddfSDavid du Colombier 			return 0;
4497dd7cddfSDavid du Colombier 	}
4507dd7cddfSDavid du Colombier 	return 1;
4517dd7cddfSDavid du Colombier }
4527dd7cddfSDavid du Colombier 
4537dd7cddfSDavid du Colombier 
4547dd7cddfSDavid du Colombier typedef struct Dest	Dest;
4557dd7cddfSDavid du Colombier struct Dest
4567dd7cddfSDavid du Colombier {
4577dd7cddfSDavid du Colombier 	uchar	a[IPaddrlen];	/* ip address */
4587dd7cddfSDavid du Colombier 	DN	*s;		/* name server */
4597dd7cddfSDavid du Colombier 	int	nx;		/* number of transmissions */
4607dd7cddfSDavid du Colombier 	int	code;
4617dd7cddfSDavid du Colombier };
4627dd7cddfSDavid du Colombier 
4636b6b9ac8SDavid du Colombier 
4646b6b9ac8SDavid du Colombier /*
4656b6b9ac8SDavid du Colombier  *  return multicast version if any
4666b6b9ac8SDavid du Colombier  */
4676b6b9ac8SDavid du Colombier int
4686b6b9ac8SDavid du Colombier ipisbm(uchar *ip)
4696b6b9ac8SDavid du Colombier {
4706b6b9ac8SDavid du Colombier 	if(isv4(ip)){
4715aa528faSDavid du Colombier 		if (ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0 ||
4725aa528faSDavid du Colombier 		    ipcmp(ip, IPv4bcast) == 0)
4736b6b9ac8SDavid du Colombier 			return 4;
4745aa528faSDavid du Colombier 	} else
4756b6b9ac8SDavid du Colombier 		if(ip[0] == 0xff)
4766b6b9ac8SDavid du Colombier 			return 6;
4776b6b9ac8SDavid du Colombier 	return 0;
4786b6b9ac8SDavid du Colombier }
4796b6b9ac8SDavid du Colombier 
480*28a8a86bSDavid du Colombier static Ndbtuple *indoms, *innmsrvs, *outnmsrvs;
481*28a8a86bSDavid du Colombier static QLock readlock;
482*28a8a86bSDavid du Colombier 
483*28a8a86bSDavid du Colombier /*
484*28a8a86bSDavid du Colombier  * is this domain (or DOMAIN or Domain or dOMAIN)
485*28a8a86bSDavid du Colombier  * internal to our organisation (behind our firewall)?
486*28a8a86bSDavid du Colombier  */
487*28a8a86bSDavid du Colombier static int
488*28a8a86bSDavid du Colombier insideaddr(char *dom)
489*28a8a86bSDavid du Colombier {
490*28a8a86bSDavid du Colombier 	int domlen, vallen;
491*28a8a86bSDavid du Colombier 	Ndb *db;
492*28a8a86bSDavid du Colombier 	Ndbs s;
493*28a8a86bSDavid du Colombier 	Ndbtuple *t;
494*28a8a86bSDavid du Colombier 
495*28a8a86bSDavid du Colombier 	if (0 && indoms == nil) {	/* not ready for prime time */
496*28a8a86bSDavid du Colombier 		db = ndbopen("/lib/ndb/local");
497*28a8a86bSDavid du Colombier 		if (db != nil) {
498*28a8a86bSDavid du Colombier 			qlock(&readlock);
499*28a8a86bSDavid du Colombier 			if (indoms == nil) {	/* retest under lock */
500*28a8a86bSDavid du Colombier 				free(ndbgetvalue(db, &s, "sys", "inside-dom",
501*28a8a86bSDavid du Colombier 					"dom", &indoms));
502*28a8a86bSDavid du Colombier 				free(ndbgetvalue(db, &s, "sys", "inside-ns",
503*28a8a86bSDavid du Colombier 					"ip", &innmsrvs));
504*28a8a86bSDavid du Colombier 				free(ndbgetvalue(db, &s, "sys", "outside-ns",
505*28a8a86bSDavid du Colombier 					"ip", &outnmsrvs));
506*28a8a86bSDavid du Colombier 			}
507*28a8a86bSDavid du Colombier 			qunlock(&readlock);
508*28a8a86bSDavid du Colombier 			ndbclose(db);	/* destroys *indoms, *innmsrvs? */
509*28a8a86bSDavid du Colombier 		}
510*28a8a86bSDavid du Colombier 	}
511*28a8a86bSDavid du Colombier 	if (indoms == nil)
512*28a8a86bSDavid du Colombier 		return 1;	/* no "inside" sys, try inside nameservers */
513*28a8a86bSDavid du Colombier 
514*28a8a86bSDavid du Colombier 	domlen = strlen(dom);
515*28a8a86bSDavid du Colombier 	for (t = indoms; t != nil; t = t->entry) {
516*28a8a86bSDavid du Colombier 		if (strcmp(t->attr, "dom") != 0)
517*28a8a86bSDavid du Colombier 			continue;
518*28a8a86bSDavid du Colombier 		vallen = strlen(t->val);
519*28a8a86bSDavid du Colombier 		if (cistrcmp(dom, t->val) == 0 ||
520*28a8a86bSDavid du Colombier 		    domlen > vallen &&
521*28a8a86bSDavid du Colombier 		     cistrcmp(dom + domlen - vallen, t->val) == 0 &&
522*28a8a86bSDavid du Colombier 		     dom[domlen - vallen - 1] == '.')
523*28a8a86bSDavid du Colombier 			return 1;
524*28a8a86bSDavid du Colombier 	}
525*28a8a86bSDavid du Colombier 	return 0;
526*28a8a86bSDavid du Colombier }
527*28a8a86bSDavid du Colombier 
528*28a8a86bSDavid du Colombier static int
529*28a8a86bSDavid du Colombier insidens(uchar *ip)
530*28a8a86bSDavid du Colombier {
531*28a8a86bSDavid du Colombier 	uchar ipa[IPaddrlen];
532*28a8a86bSDavid du Colombier 	Ndbtuple *t;
533*28a8a86bSDavid du Colombier 
534*28a8a86bSDavid du Colombier 	for (t = innmsrvs; t != nil; t = t->entry)
535*28a8a86bSDavid du Colombier 		if (strcmp(t->attr, "ip") == 0) {
536*28a8a86bSDavid du Colombier 			parseip(ipa, t->val);
537*28a8a86bSDavid du Colombier 			if (memcmp(ipa, ip, sizeof ipa) == 0)
538*28a8a86bSDavid du Colombier 				return 1;
539*28a8a86bSDavid du Colombier 		}
540*28a8a86bSDavid du Colombier 	return 0;
541*28a8a86bSDavid du Colombier }
542*28a8a86bSDavid du Colombier 
543*28a8a86bSDavid du Colombier static uchar *
544*28a8a86bSDavid du Colombier outsidens(void)
545*28a8a86bSDavid du Colombier {
546*28a8a86bSDavid du Colombier 	Ndbtuple *t;
547*28a8a86bSDavid du Colombier 	static uchar ipa[IPaddrlen];
548*28a8a86bSDavid du Colombier 
549*28a8a86bSDavid du Colombier 	for (t = outnmsrvs; t != nil; t = t->entry)
550*28a8a86bSDavid du Colombier 		if (strcmp(t->attr, "ip") == 0) {
551*28a8a86bSDavid du Colombier 			parseip(ipa, t->val);
552*28a8a86bSDavid du Colombier 			return ipa;
553*28a8a86bSDavid du Colombier 		}
554*28a8a86bSDavid du Colombier 	return nil;
555*28a8a86bSDavid du Colombier }
556*28a8a86bSDavid du Colombier 
5577dd7cddfSDavid du Colombier /*
5587dd7cddfSDavid du Colombier  *  Get next server address
5597dd7cddfSDavid du Colombier  */
5607dd7cddfSDavid du Colombier static int
561*28a8a86bSDavid du Colombier serveraddrs(DN *dp, RR *nsrp, Dest *dest, int nd, int depth, Request *reqp)
5627dd7cddfSDavid du Colombier {
5637dd7cddfSDavid du Colombier 	RR *rp, *arp, *trp;
5647dd7cddfSDavid du Colombier 	Dest *cur;
5657dd7cddfSDavid du Colombier 
5667dd7cddfSDavid du Colombier 	if(nd >= Maxdest)
5677dd7cddfSDavid du Colombier 		return 0;
5687dd7cddfSDavid du Colombier 
5697dd7cddfSDavid du Colombier 	/*
5707dd7cddfSDavid du Colombier 	 *  look for a server whose address we already know.
5717dd7cddfSDavid du Colombier 	 *  if we find one, mark it so we ignore this on
5727dd7cddfSDavid du Colombier 	 *  subsequent passes.
5737dd7cddfSDavid du Colombier 	 */
5747dd7cddfSDavid du Colombier 	arp = 0;
5757dd7cddfSDavid du Colombier 	for(rp = nsrp; rp; rp = rp->next){
57634f77ae3SDavid du Colombier 		assert(rp->magic == RRmagic);
5777dd7cddfSDavid du Colombier 		if(rp->marker)
5787dd7cddfSDavid du Colombier 			continue;
5797dd7cddfSDavid du Colombier 		arp = rrlookup(rp->host, Ta, NOneg);
5807dd7cddfSDavid du Colombier 		if(arp){
5817dd7cddfSDavid du Colombier 			rp->marker = 1;
5827dd7cddfSDavid du Colombier 			break;
5837dd7cddfSDavid du Colombier 		}
5847dd7cddfSDavid du Colombier 		arp = dblookup(rp->host->name, Cin, Ta, 0, 0);
5857dd7cddfSDavid du Colombier 		if(arp){
5867dd7cddfSDavid du Colombier 			rp->marker = 1;
5877dd7cddfSDavid du Colombier 			break;
5887dd7cddfSDavid du Colombier 		}
5897dd7cddfSDavid du Colombier 	}
5907dd7cddfSDavid du Colombier 
5917dd7cddfSDavid du Colombier 	/*
5927dd7cddfSDavid du Colombier 	 *  if the cache and database lookup didn't find any new
5937dd7cddfSDavid du Colombier 	 *  server addresses, try resolving one via the network.
5947dd7cddfSDavid du Colombier 	 *  Mark any we try to resolve so we don't try a second time.
5957dd7cddfSDavid du Colombier 	 */
5965aa528faSDavid du Colombier 	if(arp == 0)
5977dd7cddfSDavid du Colombier 		for(rp = nsrp; rp; rp = rp->next){
5987dd7cddfSDavid du Colombier 			if(rp->marker)
5997dd7cddfSDavid du Colombier 				continue;
6007dd7cddfSDavid du Colombier 			rp->marker = 1;
6017dd7cddfSDavid du Colombier 
6027dd7cddfSDavid du Colombier 			/*
6037dd7cddfSDavid du Colombier 			 *  avoid loops looking up a server under itself
6047dd7cddfSDavid du Colombier 			 */
6057dd7cddfSDavid du Colombier 			if(subsume(rp->owner->name, rp->host->name))
6067dd7cddfSDavid du Colombier 				continue;
6077dd7cddfSDavid du Colombier 
6085aa528faSDavid du Colombier 			arp = dnresolve(rp->host->name, Cin, Ta, reqp, 0,
6095aa528faSDavid du Colombier 				depth+1, Recurse, 1, 0);
6107dd7cddfSDavid du Colombier 			rrfreelist(rrremneg(&arp));
6117dd7cddfSDavid du Colombier 			if(arp)
6127dd7cddfSDavid du Colombier 				break;
6137dd7cddfSDavid du Colombier 		}
6147dd7cddfSDavid du Colombier 
6157dd7cddfSDavid du Colombier 	/* use any addresses that we found */
6167dd7cddfSDavid du Colombier 	for(trp = arp; trp; trp = trp->next){
6177dd7cddfSDavid du Colombier 		if(nd >= Maxdest)
6187dd7cddfSDavid du Colombier 			break;
6196b6b9ac8SDavid du Colombier 		cur = &dest[nd];
6207dd7cddfSDavid du Colombier 		parseip(cur->a, trp->ip->name);
621*28a8a86bSDavid du Colombier 		if (ipisbm(cur->a) ||
622*28a8a86bSDavid du Colombier 		    !insideaddr(dp->name) && insidens(cur->a))
6236b6b9ac8SDavid du Colombier 			continue;
6247dd7cddfSDavid du Colombier 		cur->nx = 0;
6257dd7cddfSDavid du Colombier 		cur->s = trp->owner;
6267dd7cddfSDavid du Colombier 		cur->code = Rtimeout;
6276b6b9ac8SDavid du Colombier 		nd++;
6287dd7cddfSDavid du Colombier 	}
6297dd7cddfSDavid du Colombier 	rrfreelist(arp);
6307dd7cddfSDavid du Colombier 	return nd;
6317dd7cddfSDavid du Colombier }
6327dd7cddfSDavid du Colombier 
6337dd7cddfSDavid du Colombier /*
6347dd7cddfSDavid du Colombier  *  cache negative responses
6357dd7cddfSDavid du Colombier  */
6367dd7cddfSDavid du Colombier static void
6377dd7cddfSDavid du Colombier cacheneg(DN *dp, int type, int rcode, RR *soarr)
6387dd7cddfSDavid du Colombier {
6397dd7cddfSDavid du Colombier 	RR *rp;
6407dd7cddfSDavid du Colombier 	DN *soaowner;
6419a747e4fSDavid du Colombier 	ulong ttl;
6427dd7cddfSDavid du Colombier 
6435aa528faSDavid du Colombier 	/* no cache time specified, don't make anything up */
6447dd7cddfSDavid du Colombier 	if(soarr != nil){
6457dd7cddfSDavid du Colombier 		if(soarr->next != nil){
6467dd7cddfSDavid du Colombier 			rrfreelist(soarr->next);
6477dd7cddfSDavid du Colombier 			soarr->next = nil;
6487dd7cddfSDavid du Colombier 		}
6497dd7cddfSDavid du Colombier 		soaowner = soarr->owner;
6507dd7cddfSDavid du Colombier 	} else
6517dd7cddfSDavid du Colombier 		soaowner = nil;
6527dd7cddfSDavid du Colombier 
6539a747e4fSDavid du Colombier 	/* the attach can cause soarr to be freed so mine it now */
6549a747e4fSDavid du Colombier 	if(soarr != nil && soarr->soa != nil)
6559a747e4fSDavid du Colombier 		ttl = soarr->soa->minttl+now;
6569a747e4fSDavid du Colombier 	else
6579a747e4fSDavid du Colombier 		ttl = 5*Min;
6589a747e4fSDavid du Colombier 
6597dd7cddfSDavid du Colombier 	/* add soa and negative RR to the database */
6607dd7cddfSDavid du Colombier 	rrattach(soarr, 1);
6617dd7cddfSDavid du Colombier 
6627dd7cddfSDavid du Colombier 	rp = rralloc(type);
6637dd7cddfSDavid du Colombier 	rp->owner = dp;
6647dd7cddfSDavid du Colombier 	rp->negative = 1;
6657dd7cddfSDavid du Colombier 	rp->negsoaowner = soaowner;
6667dd7cddfSDavid du Colombier 	rp->negrcode = rcode;
6679a747e4fSDavid du Colombier 	rp->ttl = ttl;
6687dd7cddfSDavid du Colombier 	rrattach(rp, 1);
6697dd7cddfSDavid du Colombier }
6707dd7cddfSDavid du Colombier 
6717dd7cddfSDavid du Colombier /*
6727dd7cddfSDavid du Colombier  *  query name servers.  If the name server returns a pointer to another
6737dd7cddfSDavid du Colombier  *  name server, recurse.
6747dd7cddfSDavid du Colombier  */
6757dd7cddfSDavid du Colombier static int
6765aa528faSDavid du Colombier netquery1(int fd, DN *dp, int type, RR *nsrp, Request *reqp, int depth,
677*28a8a86bSDavid du Colombier 	uchar *ibuf, uchar *obuf, int waitsecs, int inns)
6787dd7cddfSDavid du Colombier {
6797dd7cddfSDavid du Colombier 	int ndest, j, len, replywaits, rv;
6807dd7cddfSDavid du Colombier 	ulong endtime;
6815aa528faSDavid du Colombier 	ushort req;
6825aa528faSDavid du Colombier 	char buf[12];
6835aa528faSDavid du Colombier 	DN *ndp;
6845aa528faSDavid du Colombier 	DNSmsg m;
6855aa528faSDavid du Colombier 	Dest *p, *l, *np;
6865aa528faSDavid du Colombier 	Dest dest[Maxdest];
6875aa528faSDavid du Colombier 	RR *tp, *soarr;
6883e12c5d1SDavid du Colombier 
6893e12c5d1SDavid du Colombier 	/* pack request into a message */
6907dd7cddfSDavid du Colombier 	req = rand();
691dc5a79c1SDavid du Colombier 	len = mkreq(dp, type, obuf, Frecurse|Oquery, req);
6923e12c5d1SDavid du Colombier 
6937dd7cddfSDavid du Colombier 	/* no server addresses yet */
6947dd7cddfSDavid du Colombier 	l = dest;
6957dd7cddfSDavid du Colombier 
6963e12c5d1SDavid du Colombier 	/*
6973e12c5d1SDavid du Colombier 	 *  transmit requests and wait for answers.
698219b2ee8SDavid du Colombier 	 *  at most Maxtrans attempts to each address.
6993e12c5d1SDavid du Colombier 	 *  each cycle send one more message than the previous.
7003e12c5d1SDavid du Colombier 	 */
7017dd7cddfSDavid du Colombier 	for(ndest = 1; ndest < Maxdest; ndest++){
7023e12c5d1SDavid du Colombier 		p = dest;
7037dd7cddfSDavid du Colombier 
7047dd7cddfSDavid du Colombier 		endtime = time(0);
7057dd7cddfSDavid du Colombier 		if(endtime >= reqp->aborttime)
7063e12c5d1SDavid du Colombier 			break;
7073e12c5d1SDavid du Colombier 
7087dd7cddfSDavid du Colombier 		/* get a server address if we need one */
7097dd7cddfSDavid du Colombier 		if(ndest > l - p){
710*28a8a86bSDavid du Colombier 			j = serveraddrs(dp, nsrp, dest, l - p, depth, reqp);
7117dd7cddfSDavid du Colombier 			l = &dest[j];
7127dd7cddfSDavid du Colombier 		}
7137dd7cddfSDavid du Colombier 
7147dd7cddfSDavid du Colombier 		/* no servers, punt */
7157dd7cddfSDavid du Colombier 		if(l == dest)
716*28a8a86bSDavid du Colombier 			if (0 && inside) {	/* not ready for prime time */
717*28a8a86bSDavid du Colombier 				/* HACK: use sys=outside ips */
718*28a8a86bSDavid du Colombier 				if (0 && outsidens() == nil)
719*28a8a86bSDavid du Colombier 					sysfatal("no outside-ns in ndb");
720*28a8a86bSDavid du Colombier 				p = dest;
721*28a8a86bSDavid du Colombier 				memmove(p->a, outsidens(), sizeof p->a);
722*28a8a86bSDavid du Colombier 				p->s = dnlookup("outside", Cin, 1);
723*28a8a86bSDavid du Colombier 				p->nx = p->code = 0;
724*28a8a86bSDavid du Colombier 				l = p + 1;
725*28a8a86bSDavid du Colombier 			} else {
726*28a8a86bSDavid du Colombier syslog(0, LOG, "netquery1: no servers for %s", dp->name);	// DEBUG
7277dd7cddfSDavid du Colombier 				break;
728*28a8a86bSDavid du Colombier 			}
7297dd7cddfSDavid du Colombier 
7307dd7cddfSDavid du Colombier 		/* send to first 'ndest' destinations */
7317dd7cddfSDavid du Colombier 		j = 0;
7327dd7cddfSDavid du Colombier 		for(; p < &dest[ndest] && p < l; p++){
7337dd7cddfSDavid du Colombier 			/* skip destinations we've finished with */
7347dd7cddfSDavid du Colombier 			if(p->nx >= Maxtrans)
7357dd7cddfSDavid du Colombier 				continue;
7367dd7cddfSDavid du Colombier 
7377dd7cddfSDavid du Colombier 			j++;
7387dd7cddfSDavid du Colombier 
7397dd7cddfSDavid du Colombier 			/* exponential backoff of requests */
7407dd7cddfSDavid du Colombier 			if((1<<p->nx) > ndest)
7417dd7cddfSDavid du Colombier 				continue;
7427dd7cddfSDavid du Colombier 
7435aa528faSDavid du Colombier 			memmove(obuf, p->a, sizeof p->a);
744*28a8a86bSDavid du Colombier 			procsetname("req slave: %sside query to %I/%s %s %s",
745*28a8a86bSDavid du Colombier 				(inns? "in": "out"), obuf, p->s->name, dp->name,
746*28a8a86bSDavid du Colombier 				rrname(type, buf, sizeof buf));
747219b2ee8SDavid du Colombier 			if(debug)
7487dd7cddfSDavid du Colombier 				logsend(reqp->id, depth, obuf, p->s->name,
7497dd7cddfSDavid du Colombier 					dp->name, type);
750*28a8a86bSDavid du Colombier 
751*28a8a86bSDavid du Colombier 			/* actually send the UDP packet */
752d9dc5dd1SDavid du Colombier 			if(write(fd, obuf, len + OUdphdrsize) < 0)
753bd389b36SDavid du Colombier 				warning("sending udp msg %r");
7547dd7cddfSDavid du Colombier 			p->nx++;
7553e12c5d1SDavid du Colombier 		}
7563e12c5d1SDavid du Colombier 		if(j == 0)
7573e12c5d1SDavid du Colombier 			break;		/* no destinations left */
7583e12c5d1SDavid du Colombier 
759*28a8a86bSDavid du Colombier 		endtime = time(0) + waitsecs;
7607dd7cddfSDavid du Colombier 		if(endtime > reqp->aborttime)
7617dd7cddfSDavid du Colombier 			endtime = reqp->aborttime;
7627dd7cddfSDavid du Colombier 
7637dd7cddfSDavid du Colombier 		for(replywaits = 0; replywaits < ndest; replywaits++){
764*28a8a86bSDavid du Colombier 			procsetname(
765*28a8a86bSDavid du Colombier 			    "req slave: reading %sside reply from %I for %s %s",
766*28a8a86bSDavid du Colombier 				(inns? "in": "out"), obuf, dp->name,
767*28a8a86bSDavid du Colombier 				rrname(type, buf, sizeof buf));
7685aa528faSDavid du Colombier 			memset(&m, 0, sizeof m);
7697dd7cddfSDavid du Colombier 			if(readreply(fd, dp, type, req, ibuf, &m, endtime, reqp) < 0)
7703e12c5d1SDavid du Colombier 				break;		/* timed out */
7713e12c5d1SDavid du Colombier 
7727dd7cddfSDavid du Colombier 			/* find responder */
7733e12c5d1SDavid du Colombier 			for(p = dest; p < l; p++)
7747dd7cddfSDavid du Colombier 				if(memcmp(p->a, ibuf, sizeof(p->a)) == 0)
7753e12c5d1SDavid du Colombier 					break;
7767dd7cddfSDavid du Colombier 
7777dd7cddfSDavid du Colombier 			/* remove all addrs of responding server from list */
7787dd7cddfSDavid du Colombier 			for(np = dest; np < l; np++)
7797dd7cddfSDavid du Colombier 				if(np->s == p->s)
7807dd7cddfSDavid du Colombier 					p->nx = Maxtrans;
7817dd7cddfSDavid du Colombier 
78259cc4ca5SDavid du Colombier 			/* ignore any error replies */
78359cc4ca5SDavid du Colombier 			if((m.flags & Rmask) == Rserver){
7847dd7cddfSDavid du Colombier 				rrfreelist(m.qd);
7857dd7cddfSDavid du Colombier 				rrfreelist(m.an);
7867dd7cddfSDavid du Colombier 				rrfreelist(m.ar);
7877dd7cddfSDavid du Colombier 				rrfreelist(m.ns);
7887dd7cddfSDavid du Colombier 				if(p != l)
7897dd7cddfSDavid du Colombier 					p->code = Rserver;
7907dd7cddfSDavid du Colombier 				continue;
7913e12c5d1SDavid du Colombier 			}
7923e12c5d1SDavid du Colombier 
79359cc4ca5SDavid du Colombier 			/* ignore any bad delegations */
79459cc4ca5SDavid du Colombier 			if(m.ns && baddelegation(m.ns, nsrp, ibuf)){
79559cc4ca5SDavid du Colombier 				rrfreelist(m.ns);
79659cc4ca5SDavid du Colombier 				m.ns = nil;
79759cc4ca5SDavid du Colombier 				if(m.an == nil){
79859cc4ca5SDavid du Colombier 					rrfreelist(m.qd);
79959cc4ca5SDavid du Colombier 					rrfreelist(m.ar);
80059cc4ca5SDavid du Colombier 					if(p != l)
80159cc4ca5SDavid du Colombier 						p->code = Rserver;
80259cc4ca5SDavid du Colombier 					continue;
80359cc4ca5SDavid du Colombier 				}
80459cc4ca5SDavid du Colombier 			}
80559cc4ca5SDavid du Colombier 
80659cc4ca5SDavid du Colombier 
8077dd7cddfSDavid du Colombier 			/* remove any soa's from the authority section */
8087dd7cddfSDavid du Colombier 			soarr = rrremtype(&m.ns, Tsoa);
8097dd7cddfSDavid du Colombier 
8103e12c5d1SDavid du Colombier 			/* incorporate answers */
8113e12c5d1SDavid du Colombier 			if(m.an)
8125aa528faSDavid du Colombier 				rrattach(m.an, (m.flags & Fauth) != 0);
8133e12c5d1SDavid du Colombier 			if(m.ar)
8143e12c5d1SDavid du Colombier 				rrattach(m.ar, 0);
8157dd7cddfSDavid du Colombier 			if(m.ns){
8167dd7cddfSDavid du Colombier 				ndp = m.ns->owner;
8177dd7cddfSDavid du Colombier 				rrattach(m.ns, 0);
8187dd7cddfSDavid du Colombier 			} else
819*28a8a86bSDavid du Colombier 				ndp = nil;
8207dd7cddfSDavid du Colombier 
8217dd7cddfSDavid du Colombier 			/* free the question */
8227dd7cddfSDavid du Colombier 			if(m.qd)
8237dd7cddfSDavid du Colombier 				rrfreelist(m.qd);
8243e12c5d1SDavid du Colombier 
8253e12c5d1SDavid du Colombier 			/*
8263e12c5d1SDavid du Colombier 			 *  Any reply from an authoritative server,
8273e12c5d1SDavid du Colombier 			 *  or a positive reply terminates the search
8283e12c5d1SDavid du Colombier 			 */
8297dd7cddfSDavid du Colombier 			if(m.an != nil || (m.flags & Fauth)){
8307dd7cddfSDavid du Colombier 				if(m.an == nil && (m.flags & Rmask) == Rname)
8317dd7cddfSDavid du Colombier 					dp->nonexistent = Rname;
8327dd7cddfSDavid du Colombier 				else
8337dd7cddfSDavid du Colombier 					dp->nonexistent = 0;
8347dd7cddfSDavid du Colombier 
8357dd7cddfSDavid du Colombier 				/*
8367dd7cddfSDavid du Colombier 				 *  cache any negative responses, free soarr
8377dd7cddfSDavid du Colombier 				 */
8387dd7cddfSDavid du Colombier 				if((m.flags & Fauth) && m.an == nil)
8395aa528faSDavid du Colombier 					cacheneg(dp, type, (m.flags & Rmask),
8405aa528faSDavid du Colombier 						soarr);
8417dd7cddfSDavid du Colombier 				else
8427dd7cddfSDavid du Colombier 					rrfreelist(soarr);
8433e12c5d1SDavid du Colombier 				return 1;
8443e12c5d1SDavid du Colombier 			}
8457dd7cddfSDavid du Colombier 			rrfreelist(soarr);
8463e12c5d1SDavid du Colombier 
8473e12c5d1SDavid du Colombier 			/*
848*28a8a86bSDavid du Colombier 			 *  if we've been given better name servers,
849*28a8a86bSDavid du Colombier 			 *  recurse.  we're called from udpquery, called from
850*28a8a86bSDavid du Colombier 			 *  netquery, which current holds dp->querylck,
851*28a8a86bSDavid du Colombier 			 *  so release it now and acquire it upon return.
8523e12c5d1SDavid du Colombier 			 */
8533e12c5d1SDavid du Colombier 			if(m.ns){
8547dd7cddfSDavid du Colombier 				tp = rrlookup(ndp, Tns, NOneg);
8557dd7cddfSDavid du Colombier 				if(!contains(nsrp, tp)){
8565aa528faSDavid du Colombier 					procsetname(
8575aa528faSDavid du Colombier 					 "req slave: recursive query for %s %s",
8585aa528faSDavid du Colombier 						dp->name,
8595aa528faSDavid du Colombier 						rrname(type, buf, sizeof buf));
860*28a8a86bSDavid du Colombier 					qunlock(&dp->querylck);
8615aa528faSDavid du Colombier 					rv = netquery(dp, type, tp, reqp,
8625aa528faSDavid du Colombier 						depth + 1);
863*28a8a86bSDavid du Colombier 					qlock(&dp->querylck);
8647dd7cddfSDavid du Colombier 					rrfreelist(tp);
8657dd7cddfSDavid du Colombier 					return rv;
8667dd7cddfSDavid du Colombier 				} else
8677dd7cddfSDavid du Colombier 					rrfreelist(tp);
8683e12c5d1SDavid du Colombier 			}
8693e12c5d1SDavid du Colombier 		}
8703e12c5d1SDavid du Colombier 	}
8717dd7cddfSDavid du Colombier 
8725aa528faSDavid du Colombier 	/* if all servers returned failure, propagate it */
8737dd7cddfSDavid du Colombier 	dp->nonexistent = Rserver;
8747dd7cddfSDavid du Colombier 	for(p = dest; p < l; p++)
8757dd7cddfSDavid du Colombier 		if(p->code != Rserver)
8767dd7cddfSDavid du Colombier 			dp->nonexistent = 0;
8777dd7cddfSDavid du Colombier 
8783e12c5d1SDavid du Colombier 	return 0;
8793e12c5d1SDavid du Colombier }
8807dd7cddfSDavid du Colombier 
881*28a8a86bSDavid du Colombier enum { Hurry, Patient, };
882*28a8a86bSDavid du Colombier enum { Outns, Inns, };
8837dd7cddfSDavid du Colombier 
884*28a8a86bSDavid du Colombier static int
885*28a8a86bSDavid du Colombier udpquery(char *mntpt, DN *dp, int type, RR *nsrp, Request *reqp, int depth,
886*28a8a86bSDavid du Colombier 	int patient, int inns)
887*28a8a86bSDavid du Colombier {
888*28a8a86bSDavid du Colombier 	int fd, rv = 0;
889*28a8a86bSDavid du Colombier 	uchar *obuf, *ibuf;
8907dd7cddfSDavid du Colombier 
8917dd7cddfSDavid du Colombier 	/* use alloced buffers rather than ones from the stack */
892d9dc5dd1SDavid du Colombier 	ibuf = emalloc(Maxudpin+OUdphdrsize);
893d9dc5dd1SDavid du Colombier 	obuf = emalloc(Maxudp+OUdphdrsize);
8947dd7cddfSDavid du Colombier 
895*28a8a86bSDavid du Colombier 	fd = udpport(mntpt);
896*28a8a86bSDavid du Colombier 	if(fd >= 0) {
897*28a8a86bSDavid du Colombier 		reqp->aborttime = time(0) + (patient? Maxreqtm: Maxreqtm/2);
898*28a8a86bSDavid du Colombier 		rv = netquery1(fd, dp, type, nsrp, reqp, depth,
899*28a8a86bSDavid du Colombier 			ibuf, obuf, (patient? 15: 10), inns);
900*28a8a86bSDavid du Colombier 		close(fd);
901*28a8a86bSDavid du Colombier 	}
902*28a8a86bSDavid du Colombier 
903*28a8a86bSDavid du Colombier 	free(obuf);
904*28a8a86bSDavid du Colombier 	free(ibuf);
905*28a8a86bSDavid du Colombier 	return rv;
906*28a8a86bSDavid du Colombier }
907*28a8a86bSDavid du Colombier 
908*28a8a86bSDavid du Colombier /* look up (dp->name,type) via *nsrp with results in *reqp */
909*28a8a86bSDavid du Colombier static int
910*28a8a86bSDavid du Colombier netquery(DN *dp, int type, RR *nsrp, Request *reqp, int depth)
911*28a8a86bSDavid du Colombier {
912*28a8a86bSDavid du Colombier 	int lock, rv, triedin;
913*28a8a86bSDavid du Colombier 	RR *rp;
914*28a8a86bSDavid du Colombier 
915*28a8a86bSDavid du Colombier 	if(depth > 12)			/* in a recursive loop? */
916*28a8a86bSDavid du Colombier 		return 0;
917*28a8a86bSDavid du Colombier 
918*28a8a86bSDavid du Colombier 	slave(reqp);			/* might fork */
919*28a8a86bSDavid du Colombier 	/* if so, parent process longjmped to req->mret; we're child slave */
920*28a8a86bSDavid du Colombier 	if (!reqp->isslave)
921*28a8a86bSDavid du Colombier 		syslog(0, LOG,
922*28a8a86bSDavid du Colombier 			"[%d] netquery: slave returned with reqp->isslave==0",
923*28a8a86bSDavid du Colombier 			getpid());
924*28a8a86bSDavid du Colombier 
925*28a8a86bSDavid du Colombier 	/* don't lock before call to slave so only children can block */
926*28a8a86bSDavid du Colombier 	lock = reqp->isslave != 0;
927*28a8a86bSDavid du Colombier 	if(lock) {
928*28a8a86bSDavid du Colombier 		procsetname("waiting for query lock on %s", dp->name);
929*28a8a86bSDavid du Colombier 		/* don't make concurrent queries for this name */
930*28a8a86bSDavid du Colombier 		qlock(&dp->querylck);
931*28a8a86bSDavid du Colombier 		procsetname("netquery: %s", dp->name);
932*28a8a86bSDavid du Colombier 	}
9337dd7cddfSDavid du Colombier 
9347dd7cddfSDavid du Colombier 	/* prepare server RR's for incremental lookup */
9357dd7cddfSDavid du Colombier 	for(rp = nsrp; rp; rp = rp->next)
9367dd7cddfSDavid du Colombier 		rp->marker = 0;
9377dd7cddfSDavid du Colombier 
938*28a8a86bSDavid du Colombier 	rv = 0;				/* pessimism */
939*28a8a86bSDavid du Colombier 	triedin = 0;
940*28a8a86bSDavid du Colombier 	/*
941*28a8a86bSDavid du Colombier 	 * don't bother to query the broken inside nameservers for outside
942*28a8a86bSDavid du Colombier 	 * addresses.
943*28a8a86bSDavid du Colombier 	 */
944*28a8a86bSDavid du Colombier 	if (!inside || insideaddr(dp->name)) {
945*28a8a86bSDavid du Colombier 		rv = udpquery(mntpt, dp, type, nsrp, reqp, depth, Hurry,
946*28a8a86bSDavid du Colombier 			(inside? Inns: Outns));
947*28a8a86bSDavid du Colombier 		triedin = 1;
948*28a8a86bSDavid du Colombier 	}
949*28a8a86bSDavid du Colombier 
950*28a8a86bSDavid du Colombier 	/*
951*28a8a86bSDavid du Colombier 	 * if we're still looking and have an outside address,
952*28a8a86bSDavid du Colombier 	 * try it on our outside interface, if any.
953*28a8a86bSDavid du Colombier 	 */
954*28a8a86bSDavid du Colombier 	if (rv == 0 && inside && !insideaddr(dp->name)) {
955*28a8a86bSDavid du Colombier 		if (triedin)
956*28a8a86bSDavid du Colombier 			syslog(0, LOG,
957*28a8a86bSDavid du Colombier 	   "[%d] netquery: internal nameservers failed for %s; trying external",
958*28a8a86bSDavid du Colombier 				getpid(), dp->name);
959*28a8a86bSDavid du Colombier 
960*28a8a86bSDavid du Colombier 		/* prepare server RR's for incremental lookup */
961*28a8a86bSDavid du Colombier 		for(rp = nsrp; rp; rp = rp->next)
962*28a8a86bSDavid du Colombier 			rp->marker = 0;
963*28a8a86bSDavid du Colombier 
964*28a8a86bSDavid du Colombier 		rv = udpquery("/net.alt", dp, type, nsrp, reqp, depth, Patient,
965*28a8a86bSDavid du Colombier 			Outns);
966*28a8a86bSDavid du Colombier 		if (rv == 0)
967*28a8a86bSDavid du Colombier 			syslog(0, LOG, "[%d] netquery: no luck for %s",
968*28a8a86bSDavid du Colombier 				getpid(), dp->name);
969*28a8a86bSDavid du Colombier 	}
970*28a8a86bSDavid du Colombier 
971*28a8a86bSDavid du Colombier 	if(lock)
972*28a8a86bSDavid du Colombier 		qunlock(&dp->querylck);
9737dd7cddfSDavid du Colombier 
9747dd7cddfSDavid du Colombier 	return rv;
9757dd7cddfSDavid du Colombier }
976