20089043 Upgrade Solaris to BIND 9.6-ESV-R11-P1 s11-update
authorBen Chang <Benjamin.Chang@Oracle.COM>
Fri, 12 Dec 2014 11:09:52 -0800
branchs11-update
changeset 3542 d33f630b995a
parent 3539 8ae3dcced1de
child 3549 57e51761462e
20089043 Upgrade Solaris to BIND 9.6-ESV-R11-P1 20089135 problem in SERVICE/DNS-SERVER
components/bind/Makefile
components/bind/bind.p5m
components/bind/bindc.p5m
components/bind/patches/reclimit-v96.patch
--- a/components/bind/Makefile	Wed Dec 10 01:55:32 2014 -0800
+++ b/components/bind/Makefile	Fri Dec 12 11:09:52 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
--- a/components/bind/bind.p5m	Wed Dec 10 01:55:32 2014 -0800
+++ b/components/bind/bind.p5m	Fri Dec 12 11:09:52 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)
--- a/components/bind/bindc.p5m	Wed Dec 10 01:55:32 2014 -0800
+++ b/components/bind/bindc.p5m	Fri Dec 12 11:09:52 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."
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/bind/patches/reclimit-v96.patch	Fri Dec 12 11:09:52 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 [email protected] 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 <config.h>
+@@ -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 <config.h>
+@@ -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; };
+     <optional> max-acache-size <replaceable>size_spec</replaceable> ; </optional>
+     <optional> clients-per-query <replaceable>number</replaceable> ; </optional>
+     <optional> max-clients-per-query <replaceable>number</replaceable> ; </optional>
++    <optional> max-recursion-depth <replaceable>number</replaceable> ; </optional>
++    <optional> max-recursion-queries <replaceable>number</replaceable> ; </optional>
+     <optional> masterfile-format (<constant>text</constant>|<constant>raw</constant>) ; </optional>
+     <optional> empty-server <replaceable>name</replaceable> ; </optional>
+     <optional> empty-contact <replaceable>name</replaceable> ; </optional>
+@@ -4729,6 +4731,35 @@ category notify { null; };
+ 	    </listitem>
+ 	  </varlistentry>
+ 
++	  <varlistentry id="max-recursion-depth">
++	    <term><command>max-recursion-depth</command></term>
++	    <listitem>
++	      <para>
++		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.
++	      </para>
++	    </listitem>
++	  </varlistentry>
++
++	  <varlistentry id="max-recursion-queries">
++	    <term><command>max-recursion-queries</command></term>
++	    <listitem>
++	      <para>
++		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.
++	      </para>
++	    </listitem>
++	  </varlistentry>
++
+ 	  <varlistentry>
+ 	    <term><command>tkey-gssapi-credential</command></term>
+ 	    <listitem>
+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 <config.h>
+ 
++#include <isc/counter.h>
+ #include <isc/log.h>
+ #include <isc/platform.h>
+ #include <isc/print.h>
+@@ -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 <config.h>
+@@ -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 <config.h>
++
++#include <stddef.h>
++
++#include <isc/counter.h>
++#include <isc/magic.h>
++#include <isc/mem.h>
++#include <isc/util.h>
++
++#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 <isc/lang.h>
++#include <isc/mutex.h>
++#include <isc/types.h>
++
++/*****
++ ***** 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 <config.h>
++#include <stdlib.h>
++
++#include <atf-c.h>
++
++#include <isc/counter.h>
++#include <isc/result.h>
++
++#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());
++}
++