? bin/named/named-cache.conf
Index: bin/named/config.c
===================================================================
RCS file: /proj/cvs/prod/bind9/bin/named/config.c,v
retrieving revision 1.47.18.28
diff -u -r1.47.18.28 config.c
--- bin/named/config.c	3 May 2006 01:46:40 -0000	1.47.18.28
+++ bin/named/config.c	28 Mar 2007 08:57:12 -0000
@@ -141,6 +141,7 @@
 	clients-per-query 10;\n\
 	max-clients-per-query 100;\n\
 	zero-no-soa-ttl-cache no;\n\
+	use-queryport-pool no;\n\
 "
 
 "	/* zone */\n\
Index: bin/named/interfacemgr.c
===================================================================
RCS file: /proj/cvs/prod/bind9/bin/named/interfacemgr.c,v
retrieving revision 1.76.18.8
diff -u -r1.76.18.8 interfacemgr.c
--- bin/named/interfacemgr.c	20 Jul 2006 01:10:30 -0000	1.76.18.8
+++ bin/named/interfacemgr.c	28 Mar 2007 08:57:12 -0000
@@ -802,7 +802,9 @@
 					(void)dns_acl_match(&listen_netaddr,
 							    NULL, ele->acl,
 							    NULL, &match, NULL);
-					if (match > 0 && ele->port == le->port)
+					if (match > 0 &&
+					    (ele->port == le->port ||
+					    ele->port == 0))
 						break;
 					else
 						match = 0;
Index: bin/named/server.c
===================================================================
RCS file: /proj/cvs/prod/bind9/bin/named/server.c,v
retrieving revision 1.419.18.49
diff -u -r1.419.18.49 server.c
--- bin/named/server.c	7 Dec 2006 05:24:19 -0000	1.419.18.49
+++ bin/named/server.c	28 Mar 2007 08:57:13 -0000
@@ -927,7 +927,7 @@
 	const char *str;
 	dns_order_t *order = NULL;
 	isc_uint32_t udpsize;
-	unsigned int check = 0;
+	unsigned int resopts = 0;
 	dns_zone_t *zone = NULL;
 	isc_uint32_t max_clients_per_query;
 	const char *sep = ": view ";
@@ -936,6 +936,7 @@
 	isc_boolean_t rfc1918;
 	isc_boolean_t empty_zones_enable;
 	const cfg_obj_t *disablelist = NULL;
+	isc_uint32_t nqports, qports_updateinterval;
 
 	REQUIRE(DNS_VIEW_VALID(view));
 
@@ -1158,14 +1159,13 @@
 
 	str = cfg_obj_asstring(obj);
 	if (strcasecmp(str, "fail") == 0) {
-		check = DNS_RESOLVER_CHECKNAMES |
+		resopts |= DNS_RESOLVER_CHECKNAMES |
 			DNS_RESOLVER_CHECKNAMESFAIL;
 		view->checknames = ISC_TRUE;
 	} else if (strcasecmp(str, "warn") == 0) {
-		check = DNS_RESOLVER_CHECKNAMES;
+		resopts |= DNS_RESOLVER_CHECKNAMES;
 		view->checknames = ISC_FALSE;
 	} else if (strcasecmp(str, "ignore") == 0) {
-		check = 0;
 		view->checknames = ISC_FALSE;
 	} else
 		INSIST(0);
@@ -1184,12 +1184,92 @@
 		result = ISC_R_UNEXPECTED;
 		goto cleanup;
 	}
+
+	obj = NULL;
+	result = ns_config_get(maps, "use-queryport-pool", &obj);
+	INSIST(result == ISC_R_SUCCESS);
+	if (cfg_obj_asboolean(obj)) {
+		isc_sockaddr_t sa;
+
+		resopts |= (DNS_RESOLVER_USEDISPATCHPOOL4 |
+			    DNS_RESOLVER_USEDISPATCHPOOL6);
+
+		/* Check consistency with query-source(-v6) */
+		if (dispatch4 == NULL)
+			resopts &= ~DNS_RESOLVER_USEDISPATCHPOOL4;
+		else {
+			result = dns_dispatch_getlocaladdress(dispatch4, &sa);
+			INSIST(result == ISC_R_SUCCESS);
+			if (isc_sockaddr_getport(&sa) != 0) {
+				cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR,
+					    "specific query-source port "
+					    "cannot coexist with "
+					    "queryport-pool. "
+					    "(Pool disabled)");
+				resopts &= ~DNS_RESOLVER_USEDISPATCHPOOL4;
+			}
+		}
+
+		if (dispatch6 == NULL)
+			resopts &= ~DNS_RESOLVER_USEDISPATCHPOOL6;
+		else {
+			result = dns_dispatch_getlocaladdress(dispatch6, &sa);
+			INSIST(result == ISC_R_SUCCESS);
+			if (isc_sockaddr_getport(&sa) != 0) {
+				cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR,
+					    "specific query-source-v6 port "
+					    "cannot coexist with "
+					    "queryport-pool. "
+					    "(Pool disabled)");
+				resopts &= ~DNS_RESOLVER_USEDISPATCHPOOL6;
+			}
+		}
+	}
+
 	CHECK(dns_view_createresolver(view, ns_g_taskmgr, 31,
 				      ns_g_socketmgr, ns_g_timermgr,
-				      check, ns_g_dispatchmgr,
+				      resopts, ns_g_dispatchmgr,
 				      dispatch4, dispatch6));
 
 	/*
+	 * Query-port pool parameters.
+	 */
+	obj = NULL;
+	nqports = 8;
+	result = ns_config_get(maps, "queryport-pool-ports", &obj);
+	if (result == ISC_R_SUCCESS) {
+		if ((resopts & (DNS_RESOLVER_USEDISPATCHPOOL4 |
+				DNS_RESOLVER_USEDISPATCHPOOL6)) == 0) {
+			cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR,
+				    "queryport-pool-ports is effective only "
+				    "with 'use-queryport-pool yes' (ignored)");
+		} else
+			nqports = cfg_obj_asuint32(obj);
+	}
+
+	obj = NULL;
+	qports_updateinterval = 15;
+	result = ns_config_get(maps, "queryport-pool-updateinterval", &obj);
+	if (result == ISC_R_SUCCESS) {
+		if ((resopts & (DNS_RESOLVER_USEDISPATCHPOOL4 |
+				DNS_RESOLVER_USEDISPATCHPOOL6)) == 0) {
+			cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR,
+				    "queryport-pool-updateinterval is "
+				    "effective only with 'use-queryport-pool "
+				    "yes' (ignored)");
+		} else
+			qports_updateinterval = cfg_obj_asuint32(obj);
+	}
+
+	if ((resopts & (DNS_RESOLVER_USEDISPATCHPOOL4 |
+			DNS_RESOLVER_USEDISPATCHPOOL6)) != 0) {
+		CHECK(dns_resolver_createdispatchpool(view->resolver,
+						      nqports,
+						      qports_updateinterval
+						      * 60));
+	}
+
+	/*
 	 * Set the ADB cache size to 1/8th of the max-cache-size.
 	 */
 	max_adb_size = 0;
@@ -1215,7 +1295,7 @@
 	result = ns_config_get(maps, "zero-no-soa-ttl-cache", &obj);
 	INSIST(result == ISC_R_SUCCESS);
 	dns_resolver_setzeronosoattl(view->resolver, cfg_obj_asboolean(obj));
-	
+
 	/*
 	 * Set the resolver's EDNS UDP size.
 	 */
@@ -2350,7 +2430,9 @@
 }
 
 static isc_result_t
-add_listenelt(isc_mem_t *mctx, ns_listenlist_t *list, isc_sockaddr_t *addr) {
+add_listenelt(isc_mem_t *mctx, ns_listenlist_t *list, isc_sockaddr_t *addr,
+	      isc_boolean_t wcardport_ok)
+{
 	ns_listenelt_t *lelt = NULL;
 	dns_acl_t *src_acl = NULL;
 	dns_aclelement_t aelt;
@@ -2360,7 +2442,8 @@
 	REQUIRE(isc_sockaddr_pf(addr) == AF_INET6);
 
 	isc_sockaddr_any6(&any_sa6);
-	if (!isc_sockaddr_equal(&any_sa6, addr)) {
+	if (!isc_sockaddr_equal(&any_sa6, addr) &&
+	    (wcardport_ok || isc_sockaddr_getport(addr) != 0)) {
 		aelt.type = dns_aclelementtype_ipprefix;
 		aelt.negative = ISC_FALSE;
 		aelt.u.ip_prefix.prefixlen = 128;
@@ -2412,6 +2495,8 @@
 	     view != NULL;
 	     view = ISC_LIST_NEXT(view, link)) {
 		dns_dispatch_t *dispatch6;
+		isc_boolean_t use_portpool = ISC_FALSE;
+		unsigned int resopts;
 
 		dispatch6 = dns_resolver_dispatchv6(view->resolver);
 		if (dispatch6 == NULL)
@@ -2419,7 +2504,19 @@
 		result = dns_dispatch_getlocaladdress(dispatch6, &addr);
 		if (result != ISC_R_SUCCESS)
 			goto fail;
-		result = add_listenelt(mctx, list, &addr);
+		resopts = dns_resolver_getoptions(view->resolver);
+		if ((resopts & (DNS_RESOLVER_USEDISPATCHPOOL4 |
+				DNS_RESOLVER_USEDISPATCHPOOL6)) != 0) {
+			/*
+			 * If the resolver uses a dynamic pool of query ports
+			 * with a specific source address, some of the current
+			 * and future ports may override an existing wildcard
+			 * IPv6 port.  So we need to allow wildcard match
+			 * in this case.
+			 */
+			use_portpool = ISC_TRUE;
+		}
+		result = add_listenelt(mctx, list, &addr, use_portpool);
 		if (result != ISC_R_SUCCESS)
 			goto fail;
 	}
@@ -2449,12 +2546,12 @@
 			continue;
 
 		addrp = dns_zone_getnotifysrc6(zone);
-		result = add_listenelt(mctx, list, addrp);
+		result = add_listenelt(mctx, list, addrp, ISC_FALSE);
 		if (result != ISC_R_SUCCESS)
 			goto fail;
 
 		addrp = dns_zone_getxfrsource6(zone);
-		result = add_listenelt(mctx, list, addrp);
+		result = add_listenelt(mctx, list, addrp, ISC_FALSE);
 		if (result != ISC_R_SUCCESS)
 			goto fail;
 	}
Index: lib/dns/dispatch.c
===================================================================
RCS file: /proj/cvs/prod/bind9/lib/dns/dispatch.c,v
retrieving revision 1.116.18.13
diff -u -r1.116.18.13 dispatch.c
--- lib/dns/dispatch.c	7 Feb 2007 23:57:58 -0000	1.116.18.13
+++ lib/dns/dispatch.c	28 Mar 2007 08:57:16 -0000
@@ -173,6 +173,7 @@
 				       isc_socketmgr_t *sockmgr,
 				       isc_taskmgr_t *taskmgr,
 				       isc_sockaddr_t *localaddr,
+				       isc_sockaddr_t *localaddr_bound,
 				       unsigned int maxrequests,
 				       unsigned int attributes,
 				       dns_dispatch_t **dispp);
@@ -283,6 +284,20 @@
 }
 
 /*
+ * Return an unpredictable non-reserved UDP port.  We share the QID
+ * framework for this purpose.
+ */
+static in_port_t
+get_randomport(dns_qid_t *qid) {
+	isc_uint32_t p;
+
+	p = isc_lfsr_generate32(&qid->qid_lfsr1, &qid->qid_lfsr2);
+
+	/* XXX: should the range be configurable? */
+	return ((in_port_t)(1024 + (p % (65535 - 1024))));
+}
+
+/*
  * Return an unpredictable message ID.
  */
 static dns_messageid_t
@@ -1670,6 +1685,7 @@
 		    dns_dispatch_t **dispp)
 {
 	isc_result_t result;
+	isc_sockaddr_t localaddr_bound;
 	dns_dispatch_t *disp;
 
 	REQUIRE(VALID_DISPATCHMGR(mgr));
@@ -1690,11 +1706,18 @@
 
 	LOCK(&mgr->lock);
 
+	localaddr_bound = *localaddr;
+	if ((attributes & DNS_DISPATCHATTR_RANDOMPORT) != 0) {
+		REQUIRE(isc_sockaddr_getport(localaddr) == 0);
+		isc_sockaddr_setport(&localaddr_bound,
+				     get_randomport(mgr->qid));
+	}
+
 	/*
 	 * First, see if we have a dispatcher that matches.
 	 */
 	disp = NULL;
-	result = dispatch_find(mgr, localaddr, attributes, mask, &disp);
+	result = dispatch_find(mgr, &localaddr_bound, attributes, mask, &disp);
 	if (result == ISC_R_SUCCESS) {
 		disp->refcount++;
 
@@ -1722,7 +1745,8 @@
 	 * Nope, create one.
 	 */
 	result = dispatch_createudp(mgr, sockmgr, taskmgr, localaddr,
-				    maxrequests, attributes, &disp);
+				    &localaddr_bound, maxrequests, attributes,
+				    &disp);
 	if (result != ISC_R_SUCCESS) {
 		UNLOCK(&mgr->lock);
 		return (result);
@@ -1744,7 +1768,7 @@
 static isc_result_t
 dispatch_createudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr,
 		   isc_taskmgr_t *taskmgr,
-		   isc_sockaddr_t *localaddr,
+		   isc_sockaddr_t *localaddr, isc_sockaddr_t *localaddr_bound,
 		   unsigned int maxrequests,
 		   unsigned int attributes,
 		   dns_dispatch_t **dispp)
@@ -1753,7 +1777,7 @@
 	dns_dispatch_t *disp;
 	isc_socket_t *sock = NULL;
 	isc_socket_t *held[DNS_DISPATCH_HELD];
-	unsigned int i = 0, j = 0;
+	unsigned int i = 0, j = 0, k = 0;
 
 	/*
 	 * dispatch_allocate() checks mgr for us.
@@ -1770,8 +1794,20 @@
 	 */
 	memset(held, 0, sizeof(held));
  getsocket:
-	result = create_socket(sockmgr, localaddr, &sock);
-	if (result != ISC_R_SUCCESS)
+	result = create_socket(sockmgr, localaddr_bound, &sock);
+	if (result == ISC_R_ADDRINUSE &&
+	    (attributes & DNS_DISPATCHATTR_RANDOMPORT) != 0) {
+		if (++k == 1024) { /* XXX: how many times can we retry? */
+			mgr_log(mgr, ISC_LOG_ERROR, "unable to allocate a "
+				"random UDP/IPv%s port",
+				isc_sockaddr_pf(localaddr) == AF_INET ?
+					"4" : "6");
+			goto deallocate_dispatch;
+		}
+		isc_sockaddr_setport(localaddr_bound,
+				     get_randomport(mgr->qid));
+		goto getsocket;
+	} else if (result != ISC_R_SUCCESS)
 		goto deallocate_dispatch;
 	if (isc_sockaddr_getport(localaddr) == 0 && blacklisted(mgr, sock)) {
 		if (held[i] != NULL)
Index: lib/dns/resolver.c
===================================================================
RCS file: /proj/cvs/prod/bind9/lib/dns/resolver.c,v
retrieving revision 1.284.18.57
diff -u -r1.284.18.57 resolver.c
--- lib/dns/resolver.c	14 Feb 2007 23:41:01 -0000	1.284.18.57
+++ lib/dns/resolver.c	28 Mar 2007 08:57:17 -0000
@@ -23,6 +23,7 @@
 
 #include <isc/print.h>
 #include <isc/string.h>
+#include <isc/random.h>
 #include <isc/task.h>
 #include <isc/timer.h>
 #include <isc/util.h>
@@ -290,13 +291,36 @@
 	ISC_LINK(struct alternate)	link;
 } alternate_t;
 
+#ifdef ISC_RWLOCK_USEATOMIC
+#define DNS_RESOLVER_USERWLOCK 1
+#else
+#define DNS_RESOLVER_USERWLOCK 0
+#endif
+
+#if DNS_RESOLVER_USERWLOCK
+#define RES_INITLOCK(l)		isc_rwlock_init((l), 0, 0)
+#define RES_DESTROYLOCK(l)	isc_rwlock_destroy(l)
+#define RES_LOCK(l, t)		RWLOCK((l), (t))
+#define RES_UNLOCK(l, t)	RWUNLOCK((l), (t))
+#else
+#define RES_INITLOCK(l)		isc_mutex_init(l)
+#define RES_DESTROYLOCK(l)	DESTROYLOCK(l)
+#define RES_LOCK(l, t)		LOCK(l)
+#define RES_UNLOCK(l, t)	UNLOCK(l)
+#endif
+
 struct dns_resolver {
 	/* Unlocked. */
 	unsigned int			magic;
 	isc_mem_t *			mctx;
 	isc_mutex_t			lock;
 	isc_mutex_t			nlock;	
-	isc_mutex_t			primelock;	
+	isc_mutex_t			primelock;
+#if DNS_RESOLVER_USERWLOCK
+	isc_rwlock_t			poollock;
+#else
+	isc_mutex_t			poollock;
+#endif
 	dns_rdataclass_t		rdclass;
 	isc_socketmgr_t *		socketmgr;
 	isc_timermgr_t *		timermgr;
@@ -307,6 +331,7 @@
 	dns_dispatchmgr_t *		dispatchmgr;
 	dns_dispatch_t *		dispatchv4;
 	dns_dispatch_t *		dispatchv6;
+	unsigned int			ndisps;
 	unsigned int			nbuckets;
 	fctxbucket_t *			buckets;
 	isc_uint32_t			lame_ttl;
@@ -324,6 +349,7 @@
 	unsigned int			spillatmin;
 	isc_timer_t *			spillattimer;
 	isc_boolean_t			zero_no_soa_ttl;
+	isc_timer_t *			disppooltimer;
 	/* Locked by lock. */
 	unsigned int			references;
 	isc_boolean_t			exiting;
@@ -331,10 +357,14 @@
 	unsigned int			activebuckets;
 	isc_boolean_t			priming;
 	unsigned int			spillat;
+	unsigned int			nextdisp;
 	/* Locked by primelock. */
 	dns_fetch_t *			primefetch;
 	/* Locked by nlock. */
 	unsigned int			nfctx;
+	/* Locked by poollock. */
+	dns_dispatch_t **		dispatchv4pool;
+	dns_dispatch_t **		dispatchv6pool;
 };
 
 #define RES_MAGIC			ISC_MAGIC('R', 'e', 's', '!')
@@ -1143,14 +1173,39 @@
 			if (result != ISC_R_SUCCESS)
 				goto cleanup_query;
 		} else {
+			int did = 0;
+			isc_uint32_t val;
+
+			if (res->ndisps > 0) {
+				isc_random_get(&val);
+				did = val % res->ndisps;
+			}
 			switch (isc_sockaddr_pf(&addrinfo->sockaddr)) {
 			case PF_INET:
-				dns_dispatch_attach(res->dispatchv4,
-						    &query->dispatch);
+				if (res->dispatchv4pool != NULL) {
+					RES_LOCK(&res->poollock,
+						 isc_rwlocktype_read);
+					dns_dispatch_attach(res->dispatchv4pool[did],
+							    &query->dispatch);
+					RES_UNLOCK(&res->poollock,
+						   isc_rwlocktype_read);
+				} else {
+					dns_dispatch_attach(res->dispatchv4,
+							    &query->dispatch);
+				}
 				break;
 			case PF_INET6:
-				dns_dispatch_attach(res->dispatchv6,
-						    &query->dispatch);
+				if (res->dispatchv6pool != NULL) {
+					RES_LOCK(&res->poollock,
+						 isc_rwlocktype_read);
+					dns_dispatch_attach(res->dispatchv6pool[did],
+							    &query->dispatch);
+					RES_UNLOCK(&res->poollock,
+						   isc_rwlocktype_read);
+				} else {
+					dns_dispatch_attach(res->dispatchv6,
+							    &query->dispatch);
+				}
 				break;
 			default:
 				result = ISC_R_NOTIMPLEMENTED;
@@ -5962,6 +6017,7 @@
 
 	INSIST(res->nfctx == 0);
 
+	RES_DESTROYLOCK(&res->poollock);
 	DESTROYLOCK(&res->primelock);
 	DESTROYLOCK(&res->nlock);
 	DESTROYLOCK(&res->lock);
@@ -5978,12 +6034,26 @@
 		dns_dispatch_detach(&res->dispatchv4);
 	if (res->dispatchv6 != NULL)
 		dns_dispatch_detach(&res->dispatchv6);
+	if (res->dispatchv4pool != NULL) {
+		for (i = 0; i < res->ndisps; i++)
+			dns_dispatch_detach(&res->dispatchv4pool[i]);
+		isc_mem_put(res->mctx, res->dispatchv4pool,
+			    res->ndisps * sizeof(dns_dispatch_t *));
+	}
+	if (res->dispatchv6pool != NULL) {
+		for (i = 0; i < res->ndisps; i++)
+			dns_dispatch_detach(&res->dispatchv6pool[i]);
+		isc_mem_put(res->mctx, res->dispatchv6pool,
+			    res->ndisps * sizeof(dns_dispatch_t *));
+	}
 	while ((a = ISC_LIST_HEAD(res->alternates)) != NULL) {
 		ISC_LIST_UNLINK(res->alternates, a, link);
 		if (!a->isaddress)
 			dns_name_free(&a->_u._n.name, res->mctx);
 		isc_mem_put(res->mctx, a, sizeof(*a));
 	}
+	if (res->disppooltimer != NULL)
+		isc_timer_detach(&res->disppooltimer);
 	dns_resolver_reset_algorithms(res);
 	dns_resolver_resetmustbesecure(res);
 #if USE_ALGLOCK
@@ -6112,6 +6182,11 @@
 	res->spillatmax = 100;
 	res->spillattimer = NULL;
 	res->zero_no_soa_ttl = ISC_FALSE;
+	res->ndisps = 0;
+	res->nextdisp = 0; /* meaningless at this point, but init it */
+	res->dispatchv4pool = NULL;
+	res->dispatchv6pool = NULL;
+	res->disppooltimer = NULL;
 
 	res->nbuckets = ntasks;
 	res->activebuckets = ntasks;
@@ -6147,7 +6222,8 @@
 
 	res->dispatchv4 = NULL;
 	if (dispatchv4 != NULL)
-		dns_dispatch_attach(dispatchv4, &res->dispatchv4);
+			dns_dispatch_attach(dispatchv4, &res->dispatchv4);
+
 	res->dispatchv6 = NULL;
 	if (dispatchv6 != NULL)
 		dns_dispatch_attach(dispatchv6, &res->dispatchv6);
@@ -6172,17 +6248,21 @@
 	if (result != ISC_R_SUCCESS)
 		goto cleanup_nlock;
 
+	result = RES_INITLOCK(&res->poollock);
+	if (result != ISC_R_SUCCESS)
+		goto cleanup_primelock;
+
 	task = NULL;
 	result = isc_task_create(taskmgr, 0, &task);
 	if (result != ISC_R_SUCCESS)
-		 goto cleanup_primelock;
+		 goto cleanup_poollock;
 
 	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;
+		 goto cleanup_poollock;
 
 #if USE_ALGLOCK
 	result = isc_rwlock_init(&res->alglock, 0, 0);
@@ -6212,6 +6292,9 @@
 	isc_timer_detach(&res->spillattimer);
 #endif
 
+ cleanup_poollock:
+	RES_DESTROYLOCK(&res->poollock);
+
  cleanup_primelock:
 	DESTROYLOCK(&res->primelock);
 
@@ -7112,3 +7195,254 @@
 
 	resolver->zero_no_soa_ttl = state;
 }
+
+unsigned int
+dns_resolver_getoptions(dns_resolver_t *resolver) {
+	REQUIRE(VALID_RESOLVER(resolver));
+	
+	return (resolver->options);
+}
+
+static void
+disppooltimer_update(isc_task_t *task, isc_event_t *event) {
+	dns_resolver_t *res = event->ev_arg;
+	isc_sockaddr_t addr4, addr6;
+	dns_dispatch_t *disp4 = NULL, *disp6 = NULL;
+	isc_result_t result;
+	unsigned int nxt;
+	unsigned int attrs_base, attrs, attrmask;
+
+	REQUIRE(VALID_RESOLVER(res));
+	REQUIRE((res->options & DNS_RESOLVER_USEDISPATCHPOOL4) != 0 ||
+		(res->options & DNS_RESOLVER_USEDISPATCHPOOL6) != 0);
+
+	UNUSED(task);
+	isc_event_free(&event);
+	
+	LOCK(&res->lock);
+	nxt = res->nextdisp++;
+	if (res->nextdisp == res->ndisps)
+		res->nextdisp = 0;
+	UNLOCK(&res->lock);
+
+	attrs_base = 0;
+	attrs_base |= DNS_DISPATCHATTR_UDP;
+	attrs_base |= DNS_DISPATCHATTR_RANDOMPORT;
+
+	attrmask = 0;
+	attrmask |= DNS_DISPATCHATTR_UDP;
+	attrmask |= DNS_DISPATCHATTR_TCP;
+	attrmask |= DNS_DISPATCHATTR_IPV4;
+	attrmask |= DNS_DISPATCHATTR_IPV6;
+
+	RES_LOCK(&res->poollock, isc_rwlocktype_read);
+	if ((res->options & DNS_RESOLVER_USEDISPATCHPOOL4) != 0) {
+		result = dns_dispatch_getlocaladdress(res->dispatchv4pool[nxt],
+						      &addr4);
+		INSIST(result == ISC_R_SUCCESS);
+	}
+	if ((res->options & DNS_RESOLVER_USEDISPATCHPOOL6) != 0) {
+		result = dns_dispatch_getlocaladdress(res->dispatchv6pool[nxt],
+						      &addr6);
+		INSIST(result == ISC_R_SUCCESS);
+	}
+	RES_UNLOCK(&res->poollock, isc_rwlocktype_read);
+
+	if ((res->options & DNS_RESOLVER_USEDISPATCHPOOL4) != 0) {
+		attrs = attrs_base;
+		attrs |= DNS_DISPATCHATTR_IPV4;
+
+		disp4 = NULL;
+		result = dns_dispatch_getudp(res->dispatchmgr,
+					     res->socketmgr,
+					     res->taskmgr, &addr4,
+					     4096, 1000, 32768, 16411,
+					     16433, attrs, attrmask,
+					     &disp4);
+		if (result != ISC_R_SUCCESS) {
+			isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
+				      DNS_LOGMODULE_RESOLVER, ISC_LOG_ERROR,
+				      "could not update an IPv4 random query "
+				      "port: %s",
+				      isc_result_totext(result));
+			/* keep the old one */
+		}
+
+		/*
+		 * We don't try to ensure the new dispatch is unique (see the
+		 * comments in dns_resolver_createdispatchpool()).
+		 */
+	}
+	if ((res->options & DNS_RESOLVER_USEDISPATCHPOOL6) != 0) {
+		attrs = attrs_base;
+		attrs |= DNS_DISPATCHATTR_IPV6;
+
+		disp6 = NULL;
+		result = dns_dispatch_getudp(res->dispatchmgr,
+					     res->socketmgr,
+					     res->taskmgr, &addr6,
+					     4096, 1000, 32768, 16411,
+					     16433, attrs, attrmask,
+					     &disp6);
+		if (result != ISC_R_SUCCESS) {
+			isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
+				      DNS_LOGMODULE_RESOLVER, ISC_LOG_ERROR,
+				      "could not update an IPv6 random query "
+				      "port: %s",
+				      isc_result_totext(result));
+		}
+	}
+
+	RES_LOCK(&res->poollock, isc_rwlocktype_write);
+	if (disp4 != NULL) {
+		dns_dispatch_detach(&res->dispatchv4pool[nxt]);
+		res->dispatchv4pool[nxt] = disp4;
+	}
+	if (disp6 != NULL) {
+		dns_dispatch_detach(&res->dispatchv6pool[nxt]);
+		res->dispatchv6pool[nxt] = disp6;
+	}
+	RES_UNLOCK(&res->poollock, isc_rwlocktype_write);
+
+	return;
+}
+
+isc_result_t
+dns_resolver_createdispatchpool(dns_resolver_t *res, unsigned int ndisps,
+				unsigned int tick)
+{
+	unsigned int i;
+	isc_result_t result = ISC_R_SUCCESS;
+	unsigned int attrs_base, attrs, attrmask;
+	isc_sockaddr_t addr4, addr6;
+	dns_dispatch_t *disp;
+	isc_task_t *task;
+	isc_interval_t interval;
+
+	REQUIRE(VALID_RESOLVER(res));
+	REQUIRE(!res->frozen);	/* meaning we don't have to lock res */
+	REQUIRE(ndisps > 0);
+	REQUIRE((res->options & DNS_RESOLVER_USEDISPATCHPOOL4) != 0 ||
+		(res->options & DNS_RESOLVER_USEDISPATCHPOOL6) != 0);
+
+	attrs_base = 0;
+	attrs_base |= DNS_DISPATCHATTR_UDP;
+	attrs_base |= DNS_DISPATCHATTR_RANDOMPORT;
+
+	attrmask = 0;
+	attrmask |= DNS_DISPATCHATTR_UDP;
+	attrmask |= DNS_DISPATCHATTR_TCP;
+	attrmask |= DNS_DISPATCHATTR_IPV4;
+	attrmask |= DNS_DISPATCHATTR_IPV6;
+
+	if ((res->options & DNS_RESOLVER_USEDISPATCHPOOL4) != 0) {
+		INSIST(res->dispatchv4 != NULL);
+		result = dns_dispatch_getlocaladdress(res->dispatchv4, &addr4);
+		INSIST(result == ISC_R_SUCCESS &&
+		       isc_sockaddr_getport(&addr4) == 0);
+		res->dispatchv4pool = isc_mem_get(res->mctx,
+						  sizeof(dns_dispatch_t *) *
+						  ndisps);
+		if (res->dispatchv4pool == NULL)
+			return (ISC_R_NOMEMORY);
+		for (i = 0; i < ndisps; i++)
+			res->dispatchv4pool[i] = NULL;
+	}
+	if ((res->options & DNS_RESOLVER_USEDISPATCHPOOL6) != 0) {
+		INSIST(res->dispatchv6 != NULL);
+		result = dns_dispatch_getlocaladdress(res->dispatchv6, &addr6);
+		INSIST(result == ISC_R_SUCCESS &&
+		       isc_sockaddr_getport(&addr6) == 0);
+		res->dispatchv6pool = isc_mem_get(res->mctx,
+						  sizeof(dns_dispatch_t *) *
+						  ndisps);
+		if (res->dispatchv6pool == NULL) {
+			isc_mem_put(res->mctx, res->dispatchv4pool,
+				    sizeof(dns_dispatch_t *) * ndisps);
+			res->dispatchv4pool = NULL;
+			return (ISC_R_NOMEMORY);
+		}
+		for (i = 0; i < ndisps; i++)
+			res->dispatchv6pool[i] = NULL;
+	}
+
+	for (i = 0; i < ndisps; i++) {
+		if ((res->options & DNS_RESOLVER_USEDISPATCHPOOL4) != 0) {
+			attrs = attrs_base;
+			attrs |= DNS_DISPATCHATTR_IPV4;
+
+			disp = NULL;
+			result = dns_dispatch_getudp(res->dispatchmgr,
+						     res->socketmgr,
+						     res->taskmgr, &addr4,
+						     4096, 1000, 32768, 16411,
+						     16433, attrs, attrmask,
+						     &disp);
+			if (result != ISC_R_SUCCESS)
+				goto cleanup;
+			res->dispatchv4pool[i] = disp;
+
+			/*
+			 * It might be better to ensure all ports are
+			 * different, but in practice it's probably okay to
+			 * assume dns_dispatch_getudp() made reasonable
+			 * choices.
+			 */
+		}
+		if ((res->options & DNS_RESOLVER_USEDISPATCHPOOL6) != 0) {
+			attrs = attrs_base;
+			attrs |= DNS_DISPATCHATTR_IPV6;
+
+			disp = NULL;
+			result = dns_dispatch_getudp(res->dispatchmgr,
+						     res->socketmgr,
+						     res->taskmgr, &addr6,
+						     4096, 1000, 32768, 16411,
+						     16433, attrs, attrmask,
+						     &disp);
+			if (result != ISC_R_SUCCESS)
+				goto cleanup;
+
+			res->dispatchv6pool[i] = disp;
+		}
+	}
+
+	/* start update timer */
+	if (tick != 0) { 
+		task = NULL;
+		result = isc_task_create(res->taskmgr, 0, &task);
+		if (result != ISC_R_SUCCESS)
+			goto cleanup;
+		isc_interval_set(&interval, tick, 0);
+		result = isc_timer_create(res->timermgr, isc_timertype_ticker,
+					  NULL, &interval, task,
+					  disppooltimer_update,
+					  res, &res->disppooltimer);
+		isc_task_detach(&task);
+		if (result != ISC_R_SUCCESS)
+			goto cleanup;
+	}
+
+	res->ndisps = ndisps;
+	res->nextdisp = 0;
+
+	return (result);
+
+  cleanup:
+	for (i = 0; i < ndisps; i++) {
+		if (res->dispatchv4pool[i] != NULL)
+			dns_dispatch_detach(&res->dispatchv4pool[i]);
+		if (res->dispatchv6pool[i] != NULL)
+			dns_dispatch_detach(&res->dispatchv6pool[i]);
+	}
+	if (res->dispatchv4pool != NULL) {
+		isc_mem_put(res->mctx, res->dispatchv4pool,
+			    sizeof(dns_dispatch_t *) * ndisps);
+	}
+	if (res->dispatchv6pool != NULL) {
+		isc_mem_put(res->mctx, res->dispatchv6pool,
+			    sizeof(dns_dispatch_t *) * ndisps);
+	}
+
+	return (result);
+}
Index: lib/dns/include/dns/dispatch.h
===================================================================
RCS file: /proj/cvs/prod/bind9/lib/dns/include/dns/dispatch.h,v
retrieving revision 1.48.18.2
diff -u -r1.48.18.2 dispatch.h
--- lib/dns/include/dns/dispatch.h	29 Apr 2005 00:16:12 -0000	1.48.18.2
+++ lib/dns/include/dns/dispatch.h	28 Mar 2007 08:57:17 -0000
@@ -113,6 +113,9 @@
  * _MAKEQUERY
  *	The dispatcher can be used to issue queries to other servers, and
  *	accept replies from them.
+ *
+ * _RANDOMPORT
+ *	TBD
  */
 #define DNS_DISPATCHATTR_PRIVATE	0x00000001U
 #define DNS_DISPATCHATTR_TCP		0x00000002U
@@ -122,6 +125,7 @@
 #define DNS_DISPATCHATTR_NOLISTEN	0x00000020U
 #define DNS_DISPATCHATTR_MAKEQUERY	0x00000040U
 #define DNS_DISPATCHATTR_CONNECTED	0x00000080U
+#define DNS_DISPATCHATTR_RANDOMPORT	0x00000100U
 /*@}*/
 
 isc_result_t
Index: lib/dns/include/dns/resolver.h
===================================================================
RCS file: /proj/cvs/prod/bind9/lib/dns/include/dns/resolver.h,v
retrieving revision 1.40.18.11
diff -u -r1.40.18.11 resolver.h
--- lib/dns/include/dns/resolver.h	1 Feb 2006 22:39:17 -0000	1.40.18.11
+++ lib/dns/include/dns/resolver.h	28 Mar 2007 08:57:17 -0000
@@ -106,6 +106,8 @@
 
 #define DNS_RESOLVER_CHECKNAMES		0x01
 #define DNS_RESOLVER_CHECKNAMESFAIL	0x02
+#define	DNS_RESOLVER_USEDISPATCHPOOL4	0x04
+#define	DNS_RESOLVER_USEDISPATCHPOOL6	0x08
 
 isc_result_t
 dns_resolver_create(dns_view_t *view,
@@ -474,6 +476,12 @@
 void
 dns_resolver_setzeronosoattl(dns_resolver_t *resolver, isc_boolean_t state);
 
+unsigned int
+dns_resolver_getoptions(dns_resolver_t *resolver);
+
+isc_result_t
+dns_resolver_createdispatchpool(dns_resolver_t *res, unsigned int ndisps,
+				unsigned int interval);
 ISC_LANG_ENDDECLS
 
 #endif /* DNS_RESOLVER_H */
Index: lib/isccfg/namedconf.c
===================================================================
RCS file: /proj/cvs/prod/bind9/lib/isccfg/namedconf.c,v
retrieving revision 1.30.18.38
diff -u -r1.30.18.38 namedconf.c
--- lib/isccfg/namedconf.c	3 May 2006 01:46:40 -0000	1.30.18.38
+++ lib/isccfg/namedconf.c	28 Mar 2007 08:57:17 -0000
@@ -784,6 +784,9 @@
 	{ "empty-zones-enable", &cfg_type_boolean, 0 },
 	{ "disable-empty-zone", &cfg_type_astring, CFG_CLAUSEFLAG_MULTI },
 	{ "zero-no-soa-ttl-cache", &cfg_type_boolean, 0 },
+	{ "use-queryport-pool", &cfg_type_boolean, 0 },
+	{ "queryport-pool-ports", &cfg_type_uint32, 0 },
+	{ "queryport-pool-updateinterval", &cfg_type_uint32, 0 },
 	{ NULL, NULL, 0 }
 };
 
