From 6df9693fc1899de774712d6421c2fc401db2eadd Mon Sep 17 00:00:00 2001
From: dougb <dougb@FreeBSD.org>
Date: Sat, 2 Jun 2007 23:21:47 +0000
Subject: Vendor import of BIND 9.4.1

---
 contrib/bind9/lib/dns/resolver.c | 730 ++++++++++++++++++++++++++++++---------
 1 file changed, 568 insertions(+), 162 deletions(-)

(limited to 'contrib/bind9/lib/dns/resolver.c')

diff --git a/contrib/bind9/lib/dns/resolver.c b/contrib/bind9/lib/dns/resolver.c
index a56fecf..7312841 100644
--- a/contrib/bind9/lib/dns/resolver.c
+++ b/contrib/bind9/lib/dns/resolver.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2004-2006  Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2004-2007  Internet Systems Consortium, Inc. ("ISC")
  * Copyright (C) 1999-2003  Internet Software Consortium.
  *
  * Permission to use, copy, modify, and distribute this software for any
@@ -15,7 +15,9 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: resolver.c,v 1.218.2.18.4.64.4.2 2007/01/11 05:05:10 marka Exp $ */
+/* $Id: resolver.c,v 1.284.18.57 2007/02/14 23:41:01 marka Exp $ */
+
+/*! \file */
 
 #include <config.h>
 
@@ -99,12 +101,12 @@
 #define QTRACE(m)
 #endif
 
-/*
+/*%
  * Maximum EDNS0 input packet size.
  */
 #define RECV_BUFFER_SIZE		4096		/* XXXRTH  Constant. */
 
-/*
+/*%
  * This defines the maximum number of timeouts we will permit before we
  * disable EDNS0 on the query.
  */
@@ -146,13 +148,13 @@ typedef struct query {
 #define RESQUERY_SENDING(q)		((q)->sends > 0)
 
 typedef enum {
-	fetchstate_init = 0,		/* Start event has not run yet. */
+	fetchstate_init = 0,		/*%< Start event has not run yet. */
 	fetchstate_active,
-	fetchstate_done			/* FETCHDONE events posted. */
+	fetchstate_done			/*%< FETCHDONE events posted. */
 } fetchstate;
 
 struct fetchctx {
-	/* Not locked. */
+	/*% Not locked. */
 	unsigned int			magic;
 	dns_resolver_t *		res;
 	dns_name_t			name;
@@ -160,15 +162,16 @@ struct fetchctx {
 	unsigned int			options;
 	unsigned int			bucketnum;
 	char *				info;
-	/* Locked by appropriate bucket lock. */
+	/*% Locked by appropriate bucket lock. */
 	fetchstate			state;
 	isc_boolean_t			want_shutdown;
 	isc_boolean_t			cloned;
+	isc_boolean_t			spilled;
 	unsigned int			references;
 	isc_event_t			control_event;
 	ISC_LINK(struct fetchctx)	link;
 	ISC_LIST(dns_fetchevent_t)	events;
-	/* Locked by task event serialization. */
+	/*% Locked by task event serialization. */
 	dns_name_t			domain;
 	dns_rdataset_t			nameservers;
 	unsigned int			attributes;
@@ -187,16 +190,18 @@ struct fetchctx {
 	isc_sockaddrlist_t		forwarders;
 	dns_fwdpolicy_t			fwdpolicy;
 	isc_sockaddrlist_t		bad;
+	isc_sockaddrlist_t		edns;
+	isc_sockaddrlist_t		edns512;
 	ISC_LIST(dns_validator_t)	validators;
 	dns_db_t *			cache;
 	dns_adb_t *			adb;
 
-	/*
+	/*%
 	 * The number of events we're waiting for.
 	 */
 	unsigned int			pending;
 
-	/*
+	/*%
 	 * The number of times we've "restarted" the current
 	 * nameserver set.  This acts as a failsafe to prevent
 	 * us from pounding constantly on a particular set of
@@ -206,13 +211,13 @@ struct fetchctx {
 	 */
 	unsigned int			restarts;
 
-	/*
+	/*%
 	 * The number of timeouts that have occurred since we 
 	 * last successfully received a response packet.  This
 	 * is used for EDNS0 black hole detection.
 	 */
 	unsigned int			timeouts;
-	/*
+	/*%
 	 * Look aside state for DS lookups.
 	 */
 	dns_name_t 			nsname; 
@@ -270,6 +275,7 @@ typedef struct fctxbucket {
 	isc_mutex_t			lock;
 	ISC_LIST(fetchctx_t)		fctxs;
 	isc_boolean_t			exiting;
+	isc_mem_t *			mctx;
 } fctxbucket_t;
 
 typedef struct alternate {
@@ -314,12 +320,17 @@ struct dns_resolver {
 	isc_rwlock_t			mbslock;
 #endif
 	dns_rbt_t *			mustbesecure;
+	unsigned int			spillatmax;
+	unsigned int			spillatmin;
+	isc_timer_t *			spillattimer;
+	isc_boolean_t			zero_no_soa_ttl;
 	/* Locked by lock. */
 	unsigned int			references;
 	isc_boolean_t			exiting;
 	isc_eventlist_t			whenshutdown;
 	unsigned int			activebuckets;
 	isc_boolean_t			priming;
+	unsigned int			spillat;
 	/* Locked by primelock. */
 	dns_fetch_t *			primefetch;
 	/* Locked by nlock. */
@@ -329,7 +340,7 @@ struct dns_resolver {
 #define RES_MAGIC			ISC_MAGIC('R', 'e', 's', '!')
 #define VALID_RESOLVER(res)		ISC_MAGIC_VALID(res, RES_MAGIC)
 
-/*
+/*%
  * Private addrinfo flags.  These must not conflict with DNS_FETCHOPT_NOEDNS0,
  * which we also use as an addrinfo flag.
  */
@@ -368,7 +379,8 @@ valcreate(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, dns_name_t *name,
 	dns_valarg_t *valarg;
 	isc_result_t result;
 
-	valarg = isc_mem_get(fctx->res->mctx, sizeof(*valarg));
+	valarg = isc_mem_get(fctx->res->buckets[fctx->bucketnum].mctx,
+			     sizeof(*valarg));
 	if (valarg == NULL)
 		return (ISC_R_NOMEMORY);
 
@@ -385,7 +397,8 @@ valcreate(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, dns_name_t *name,
 	if (result == ISC_R_SUCCESS)
 		ISC_LIST_APPEND(fctx->validators, validator, link);
 	else
-		isc_mem_put(fctx->res->mctx, valarg, sizeof(*valarg));
+		isc_mem_put(fctx->res->buckets[fctx->bucketnum].mctx,
+			    valarg, sizeof(*valarg));
 	return (result);
 }
 
@@ -571,8 +584,7 @@ fctx_cancelquery(resquery_t **queryp, dns_dispatchevent_t **deventp,
 			 * slow.  We don't know.  Increase the RTT.
 			 */
 			INSIST(no_response);
-			rtt = query->addrinfo->srtt +
-				(200000 * fctx->restarts);
+			rtt = query->addrinfo->srtt + 200000;
 			if (rtt > 10000000)
 				rtt = 10000000;
 			/*
@@ -755,6 +767,9 @@ static inline void
 fctx_sendevents(fetchctx_t *fctx, isc_result_t result) {
 	dns_fetchevent_t *event, *next_event;
 	isc_task_t *task;
+	unsigned int count = 0;
+	isc_interval_t i;
+	isc_boolean_t logit = ISC_FALSE;
 
 	/*
 	 * Caller must be holding the appropriate bucket lock.
@@ -780,6 +795,31 @@ fctx_sendevents(fetchctx_t *fctx, isc_result_t result) {
 		       fctx->type == dns_rdatatype_sig);
 
 		isc_task_sendanddetach(&task, ISC_EVENT_PTR(&event));
+		count++;
+	}
+
+	if ((fctx->attributes & FCTX_ATTR_HAVEANSWER) != 0 &&
+	    fctx->spilled &&
+	    (count < fctx->res->spillatmax || fctx->res->spillatmax == 0)) {
+		LOCK(&fctx->res->lock);
+	    	if (count == fctx->res->spillat && !fctx->res->exiting) {
+			fctx->res->spillat += 5;
+			if (fctx->res->spillat > fctx->res->spillatmax &&
+			    fctx->res->spillatmax != 0)
+				fctx->res->spillat = fctx->res->spillatmax;
+			isc_interval_set(&i, 20 * 60, 0);
+			result = isc_timer_reset(fctx->res->spillattimer,
+						 isc_timertype_ticker, NULL,
+						 &i, ISC_TRUE);
+			RUNTIME_CHECK(result == ISC_R_SUCCESS);
+			logit = ISC_TRUE;
+		}
+		UNLOCK(&fctx->res->lock);
+		if (logit)
+			isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
+				      DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE,
+				      "clients-per-query increased to %u",
+				      count + 1);
 	}
 }
 
@@ -884,7 +924,8 @@ resquery_senddone(isc_task_t *task, isc_event_t *event) {
 }
 
 static inline isc_result_t
-fctx_addopt(dns_message_t *message, dns_resolver_t *res) {
+fctx_addopt(dns_message_t *message, unsigned int version, isc_uint16_t udpsize)
+{ 
 	dns_rdataset_t *rdataset;
 	dns_rdatalist_t *rdatalist;
 	dns_rdata_t *rdata;
@@ -910,12 +951,13 @@ fctx_addopt(dns_message_t *message, dns_resolver_t *res) {
 	/*
 	 * Set Maximum UDP buffer size.
 	 */
-	rdatalist->rdclass = res->udpsize;
+	rdatalist->rdclass = udpsize;
 
 	/*
-	 * Set EXTENDED-RCODE, VERSION, and Z to 0, and the DO bit to 1.
+	 * Set EXTENDED-RCODE and Z to 0, DO to 1.
 	 */
-	rdatalist->ttl = DNS_MESSAGEEXTFLAG_DO;
+	rdatalist->ttl = (version << 16);
+	rdatalist->ttl |= DNS_MESSAGEEXTFLAG_DO;
 
 	/*
 	 * No EDNS options.
@@ -936,34 +978,37 @@ fctx_addopt(dns_message_t *message, dns_resolver_t *res) {
 static inline void
 fctx_setretryinterval(fetchctx_t *fctx, unsigned int rtt) {
 	unsigned int seconds;
+	unsigned int us;
 
 	/*
-	 * We retry every 2 seconds the first two times through the address
+	 * We retry every .5 seconds the first two times through the address
 	 * list, and then we do exponential back-off.
 	 */
 	if (fctx->restarts < 3)
-		seconds = 2;
+		us = 500000;
 	else
-		seconds = (2 << (fctx->restarts - 1));
+		us = (500000 << (fctx->restarts - 2));
 
 	/*
-	 * Double the round-trip time and convert to seconds.
+	 * Double the round-trip time.
 	 */
-	rtt /= 500000;
+	rtt *= 2;
 
 	/*
 	 * Always wait for at least the doubled round-trip time.
 	 */
-	if (seconds < rtt)
-		seconds = rtt;
+	if (us < rtt)
+		us = rtt;
 
 	/*
-	 * But don't ever wait for more than 30 seconds.
+	 * But don't ever wait for more than 10 seconds.
 	 */
-	if (seconds > 30)
-		seconds = 30;
+	if (us > 10000000)
+		us = 10000000;
 
-	isc_interval_set(&fctx->interval, seconds, 0);
+	seconds = us / 1000000;
+	us -= seconds * 1000000;
+	isc_interval_set(&fctx->interval, seconds, us * 1000);
 }
 
 static isc_result_t
@@ -974,6 +1019,8 @@ fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
 	isc_task_t *task;
 	isc_result_t result;
 	resquery_t *query;
+	isc_sockaddr_t addr;
+	isc_boolean_t have_addr = ISC_FALSE;
 
 	FCTXTRACE("query");
 
@@ -989,12 +1036,13 @@ fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
 
 	dns_message_reset(fctx->rmessage, DNS_MESSAGE_INTENTPARSE);
 
-	query = isc_mem_get(res->mctx, sizeof(*query));
+	query = isc_mem_get(res->buckets[fctx->bucketnum].mctx,
+			    sizeof(*query));
 	if (query == NULL) {
 		result = ISC_R_NOMEMORY;
 		goto stop_idle_timer;
 	}
-	query->mctx = res->mctx;
+	query->mctx = res->buckets[fctx->bucketnum].mctx;
 	query->options = options;
 	query->attributes = 0;
 	query->sends = 0;
@@ -1014,28 +1062,42 @@ fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
 	query->dispatchmgr = res->dispatchmgr;
 	query->dispatch = NULL;
 	query->tcpsocket = NULL;
+	if (res->view->peers != NULL) {
+		dns_peer_t *peer = NULL;
+		isc_netaddr_t dstip;
+		isc_netaddr_fromsockaddr(&dstip, &addrinfo->sockaddr);
+		result = dns_peerlist_peerbyaddr(res->view->peers,
+					         &dstip, &peer);
+		if (result == ISC_R_SUCCESS) {
+			result = dns_peer_getquerysource(peer, &addr);
+			if (result == ISC_R_SUCCESS)
+				have_addr = ISC_TRUE;
+		}
+	}
+
 	if ((query->options & DNS_FETCHOPT_TCP) != 0) {
-		isc_sockaddr_t addr;
 		int pf;
 
 		pf = isc_sockaddr_pf(&addrinfo->sockaddr);
-
-		switch (pf) {
-		case PF_INET:
-			result = dns_dispatch_getlocaladdress(res->dispatchv4,
-							      &addr);
-			break;
-		case PF_INET6:
-			result = dns_dispatch_getlocaladdress(res->dispatchv6,
-							      &addr);
-			break;
-		default:
-			result = ISC_R_NOTIMPLEMENTED;
-			break;
+		if (!have_addr) {
+			switch (pf) {
+			case PF_INET:
+				result =
+				  dns_dispatch_getlocaladdress(res->dispatchv4,
+							       &addr);
+				break;
+			case PF_INET6:
+				result =
+				  dns_dispatch_getlocaladdress(res->dispatchv6,
+							       &addr);
+				break;
+			default:
+				result = ISC_R_NOTIMPLEMENTED;
+				break;
+			}
+			if (result != ISC_R_SUCCESS)
+				goto cleanup_query;
 		}
-		if (result != ISC_R_SUCCESS)
-			goto cleanup_query;
-
 		isc_sockaddr_setport(&addr, 0);
 
 		result = isc_socket_create(res->socketmgr, pf,
@@ -1054,16 +1116,46 @@ fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
 		 * A dispatch will be created once the connect succeeds.
 		 */
 	} else {
-		switch (isc_sockaddr_pf(&addrinfo->sockaddr)) {
-		case PF_INET:
-			dns_dispatch_attach(res->dispatchv4, &query->dispatch);
-			break;
-		case PF_INET6:
-			dns_dispatch_attach(res->dispatchv6, &query->dispatch);
-			break;
-		default:
-			result = ISC_R_NOTIMPLEMENTED;
-			goto cleanup_query;
+		if (have_addr) {
+			unsigned int attrs, attrmask;
+			attrs = DNS_DISPATCHATTR_UDP;
+			switch (isc_sockaddr_pf(&addr)) {
+			case AF_INET:
+				attrs |= DNS_DISPATCHATTR_IPV4;
+				break;
+			case AF_INET6:
+				attrs |= DNS_DISPATCHATTR_IPV6;
+				break;
+			default:
+				result = ISC_R_NOTIMPLEMENTED;
+				goto cleanup_query;
+			}
+			attrmask = DNS_DISPATCHATTR_UDP;
+			attrmask |= DNS_DISPATCHATTR_TCP;
+			attrmask |= DNS_DISPATCHATTR_IPV4;
+			attrmask |= DNS_DISPATCHATTR_IPV6;
+			result = dns_dispatch_getudp(res->dispatchmgr,
+						     res->socketmgr,
+						     res->taskmgr, &addr,
+						     4096, 1000, 32768, 16411,
+						     16433, attrs, attrmask,
+						     &query->dispatch);
+			if (result != ISC_R_SUCCESS)
+				goto cleanup_query;
+		} else {
+			switch (isc_sockaddr_pf(&addrinfo->sockaddr)) {
+			case PF_INET:
+				dns_dispatch_attach(res->dispatchv4,
+						    &query->dispatch);
+				break;
+			case PF_INET6:
+				dns_dispatch_attach(res->dispatchv6,
+						    &query->dispatch);
+				break;
+			default:
+				result = ISC_R_NOTIMPLEMENTED;
+				goto cleanup_query;
+			}
 		}
 		/*
 		 * We should always have a valid dispatcher here.  If we
@@ -1115,7 +1207,8 @@ fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
 
  cleanup_query:
 	query->magic = 0;
-	isc_mem_put(res->mctx, query, sizeof(*query));
+	isc_mem_put(res->buckets[fctx->bucketnum].mctx,
+		    query, sizeof(*query));
 
  stop_idle_timer:
 	RUNTIME_CHECK(fctx_stopidletimer(fctx) == ISC_R_SUCCESS);
@@ -1123,6 +1216,66 @@ fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
 	return (result);
 }
 
+static isc_boolean_t
+triededns(fetchctx_t *fctx, isc_sockaddr_t *address) {
+	isc_sockaddr_t *sa;
+
+	for (sa = ISC_LIST_HEAD(fctx->edns);
+	     sa != NULL;
+	     sa = ISC_LIST_NEXT(sa, link)) {
+		if (isc_sockaddr_equal(sa, address))
+			return (ISC_TRUE);
+	}
+
+	return (ISC_FALSE);
+}
+
+static void
+add_triededns(fetchctx_t *fctx, isc_sockaddr_t *address) {
+	isc_sockaddr_t *sa;
+
+	if (triededns(fctx, address))
+		return;
+
+	sa = isc_mem_get(fctx->res->buckets[fctx->bucketnum].mctx,
+			 sizeof(*sa));
+	if (sa == NULL)
+		return;
+
+	*sa = *address;
+	ISC_LIST_INITANDAPPEND(fctx->edns, sa, link);
+}
+
+static isc_boolean_t
+triededns512(fetchctx_t *fctx, isc_sockaddr_t *address) {
+	isc_sockaddr_t *sa;
+
+	for (sa = ISC_LIST_HEAD(fctx->edns512);
+	     sa != NULL;
+	     sa = ISC_LIST_NEXT(sa, link)) {
+		if (isc_sockaddr_equal(sa, address))
+			return (ISC_TRUE);
+	}
+
+	return (ISC_FALSE);
+}
+
+static void
+add_triededns512(fetchctx_t *fctx, isc_sockaddr_t *address) {
+	isc_sockaddr_t *sa;
+
+	if (triededns512(fctx, address))
+		return;
+
+	sa = isc_mem_get(fctx->res->buckets[fctx->bucketnum].mctx,
+			 sizeof(*sa));
+	if (sa == NULL)
+		return;
+
+	*sa = *address;
+	ISC_LIST_INITANDAPPEND(fctx->edns512, sa, link);
+}
+
 static isc_result_t
 resquery_send(resquery_t *query) {
 	fetchctx_t *fctx;
@@ -1211,7 +1364,9 @@ resquery_send(resquery_t *query) {
 	 * Set CD if the client says don't validate or the question is
 	 * under a secure entry point.
 	 */
-	if ((query->options & DNS_FETCHOPT_NOVALIDATE) == 0) {
+	if ((query->options & DNS_FETCHOPT_NOVALIDATE) != 0) {
+		fctx->qmessage->flags |= DNS_MESSAGEFLAG_CD;
+	} else if (res->view->enablevalidation) {
 		result = dns_keytable_issecuredomain(res->view->secroots,
 						     &fctx->name,
 						     &secure_domain);
@@ -1221,8 +1376,7 @@ resquery_send(resquery_t *query) {
 			secure_domain = ISC_TRUE;
 		if (secure_domain)
 			fctx->qmessage->flags |= DNS_MESSAGEFLAG_CD;
-	} else
-		fctx->qmessage->flags |= DNS_MESSAGEFLAG_CD;
+	}
 
 	/*
 	 * We don't have to set opcode because it defaults to query.
@@ -1271,15 +1425,35 @@ resquery_send(resquery_t *query) {
 	 * Use EDNS0, unless the caller doesn't want it, or we know that
 	 * the remote server doesn't like it.
 	 */
-	if (fctx->timeouts >= MAX_EDNS0_TIMEOUTS &&
+
+	if ((triededns512(fctx, &query->addrinfo->sockaddr) ||
+	     fctx->timeouts >= (MAX_EDNS0_TIMEOUTS * 2)) &&
 	    (query->options & DNS_FETCHOPT_NOEDNS0) == 0) {
 		query->options |= DNS_FETCHOPT_NOEDNS0;
 		FCTXTRACE("too many timeouts, disabling EDNS0");
+	} else if ((triededns(fctx, &query->addrinfo->sockaddr) ||
+		    fctx->timeouts >= MAX_EDNS0_TIMEOUTS) &&
+	           (query->options & DNS_FETCHOPT_NOEDNS0) == 0) {
+		query->options |= DNS_FETCHOPT_EDNS512;
+		FCTXTRACE("too many timeouts, setting EDNS size to 512");
 	}
 
 	if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0) {
 		if ((query->addrinfo->flags & DNS_FETCHOPT_NOEDNS0) == 0) {
-			result = fctx_addopt(fctx->qmessage, res);
+			unsigned int version = 0;	/* Default version. */
+			unsigned int flags;
+			isc_uint16_t udpsize = res->udpsize;
+
+			flags = query->addrinfo->flags;
+			if ((flags & DNS_FETCHOPT_EDNSVERSIONSET) != 0) {
+				version = flags & DNS_FETCHOPT_EDNSVERSIONMASK;
+				version >>= DNS_FETCHOPT_EDNSVERSIONSHIFT;
+			}
+			if ((query->options & DNS_FETCHOPT_EDNS512) != 0)
+				udpsize = 512;
+			else if (peer != NULL)
+				(void)dns_peer_getudpsize(peer, &udpsize);
+			result = fctx_addopt(fctx->qmessage, version, udpsize);
 			if (result != ISC_R_SUCCESS) {
 				/*
 				 * We couldn't add the OPT, but we'll press on.
@@ -1306,6 +1480,12 @@ resquery_send(resquery_t *query) {
 		goto cleanup_message;
 	}
 
+	if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0)
+		add_triededns(fctx, &query->addrinfo->sockaddr);
+
+	if ((query->options & DNS_FETCHOPT_EDNS512) != 0)
+		add_triededns512(fctx, &query->addrinfo->sockaddr);
+
 	/*
 	 * Clear CD if EDNS is not in use.
 	 */
@@ -1680,7 +1860,8 @@ add_bad(fetchctx_t *fctx, isc_sockaddr_t *address, isc_result_t reason) {
 
 	FCTXTRACE("add_bad");
 
-	sa = isc_mem_get(fctx->res->mctx, sizeof(*sa));
+	sa = isc_mem_get(fctx->res->buckets[fctx->bucketnum].mctx,
+			 sizeof(*sa));
 	if (sa == NULL)
 		return;
 	*sa = *address;
@@ -1795,7 +1976,7 @@ sort_finds(fetchctx_t *fctx) {
 static void
 findname(fetchctx_t *fctx, dns_name_t *name, in_port_t port,
 	 unsigned int options, unsigned int flags, isc_stdtime_t now,
-	 isc_boolean_t *pruned, isc_boolean_t *need_alternate)
+	 isc_boolean_t *need_alternate)
 {
 	dns_adbaddrinfo_t *ai;
 	dns_adbfind_t *find;
@@ -1824,7 +2005,8 @@ findname(fetchctx_t *fctx, dns_name_t *name, in_port_t port,
 	result = dns_adb_createfind(fctx->adb,
 				    res->buckets[fctx->bucketnum].task,
 				    fctx_finddone, fctx, name,
-				    &fctx->domain, options, now, NULL,
+				    &fctx->name, fctx->type,
+				    options, now, NULL,
 				    res->view->dstport, &find);
 	if (result != ISC_R_SUCCESS) {
 		if (result == DNS_R_ALIAS) {
@@ -1887,18 +2069,6 @@ findname(fetchctx_t *fctx, dns_name_t *name, in_port_t port,
 			     (res->dispatchv6 == NULL &&
 			      find->result_v4 == DNS_R_NXRRSET)))
 				*need_alternate = ISC_TRUE;
-			/*
-			 * And ADB isn't going to send us any events
-			 * either.  This find loses.
-			 */
-			if ((find->options & DNS_ADBFIND_LAMEPRUNED) != 0) {
-				/*
-				 * The ADB pruned lame servers for
-				 * this name.  Remember that in case
-				 * we get desperate later on.
-				 */
-				*pruned = ISC_TRUE;
-			}
 			dns_adb_destroyfind(&find);
 		}
 	}
@@ -1913,7 +2083,7 @@ fctx_getaddresses(fetchctx_t *fctx) {
 	unsigned int stdoptions;
 	isc_sockaddr_t *sa;
 	dns_adbaddrinfo_t *ai;
-	isc_boolean_t pruned, all_bad;
+	isc_boolean_t all_bad;
 	dns_rdata_ns_t ns;
 	isc_boolean_t need_alternate = ISC_FALSE;
 
@@ -1929,7 +2099,6 @@ fctx_getaddresses(fetchctx_t *fctx) {
 	}
 
 	res = fctx->res;
-	pruned = ISC_FALSE;
 	stdoptions = 0;		/* Keep compiler happy. */
 
 	/*
@@ -2021,7 +2190,6 @@ fctx_getaddresses(fetchctx_t *fctx) {
 		stdoptions |= DNS_ADBFIND_INET6;
 	isc_stdtime_get(&now);
 
- restart:
 	INSIST(ISC_LIST_EMPTY(fctx->finds));
 	INSIST(ISC_LIST_EMPTY(fctx->altfinds));
 
@@ -2038,7 +2206,7 @@ fctx_getaddresses(fetchctx_t *fctx) {
 			continue;
 
 		findname(fctx, &ns.name, 0, stdoptions, 0, now,
-			 &pruned, &need_alternate);
+			 &need_alternate);
 		dns_rdata_reset(&rdata);
 		dns_rdata_freestruct(&ns);
 	}
@@ -2058,7 +2226,7 @@ fctx_getaddresses(fetchctx_t *fctx) {
 			if (!a->isaddress) {
 				findname(fctx, &a->_u._n.name, a->_u._n.port,
 					 stdoptions, FCTX_ADDRINFO_FORWARDER,
-					 now, &pruned, NULL);
+					 now, NULL);
 				continue;
 			}
 			if (isc_sockaddr_pf(&a->_u.addr) != family)
@@ -2101,18 +2269,6 @@ fctx_getaddresses(fetchctx_t *fctx) {
 			 * yet.   Tell the caller to wait for an answer.
 			 */
 			result = DNS_R_WAIT;
-		} else if (pruned) {
-			/*
-			 * Some addresses were removed by lame pruning.
-			 * Turn pruning off and try again.
-			 */
-			FCTXTRACE("restarting with returnlame");
-			INSIST((stdoptions & DNS_ADBFIND_RETURNLAME) == 0);
-			stdoptions |= DNS_ADBFIND_RETURNLAME;
-			pruned = ISC_FALSE;
-			fctx_cleanupaltfinds(fctx);
-			fctx_cleanupfinds(fctx);
-			goto restart;
 		} else {
 			/*
 			 * We've lost completely.  We don't know any
@@ -2427,21 +2583,37 @@ fctx_destroy(fetchctx_t *fctx) {
 	     sa = next_sa) {
 		next_sa = ISC_LIST_NEXT(sa, link);
 		ISC_LIST_UNLINK(fctx->bad, sa, link);
-		isc_mem_put(res->mctx, sa, sizeof(*sa));
+		isc_mem_put(res->buckets[bucketnum].mctx, sa, sizeof(*sa));
+	}
+
+	for (sa = ISC_LIST_HEAD(fctx->edns);
+	     sa != NULL;
+	     sa = next_sa) {
+		next_sa = ISC_LIST_NEXT(sa, link);
+		ISC_LIST_UNLINK(fctx->edns, sa, link);
+		isc_mem_put(res->buckets[bucketnum].mctx, sa, sizeof(*sa));
+	}
+
+	for (sa = ISC_LIST_HEAD(fctx->edns512);
+	     sa != NULL;
+	     sa = next_sa) {
+		next_sa = ISC_LIST_NEXT(sa, link);
+		ISC_LIST_UNLINK(fctx->edns512, sa, link);
+		isc_mem_put(res->buckets[bucketnum].mctx, sa, sizeof(*sa));
 	}
 
 	isc_timer_detach(&fctx->timer);
 	dns_message_destroy(&fctx->rmessage);
 	dns_message_destroy(&fctx->qmessage);
 	if (dns_name_countlabels(&fctx->domain) > 0)
-		dns_name_free(&fctx->domain, res->mctx);
+		dns_name_free(&fctx->domain, res->buckets[bucketnum].mctx);
 	if (dns_rdataset_isassociated(&fctx->nameservers))
 		dns_rdataset_disassociate(&fctx->nameservers);
-	dns_name_free(&fctx->name, res->mctx);
+	dns_name_free(&fctx->name, res->buckets[bucketnum].mctx);
 	dns_db_detach(&fctx->cache);
 	dns_adb_detach(&fctx->adb);
-	isc_mem_free(res->mctx, fctx->info);
-	isc_mem_put(res->mctx, fctx, sizeof(*fctx));
+	isc_mem_free(res->buckets[bucketnum].mctx, fctx->info);
+	isc_mem_put(res->buckets[bucketnum].mctx, fctx, sizeof(*fctx));
 
 	LOCK(&res->nlock);
 	res->nfctx--;
@@ -2670,8 +2842,9 @@ fctx_start(isc_task_t *task, isc_event_t *event) {
  */
 
 static inline isc_result_t
-fctx_join(fetchctx_t *fctx, isc_task_t *task, isc_taskaction_t action,
-	  void *arg, dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
+fctx_join(fetchctx_t *fctx, isc_task_t *task, isc_sockaddr_t *client,
+	  dns_messageid_t id, isc_taskaction_t action, void *arg,
+	  dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
 	  dns_fetch_t *fetch)
 {
 	isc_task_t *clone;
@@ -2687,8 +2860,7 @@ fctx_join(fetchctx_t *fctx, isc_task_t *task, isc_taskaction_t action,
 	clone = NULL;
 	isc_task_attach(task, &clone);
 	event = (dns_fetchevent_t *)
-		isc_event_allocate(fctx->res->mctx, clone,
-				   DNS_EVENT_FETCHDONE,
+		isc_event_allocate(fctx->res->mctx, clone, DNS_EVENT_FETCHDONE,
 				   action, arg, sizeof(*event));
 	if (event == NULL) {
 		isc_task_detach(&clone);
@@ -2701,6 +2873,8 @@ fctx_join(fetchctx_t *fctx, isc_task_t *task, isc_taskaction_t action,
 	event->rdataset = rdataset;
 	event->sigrdataset = sigrdataset;
 	event->fetch = fetch;
+	event->client = client;
+	event->id = id;
 	dns_fixedname_init(&event->foundname);
 
 	/*
@@ -2739,21 +2913,21 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type,
 	 */
 	REQUIRE(fctxp != NULL && *fctxp == NULL);
 
-	fctx = isc_mem_get(res->mctx, sizeof(*fctx));
+	fctx = isc_mem_get(res->buckets[bucketnum].mctx, sizeof(*fctx));
 	if (fctx == NULL)
 		return (ISC_R_NOMEMORY);
 	dns_name_format(name, buf, sizeof(buf));
 	dns_rdatatype_format(type, typebuf, sizeof(typebuf));
 	strcat(buf, "/");	/* checked */
 	strcat(buf, typebuf);	/* checked */
-	fctx->info = isc_mem_strdup(res->mctx, buf);
+	fctx->info = isc_mem_strdup(res->buckets[bucketnum].mctx, buf);
 	if (fctx->info == NULL) {
 		result = ISC_R_NOMEMORY;
 		goto cleanup_fetch;
 	}
 	FCTXTRACE("create");
 	dns_name_init(&fctx->name, NULL);
-	result = dns_name_dup(name, res->mctx, &fctx->name);
+	result = dns_name_dup(name, res->buckets[bucketnum].mctx, &fctx->name);
 	if (result != ISC_R_SUCCESS)
 		goto cleanup_info;
 	dns_name_init(&fctx->domain, NULL);
@@ -2780,6 +2954,8 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type,
 	ISC_LIST_INIT(fctx->forwarders);
 	fctx->fwdpolicy = dns_fwdpolicy_none;
 	ISC_LIST_INIT(fctx->bad);
+	ISC_LIST_INIT(fctx->edns);
+	ISC_LIST_INIT(fctx->edns512);
 	ISC_LIST_INIT(fctx->validators);
 	fctx->find = NULL;
 	fctx->altfind = NULL;
@@ -2787,6 +2963,7 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type,
 	fctx->restarts = 0;
 	fctx->timeouts = 0;
 	fctx->attributes = 0;
+	fctx->spilled = ISC_FALSE;
 	fctx->nqueries = 0;
 
 	dns_name_init(&fctx->nsname, NULL);
@@ -2829,7 +3006,9 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type,
 						      NULL);
 			if (result != ISC_R_SUCCESS)
 				goto cleanup_name;
-			result = dns_name_dup(domain, res->mctx, &fctx->domain);
+			result = dns_name_dup(domain,
+					      res->buckets[bucketnum].mctx,
+					      &fctx->domain);
 			if (result != ISC_R_SUCCESS) {
 				dns_rdataset_disassociate(&fctx->nameservers);
 				goto cleanup_name;
@@ -2838,12 +3017,16 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type,
 			/*
 			 * We're in forward-only mode.  Set the query domain.
 			 */
-			result = dns_name_dup(domain, res->mctx, &fctx->domain);
+			result = dns_name_dup(domain,
+					      res->buckets[bucketnum].mctx,
+					      &fctx->domain);
 			if (result != ISC_R_SUCCESS)
 				goto cleanup_name;
 		}
 	} else {
-		result = dns_name_dup(domain, res->mctx, &fctx->domain);
+		result = dns_name_dup(domain,
+				      res->buckets[bucketnum].mctx,
+				      &fctx->domain);
 		if (result != ISC_R_SUCCESS)
 			goto cleanup_name;
 		dns_rdataset_clone(nameservers, &fctx->nameservers);
@@ -2852,14 +3035,16 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type,
 	INSIST(dns_name_issubdomain(&fctx->name, &fctx->domain));
 
 	fctx->qmessage = NULL;
-	result = dns_message_create(res->mctx, DNS_MESSAGE_INTENTRENDER,
+	result = dns_message_create(res->buckets[bucketnum].mctx,
+				    DNS_MESSAGE_INTENTRENDER,
 				    &fctx->qmessage);
 
 	if (result != ISC_R_SUCCESS)
 		goto cleanup_domain;
 
 	fctx->rmessage = NULL;
-	result = dns_message_create(res->mctx, DNS_MESSAGE_INTENTPARSE,
+	result = dns_message_create(res->buckets[bucketnum].mctx,
+				    DNS_MESSAGE_INTENTPARSE,
 				    &fctx->rmessage);
 
 	if (result != ISC_R_SUCCESS)
@@ -2932,18 +3117,18 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type,
 
  cleanup_domain:
 	if (dns_name_countlabels(&fctx->domain) > 0)
-		dns_name_free(&fctx->domain, res->mctx);
+		dns_name_free(&fctx->domain, res->buckets[bucketnum].mctx);
 	if (dns_rdataset_isassociated(&fctx->nameservers))
 		dns_rdataset_disassociate(&fctx->nameservers);
 
  cleanup_name:
-	dns_name_free(&fctx->name, res->mctx);
+	dns_name_free(&fctx->name, res->buckets[bucketnum].mctx);
 
  cleanup_info:
-	isc_mem_free(res->mctx, fctx->info);
+	isc_mem_free(res->buckets[bucketnum].mctx, fctx->info);
 
  cleanup_fetch:
-	isc_mem_put(res->mctx, fctx, sizeof(*fctx));
+	isc_mem_put(res->buckets[bucketnum].mctx, fctx, sizeof(*fctx));
 
 	return (result);
 }
@@ -3180,7 +3365,8 @@ validated(isc_task_t *task, isc_event_t *event) {
 	 * destroy the fctx if necessary.
 	 */
 	dns_validator_destroy(&vevent->validator);
-	isc_mem_put(fctx->res->mctx, valarg, sizeof(*valarg));
+	isc_mem_put(fctx->res->buckets[fctx->bucketnum].mctx,
+		    valarg, sizeof(*valarg));
 
 	negative = ISC_TF(vevent->rdataset == NULL);
 
@@ -3290,7 +3476,8 @@ validated(isc_task_t *task, isc_event_t *event) {
 		 */
 		ttl = fctx->res->view->maxncachettl;
 		if (fctx->type == dns_rdatatype_soa &&
-		    covers == dns_rdatatype_any)
+		    covers == dns_rdatatype_any &&
+		    fctx->res->zero_no_soa_ttl)
 			ttl = 0;
 
 		result = ncache_adderesult(fctx->rmessage, fctx->cache, node,
@@ -3471,14 +3658,16 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo,
 	/*
 	 * Is DNSSEC validation required for this name?
 	 */
-	result = dns_keytable_issecuredomain(res->view->secroots, name,
-					     &secure_domain);
-	if (result != ISC_R_SUCCESS)
-		return (result);
+	if (res->view->enablevalidation) {
+		result = dns_keytable_issecuredomain(res->view->secroots, name,
+						     &secure_domain);
+		if (result != ISC_R_SUCCESS)
+			return (result);
 
-	if (!secure_domain && res->view->dlv != NULL) {
-		valoptions = DNS_VALIDATOR_DLV;
-		secure_domain = ISC_TRUE;
+		if (!secure_domain && res->view->dlv != NULL) {
+			valoptions = DNS_VALIDATOR_DLV;
+			secure_domain = ISC_TRUE;
+		}
 	}
 
 	if ((fctx->options & DNS_FETCHOPT_NOVALIDATE) != 0)
@@ -3899,14 +4088,16 @@ ncache_message(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
 	/*
 	 * Is DNSSEC validation required for this name?
 	 */
-	result = dns_keytable_issecuredomain(res->view->secroots, name,
-					     &secure_domain);
-	if (result != ISC_R_SUCCESS)
-		return (result);
+	if (fctx->res->view->enablevalidation) {
+		result = dns_keytable_issecuredomain(res->view->secroots, name,
+						     &secure_domain);
+		if (result != ISC_R_SUCCESS)
+			return (result);
 
-	if (!secure_domain && res->view->dlv != NULL) {
-		valoptions = DNS_VALIDATOR_DLV;
-		secure_domain = ISC_TRUE;
+		if (!secure_domain && res->view->dlv != NULL) {
+			valoptions = DNS_VALIDATOR_DLV;
+			secure_domain = ISC_TRUE;
+		}
 	}
 
 	if ((fctx->options & DNS_FETCHOPT_NOVALIDATE) != 0)
@@ -4211,7 +4402,7 @@ noanswer_response(fetchctx_t *fctx, dns_name_t *oqname,
 	dns_message_t *message;
 	dns_name_t *name, *qname, *ns_name, *soa_name, *ds_name;
 	dns_rdataset_t *rdataset, *ns_rdataset;
-	isc_boolean_t done, aa, negative_response;
+	isc_boolean_t aa, negative_response;
 	dns_rdatatype_t type;
 	dns_section_t section =
 		bind8_ns_resp ? DNS_SECTION_ANSWER : DNS_SECTION_AUTHORITY;
@@ -4270,13 +4461,12 @@ noanswer_response(fetchctx_t *fctx, dns_name_t *oqname,
 	/*
 	 * Process the authority section.
 	 */
-	done = ISC_FALSE;
 	ns_name = NULL;
 	ns_rdataset = NULL;
 	soa_name = NULL;
 	ds_name = NULL;
 	result = dns_message_firstname(message, section);
-	while (!done && result == ISC_R_SUCCESS) {
+	while (result == ISC_R_SUCCESS) {
 		name = NULL;
 		dns_message_currentname(message, section, &name);
 		if (dns_name_issubdomain(name, &fctx->domain)) {
@@ -4338,15 +4528,29 @@ noanswer_response(fetchctx_t *fctx, dns_name_t *oqname,
 							dns_trust_additional;
 				}
 			}
-			/*
-			 * A negative response has a SOA record (Type 2) 
-			 * and a optional NS RRset (Type 1) or it has neither
-			 * a SOA or a NS RRset (Type 3, handled above) or
-			 * rcode is NXDOMAIN (handled above) in which case
-			 * the NS RRset is allowed (Type 4).
-			 */
-			if (soa_name != NULL)
-				negative_response = ISC_TRUE;
+		}
+		result = dns_message_nextname(message, section);
+		if (result == ISC_R_NOMORE)
+			break;
+		else if (result != ISC_R_SUCCESS)
+			return (result);
+	}
+
+	/*
+	 * A negative response has a SOA record (Type 2) 
+	 * and a optional NS RRset (Type 1) or it has neither
+	 * a SOA or a NS RRset (Type 3, handled above) or
+	 * rcode is NXDOMAIN (handled above) in which case
+	 * the NS RRset is allowed (Type 4).
+	 */
+	if (soa_name != NULL)
+		negative_response = ISC_TRUE;
+
+	result = dns_message_firstname(message, section);
+	while (result == ISC_R_SUCCESS) {
+		name = NULL;
+		dns_message_currentname(message, section, &name);
+		if (dns_name_issubdomain(name, &fctx->domain)) {
 			for (rdataset = ISC_LIST_HEAD(name->list);
 			     rdataset != NULL;
 			     rdataset = ISC_LIST_NEXT(rdataset, link)) {
@@ -4501,11 +4705,14 @@ noanswer_response(fetchctx_t *fctx, dns_name_t *oqname,
 		 *	   if so we should bail out.
 		 */
 		INSIST(dns_name_countlabels(&fctx->domain) > 0);
-		dns_name_free(&fctx->domain, fctx->res->mctx);
+		dns_name_free(&fctx->domain,
+			      fctx->res->buckets[fctx->bucketnum].mctx);
 		if (dns_rdataset_isassociated(&fctx->nameservers))
 			dns_rdataset_disassociate(&fctx->nameservers);
 		dns_name_init(&fctx->domain, NULL);
-		result = dns_name_dup(ns_name, fctx->res->mctx, &fctx->domain);
+		result = dns_name_dup(ns_name,
+				      fctx->res->buckets[fctx->bucketnum].mctx,
+				      &fctx->domain);
 		if (result != ISC_R_SUCCESS)
 			return (result);
 		fctx->attributes |= FCTX_ATTR_WANTCACHE;
@@ -4960,9 +5167,11 @@ resume_dslookup(isc_task_t *task, isc_event_t *event) {
 		if (dns_rdataset_isassociated(&fctx->nameservers))
 			dns_rdataset_disassociate(&fctx->nameservers);
 		dns_rdataset_clone(fevent->rdataset, &fctx->nameservers);
-		dns_name_free(&fctx->domain, fctx->res->mctx);
+		dns_name_free(&fctx->domain,
+			      fctx->res->buckets[bucketnum].mctx);
 		dns_name_init(&fctx->domain, NULL);
-		result = dns_name_dup(&fctx->nsname, fctx->res->mctx,
+		result = dns_name_dup(&fctx->nsname,
+				      fctx->res->buckets[bucketnum].mctx,
 				      &fctx->domain);
 		if (result != ISC_R_SUCCESS) {
 			fctx_done(fctx, DNS_R_SERVFAIL);
@@ -5386,6 +5595,28 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 			 * for this fetch.
 			 */
 			result = DNS_R_YXDOMAIN;
+		} else if (message->rcode == dns_rcode_badvers) {
+			dns_rdataset_t *opt;
+			unsigned int flags, mask;
+			unsigned int version;
+
+			resend = ISC_TRUE;
+			opt = dns_message_getopt(message);
+			version = (opt->ttl >> 16) & 0xff;
+			flags = (version << DNS_FETCHOPT_EDNSVERSIONSHIFT) |
+				DNS_FETCHOPT_EDNSVERSIONSET;
+			mask = DNS_FETCHOPT_EDNSVERSIONMASK |
+			       DNS_FETCHOPT_EDNSVERSIONSET;
+			switch (version) {
+			case 0:
+				dns_adb_changeflags(fctx->adb, query->addrinfo,
+						    flags, mask);
+				break;
+			default:
+				broken_server = DNS_R_BADVERS;
+				keep_trying = ISC_TRUE;
+				break;
+			}
 		} else {
 			/*
 			 * XXXRTH log.
@@ -5415,7 +5646,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 	    is_lame(fctx)) {
 		log_lame(fctx, query->addrinfo);
 		result = dns_adb_marklame(fctx->adb, query->addrinfo,
-					  &fctx->domain,
+					  &fctx->name, fctx->type,
 					  now + fctx->res->lame_ttl);
 		if (result != ISC_R_SUCCESS)
 			isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
@@ -5637,9 +5868,11 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 				fctx_done(fctx, DNS_R_SERVFAIL);
 				return;
 			}
-			dns_name_free(&fctx->domain, fctx->res->mctx);
+			dns_name_free(&fctx->domain,
+				      fctx->res->buckets[fctx->bucketnum].mctx);
 			dns_name_init(&fctx->domain, NULL);
-			result = dns_name_dup(fname, fctx->res->mctx,
+			result = dns_name_dup(fname,
+					      fctx->res->buckets[fctx->bucketnum].mctx,
 					      &fctx->domain);
 			if (result != ISC_R_SUCCESS) {
 				fctx_done(fctx, DNS_R_SERVFAIL);
@@ -5737,6 +5970,7 @@ destroy(dns_resolver_t *res) {
 		isc_task_shutdown(res->buckets[i].task);
 		isc_task_detach(&res->buckets[i].task);
 		DESTROYLOCK(&res->buckets[i].lock);
+		isc_mem_detach(&res->buckets[i].mctx);
 	}
 	isc_mem_put(res->mctx, res->buckets,
 		    res->nbuckets * sizeof(fctxbucket_t));
@@ -5758,6 +5992,7 @@ destroy(dns_resolver_t *res) {
 #if USE_MBSLOCK
 	isc_rwlock_destroy(&res->mbslock);
 #endif
+	isc_timer_detach(&res->spillattimer);
 	res->magic = 0;
 	isc_mem_put(res->mctx, res, sizeof(*res));
 }
@@ -5796,11 +6031,44 @@ empty_bucket(dns_resolver_t *res) {
 	UNLOCK(&res->lock);
 }
 
+static void
+spillattimer_countdown(isc_task_t *task, isc_event_t *event) {
+	dns_resolver_t *res = event->ev_arg;
+	isc_result_t result;
+	unsigned int count;
+	isc_boolean_t logit = ISC_FALSE;
+
+	REQUIRE(VALID_RESOLVER(res));
+
+	UNUSED(task);
+	
+	LOCK(&res->lock);
+	INSIST(!res->exiting);
+	if (res->spillat > res->spillatmin) {
+		res->spillat--;
+		logit = ISC_TRUE;
+	}
+	if (res->spillat <= res->spillatmin) {
+		result = isc_timer_reset(res->spillattimer,
+					 isc_timertype_inactive, NULL,
+					 NULL, ISC_TRUE);
+		RUNTIME_CHECK(result == ISC_R_SUCCESS);
+	}
+	count = res->spillat;
+	UNLOCK(&res->lock);
+	if (logit)
+		isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
+			      DNS_LOGMODULE_RESOLVER, ISC_LOG_NOTICE,
+			      "clients-per-query decreased to %u", count);
+
+	isc_event_free(&event);
+}
+
 isc_result_t
 dns_resolver_create(dns_view_t *view,
 		    isc_taskmgr_t *taskmgr, unsigned int ntasks,
 		    isc_socketmgr_t *socketmgr,
-		    isc_timermgr_t *timermgr,
+		    isc_timermgr_t *timermgr, 
 		    unsigned int options,
 		    dns_dispatchmgr_t *dispatchmgr,
 		    dns_dispatch_t *dispatchv4,
@@ -5810,6 +6078,7 @@ dns_resolver_create(dns_view_t *view,
 	dns_resolver_t *res;
 	isc_result_t result = ISC_R_SUCCESS;
 	unsigned int i, buckets_created = 0;
+	isc_task_t *task = NULL;
 	char name[16];
 
 	/*
@@ -5839,6 +6108,10 @@ dns_resolver_create(dns_view_t *view,
 	res->udpsize = RECV_BUFFER_SIZE;
 	res->algorithms = NULL;
 	res->mustbesecure = NULL;
+	res->spillatmin = res->spillat = 10;
+	res->spillatmax = 100;
+	res->spillattimer = NULL;
+	res->zero_no_soa_ttl = ISC_FALSE;
 
 	res->nbuckets = ntasks;
 	res->activebuckets = ntasks;
@@ -5858,6 +6131,13 @@ dns_resolver_create(dns_view_t *view,
 			DESTROYLOCK(&res->buckets[i].lock);
 			goto cleanup_buckets;
 		}
+		res->buckets[i].mctx = NULL;
+		result = isc_mem_create(0, 0, &res->buckets[i].mctx);
+		if (result != ISC_R_SUCCESS) {
+			isc_task_detach(&res->buckets[i].task);
+			DESTROYLOCK(&res->buckets[i].lock);
+			goto cleanup_buckets;
+		}
 		snprintf(name, sizeof(name), "res%u", i);
 		isc_task_setname(res->buckets[i].task, name, res);
 		ISC_LIST_INIT(res->buckets[i].fctxs);
@@ -5892,10 +6172,22 @@ dns_resolver_create(dns_view_t *view,
 	if (result != ISC_R_SUCCESS)
 		goto cleanup_nlock;
 
+	task = NULL;
+	result = isc_task_create(taskmgr, 0, &task);
+	if (result != ISC_R_SUCCESS)
+		 goto cleanup_primelock;
+
+	result = isc_timer_create(timermgr, isc_timertype_inactive, NULL, NULL,
+				  task, spillattimer_countdown, res,
+				  &res->spillattimer);
+	isc_task_detach(&task);
+	if (result != ISC_R_SUCCESS)
+		 goto cleanup_primelock;
+
 #if USE_ALGLOCK
 	result = isc_rwlock_init(&res->alglock, 0, 0);
 	if (result != ISC_R_SUCCESS)
-		goto cleanup_primelock;
+		goto cleanup_spillattimer;
 #endif
 #if USE_MBSLOCK
 	result = isc_rwlock_init(&res->mbslock, 0, 0);
@@ -5916,9 +6208,12 @@ dns_resolver_create(dns_view_t *view,
 #endif
 #endif
 #if USE_ALGLOCK || USE_MBSLOCK
+ cleanup_spillattimer:
+	isc_timer_detach(&res->spillattimer);
+#endif
+
  cleanup_primelock:
 	DESTROYLOCK(&res->primelock);
-#endif
 
  cleanup_nlock:
 	DESTROYLOCK(&res->nlock);
@@ -5934,6 +6229,7 @@ dns_resolver_create(dns_view_t *view,
 
  cleanup_buckets:
 	for (i = 0; i < buckets_created; i++) {
+		isc_mem_detach(&res->buckets[i].mctx);
 		DESTROYLOCK(&res->buckets[i].lock);
 		isc_task_shutdown(res->buckets[i].task);
 		isc_task_detach(&res->buckets[i].task);
@@ -5952,6 +6248,7 @@ prime_done(isc_task_t *task, isc_event_t *event) {
 	dns_resolver_t *res;
 	dns_fetchevent_t *fevent;
 	dns_fetch_t *fetch;
+	dns_db_t *db = NULL;
 
 	REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE);
 	fevent = (dns_fetchevent_t *)event;
@@ -5970,6 +6267,13 @@ prime_done(isc_task_t *task, isc_event_t *event) {
 	UNLOCK(&res->primelock);
 
 	UNLOCK(&res->lock);
+	
+	if (fevent->result == ISC_R_SUCCESS &&
+	    res->view->cache != NULL && res->view->hints != NULL) {
+		dns_cache_attachdb(res->view->cache, &db);
+		dns_root_checkhints(res->view, res->view->hints, db);
+		dns_db_detach(&db);
+	}
 
 	if (fevent->node != NULL)
 		dns_db_detachnode(fevent->db, &fevent->node);
@@ -6111,6 +6415,7 @@ dns_resolver_shutdown(dns_resolver_t *res) {
 	unsigned int i;
 	fetchctx_t *fctx;
 	isc_socket_t *sock;
+	isc_result_t result;
 
 	REQUIRE(VALID_RESOLVER(res));
 
@@ -6147,6 +6452,10 @@ dns_resolver_shutdown(dns_resolver_t *res) {
 		}
 		if (res->activebuckets == 0)
 			send_shutdown_events(res);
+		result = isc_timer_reset(res->spillattimer,
+					 isc_timertype_inactive, NULL,
+					 NULL, ISC_TRUE);
+		RUNTIME_CHECK(result == ISC_R_SUCCESS);
 	}
 
 	UNLOCK(&res->lock);
@@ -6217,12 +6526,32 @@ dns_resolver_createfetch(dns_resolver_t *res, dns_name_t *name,
 			 dns_rdataset_t *sigrdataset,
 			 dns_fetch_t **fetchp)
 {
+	return (dns_resolver_createfetch2(res, name, type, domain,
+					  nameservers, forwarders, NULL, 0,
+					  options, task, action, arg,
+					  rdataset, sigrdataset, fetchp));
+}
+
+isc_result_t
+dns_resolver_createfetch2(dns_resolver_t *res, dns_name_t *name,
+			  dns_rdatatype_t type,
+			  dns_name_t *domain, dns_rdataset_t *nameservers,
+			  dns_forwarders_t *forwarders,
+			  isc_sockaddr_t *client, dns_messageid_t id,
+			  unsigned int options, isc_task_t *task,
+			  isc_taskaction_t action, void *arg,
+			  dns_rdataset_t *rdataset,
+			  dns_rdataset_t *sigrdataset,
+			  dns_fetch_t **fetchp)
+{
 	dns_fetch_t *fetch;
 	fetchctx_t *fctx = NULL;
-	isc_result_t result;
+	isc_result_t result = ISC_R_SUCCESS;
 	unsigned int bucketnum;
 	isc_boolean_t new_fctx = ISC_FALSE;
 	isc_event_t *event;
+	unsigned int count = 0;
+	unsigned int spillat;
 
 	UNUSED(forwarders);
 
@@ -6249,8 +6578,11 @@ dns_resolver_createfetch(dns_resolver_t *res, dns_name_t *name,
 	if (fetch == NULL)
 		return (ISC_R_NOMEMORY);
 
-	bucketnum = dns_name_hash(name, ISC_FALSE) % res->nbuckets;
+	bucketnum = dns_name_fullhash(name, ISC_FALSE) % res->nbuckets;
 
+	LOCK(&res->lock);
+	spillat = res->spillat;
+	UNLOCK(&res->lock);
 	LOCK(&res->buckets[bucketnum].lock);
 
 	if (res->buckets[bucketnum].exiting) {
@@ -6266,6 +6598,31 @@ dns_resolver_createfetch(dns_resolver_t *res, dns_name_t *name,
 				break;
 		}
 	}
+	
+	/*
+	 * Is this a duplicate?
+	 */
+	if (fctx != NULL && client != NULL) {
+		dns_fetchevent_t *fevent;
+		for (fevent = ISC_LIST_HEAD(fctx->events);
+		     fevent != NULL;
+		     fevent = ISC_LIST_NEXT(fevent, ev_link)) {
+			if (fevent->client != NULL && fevent->id == id &&
+			    isc_sockaddr_equal(fevent->client, client)) {
+				result = DNS_R_DUPLICATE;
+				goto unlock;
+			}
+			count++;
+		}
+	}
+	if (count >= res->spillatmin && res->spillatmin != 0) {
+		if (count >= spillat)
+			fctx->spilled = ISC_TRUE;
+		if (fctx->spilled) {
+			result = DNS_R_DROP;
+			goto unlock;
+		}
+	}
 
 	/*
 	 * If we didn't have a fetch, would attach to a done fetch, this
@@ -6285,7 +6642,7 @@ dns_resolver_createfetch(dns_resolver_t *res, dns_name_t *name,
 		new_fctx = ISC_TRUE;
 	}
 
-	result = fctx_join(fctx, task, action, arg,
+	result = fctx_join(fctx, task, client, id, action, arg,
 			   rdataset, sigrdataset, fetch);
 	if (new_fctx) {
 		if (result == ISC_R_SUCCESS) {
@@ -6641,6 +6998,13 @@ dns_resolver_algorithm_supported(dns_resolver_t *resolver, dns_name_t *name,
 	return (dst_algorithm_supported(alg));
 }
 
+isc_boolean_t
+dns_resolver_digest_supported(dns_resolver_t *resolver, unsigned int digest) {
+
+	UNUSED(resolver);
+	return (dns_ds_digest_supported(digest));
+}
+
 void
 dns_resolver_resetmustbesecure(dns_resolver_t *resolver) {
 
@@ -6706,3 +7070,45 @@ dns_resolver_getmustbesecure(dns_resolver_t *resolver, dns_name_t *name) {
 #endif
 	return (value);
 }
+
+void
+dns_resolver_getclientsperquery(dns_resolver_t *resolver, isc_uint32_t *cur,
+				isc_uint32_t *min, isc_uint32_t *max)
+{
+	REQUIRE(VALID_RESOLVER(resolver));
+
+	LOCK(&resolver->lock);
+	if (cur != NULL)
+		*cur = resolver->spillat;
+	if (min != NULL)
+		*min = resolver->spillatmin;
+	if (max != NULL)
+		*max = resolver->spillatmax;
+	UNLOCK(&resolver->lock);
+}
+
+void
+dns_resolver_setclientsperquery(dns_resolver_t *resolver, isc_uint32_t min,
+				isc_uint32_t max)
+{
+	REQUIRE(VALID_RESOLVER(resolver));
+
+	LOCK(&resolver->lock);
+	resolver->spillatmin = resolver->spillat = min;
+	resolver->spillatmax = max;
+	UNLOCK(&resolver->lock);
+}
+
+isc_boolean_t
+dns_resolver_getzeronosoattl(dns_resolver_t *resolver) {
+	REQUIRE(VALID_RESOLVER(resolver));
+
+	return (resolver->zero_no_soa_ttl);
+}
+
+void
+dns_resolver_setzeronosoattl(dns_resolver_t *resolver, isc_boolean_t state) {
+	REQUIRE(VALID_RESOLVER(resolver));
+
+	resolver->zero_no_soa_ttl = state;
+}
-- 
cgit v1.1