diff --git a/CHANGES b/CHANGES index 3243047d2a..bc36ef13a0 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ +2129. [func] Provide a pool of UDP sockets for queries to be + made over. See use-queryport-pool, queryport-pool-ports + and queryport-pool-updateinterval. [RT #16415] + 2128. [doc] xsltproc --nonet, update DTD versions. [RT #16635] 2127. [port] Improved OpenSSL 0.9.8 support. [RT #16563] diff --git a/bin/named/interfacemgr.c b/bin/named/interfacemgr.c index ddece0bca2..fd5a6ead97 100644 --- a/bin/named/interfacemgr.c +++ b/bin/named/interfacemgr.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: interfacemgr.c,v 1.85 2006/07/20 01:10:31 marka Exp $ */ +/* $Id: interfacemgr.c,v 1.86 2007/02/02 02:18:03 marka Exp $ */ /*! \file */ @@ -802,7 +802,9 @@ do_scan(ns_interfacemgr_t *mgr, ns_listenlist_t *ext_listen, (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; diff --git a/bin/named/named.conf.docbook b/bin/named/named.conf.docbook index d2e0810c89..02299b5828 100644 --- a/bin/named/named.conf.docbook +++ b/bin/named/named.conf.docbook @@ -17,7 +17,7 @@ - PERFORMANCE OF THIS SOFTWARE. --> - + Aug 13, 2004 @@ -235,6 +235,9 @@ options { additional-from-cache boolean; query-source ( ( ipv4_address | * ) | address ( ipv4_address | * ) ) port ( integer | * ) ; query-source-v6 ( ( ipv6_address | * ) | address ( ipv6_address | * ) ) port ( integer | * ) ; + use-queryport-pool boolean; + queryport-pool-ports integer; + queryport-pool-updateinterval integer; cleaning-interval integer; min-roots integer; // not implemented lame-ttl integer; @@ -384,6 +387,9 @@ view string optional_class additional-from-cache boolean; query-source ( ( ipv4_address | * ) | address ( ipv4_address | * ) ) port ( integer | * ) ; query-source-v6 ( ( ipv6_address | * ) | address ( ipv6_address | * ) ) port ( integer | * ) ; + use-queryport-pool boolean; + queryport-pool-ports integer; + queryport-pool-updateinterval integer; cleaning-interval integer; min-roots integer; // not implemented lame-ttl integer; diff --git a/bin/named/server.c b/bin/named/server.c index 889300e79d..e45d51205c 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: server.c,v 1.475 2007/01/12 00:14:51 marka Exp $ */ +/* $Id: server.c,v 1.476 2007/02/02 02:18:05 marka Exp $ */ /*! \file */ @@ -953,7 +953,7 @@ configure_view(dns_view_t *view, const cfg_obj_t *config, 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 "; @@ -962,6 +962,7 @@ configure_view(dns_view_t *view, const cfg_obj_t *config, 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)); @@ -1184,14 +1185,13 @@ configure_view(dns_view_t *view, const cfg_obj_t *config, 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); @@ -1210,11 +1210,93 @@ configure_view(dns_view_t *view, const cfg_obj_t *config, result = ISC_R_UNEXPECTED; goto cleanup; } + + obj = NULL; + (void)ns_config_get(maps, "use-queryport-pool", &obj); + if (obj == NULL || cfg_obj_asboolean(obj)) { + isc_sockaddr_t sa; + isc_boolean_t logit4 = ISC_FALSE, logit6 = ISC_FALSE; + + 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) { + logit4 = ISC_TRUE; + 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) { + logit6 = ISC_TRUE; + resopts &= ~DNS_RESOLVER_USEDISPATCHPOOL6; + } + } + if (logit4 && obj != NULL) + cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR, + "specific query-source port " + "cannot coexist with queryport-pool. " + "(Pool disabled)"); + if (logit6 && obj != NULL) + cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR, + "specific query-source-v6 port " + "cannot coexist with queryport-pool. " + "(Pool disabled)"); + } + 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. */ @@ -1241,7 +1323,7 @@ configure_view(dns_view_t *view, const cfg_obj_t *config, 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. */ @@ -2376,7 +2458,9 @@ scan_interfaces(ns_server_t *server, isc_boolean_t verbose) { } 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; @@ -2386,7 +2470,8 @@ add_listenelt(isc_mem_t *mctx, ns_listenlist_t *list, isc_sockaddr_t *addr) { 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; @@ -2438,6 +2523,8 @@ adjust_interfaces(ns_server_t *server, isc_mem_t *mctx) { 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) @@ -2445,7 +2532,19 @@ adjust_interfaces(ns_server_t *server, isc_mem_t *mctx) { 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; } @@ -2475,12 +2574,12 @@ adjust_interfaces(ns_server_t *server, isc_mem_t *mctx) { 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; } diff --git a/doc/arm/Bv9ARM-book.xml b/doc/arm/Bv9ARM-book.xml index 29b3b1ba28..7a806cda41 100644 --- a/doc/arm/Bv9ARM-book.xml +++ b/doc/arm/Bv9ARM-book.xml @@ -18,7 +18,7 @@ - PERFORMANCE OF THIS SOFTWARE. --> - + BIND 9 Administrator Reference Manual @@ -4443,6 +4443,9 @@ category notify { null; }; port ( ip_port | * ) | address ( ip6_addr | * ) port ( ip_port | * ) ) ; + use-queryport-pool yse_or_no; + queryport-pool-ports number; + queryport-pool-interval number; max-transfer-time-in number; max-transfer-time-out number; max-transfer-idle-in number; @@ -6074,7 +6077,7 @@ listen-on-v6 port 1234 { !2001:db8::/32; any; }; - + Query Address If the server doesn't know the answer to a question, it will @@ -6085,22 +6088,61 @@ listen-on-v6 port 1234 { !2001:db8::/32; any; }; a wildcard IP address (INADDR_ANY) will be used. If port is * or is omitted, - a random unprivileged port will be used. The avoid-v4-udp-ports + a pool of random unprivileged port will be used. See + use-queryport-pool, + queryport-pool-ports and + queryport-pool-updateinterval for how the pool + is configured. + The avoid-v4-udp-ports and avoid-v6-udp-ports options can be used to prevent named - from selecting certain ports. The defaults are: + from selecting certain ports. + The defaults are: query-source address * port *; query-source-v6 address * port *; + + + use-queryport-pool + + + Enable they use of query port pools. By default query port + pools are enabled unless there is a explicit port defined + in query-source or + query-source-v6. + + + + + + queryport-pool-ports + + + Specify how many pool ports to use. The default is 8. + + + + + + queryport-pool-updateinterval + + + Specify how often, in minutes, that the queryport pool + should be recreated (new ports selected). The default + is 15 minutes. + + + + + The address specified in the query-source option is used for both UDP and TCP queries, but the port applies only - to - UDP queries. TCP queries always use a random + to UDP queries. TCP queries always use a random unprivileged port. @@ -7669,6 +7711,9 @@ query-source-v6 address * port *; query-source address ( ip_addr | * ) port ( ip_port | * ) ; query-source-v6 address ( ip_addr | * ) port ( ip_port | * ) ; }; + use-queryport-pool yse_or_no; + queryport-pool-ports number; + queryport-pool-interval number; diff --git a/lib/dns/dispatch.c b/lib/dns/dispatch.c index 2a12d3cb71..db8b603b58 100644 --- a/lib/dns/dispatch.c +++ b/lib/dns/dispatch.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: dispatch.c,v 1.127 2006/07/19 00:42:13 marka Exp $ */ +/* $Id: dispatch.c,v 1.128 2007/02/02 02:18:06 marka Exp $ */ /*! \file */ @@ -282,6 +282,20 @@ reseed_lfsr(isc_lfsr_t *lfsr, void *arg) lfsr->state = random(); } +/* + * 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. */ @@ -1290,20 +1304,26 @@ dns_dispatchmgr_destroy(dns_dispatchmgr_t **mgrp) { } static isc_boolean_t -blacklisted(dns_dispatchmgr_t *mgr, isc_socket_t *sock) { +blacklisted(dns_dispatchmgr_t *mgr, isc_socket_t *sock, + isc_sockaddr_t *sockaddrp) +{ isc_sockaddr_t sockaddr; isc_result_t result; + REQUIRE(sock != NULL || sockaddrp != NULL); + if (mgr->portlist == NULL) return (ISC_FALSE); - result = isc_socket_getsockname(sock, &sockaddr); - if (result != ISC_R_SUCCESS) - return (ISC_FALSE); + if (sock != NULL) { + sockaddrp = &sockaddr; + result = isc_socket_getsockname(sock, sockaddrp); + if (result != ISC_R_SUCCESS) + return (ISC_FALSE); + } - if (mgr->portlist != NULL && - dns_portlist_match(mgr->portlist, isc_sockaddr_pf(&sockaddr), - isc_sockaddr_getport(&sockaddr))) + if (dns_portlist_match(mgr->portlist, isc_sockaddr_pf(sockaddrp), + isc_sockaddr_getport(sockaddrp))) return (ISC_TRUE); return (ISC_FALSE); } @@ -1324,7 +1344,7 @@ local_addr_match(dns_dispatch_t *disp, isc_sockaddr_t *addr) { if (disp->mgr->portlist != NULL && isc_sockaddr_getport(addr) == 0 && isc_sockaddr_getport(&disp->local) == 0 && - blacklisted(disp->mgr, disp->socket)) + blacklisted(disp->mgr, disp->socket, NULL)) return (ISC_FALSE); /* @@ -1669,7 +1689,7 @@ dns_dispatch_getudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, dns_dispatch_t **dispp) { isc_result_t result; - dns_dispatch_t *disp; + dns_dispatch_t *disp = NULL; REQUIRE(VALID_DISPATCHMGR(mgr)); REQUIRE(sockmgr != NULL); @@ -1689,10 +1709,14 @@ dns_dispatch_getudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, LOCK(&mgr->lock); + if ((attributes & DNS_DISPATCHATTR_RANDOMPORT) != 0) { + REQUIRE(isc_sockaddr_getport(localaddr) == 0); + goto createudp; + } + /* - * First, see if we have a dispatcher that matches. + * See if we have a dispatcher that matches. */ - disp = NULL; result = dispatch_find(mgr, localaddr, attributes, mask, &disp); if (result == ISC_R_SUCCESS) { disp->refcount++; @@ -1717,6 +1741,7 @@ dns_dispatch_getudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, return (ISC_R_SUCCESS); } + createudp: /* * Nope, create one. */ @@ -1752,7 +1777,8 @@ dispatch_createudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, 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; + isc_sockaddr_t localaddr_bound; /* * dispatch_allocate() checks mgr for us. @@ -1768,11 +1794,30 @@ dispatch_createudp(dns_dispatchmgr_t *mgr, isc_socketmgr_t *sockmgr, * from returning the same port to us too quickly. */ memset(held, 0, sizeof(held)); + localaddr_bound = *localaddr; getsocket: - result = create_socket(sockmgr, localaddr, &sock); + if ((attributes & DNS_DISPATCHATTR_RANDOMPORT) != 0) { + isc_sockaddr_setport(&localaddr_bound, + get_randomport(mgr->qid)); + if (blacklisted(mgr, NULL, &localaddr_bound)) { + if (++k == 1024) + attributes &= ~DNS_DISPATCHATTR_RANDOMPORT; + goto getsocket; + } + result = create_socket(sockmgr, &localaddr_bound, &sock); + if (result == ISC_R_ADDRINUSE) { + if (++k == 1024) + attributes &= ~DNS_DISPATCHATTR_RANDOMPORT; + goto getsocket; + } + } else + result = create_socket(sockmgr, localaddr, &sock); if (result != ISC_R_SUCCESS) goto deallocate_dispatch; - if (isc_sockaddr_getport(localaddr) == 0 && blacklisted(mgr, sock)) { + if ((attributes & DNS_DISPATCHATTR_RANDOMPORT) == 0 && + isc_sockaddr_getport(localaddr) == 0 && + blacklisted(mgr, sock, NULL)) + { if (held[i] != NULL) isc_socket_detach(&held[i]); held[i++] = sock; diff --git a/lib/dns/include/dns/dispatch.h b/lib/dns/include/dns/dispatch.h index ed6cec5373..dc65a50d73 100644 --- a/lib/dns/include/dns/dispatch.h +++ b/lib/dns/include/dns/dispatch.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: dispatch.h,v 1.52 2006/12/22 01:59:43 marka Exp $ */ +/* $Id: dispatch.h,v 1.53 2007/02/02 02:18:06 marka Exp $ */ #ifndef DNS_DISPATCH_H #define DNS_DISPATCH_H 1 @@ -113,6 +113,9 @@ struct dns_dispatchevent { * _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 @@ struct dns_dispatchevent { #define DNS_DISPATCHATTR_NOLISTEN 0x00000020U #define DNS_DISPATCHATTR_MAKEQUERY 0x00000040U #define DNS_DISPATCHATTR_CONNECTED 0x00000080U +#define DNS_DISPATCHATTR_RANDOMPORT 0x00000100U /*@}*/ isc_result_t diff --git a/lib/dns/include/dns/resolver.h b/lib/dns/include/dns/resolver.h index 163018dce5..d23e9869c1 100644 --- a/lib/dns/include/dns/resolver.h +++ b/lib/dns/include/dns/resolver.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: resolver.h,v 1.53 2006/12/22 01:45:00 marka Exp $ */ +/* $Id: resolver.h,v 1.54 2007/02/02 02:18:06 marka Exp $ */ #ifndef DNS_RESOLVER_H #define DNS_RESOLVER_H 1 @@ -106,6 +106,8 @@ typedef struct dns_fetchevent { #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, @@ -126,8 +128,6 @@ dns_resolver_create(dns_view_t *view, *\li Generally, applications should not create a resolver directly, but * should instead call dns_view_createresolver(). * - *\li No options are currently defined. - * * Requires: * *\li 'view' is a valid view. @@ -474,6 +474,36 @@ dns_resolver_getzeronosoattl(dns_resolver_t *resolver); 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); +/*%< + * Create a pool of dispatches + * + * Notes: + * + *\li Generally, applications should not create a resolver directly, but + * should instead call dns_view_createresolver(). + * + * Requires: + * + *\li 'res' is a valid resolver that has not been frozen. Also it must have + * either the _USEDISPATCHPOOL4 or _USEDISPATCHPOOL6 option. + * + *\li 'taskmgr' is a valid task manager. + * + *\li 'ndisps' > 0. + * + * Returns: + * + *\li #ISC_R_SUCCESS On success. + * + *\li Anything else Failure. + */ + ISC_LANG_ENDDECLS #endif /* DNS_RESOLVER_H */ diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index e11f6f618f..52b142c99e 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: resolver.c,v 1.340 2007/01/08 01:13:38 marka Exp $ */ +/* $Id: resolver.c,v 1.341 2007/02/02 02:18:06 marka Exp $ */ /*! \file */ @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -290,13 +291,36 @@ typedef struct alternate { 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 @@ struct dns_resolver { 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 @@ struct dns_resolver { 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 @@ struct dns_resolver { 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 @@ fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, 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->ndisps > 0) { + 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->ndisps > 0) { + 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 @@ destroy(dns_resolver_t *res) { INSIST(res->nfctx == 0); + RES_DESTROYLOCK(&res->poollock); DESTROYLOCK(&res->primelock); DESTROYLOCK(&res->nlock); DESTROYLOCK(&res->lock); @@ -5978,12 +6034,26 @@ destroy(dns_resolver_t *res) { 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 @@ dns_resolver_create(dns_view_t *view, 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 @@ dns_resolver_create(dns_view_t *view, 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 @@ dns_resolver_create(dns_view_t *view, 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 @@ dns_resolver_create(dns_view_t *view, isc_timer_detach(&res->spillattimer); #endif + cleanup_poollock: + RES_DESTROYLOCK(&res->poollock); + cleanup_primelock: DESTROYLOCK(&res->primelock); @@ -7112,3 +7195,254 @@ dns_resolver_setzeronosoattl(dns_resolver_t *resolver, isc_boolean_t state) { 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, *disp6; + 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); +} diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index dad4e54ad1..a7eac1ca17 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: namedconf.c,v 1.72 2006/12/21 06:02:30 marka Exp $ */ +/* $Id: namedconf.c,v 1.73 2007/02/02 02:18:06 marka Exp $ */ /*! \file */ @@ -790,6 +790,9 @@ view_clauses[] = { { "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 } };