# HG changeset patch # User Ben Chang # Date 1418671017 28800 # Node ID 4fd4fb5d5c82e2ba501e527e43d7eafd7a1e55e0 # Parent 53d07f5ab9415d789539e51443e53147c7a4348f 20089043 Upgrade Solaris to BIND 9.6-ESV-R11-P1 20089135 problem in SERVICE/DNS-SERVER diff -r 53d07f5ab941 -r 4fd4fb5d5c82 components/bind/Makefile --- a/components/bind/Makefile Sun Dec 14 21:39:43 2014 -0800 +++ b/components/bind/Makefile Mon Dec 15 11:16:57 2014 -0800 @@ -24,7 +24,8 @@ COMPONENT_NAME= bind COMPONENT_VERSION= 9.6-ESV-R11 -IPS_COMPONENT_VERSION= 9.6.3.11.0 +HUMAN_VERSION= $(COMPONENT_VERSION)-P1 +IPS_COMPONENT_VERSION= 9.6.3.11.1 COMPONENT_SRC= $(COMPONENT_NAME)-$(COMPONENT_VERSION) COMPONENT_ARCHIVE= $(COMPONENT_SRC).tar.gz COMPONENT_PROJECT_URL= http://www.isc.org/software/bind/ @@ -36,6 +37,8 @@ http://ftp.isc.org/isc/bind9/$(COMPONENT_VERSION)/$(COMPONENT_ARCHIVE) COMPONENT_BUGDB= service/dns-server +TPNO= 20537 + include ../../make-rules/prep.mk include ../../make-rules/configure.mk include ../../make-rules/ips.mk diff -r 53d07f5ab941 -r 4fd4fb5d5c82 components/bind/bind.p5m --- a/components/bind/bind.p5m Sun Dec 14 21:39:43 2014 -0800 +++ b/components/bind/bind.p5m Mon Dec 15 11:16:57 2014 -0800 @@ -26,13 +26,13 @@ set name=pkg.fmri \ value=pkg:/service/network/dns/bind@$(IPS_COMPONENT_VERSION),$(BUILD_VERSION) -set name=pkg.human-version value=$(COMPONENT_VERSION) +set name=pkg.human-version value=$(HUMAN_VERSION) set name=pkg.description \ value="BIND is open source software that implements the Domain Name System (DNS) protocols for the Internet. This package contains the DNS server 'named' and tools used to setup and validate configuration." set name=pkg.summary \ value="BIND DNS name server and configuration tools." set name=com.oracle.info.description value="the BIND DNS name server and configuration tools" -set name=com.oracle.info.tpno value=16931 +set name=com.oracle.info.tpno value=$(TPNO) set name=info.classification \ value="org.opensolaris.category.2008:System/Services" set name=info.upstream-url value=$(COMPONENT_PROJECT_URL) diff -r 53d07f5ab941 -r 4fd4fb5d5c82 components/bind/bindc.p5m --- a/components/bind/bindc.p5m Sun Dec 14 21:39:43 2014 -0800 +++ b/components/bind/bindc.p5m Mon Dec 15 11:16:57 2014 -0800 @@ -23,10 +23,10 @@ set name=pkg.fmri \ value=pkg:/network/dns/bind@$(IPS_COMPONENT_VERSION),$(BUILD_VERSION) -set name=pkg.human-version value=$(COMPONENT_VERSION) +set name=pkg.human-version value=$(HUMAN_VERSION) set name=pkg.summary value="BIND DNS tools" set name=com.oracle.info.description value="the BIND DNS tools" -set name=com.oracle.info.tpno value=16931 +set name=com.oracle.info.tpno value=$(TPNO) set name=info.classification \ value="org.opensolaris.category.2008:Applications/Internet" set name=pkg.description value="DNS tools and related libraries." diff -r 53d07f5ab941 -r 4fd4fb5d5c82 components/bind/patches/reclimit-v96.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/components/bind/patches/reclimit-v96.patch Mon Dec 15 11:16:57 2014 -0800 @@ -0,0 +1,1064 @@ +This patch was obtained from ISC for 9.6-ESV-R11-P1. The patch can +only be obtained from security-officer@isc.org on an as needed +basis. + +diff --git a/CHANGES b/CHANGES +index 178f73d..8ace4fb 100644 +--- a/CHANGES ++++ b/CHANGES +@@ -1,3 +1,17 @@ ++ --- 9.6-ESV-R11-P1 released --- ++ ++4006. [security] A flaw in delegation handling could be exploited ++ to put named into an infinite loop. This has ++ been addressed by placing limits on the number ++ of levels of recursion named will allow (default 7), ++ and the number of iterative queries that it will ++ send (default 50) before terminating a recursive ++ query (CVE-2014-8500). ++ ++ The recursion depth limit is configured via the ++ "max-recursion-depth" option, and the query limit ++ via the "max-recursion-queries" option. [RT #37580] ++ + --- 9.6-ESV-R11 released --- + + --- 9.6-ESV-R11rc2 released --- +diff --git a/bin/named/config.c b/bin/named/config.c +index d85afa7..823d101 100644 +--- a/bin/named/config.c ++++ b/bin/named/config.c +@@ -15,8 +15,6 @@ + * PERFORMANCE OF THIS SOFTWARE. + */ + +-/* $Id$ */ +- + /*! \file */ + + #include +@@ -145,6 +143,8 @@ options {\n\ + dnssec-accept-expired no;\n\ + clients-per-query 10;\n\ + max-clients-per-query 100;\n\ ++ max-recursion-depth 7;\n\ ++ max-recursion-queries 50;\n\ + zero-no-soa-ttl-cache no;\n\ + nsec3-test-zone no;\n\ + " +diff --git a/bin/named/include/named/query.h b/bin/named/include/named/query.h +index 20aff40..771345e 100644 +--- a/bin/named/include/named/query.h ++++ b/bin/named/include/named/query.h +@@ -1,5 +1,5 @@ + /* +- * Copyright (C) 2004, 2005, 2007, 2010, 2012 Internet Systems Consortium, Inc. ("ISC") ++ * Copyright (C) 2004, 2005, 2007, 2010, 2011, 2013, 2014 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 1999-2002 Internet Software Consortium. + * + * Permission to use, copy, modify, and/or distribute this software for any +@@ -15,8 +15,6 @@ + * PERFORMANCE OF THIS SOFTWARE. + */ + +-/* $Id$ */ +- + #ifndef NAMED_QUERY_H + #define NAMED_QUERY_H 1 + +diff --git a/bin/named/query.c b/bin/named/query.c +index 10a7d9a..48e4822 100644 +--- a/bin/named/query.c ++++ b/bin/named/query.c +@@ -3343,13 +3343,12 @@ query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qdomain, + peeraddr = &client->peeraddr; + else + peeraddr = NULL; +- result = dns_resolver_createfetch2(client->view->resolver, ++ result = dns_resolver_createfetch3(client->view->resolver, + client->query.qname, + qtype, qdomain, nameservers, + NULL, peeraddr, client->message->id, +- client->query.fetchoptions, +- client->task, +- query_resume, client, ++ client->query.fetchoptions, 0, NULL, ++ client->task, query_resume, client, + rdataset, sigrdataset, + &client->query.fetch); + +diff --git a/bin/named/server.c b/bin/named/server.c +index af53b48..4d48074 100644 +--- a/bin/named/server.c ++++ b/bin/named/server.c +@@ -15,8 +15,6 @@ + * PERFORMANCE OF THIS SOFTWARE. + */ + +-/* $Id$ */ +- + /*! \file */ + + #include +@@ -2048,6 +2046,16 @@ configure_view(dns_view_t *view, const cfg_obj_t *config, + max_clients_per_query); + + obj = NULL; ++ result = ns_config_get(maps, "max-recursion-depth", &obj); ++ INSIST(result == ISC_R_SUCCESS); ++ dns_resolver_setmaxdepth(view->resolver, cfg_obj_asuint32(obj)); ++ ++ obj = NULL; ++ result = ns_config_get(maps, "max-recursion-queries", &obj); ++ INSIST(result == ISC_R_SUCCESS); ++ dns_resolver_setmaxqueries(view->resolver, cfg_obj_asuint32(obj)); ++ ++ obj = NULL; + result = ns_config_get(maps, "dnssec-enable", &obj); + INSIST(result == ISC_R_SUCCESS); + view->enablednssec = cfg_obj_asboolean(obj); +diff --git a/doc/arm/Bv9ARM-book.xml b/doc/arm/Bv9ARM-book.xml +index f894aab..b823dc4 100644 +--- a/doc/arm/Bv9ARM-book.xml ++++ b/doc/arm/Bv9ARM-book.xml +@@ -4652,6 +4652,8 @@ category notify { null; }; + max-acache-size size_spec ; + clients-per-query number ; + max-clients-per-query number ; ++ max-recursion-depth number ; ++ max-recursion-queries number ; + masterfile-format (text|raw) ; + empty-server name ; + empty-contact name ; +@@ -4729,6 +4731,35 @@ category notify { null; }; + + + ++ ++ max-recursion-depth ++ ++ ++ Sets the maximum number of levels of recursion ++ that are permitted at any one time while servicing ++ a recursive query. Resolving a name may require ++ looking up a name server address, which in turn ++ requires resolving another name, etc; if the number ++ of indirections exceeds this value, the recursive ++ query is terminated and returns SERVFAIL. The ++ default is 7. ++ ++ ++ ++ ++ ++ max-recursion-queries ++ ++ ++ Sets the maximum number of iterative queries that ++ may be sent while servicing a recursive query. ++ If more queries are sent, the recursive query ++ is terminated and returns SERVFAIL. The default ++ is 50. ++ ++ ++ ++ + + tkey-gssapi-credential + +diff --git a/lib/dns/adb.c b/lib/dns/adb.c +index 2d7b904..422e59c 100644 +--- a/lib/dns/adb.c ++++ b/lib/dns/adb.c +@@ -200,6 +200,7 @@ struct dns_adbfetch { + unsigned int magic; + dns_fetch_t *fetch; + dns_rdataset_t rdataset; ++ unsigned int depth; + }; + + /*% +@@ -298,8 +299,7 @@ static inline isc_boolean_t dec_entry_refcnt(dns_adb_t *, isc_boolean_t, + static inline void violate_locking_hierarchy(isc_mutex_t *, isc_mutex_t *); + static isc_boolean_t clean_namehooks(dns_adb_t *, dns_adbnamehooklist_t *); + static void clean_target(dns_adb_t *, dns_name_t *); +-static void clean_finds_at_name(dns_adbname_t *, isc_eventtype_t, +- unsigned int); ++static void clean_finds_at_name(dns_adbname_t *, isc_eventtype_t, unsigned int); + static isc_boolean_t check_expire_namehooks(dns_adbname_t *, isc_stdtime_t); + static isc_boolean_t check_expire_entry(dns_adb_t *, dns_adbentry_t **, + isc_stdtime_t); +@@ -307,6 +307,7 @@ static void cancel_fetches_at_name(dns_adbname_t *); + static isc_result_t dbfind_name(dns_adbname_t *, isc_stdtime_t, + dns_rdatatype_t); + static isc_result_t fetch_name(dns_adbname_t *, isc_boolean_t, ++ unsigned int, isc_counter_t *qc, + dns_rdatatype_t); + static inline void check_exit(dns_adb_t *); + static void destroy(dns_adb_t *); +@@ -2282,6 +2283,19 @@ dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, + isc_stdtime_t now, dns_name_t *target, + in_port_t port, dns_adbfind_t **findp) + { ++ return (dns_adb_createfind2(adb, task, action, arg, name, ++ qname, qtype, options, now, ++ target, port, 0, NULL, findp)); ++} ++ ++isc_result_t ++dns_adb_createfind2(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, ++ void *arg, dns_name_t *name, dns_name_t *qname, ++ dns_rdatatype_t qtype, unsigned int options, ++ isc_stdtime_t now, dns_name_t *target, ++ in_port_t port, unsigned int depth, isc_counter_t *qc, ++ dns_adbfind_t **findp) ++{ + dns_adbfind_t *find; + dns_adbname_t *adbname; + int bucket; +@@ -2512,7 +2526,7 @@ dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, + * Start V4. + */ + if (WANT_INET(wanted_fetches) && +- fetch_name(adbname, start_at_zone, ++ fetch_name(adbname, start_at_zone, depth, qc, + dns_rdatatype_a) == ISC_R_SUCCESS) { + DP(DEF_LEVEL, + "dns_adb_createfind: started A fetch for name %p", +@@ -2523,7 +2537,7 @@ dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, + * Start V6. + */ + if (WANT_INET6(wanted_fetches) && +- fetch_name(adbname, start_at_zone, ++ fetch_name(adbname, start_at_zone, depth, qc, + dns_rdatatype_aaaa) == ISC_R_SUCCESS) { + DP(DEF_LEVEL, + "dns_adb_createfind: " +@@ -3256,6 +3270,12 @@ fetch_callback(isc_task_t *task, isc_event_t *ev) { + DP(DEF_LEVEL, "adb: fetch of '%s' %s failed: %s", + buf, address_type == DNS_ADBFIND_INET ? "A" : "AAAA", + dns_result_totext(dev->result)); ++ /* ++ * Don't record a failure unless this is the initial ++ * fetch of a chain. ++ */ ++ if (fetch->depth > 1) ++ goto out; + /* XXXMLG Don't pound on bad servers. */ + if (address_type == DNS_ADBFIND_INET) { + name->expire_v4 = ISC_MIN(name->expire_v4, now + 300); +@@ -3293,9 +3313,8 @@ fetch_callback(isc_task_t *task, isc_event_t *ev) { + } + + static isc_result_t +-fetch_name(dns_adbname_t *adbname, +- isc_boolean_t start_at_zone, +- dns_rdatatype_t type) ++fetch_name(dns_adbname_t *adbname, isc_boolean_t start_at_zone, ++ unsigned int depth, isc_counter_t *qc, dns_rdatatype_t type) + { + isc_result_t result; + dns_adbfetch_t *fetch = NULL; +@@ -3340,12 +3359,14 @@ fetch_name(dns_adbname_t *adbname, + result = ISC_R_NOMEMORY; + goto cleanup; + } ++ fetch->depth = depth; + +- result = dns_resolver_createfetch(adb->view->resolver, &adbname->name, +- type, name, nameservers, NULL, +- options, adb->task, fetch_callback, +- adbname, &fetch->rdataset, NULL, +- &fetch->fetch); ++ result = dns_resolver_createfetch3(adb->view->resolver, &adbname->name, ++ type, name, nameservers, NULL, ++ NULL, 0, options, depth, qc, ++ adb->task, fetch_callback, adbname, ++ &fetch->rdataset, NULL, ++ &fetch->fetch); + if (result != ISC_R_SUCCESS) + goto cleanup; + +diff --git a/lib/dns/include/dns/adb.h b/lib/dns/include/dns/adb.h +index d4d1b05..556fcc2 100644 +--- a/lib/dns/include/dns/adb.h ++++ b/lib/dns/include/dns/adb.h +@@ -334,6 +334,13 @@ dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, + dns_rdatatype_t qtype, unsigned int options, + isc_stdtime_t now, dns_name_t *target, + in_port_t port, dns_adbfind_t **find); ++isc_result_t ++dns_adb_createfind2(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, ++ void *arg, dns_name_t *name, dns_name_t *qname, ++ dns_rdatatype_t qtype, unsigned int options, ++ isc_stdtime_t now, dns_name_t *target, in_port_t port, ++ unsigned int depth, isc_counter_t *qc, ++ dns_adbfind_t **find); + /*%< + * Main interface for clients. The adb will look up the name given in + * "name" and will build up a list of found addresses, and perhaps start +diff --git a/lib/dns/include/dns/resolver.h b/lib/dns/include/dns/resolver.h +index d293daa..10c3a3a 100644 +--- a/lib/dns/include/dns/resolver.h ++++ b/lib/dns/include/dns/resolver.h +@@ -270,6 +270,18 @@ dns_resolver_createfetch2(dns_resolver_t *res, dns_name_t *name, + dns_rdataset_t *rdataset, + dns_rdataset_t *sigrdataset, + dns_fetch_t **fetchp); ++isc_result_t ++dns_resolver_createfetch3(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, isc_uint16_t id, ++ unsigned int options, unsigned int depth, ++ isc_counter_t *qc, isc_task_t *task, ++ isc_taskaction_t action, void *arg, ++ dns_rdataset_t *rdataset, ++ dns_rdataset_t *sigrdataset, ++ dns_fetch_t **fetchp); + /*%< + * Recurse to answer a question. + * +@@ -550,6 +562,30 @@ dns_resolver_printbadcache(dns_resolver_t *resolver, FILE *fp); + * \li resolver to be valid. + */ + ++void ++dns_resolver_setmaxdepth(dns_resolver_t *resolver, unsigned int maxdepth); ++unsigned int ++dns_resolver_getmaxdepth(dns_resolver_t *resolver); ++/*% ++ * Get and set how many NS indirections will be followed when looking for ++ * nameserver addresses. ++ * ++ * Requires: ++ * \li resolver to be valid. ++ */ ++ ++void ++dns_resolver_setmaxqueries(dns_resolver_t *resolver, unsigned int queries); ++unsigned int ++dns_resolver_getmaxqueries(dns_resolver_t *resolver); ++/*% ++ * Get and set how many iterative queries will be allowed before ++ * terminating a recursive query. ++ * ++ * Requires: ++ * \li resolver to be valid. ++ */ ++ + ISC_LANG_ENDDECLS + + #endif /* DNS_RESOLVER_H */ +diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c +index 7dcea6d..bd3d9fd 100644 +--- a/lib/dns/resolver.c ++++ b/lib/dns/resolver.c +@@ -21,6 +21,7 @@ + + #include + ++#include + #include + #include + #include +@@ -109,6 +110,16 @@ + #define QTRACE(m) + #endif + ++/* The default maximum number of recursions to follow before giving up. */ ++#ifndef DEFAULT_RECURSION_DEPTH ++#define DEFAULT_RECURSION_DEPTH 7 ++#endif ++ ++/* The default maximum number of iterative queries to allow before giving up. */ ++#ifndef DEFAULT_MAX_QUERIES ++#define DEFAULT_MAX_QUERIES 50 ++#endif ++ + /*% + * Maximum EDNS0 input packet size. + */ +@@ -211,12 +222,13 @@ struct fetchctx { + isc_sockaddrlist_t edns; + isc_sockaddrlist_t edns512; + isc_sockaddrlist_t bad_edns; +- dns_validator_t *validator; ++ dns_validator_t * validator; + ISC_LIST(dns_validator_t) validators; + dns_db_t * cache; + dns_adb_t * adb; + isc_boolean_t ns_ttl_ok; + isc_uint32_t ns_ttl; ++ isc_counter_t * qc; + + /*% + * The number of events we're waiting for. +@@ -283,6 +295,7 @@ struct fetchctx { + unsigned int valfail; + isc_boolean_t timeout; + dns_adbaddrinfo_t *addrinfo; ++ unsigned int depth; + }; + + #define FCTX_MAGIC ISC_MAGIC('F', '!', '!', '!') +@@ -394,6 +407,8 @@ struct dns_resolver { + unsigned int spillatmin; + isc_timer_t * spillattimer; + isc_boolean_t zero_no_soa_ttl; ++ unsigned int maxdepth; ++ unsigned int maxqueries; + + /* Locked by lock. */ + unsigned int references; +@@ -1495,6 +1510,7 @@ fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, + if (result != ISC_R_SUCCESS) + goto cleanup_dispatch; + } ++ + fctx->querysent++; + + ISC_LIST_APPEND(fctx->queries, query, link); +@@ -2146,9 +2162,9 @@ fctx_finddone(isc_task_t *task, isc_event_t *event) { + */ + INSIST(!SHUTTINGDOWN(fctx)); + fctx->attributes &= ~FCTX_ATTR_ADDRWAIT; +- if (event->ev_type == DNS_EVENT_ADBMOREADDRESSES) ++ if (event->ev_type == DNS_EVENT_ADBMOREADDRESSES) { + want_try = ISC_TRUE; +- else { ++ } else { + fctx->findfail++; + if (fctx->pending == 0) { + /* +@@ -2177,7 +2193,7 @@ fctx_finddone(isc_task_t *task, isc_event_t *event) { + else if (want_done) + fctx_done(fctx, ISC_R_FAILURE, __LINE__); + else if (destroy) { +- fctx_destroy(fctx); ++ fctx_destroy(fctx); + if (bucket_empty) + empty_bucket(res); + } +@@ -2499,12 +2515,13 @@ findname(fetchctx_t *fctx, dns_name_t *name, in_port_t port, + * See what we know about this address. + */ + find = NULL; +- result = dns_adb_createfind(fctx->adb, +- res->buckets[fctx->bucketnum].task, +- fctx_finddone, fctx, name, +- &fctx->name, fctx->type, +- options, now, NULL, +- res->view->dstport, &find); ++ result = dns_adb_createfind2(fctx->adb, ++ res->buckets[fctx->bucketnum].task, ++ fctx_finddone, fctx, name, ++ &fctx->name, fctx->type, ++ options, now, NULL, ++ res->view->dstport, ++ fctx->depth + 1, fctx->qc, &find); + if (result != ISC_R_SUCCESS) { + if (result == DNS_R_ALIAS) { + /* +@@ -2612,6 +2629,14 @@ fctx_getaddresses(fetchctx_t *fctx, isc_boolean_t badcache) { + + res = fctx->res; + ++ if (fctx->depth > res->maxdepth) { ++ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, ++ DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(3), ++ "too much NS indirection resolving '%s'", ++ fctx->info); ++ return (DNS_R_SERVFAIL); ++ } ++ + /* + * Forwarders. + */ +@@ -3087,6 +3112,16 @@ fctx_try(fetchctx_t *fctx, isc_boolean_t retrying, isc_boolean_t badcache) { + } + } + ++ result = isc_counter_increment(fctx->qc); ++ if (result != ISC_R_SUCCESS) { ++ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, ++ DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(3), ++ "exceeded max queries resolving '%s'", ++ fctx->info); ++ fctx_done(fctx, DNS_R_SERVFAIL, __LINE__); ++ return; ++ } ++ + result = fctx_query(fctx, addrinfo, fctx->options); + if (result != ISC_R_SUCCESS) + fctx_done(fctx, result, __LINE__); +@@ -3185,6 +3220,7 @@ fctx_destroy(fetchctx_t *fctx) { + isc_mem_put(fctx->mctx, sa, sizeof(*sa)); + } + ++ isc_counter_detach(&fctx->qc); + isc_timer_detach(&fctx->timer); + dns_message_destroy(&fctx->rmessage); + dns_message_destroy(&fctx->qmessage); +@@ -3512,7 +3548,8 @@ log_ns_ttl(fetchctx_t *fctx, const char *where) { + static isc_result_t + fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, + dns_name_t *domain, dns_rdataset_t *nameservers, +- unsigned int options, unsigned int bucketnum, fetchctx_t **fctxp) ++ unsigned int options, unsigned int bucketnum, unsigned int depth, ++ isc_counter_t *qc, fetchctx_t **fctxp) + { + fetchctx_t *fctx; + isc_result_t result; +@@ -3534,6 +3571,21 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, + fctx = isc_mem_get(mctx, sizeof(*fctx)); + if (fctx == NULL) + return (ISC_R_NOMEMORY); ++ ++ fctx->qc = NULL; ++ if (qc != NULL) { ++ isc_counter_attach(qc, &fctx->qc); ++ } else { ++ result = isc_counter_create(res->mctx, ++ res->maxqueries, &fctx->qc); ++ if (result != ISC_R_SUCCESS) ++ goto cleanup_fetch; ++ } ++ ++ /* ++ * Make fctx->info point to a copy of a formatted string ++ * "name/type". ++ */ + dns_name_format(name, buf, sizeof(buf)); + dns_rdatatype_format(type, typebuf, sizeof(typebuf)); + strcat(buf, "/"); /* checked */ +@@ -3541,7 +3593,7 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, + fctx->info = isc_mem_strdup(mctx, buf); + if (fctx->info == NULL) { + result = ISC_R_NOMEMORY; +- goto cleanup_fetch; ++ goto cleanup_counter; + } + FCTXTRACE("create"); + dns_name_init(&fctx->name, NULL); +@@ -3564,6 +3616,7 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, + fctx->state = fetchstate_init; + fctx->want_shutdown = ISC_FALSE; + fctx->cloned = ISC_FALSE; ++ fctx->depth = depth; + ISC_LIST_INIT(fctx->queries); + ISC_LIST_INIT(fctx->finds); + ISC_LIST_INIT(fctx->altfinds); +@@ -3768,6 +3821,9 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type, + cleanup_info: + isc_mem_free(mctx, fctx->info); + ++ cleanup_counter: ++ isc_counter_detach(&fctx->qc); ++ + cleanup_fetch: + isc_mem_put(mctx, fctx, sizeof(*fctx)); + +@@ -7339,6 +7395,8 @@ dns_resolver_create(dns_view_t *view, + res->zero_no_soa_ttl = ISC_FALSE; + res->ndisps = 0; + res->nextdisp = 0; /* meaningless at this point, but init it */ ++ res->maxdepth = DEFAULT_RECURSION_DEPTH; ++ res->maxqueries = DEFAULT_MAX_QUERIES; + res->nbuckets = ntasks; + res->activebuckets = ntasks; + res->buckets = isc_mem_get(view->mctx, +@@ -7778,9 +7836,9 @@ 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, ++ return (dns_resolver_createfetch3(res, name, type, domain, + nameservers, forwarders, NULL, 0, +- options, task, action, arg, ++ options, 0, NULL, task, action, arg, + rdataset, sigrdataset, fetchp)); + } + +@@ -7796,6 +7854,25 @@ dns_resolver_createfetch2(dns_resolver_t *res, dns_name_t *name, + dns_rdataset_t *sigrdataset, + dns_fetch_t **fetchp) + { ++ return (dns_resolver_createfetch3(res, name, type, domain, ++ nameservers, forwarders, client, id, ++ options, 0, NULL, task, action, arg, ++ rdataset, sigrdataset, fetchp)); ++} ++ ++isc_result_t ++dns_resolver_createfetch3(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, unsigned int depth, ++ isc_counter_t *qc, 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_R_SUCCESS; +@@ -7882,11 +7959,12 @@ dns_resolver_createfetch2(dns_resolver_t *res, dns_name_t *name, + + if (fctx == NULL) { + result = fctx_create(res, name, type, domain, nameservers, +- options, bucketnum, &fctx); ++ options, bucketnum, depth, qc, &fctx); + if (result != ISC_R_SUCCESS) + goto unlock; + new_fctx = ISC_TRUE; +- } ++ } else if (fctx->depth > depth) ++ fctx->depth = depth; + + result = fctx_join(fctx, task, client, id, action, arg, + rdataset, sigrdataset, fetch); +@@ -8637,3 +8715,27 @@ dns_resolver_getoptions(dns_resolver_t *resolver) { + + return (resolver->options); + } ++ ++void ++dns_resolver_setmaxdepth(dns_resolver_t *resolver, unsigned int maxdepth) { ++ REQUIRE(VALID_RESOLVER(resolver)); ++ resolver->maxdepth = maxdepth; ++} ++ ++unsigned int ++dns_resolver_getmaxdepth(dns_resolver_t *resolver) { ++ REQUIRE(VALID_RESOLVER(resolver)); ++ return (resolver->maxdepth); ++} ++ ++void ++dns_resolver_setmaxqueries(dns_resolver_t *resolver, unsigned int queries) { ++ REQUIRE(VALID_RESOLVER(resolver)); ++ resolver->maxqueries = queries; ++} ++ ++unsigned int ++dns_resolver_getmaxqueries(dns_resolver_t *resolver) { ++ REQUIRE(VALID_RESOLVER(resolver)); ++ return (resolver->maxqueries); ++} +diff --git a/lib/isc/Makefile.in b/lib/isc/Makefile.in +index 0b4020b..afc19ee 100644 +--- a/lib/isc/Makefile.in ++++ b/lib/isc/Makefile.in +@@ -13,8 +13,6 @@ + # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + # PERFORMANCE OF THIS SOFTWARE. + +-# $Id$ +- + srcdir = @srcdir@ + VPATH = @srcdir@ + top_srcdir = @top_srcdir@ +@@ -53,7 +51,7 @@ WIN32OBJS = win32/condition.@O@ win32/dir.@O@ win32/file.@O@ \ + # Alphabetically + OBJS = @ISC_EXTRA_OBJS@ \ + assertions.@O@ base32.@O@ base64.@O@ bitstring.@O@ buffer.@O@ \ +- bufferlist.@O@ commandline.@O@ error.@O@ event.@O@ \ ++ bufferlist.@O@ commandline.@O@ counter.@O@ error.@O@ event.@O@ \ + hash.@O@ heap.@O@ hex.@O@ hmacmd5.@O@ hmacsha.@O@ \ + httpd.@O@ inet_aton.@O@ iterated_hash.@O@ \ + lex.@O@ lfsr.@O@ lib.@O@ log.@O@ \ +@@ -68,7 +66,7 @@ OBJS = @ISC_EXTRA_OBJS@ \ + # Alphabetically + SRCS = @ISC_EXTRA_SRCS@ \ + assertions.c base32.c base64.c bitstring.c buffer.c \ +- bufferlist.c commandline.c error.c event.c \ ++ bufferlist.c commandline.c counter.c error.c event.c \ + heap.c hex.c hmacmd5.c hmacsha.c \ + httpd.c inet_aton.c iterated_hash.c \ + lex.c lfsr.c lib.c log.c \ +diff --git a/lib/isc/include/isc/Makefile.in b/lib/isc/include/isc/Makefile.in +index 9adca3e..1cfbbd1 100644 +--- a/lib/isc/include/isc/Makefile.in ++++ b/lib/isc/include/isc/Makefile.in +@@ -13,8 +13,6 @@ + # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + # PERFORMANCE OF THIS SOFTWARE. + +-# $Id$ +- + srcdir = @srcdir@ + VPATH = @srcdir@ + top_srcdir = @top_srcdir@ +@@ -27,7 +25,7 @@ top_srcdir = @top_srcdir@ + # install target below. + # + HEADERS = app.h assertions.h base64.h bitstring.h boolean.h buffer.h \ +- bufferlist.h commandline.h entropy.h error.h event.h \ ++ bufferlist.h commandline.h counter.h entropy.h error.h event.h \ + eventclass.h file.h formatcheck.h fsaccess.h \ + hash.h heap.h hex.h hmacmd5.h hmacsha.h \ + httpd.h \ +diff --git a/lib/isc/include/isc/types.h b/lib/isc/include/isc/types.h +index 8e8b08f..a646b8b 100644 +--- a/lib/isc/include/isc/types.h ++++ b/lib/isc/include/isc/types.h +@@ -45,6 +45,7 @@ typedef struct isc_buffer isc_buffer_t; /*%< Buffer */ + typedef ISC_LIST(isc_buffer_t) isc_bufferlist_t; /*%< Buffer List */ + typedef struct isc_constregion isc_constregion_t; /*%< Const region */ + typedef struct isc_consttextregion isc_consttextregion_t; /*%< Const Text Region */ ++typedef struct isc_counter isc_counter_t; /*%< Counter */ + typedef struct isc_entropy isc_entropy_t; /*%< Entropy */ + typedef struct isc_entropysource isc_entropysource_t; /*%< Entropy Source */ + typedef struct isc_event isc_event_t; /*%< Event */ +diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c +index fac2633..3023dcc 100644 +--- a/lib/isccfg/namedconf.c ++++ b/lib/isccfg/namedconf.c +@@ -15,8 +15,6 @@ + * PERFORMANCE OF THIS SOFTWARE. + */ + +-/* $Id$ */ +- + /*! \file */ + + #include +@@ -830,6 +828,8 @@ view_clauses[] = { + { "max-cache-ttl", &cfg_type_uint32, 0 }, + { "max-clients-per-query", &cfg_type_uint32, 0 }, + { "max-ncache-ttl", &cfg_type_uint32, 0 }, ++ { "max-recursion-depth", &cfg_type_uint32, 0 }, ++ { "max-recursion-queries", &cfg_type_uint32, 0 }, + { "max-udp-size", &cfg_type_uint32, 0 }, + { "min-roots", &cfg_type_uint32, CFG_CLAUSEFLAG_NOTIMP }, + { "minimal-responses", &cfg_type_boolean, 0 }, +diff --git a/version b/version +index 1be3c16..2058444 100644 +--- a/version ++++ b/version +@@ -10,4 +10,4 @@ MINORVER=6 + PATCHVER= + RELEASETYPE=-ESV + RELEASEVER=-R11 +-EXTENSIONS= ++EXTENSIONS=-P1 +diff --git a/lib/isc/counter.c b/lib/isc/counter.c +new file mode 100644 +index 0000000..d7d187b +--- /dev/null ++++ b/lib/isc/counter.c +@@ -0,0 +1,138 @@ ++/* ++ * Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC") ++ * ++ * Permission to use, copy, modify, and/or distribute this software for any ++ * purpose with or without fee is hereby granted, provided that the above ++ * copyright notice and this permission notice appear in all copies. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH ++ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY ++ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, ++ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM ++ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE ++ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR ++ * PERFORMANCE OF THIS SOFTWARE. ++ */ ++ ++/*! \file */ ++ ++#include ++ ++#include ++ ++#include ++#include ++#include ++#include ++ ++#define COUNTER_MAGIC ISC_MAGIC('C', 'n', 't', 'r') ++#define VALID_COUNTER(r) ISC_MAGIC_VALID(r, COUNTER_MAGIC) ++ ++struct isc_counter { ++ unsigned int magic; ++ isc_mem_t *mctx; ++ isc_mutex_t lock; ++ unsigned int references; ++ unsigned int limit; ++ unsigned int used; ++}; ++ ++isc_result_t ++isc_counter_create(isc_mem_t *mctx, int limit, isc_counter_t **counterp) { ++ isc_result_t result; ++ isc_counter_t *counter; ++ ++ REQUIRE(counterp != NULL && *counterp == NULL); ++ ++ counter = isc_mem_get(mctx, sizeof(*counter)); ++ if (counter == NULL) ++ return (ISC_R_NOMEMORY); ++ ++ result = isc_mutex_init(&counter->lock); ++ if (result != ISC_R_SUCCESS) { ++ isc_mem_put(mctx, counter, sizeof(*counter)); ++ return (result); ++ } ++ ++ counter->mctx = NULL; ++ isc_mem_attach(mctx, &counter->mctx); ++ ++ counter->references = 1; ++ counter->limit = limit; ++ counter->used = 0; ++ ++ counter->magic = COUNTER_MAGIC; ++ *counterp = counter; ++ return (ISC_R_SUCCESS); ++} ++ ++isc_result_t ++isc_counter_increment(isc_counter_t *counter) { ++ isc_result_t result = ISC_R_SUCCESS; ++ ++ LOCK(&counter->lock); ++ counter->used++; ++ if (counter->limit != 0 && counter->used >= counter->limit) ++ result = ISC_R_QUOTA; ++ UNLOCK(&counter->lock); ++ ++ return (result); ++} ++ ++unsigned int ++isc_counter_used(isc_counter_t *counter) { ++ REQUIRE(VALID_COUNTER(counter)); ++ ++ return (counter->used); ++} ++ ++void ++isc_counter_setlimit(isc_counter_t *counter, int limit) { ++ REQUIRE(VALID_COUNTER(counter)); ++ ++ LOCK(&counter->lock); ++ counter->limit = limit; ++ UNLOCK(&counter->lock); ++} ++ ++void ++isc_counter_attach(isc_counter_t *source, isc_counter_t **targetp) { ++ REQUIRE(VALID_COUNTER(source)); ++ REQUIRE(targetp != NULL && *targetp == NULL); ++ ++ LOCK(&source->lock); ++ source->references++; ++ INSIST(source->references > 0); ++ UNLOCK(&source->lock); ++ ++ *targetp = source; ++} ++ ++static void ++destroy(isc_counter_t *counter) { ++ counter->magic = 0; ++ isc_mutex_destroy(&counter->lock); ++ isc_mem_putanddetach(&counter->mctx, counter, sizeof(*counter)); ++} ++ ++void ++isc_counter_detach(isc_counter_t **counterp) { ++ isc_counter_t *counter; ++ isc_boolean_t want_destroy = ISC_FALSE; ++ ++ REQUIRE(counterp != NULL && *counterp != NULL); ++ counter = *counterp; ++ REQUIRE(VALID_COUNTER(counter)); ++ ++ *counterp = NULL; ++ ++ LOCK(&counter->lock); ++ INSIST(counter->references > 0); ++ counter->references--; ++ if (counter->references == 0) ++ want_destroy = ISC_TRUE; ++ UNLOCK(&counter->lock); ++ ++ if (want_destroy) ++ destroy(counter); ++} +diff --git a/lib/isc/include/isc/counter.h b/lib/isc/include/isc/counter.h +new file mode 100644 +index 0000000..e7ebd25 +--- /dev/null ++++ b/lib/isc/include/isc/counter.h +@@ -0,0 +1,90 @@ ++/* ++ * Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC") ++ * ++ * Permission to use, copy, modify, and/or distribute this software for any ++ * purpose with or without fee is hereby granted, provided that the above ++ * copyright notice and this permission notice appear in all copies. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH ++ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY ++ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, ++ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM ++ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE ++ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR ++ * PERFORMANCE OF THIS SOFTWARE. ++ */ ++ ++#ifndef ISC_COUNTER_H ++#define ISC_COUNTER_H 1 ++ ++/***** ++ ***** Module Info ++ *****/ ++ ++/*! \file isc/counter.h ++ * ++ * \brief The isc_counter_t object is a simplified version of the ++ * isc_quota_t object; it tracks the consumption of limited ++ * resources, returning an error condition when the quota is ++ * exceeded. However, unlike isc_quota_t, attaching and detaching ++ * from a counter object does not increment or decrement the counter. ++ */ ++ ++/*** ++ *** Imports. ++ ***/ ++ ++#include ++#include ++#include ++ ++/***** ++ ***** Types. ++ *****/ ++ ++ISC_LANG_BEGINDECLS ++ ++isc_result_t ++isc_counter_create(isc_mem_t *mctx, int limit, isc_counter_t **counterp); ++/*%< ++ * Allocate and initialize a counter object. ++ */ ++ ++isc_result_t ++isc_counter_increment(isc_counter_t *counter); ++/*%< ++ * Increment the counter. ++ * ++ * If the counter limit is nonzero and has been reached, then ++ * return ISC_R_QUOTA, otherwise ISC_R_SUCCESS. (The counter is ++ * incremented regardless of return value.) ++ */ ++ ++unsigned int ++isc_counter_used(isc_counter_t *counter); ++/*%< ++ * Return the current counter value. ++ */ ++ ++void ++isc_counter_setlimit(isc_counter_t *counter, int limit); ++/*%< ++ * Set the counter limit. ++ */ ++ ++void ++isc_counter_attach(isc_counter_t *source, isc_counter_t **targetp); ++/*%< ++ * Attach to a counter object, increasing its reference counter. ++ */ ++ ++void ++isc_counter_detach(isc_counter_t **counterp); ++/*%< ++ * Detach (and destroy if reference counter has dropped to zero) ++ * a counter object. ++ */ ++ ++ISC_LANG_ENDDECLS ++ ++#endif /* ISC_COUNTER_H */ +diff --git a/lib/isc/tests/counter_test.c b/lib/isc/tests/counter_test.c +new file mode 100644 +index 0000000..a7a1997 +--- /dev/null ++++ b/lib/isc/tests/counter_test.c +@@ -0,0 +1,69 @@ ++/* ++ * Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC") ++ * ++ * Permission to use, copy, modify, and/or distribute this software for any ++ * purpose with or without fee is hereby granted, provided that the above ++ * copyright notice and this permission notice appear in all copies. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH ++ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY ++ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, ++ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM ++ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE ++ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR ++ * PERFORMANCE OF THIS SOFTWARE. ++ */ ++ ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++#include "isctest.h" ++ ++ATF_TC(isc_counter); ++ATF_TC_HEAD(isc_counter, tc) { ++ atf_tc_set_md_var(tc, "descr", "isc counter object"); ++} ++ATF_TC_BODY(isc_counter, tc) { ++ isc_result_t result; ++ isc_counter_t *counter = NULL; ++ int i; ++ ++ result = isc_test_begin(NULL, ISC_TRUE); ++ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); ++ ++ result = isc_counter_create(mctx, 0, &counter); ++ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); ++ ++ for (i = 0; i < 10; i++) { ++ result = isc_counter_increment(counter); ++ ATF_CHECK_EQ(result, ISC_R_SUCCESS); ++ } ++ ++ ATF_CHECK_EQ(isc_counter_used(counter), 10); ++ ++ isc_counter_setlimit(counter, 15); ++ for (i = 0; i < 10; i++) { ++ result = isc_counter_increment(counter); ++ if (result != ISC_R_SUCCESS) ++ break; ++ } ++ ++ ATF_CHECK_EQ(isc_counter_used(counter), 15); ++ ++ isc_counter_detach(&counter); ++ isc_test_end(); ++} ++ ++/* ++ * Main ++ */ ++ATF_TP_ADD_TCS(tp) { ++ ATF_TP_ADD_TC(tp, isc_counter); ++ return (atf_no_error()); ++} ++