? 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 #include +#include #include #include #include @@ -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 } };