components/bind/patches/001-reclimit-v96.patch
changeset 6227 a498cb624014
parent 6226 cebcbbd80341
child 6228 37f9819bc49d
equal deleted inserted replaced
6226:cebcbbd80341 6227:a498cb624014
     1 This patch was obtained from ISC for 9.6-ESV-R11-P1. The patch can
       
     2 only be obtained from [email protected] on an as needed
       
     3 basis.
       
     4 
       
     5 diff --git a/CHANGES b/CHANGES
       
     6 index 178f73d..8ace4fb 100644
       
     7 --- a/CHANGES
       
     8 +++ b/CHANGES
       
     9 @@ -1,3 +1,17 @@
       
    10 +	--- 9.6-ESV-R11-P1 released ---
       
    11 +
       
    12 +4006.	[security]	A flaw in delegation handling could be exploited
       
    13 +			to put named into an infinite loop.  This has
       
    14 +			been addressed by placing limits on the number
       
    15 +			of levels of recursion named will allow (default 7),
       
    16 +			and the number of iterative queries that it will
       
    17 +			send (default 50) before terminating a recursive
       
    18 +			query (CVE-2014-8500).
       
    19 +
       
    20 +			The recursion depth limit is configured via the
       
    21 +			"max-recursion-depth" option, and the query limit
       
    22 +			via the "max-recursion-queries" option.  [RT #37580]
       
    23 +
       
    24  	--- 9.6-ESV-R11 released ---
       
    25  
       
    26  	--- 9.6-ESV-R11rc2 released ---
       
    27 diff --git a/bin/named/config.c b/bin/named/config.c
       
    28 index d85afa7..823d101 100644
       
    29 --- a/bin/named/config.c
       
    30 +++ b/bin/named/config.c
       
    31 @@ -15,8 +15,6 @@
       
    32   * PERFORMANCE OF THIS SOFTWARE.
       
    33   */
       
    34  
       
    35 -/* $Id$ */
       
    36 -
       
    37  /*! \file */
       
    38  
       
    39  #include <config.h>
       
    40 @@ -145,6 +143,8 @@ options {\n\
       
    41  	dnssec-accept-expired no;\n\
       
    42  	clients-per-query 10;\n\
       
    43  	max-clients-per-query 100;\n\
       
    44 +	max-recursion-depth 7;\n\
       
    45 +	max-recursion-queries 50;\n\
       
    46  	zero-no-soa-ttl-cache no;\n\
       
    47  	nsec3-test-zone no;\n\
       
    48  "
       
    49 diff --git a/bin/named/include/named/query.h b/bin/named/include/named/query.h
       
    50 index 20aff40..771345e 100644
       
    51 --- a/bin/named/include/named/query.h
       
    52 +++ b/bin/named/include/named/query.h
       
    53 @@ -1,5 +1,5 @@
       
    54  /*
       
    55 - * Copyright (C) 2004, 2005, 2007, 2010, 2012  Internet Systems Consortium, Inc. ("ISC")
       
    56 + * Copyright (C) 2004, 2005, 2007, 2010, 2011, 2013, 2014  Internet Systems Consortium, Inc. ("ISC")
       
    57   * Copyright (C) 1999-2002  Internet Software Consortium.
       
    58   *
       
    59   * Permission to use, copy, modify, and/or distribute this software for any
       
    60 @@ -15,8 +15,6 @@
       
    61   * PERFORMANCE OF THIS SOFTWARE.
       
    62   */
       
    63  
       
    64 -/* $Id$ */
       
    65 -
       
    66  #ifndef NAMED_QUERY_H
       
    67  #define NAMED_QUERY_H 1
       
    68  
       
    69 diff --git a/bin/named/query.c b/bin/named/query.c
       
    70 index 10a7d9a..48e4822 100644
       
    71 --- a/bin/named/query.c
       
    72 +++ b/bin/named/query.c
       
    73 @@ -3343,13 +3343,12 @@ query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qdomain,
       
    74  		peeraddr = &client->peeraddr;
       
    75  	else
       
    76  		peeraddr = NULL;
       
    77 -	result = dns_resolver_createfetch2(client->view->resolver,
       
    78 +	result = dns_resolver_createfetch3(client->view->resolver,
       
    79  					   client->query.qname,
       
    80  					   qtype, qdomain, nameservers,
       
    81  					   NULL, peeraddr, client->message->id,
       
    82 -					   client->query.fetchoptions,
       
    83 -					   client->task,
       
    84 -					   query_resume, client,
       
    85 +					   client->query.fetchoptions, 0, NULL,
       
    86 +					   client->task, query_resume, client,
       
    87  					   rdataset, sigrdataset,
       
    88  					   &client->query.fetch);
       
    89  
       
    90 diff --git a/bin/named/server.c b/bin/named/server.c
       
    91 index af53b48..4d48074 100644
       
    92 --- a/bin/named/server.c
       
    93 +++ b/bin/named/server.c
       
    94 @@ -15,8 +15,6 @@
       
    95   * PERFORMANCE OF THIS SOFTWARE.
       
    96   */
       
    97  
       
    98 -/* $Id$ */
       
    99 -
       
   100  /*! \file */
       
   101  
       
   102  #include <config.h>
       
   103 @@ -2048,6 +2046,16 @@ configure_view(dns_view_t *view, const cfg_obj_t *config,
       
   104  					max_clients_per_query);
       
   105  
       
   106  	obj = NULL;
       
   107 +	result = ns_config_get(maps, "max-recursion-depth", &obj);
       
   108 +	INSIST(result == ISC_R_SUCCESS);
       
   109 +	dns_resolver_setmaxdepth(view->resolver, cfg_obj_asuint32(obj));
       
   110 +
       
   111 +	obj = NULL;
       
   112 +	result = ns_config_get(maps, "max-recursion-queries", &obj);
       
   113 +	INSIST(result == ISC_R_SUCCESS);
       
   114 +	dns_resolver_setmaxqueries(view->resolver, cfg_obj_asuint32(obj));
       
   115 +
       
   116 +	obj = NULL;
       
   117  	result = ns_config_get(maps, "dnssec-enable", &obj);
       
   118  	INSIST(result == ISC_R_SUCCESS);
       
   119  	view->enablednssec = cfg_obj_asboolean(obj);
       
   120 diff --git a/doc/arm/Bv9ARM-book.xml b/doc/arm/Bv9ARM-book.xml
       
   121 index f894aab..b823dc4 100644
       
   122 --- a/doc/arm/Bv9ARM-book.xml
       
   123 +++ b/doc/arm/Bv9ARM-book.xml
       
   124 @@ -4652,6 +4652,8 @@ category notify { null; };
       
   125      <optional> max-acache-size <replaceable>size_spec</replaceable> ; </optional>
       
   126      <optional> clients-per-query <replaceable>number</replaceable> ; </optional>
       
   127      <optional> max-clients-per-query <replaceable>number</replaceable> ; </optional>
       
   128 +    <optional> max-recursion-depth <replaceable>number</replaceable> ; </optional>
       
   129 +    <optional> max-recursion-queries <replaceable>number</replaceable> ; </optional>
       
   130      <optional> masterfile-format (<constant>text</constant>|<constant>raw</constant>) ; </optional>
       
   131      <optional> empty-server <replaceable>name</replaceable> ; </optional>
       
   132      <optional> empty-contact <replaceable>name</replaceable> ; </optional>
       
   133 @@ -4729,6 +4731,35 @@ category notify { null; };
       
   134  	    </listitem>
       
   135  	  </varlistentry>
       
   136  
       
   137 +	  <varlistentry id="max-recursion-depth">
       
   138 +	    <term><command>max-recursion-depth</command></term>
       
   139 +	    <listitem>
       
   140 +	      <para>
       
   141 +		Sets the maximum number of levels of recursion
       
   142 +		that are permitted at any one time while servicing
       
   143 +		a recursive query. Resolving a name may require
       
   144 +		looking up a name server address, which in turn
       
   145 +		requires resolving another name, etc; if the number
       
   146 +		of indirections exceeds this value, the recursive
       
   147 +		query is terminated and returns SERVFAIL.  The
       
   148 +		default is 7.
       
   149 +	      </para>
       
   150 +	    </listitem>
       
   151 +	  </varlistentry>
       
   152 +
       
   153 +	  <varlistentry id="max-recursion-queries">
       
   154 +	    <term><command>max-recursion-queries</command></term>
       
   155 +	    <listitem>
       
   156 +	      <para>
       
   157 +		Sets the maximum number of iterative queries that
       
   158 +		may be sent while servicing a recursive query.
       
   159 +		If more queries are sent, the recursive query
       
   160 +		is terminated and returns SERVFAIL. The default
       
   161 +		is 50.
       
   162 +	      </para>
       
   163 +	    </listitem>
       
   164 +	  </varlistentry>
       
   165 +
       
   166  	  <varlistentry>
       
   167  	    <term><command>tkey-gssapi-credential</command></term>
       
   168  	    <listitem>
       
   169 diff --git a/lib/dns/adb.c b/lib/dns/adb.c
       
   170 index 2d7b904..422e59c 100644
       
   171 --- a/lib/dns/adb.c
       
   172 +++ b/lib/dns/adb.c
       
   173 @@ -200,6 +200,7 @@ struct dns_adbfetch {
       
   174  	unsigned int                    magic;
       
   175  	dns_fetch_t                    *fetch;
       
   176  	dns_rdataset_t                  rdataset;
       
   177 +	unsigned int			depth;
       
   178  };
       
   179  
       
   180  /*%
       
   181 @@ -298,8 +299,7 @@ static inline isc_boolean_t dec_entry_refcnt(dns_adb_t *, isc_boolean_t,
       
   182  static inline void violate_locking_hierarchy(isc_mutex_t *, isc_mutex_t *);
       
   183  static isc_boolean_t clean_namehooks(dns_adb_t *, dns_adbnamehooklist_t *);
       
   184  static void clean_target(dns_adb_t *, dns_name_t *);
       
   185 -static void clean_finds_at_name(dns_adbname_t *, isc_eventtype_t,
       
   186 -				unsigned int);
       
   187 +static void clean_finds_at_name(dns_adbname_t *, isc_eventtype_t, unsigned int);
       
   188  static isc_boolean_t check_expire_namehooks(dns_adbname_t *, isc_stdtime_t);
       
   189  static isc_boolean_t check_expire_entry(dns_adb_t *, dns_adbentry_t **,
       
   190  					isc_stdtime_t);
       
   191 @@ -307,6 +307,7 @@ static void cancel_fetches_at_name(dns_adbname_t *);
       
   192  static isc_result_t dbfind_name(dns_adbname_t *, isc_stdtime_t,
       
   193  				dns_rdatatype_t);
       
   194  static isc_result_t fetch_name(dns_adbname_t *, isc_boolean_t,
       
   195 +			       unsigned int, isc_counter_t *qc,
       
   196  			       dns_rdatatype_t);
       
   197  static inline void check_exit(dns_adb_t *);
       
   198  static void destroy(dns_adb_t *);
       
   199 @@ -2282,6 +2283,19 @@ dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action,
       
   200  		   isc_stdtime_t now, dns_name_t *target,
       
   201  		   in_port_t port, dns_adbfind_t **findp)
       
   202  {
       
   203 +	return (dns_adb_createfind2(adb, task, action, arg, name,
       
   204 +				    qname, qtype, options, now,
       
   205 +				    target, port, 0, NULL, findp));
       
   206 +}
       
   207 +
       
   208 +isc_result_t
       
   209 +dns_adb_createfind2(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action,
       
   210 +		    void *arg, dns_name_t *name, dns_name_t *qname,
       
   211 +		    dns_rdatatype_t qtype, unsigned int options,
       
   212 +		    isc_stdtime_t now, dns_name_t *target,
       
   213 +		    in_port_t port, unsigned int depth, isc_counter_t *qc,
       
   214 +		    dns_adbfind_t **findp)
       
   215 +{
       
   216  	dns_adbfind_t *find;
       
   217  	dns_adbname_t *adbname;
       
   218  	int bucket;
       
   219 @@ -2512,7 +2526,7 @@ dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action,
       
   220  		 * Start V4.
       
   221  		 */
       
   222  		if (WANT_INET(wanted_fetches) &&
       
   223 -		    fetch_name(adbname, start_at_zone,
       
   224 +		    fetch_name(adbname, start_at_zone, depth, qc,
       
   225  			       dns_rdatatype_a) == ISC_R_SUCCESS) {
       
   226  			DP(DEF_LEVEL,
       
   227  			   "dns_adb_createfind: started A fetch for name %p",
       
   228 @@ -2523,7 +2537,7 @@ dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action,
       
   229  		 * Start V6.
       
   230  		 */
       
   231  		if (WANT_INET6(wanted_fetches) &&
       
   232 -		    fetch_name(adbname, start_at_zone,
       
   233 +		    fetch_name(adbname, start_at_zone, depth, qc,
       
   234  			       dns_rdatatype_aaaa) == ISC_R_SUCCESS) {
       
   235  			DP(DEF_LEVEL,
       
   236  			   "dns_adb_createfind: "
       
   237 @@ -3256,6 +3270,12 @@ fetch_callback(isc_task_t *task, isc_event_t *ev) {
       
   238  		DP(DEF_LEVEL, "adb: fetch of '%s' %s failed: %s",
       
   239  		   buf, address_type == DNS_ADBFIND_INET ? "A" : "AAAA",
       
   240  		   dns_result_totext(dev->result));
       
   241 +		/*
       
   242 +		 * Don't record a failure unless this is the initial
       
   243 +		 * fetch of a chain.
       
   244 +		 */
       
   245 +		if (fetch->depth > 1)
       
   246 +			goto out;
       
   247  		/* XXXMLG Don't pound on bad servers. */
       
   248  		if (address_type == DNS_ADBFIND_INET) {
       
   249  			name->expire_v4 = ISC_MIN(name->expire_v4, now + 300);
       
   250 @@ -3293,9 +3313,8 @@ fetch_callback(isc_task_t *task, isc_event_t *ev) {
       
   251  }
       
   252  
       
   253  static isc_result_t
       
   254 -fetch_name(dns_adbname_t *adbname,
       
   255 -	   isc_boolean_t start_at_zone,
       
   256 -	   dns_rdatatype_t type)
       
   257 +fetch_name(dns_adbname_t *adbname, isc_boolean_t start_at_zone,
       
   258 +	   unsigned int depth, isc_counter_t *qc, dns_rdatatype_t type)
       
   259  {
       
   260  	isc_result_t result;
       
   261  	dns_adbfetch_t *fetch = NULL;
       
   262 @@ -3340,12 +3359,14 @@ fetch_name(dns_adbname_t *adbname,
       
   263  		result = ISC_R_NOMEMORY;
       
   264  		goto cleanup;
       
   265  	}
       
   266 +	fetch->depth = depth;
       
   267  
       
   268 -	result = dns_resolver_createfetch(adb->view->resolver, &adbname->name,
       
   269 -					  type, name, nameservers, NULL,
       
   270 -					  options, adb->task, fetch_callback,
       
   271 -					  adbname, &fetch->rdataset, NULL,
       
   272 -					  &fetch->fetch);
       
   273 +	result = dns_resolver_createfetch3(adb->view->resolver, &adbname->name,
       
   274 +					   type, name, nameservers, NULL,
       
   275 +					   NULL, 0, options, depth, qc,
       
   276 +					   adb->task, fetch_callback, adbname,
       
   277 +					   &fetch->rdataset, NULL,
       
   278 +					   &fetch->fetch);
       
   279  	if (result != ISC_R_SUCCESS)
       
   280  		goto cleanup;
       
   281  
       
   282 diff --git a/lib/dns/include/dns/adb.h b/lib/dns/include/dns/adb.h
       
   283 index d4d1b05..556fcc2 100644
       
   284 --- a/lib/dns/include/dns/adb.h
       
   285 +++ b/lib/dns/include/dns/adb.h
       
   286 @@ -334,6 +334,13 @@ dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action,
       
   287  		   dns_rdatatype_t qtype, unsigned int options,
       
   288  		   isc_stdtime_t now, dns_name_t *target,
       
   289  		   in_port_t port, dns_adbfind_t **find);
       
   290 +isc_result_t
       
   291 +dns_adb_createfind2(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action,
       
   292 +		    void *arg, dns_name_t *name, dns_name_t *qname,
       
   293 +		    dns_rdatatype_t qtype, unsigned int options,
       
   294 +		    isc_stdtime_t now, dns_name_t *target, in_port_t port,
       
   295 +		    unsigned int depth, isc_counter_t *qc,
       
   296 +		    dns_adbfind_t **find);
       
   297  /*%<
       
   298   * Main interface for clients. The adb will look up the name given in
       
   299   * "name" and will build up a list of found addresses, and perhaps start
       
   300 diff --git a/lib/dns/include/dns/resolver.h b/lib/dns/include/dns/resolver.h
       
   301 index d293daa..10c3a3a 100644
       
   302 --- a/lib/dns/include/dns/resolver.h
       
   303 +++ b/lib/dns/include/dns/resolver.h
       
   304 @@ -270,6 +270,18 @@ dns_resolver_createfetch2(dns_resolver_t *res, dns_name_t *name,
       
   305  			  dns_rdataset_t *rdataset,
       
   306  			  dns_rdataset_t *sigrdataset,
       
   307  			  dns_fetch_t **fetchp);
       
   308 +isc_result_t
       
   309 +dns_resolver_createfetch3(dns_resolver_t *res, dns_name_t *name,
       
   310 +			  dns_rdatatype_t type,
       
   311 +			  dns_name_t *domain, dns_rdataset_t *nameservers,
       
   312 +			  dns_forwarders_t *forwarders,
       
   313 +			  isc_sockaddr_t *client, isc_uint16_t id,
       
   314 +			  unsigned int options, unsigned int depth,
       
   315 +			  isc_counter_t *qc, isc_task_t *task,
       
   316 +			  isc_taskaction_t action, void *arg,
       
   317 +			  dns_rdataset_t *rdataset,
       
   318 +			  dns_rdataset_t *sigrdataset,
       
   319 +			  dns_fetch_t **fetchp);
       
   320  /*%<
       
   321   * Recurse to answer a question.
       
   322   *
       
   323 @@ -550,6 +562,30 @@ dns_resolver_printbadcache(dns_resolver_t *resolver, FILE *fp);
       
   324   * \li	resolver to be valid.
       
   325   */
       
   326  
       
   327 +void
       
   328 +dns_resolver_setmaxdepth(dns_resolver_t *resolver, unsigned int maxdepth);
       
   329 +unsigned int
       
   330 +dns_resolver_getmaxdepth(dns_resolver_t *resolver);
       
   331 +/*%
       
   332 + * Get and set how many NS indirections will be followed when looking for
       
   333 + * nameserver addresses.
       
   334 + *
       
   335 + * Requires:
       
   336 + * \li	resolver to be valid.
       
   337 + */
       
   338 +
       
   339 +void
       
   340 +dns_resolver_setmaxqueries(dns_resolver_t *resolver, unsigned int queries);
       
   341 +unsigned int
       
   342 +dns_resolver_getmaxqueries(dns_resolver_t *resolver);
       
   343 +/*%
       
   344 + * Get and set how many iterative queries will be allowed before
       
   345 + * terminating a recursive query.
       
   346 + *
       
   347 + * Requires:
       
   348 + * \li	resolver to be valid.
       
   349 + */
       
   350 +
       
   351  ISC_LANG_ENDDECLS
       
   352  
       
   353  #endif /* DNS_RESOLVER_H */
       
   354 diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c
       
   355 index 7dcea6d..bd3d9fd 100644
       
   356 --- a/lib/dns/resolver.c
       
   357 +++ b/lib/dns/resolver.c
       
   358 @@ -21,6 +21,7 @@
       
   359  
       
   360  #include <config.h>
       
   361  
       
   362 +#include <isc/counter.h>
       
   363  #include <isc/log.h>
       
   364  #include <isc/platform.h>
       
   365  #include <isc/print.h>
       
   366 @@ -109,6 +110,16 @@
       
   367  #define QTRACE(m)
       
   368  #endif
       
   369  
       
   370 +/* The default maximum number of recursions to follow before giving up. */
       
   371 +#ifndef DEFAULT_RECURSION_DEPTH
       
   372 +#define DEFAULT_RECURSION_DEPTH 7
       
   373 +#endif
       
   374 +
       
   375 +/* The default maximum number of iterative queries to allow before giving up. */
       
   376 +#ifndef DEFAULT_MAX_QUERIES
       
   377 +#define DEFAULT_MAX_QUERIES 50
       
   378 +#endif
       
   379 +
       
   380  /*%
       
   381   * Maximum EDNS0 input packet size.
       
   382   */
       
   383 @@ -211,12 +222,13 @@ struct fetchctx {
       
   384  	isc_sockaddrlist_t		edns;
       
   385  	isc_sockaddrlist_t		edns512;
       
   386  	isc_sockaddrlist_t		bad_edns;
       
   387 -	dns_validator_t			*validator;
       
   388 +	dns_validator_t *		validator;
       
   389  	ISC_LIST(dns_validator_t)       validators;
       
   390  	dns_db_t *			cache;
       
   391  	dns_adb_t *			adb;
       
   392  	isc_boolean_t			ns_ttl_ok;
       
   393  	isc_uint32_t			ns_ttl;
       
   394 +	isc_counter_t *			qc;
       
   395  
       
   396  	/*%
       
   397  	 * The number of events we're waiting for.
       
   398 @@ -283,6 +295,7 @@ struct fetchctx {
       
   399  	unsigned int			valfail;
       
   400  	isc_boolean_t			timeout;
       
   401  	dns_adbaddrinfo_t 		*addrinfo;
       
   402 +	unsigned int			depth;
       
   403  };
       
   404  
       
   405  #define FCTX_MAGIC			ISC_MAGIC('F', '!', '!', '!')
       
   406 @@ -394,6 +407,8 @@ struct dns_resolver {
       
   407  	unsigned int			spillatmin;
       
   408  	isc_timer_t *			spillattimer;
       
   409  	isc_boolean_t			zero_no_soa_ttl;
       
   410 +	unsigned int			maxdepth;
       
   411 +	unsigned int			maxqueries;
       
   412  
       
   413  	/* Locked by lock. */
       
   414  	unsigned int			references;
       
   415 @@ -1495,6 +1510,7 @@ fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
       
   416  		if (result != ISC_R_SUCCESS)
       
   417  			goto cleanup_dispatch;
       
   418  	}
       
   419 +
       
   420  	fctx->querysent++;
       
   421  
       
   422  	ISC_LIST_APPEND(fctx->queries, query, link);
       
   423 @@ -2146,9 +2162,9 @@ fctx_finddone(isc_task_t *task, isc_event_t *event) {
       
   424  		 */
       
   425  		INSIST(!SHUTTINGDOWN(fctx));
       
   426  		fctx->attributes &= ~FCTX_ATTR_ADDRWAIT;
       
   427 -		if (event->ev_type == DNS_EVENT_ADBMOREADDRESSES)
       
   428 +		if (event->ev_type == DNS_EVENT_ADBMOREADDRESSES) {
       
   429  			want_try = ISC_TRUE;
       
   430 -		else {
       
   431 +		} else {
       
   432  			fctx->findfail++;
       
   433  			if (fctx->pending == 0) {
       
   434  				/*
       
   435 @@ -2177,7 +2193,7 @@ fctx_finddone(isc_task_t *task, isc_event_t *event) {
       
   436  	else if (want_done)
       
   437  		fctx_done(fctx, ISC_R_FAILURE, __LINE__);
       
   438  	else if (destroy) {
       
   439 -			fctx_destroy(fctx);
       
   440 +		fctx_destroy(fctx);
       
   441  		if (bucket_empty)
       
   442  			empty_bucket(res);
       
   443  	}
       
   444 @@ -2499,12 +2515,13 @@ findname(fetchctx_t *fctx, dns_name_t *name, in_port_t port,
       
   445  	 * See what we know about this address.
       
   446  	 */
       
   447  	find = NULL;
       
   448 -	result = dns_adb_createfind(fctx->adb,
       
   449 -				    res->buckets[fctx->bucketnum].task,
       
   450 -				    fctx_finddone, fctx, name,
       
   451 -				    &fctx->name, fctx->type,
       
   452 -				    options, now, NULL,
       
   453 -				    res->view->dstport, &find);
       
   454 +	result = dns_adb_createfind2(fctx->adb,
       
   455 +				     res->buckets[fctx->bucketnum].task,
       
   456 +				     fctx_finddone, fctx, name,
       
   457 +				     &fctx->name, fctx->type,
       
   458 +				     options, now, NULL,
       
   459 +				     res->view->dstport,
       
   460 +				     fctx->depth + 1, fctx->qc, &find);
       
   461  	if (result != ISC_R_SUCCESS) {
       
   462  		if (result == DNS_R_ALIAS) {
       
   463  			/*
       
   464 @@ -2612,6 +2629,14 @@ fctx_getaddresses(fetchctx_t *fctx, isc_boolean_t badcache) {
       
   465  
       
   466  	res = fctx->res;
       
   467  
       
   468 +	if (fctx->depth > res->maxdepth) {
       
   469 +		isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
       
   470 +			      DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(3),
       
   471 +			      "too much NS indirection resolving '%s'",
       
   472 +			      fctx->info);
       
   473 +		return (DNS_R_SERVFAIL);
       
   474 +	}
       
   475 +
       
   476  	/*
       
   477  	 * Forwarders.
       
   478  	 */
       
   479 @@ -3087,6 +3112,16 @@ fctx_try(fetchctx_t *fctx, isc_boolean_t retrying, isc_boolean_t badcache) {
       
   480  		}
       
   481  	}
       
   482  
       
   483 +	result = isc_counter_increment(fctx->qc);
       
   484 +	if (result != ISC_R_SUCCESS) {
       
   485 +		isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
       
   486 +			      DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(3),
       
   487 +			      "exceeded max queries resolving '%s'",
       
   488 +			      fctx->info);
       
   489 +		fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
       
   490 +		return;
       
   491 +	}
       
   492 +
       
   493  	result = fctx_query(fctx, addrinfo, fctx->options);
       
   494  	if (result != ISC_R_SUCCESS)
       
   495  		fctx_done(fctx, result, __LINE__);
       
   496 @@ -3185,6 +3220,7 @@ fctx_destroy(fetchctx_t *fctx) {
       
   497  		isc_mem_put(fctx->mctx, sa, sizeof(*sa));
       
   498  	}
       
   499  
       
   500 +	isc_counter_detach(&fctx->qc);
       
   501  	isc_timer_detach(&fctx->timer);
       
   502  	dns_message_destroy(&fctx->rmessage);
       
   503  	dns_message_destroy(&fctx->qmessage);
       
   504 @@ -3512,7 +3548,8 @@ log_ns_ttl(fetchctx_t *fctx, const char *where) {
       
   505  static isc_result_t
       
   506  fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type,
       
   507  	    dns_name_t *domain, dns_rdataset_t *nameservers,
       
   508 -	    unsigned int options, unsigned int bucketnum, fetchctx_t **fctxp)
       
   509 +	    unsigned int options, unsigned int bucketnum, unsigned int depth,
       
   510 +	    isc_counter_t *qc, fetchctx_t **fctxp)
       
   511  {
       
   512  	fetchctx_t *fctx;
       
   513  	isc_result_t result;
       
   514 @@ -3534,6 +3571,21 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type,
       
   515  	fctx = isc_mem_get(mctx, sizeof(*fctx));
       
   516  	if (fctx == NULL)
       
   517  		return (ISC_R_NOMEMORY);
       
   518 +
       
   519 +	fctx->qc = NULL;
       
   520 +	if (qc != NULL) {
       
   521 +		isc_counter_attach(qc, &fctx->qc);
       
   522 +	} else {
       
   523 +		result = isc_counter_create(res->mctx,
       
   524 +					    res->maxqueries, &fctx->qc);
       
   525 +		if (result != ISC_R_SUCCESS)
       
   526 +			goto cleanup_fetch;
       
   527 +	}
       
   528 +
       
   529 +	/*
       
   530 +	 * Make fctx->info point to a copy of a formatted string
       
   531 +	 * "name/type".
       
   532 +	 */
       
   533  	dns_name_format(name, buf, sizeof(buf));
       
   534  	dns_rdatatype_format(type, typebuf, sizeof(typebuf));
       
   535  	strcat(buf, "/");       /* checked */
       
   536 @@ -3541,7 +3593,7 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type,
       
   537  	fctx->info = isc_mem_strdup(mctx, buf);
       
   538  	if (fctx->info == NULL) {
       
   539  		result = ISC_R_NOMEMORY;
       
   540 -		goto cleanup_fetch;
       
   541 +		goto cleanup_counter;
       
   542  	}
       
   543  	FCTXTRACE("create");
       
   544  	dns_name_init(&fctx->name, NULL);
       
   545 @@ -3564,6 +3616,7 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type,
       
   546  	fctx->state = fetchstate_init;
       
   547  	fctx->want_shutdown = ISC_FALSE;
       
   548  	fctx->cloned = ISC_FALSE;
       
   549 +	fctx->depth = depth;
       
   550  	ISC_LIST_INIT(fctx->queries);
       
   551  	ISC_LIST_INIT(fctx->finds);
       
   552  	ISC_LIST_INIT(fctx->altfinds);
       
   553 @@ -3768,6 +3821,9 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type,
       
   554   cleanup_info:
       
   555  	isc_mem_free(mctx, fctx->info);
       
   556  
       
   557 + cleanup_counter:
       
   558 +	isc_counter_detach(&fctx->qc);
       
   559 +
       
   560   cleanup_fetch:
       
   561  	isc_mem_put(mctx, fctx, sizeof(*fctx));
       
   562  
       
   563 @@ -7339,6 +7395,8 @@ dns_resolver_create(dns_view_t *view,
       
   564  	res->zero_no_soa_ttl = ISC_FALSE;
       
   565  	res->ndisps = 0;
       
   566  	res->nextdisp = 0; /* meaningless at this point, but init it */
       
   567 +	res->maxdepth = DEFAULT_RECURSION_DEPTH;
       
   568 +	res->maxqueries = DEFAULT_MAX_QUERIES;
       
   569  	res->nbuckets = ntasks;
       
   570  	res->activebuckets = ntasks;
       
   571  	res->buckets = isc_mem_get(view->mctx,
       
   572 @@ -7778,9 +7836,9 @@ dns_resolver_createfetch(dns_resolver_t *res, dns_name_t *name,
       
   573  			 dns_rdataset_t *sigrdataset,
       
   574  			 dns_fetch_t **fetchp)
       
   575  {
       
   576 -	return (dns_resolver_createfetch2(res, name, type, domain,
       
   577 +	return (dns_resolver_createfetch3(res, name, type, domain,
       
   578  					  nameservers, forwarders, NULL, 0,
       
   579 -					  options, task, action, arg,
       
   580 +					  options, 0, NULL, task, action, arg,
       
   581  					  rdataset, sigrdataset, fetchp));
       
   582  }
       
   583  
       
   584 @@ -7796,6 +7854,25 @@ dns_resolver_createfetch2(dns_resolver_t *res, dns_name_t *name,
       
   585  			  dns_rdataset_t *sigrdataset,
       
   586  			  dns_fetch_t **fetchp)
       
   587  {
       
   588 +	return (dns_resolver_createfetch3(res, name, type, domain,
       
   589 +					  nameservers, forwarders, client, id,
       
   590 +					  options, 0, NULL, task, action, arg,
       
   591 +					  rdataset, sigrdataset, fetchp));
       
   592 +}
       
   593 +
       
   594 +isc_result_t
       
   595 +dns_resolver_createfetch3(dns_resolver_t *res, dns_name_t *name,
       
   596 +			  dns_rdatatype_t type,
       
   597 +			  dns_name_t *domain, dns_rdataset_t *nameservers,
       
   598 +			  dns_forwarders_t *forwarders,
       
   599 +			  isc_sockaddr_t *client, dns_messageid_t id,
       
   600 +			  unsigned int options, unsigned int depth,
       
   601 +			  isc_counter_t *qc, isc_task_t *task,
       
   602 +			  isc_taskaction_t action, void *arg,
       
   603 +			  dns_rdataset_t *rdataset,
       
   604 +			  dns_rdataset_t *sigrdataset,
       
   605 +			  dns_fetch_t **fetchp)
       
   606 +{
       
   607  	dns_fetch_t *fetch;
       
   608  	fetchctx_t *fctx = NULL;
       
   609  	isc_result_t result = ISC_R_SUCCESS;
       
   610 @@ -7882,11 +7959,12 @@ dns_resolver_createfetch2(dns_resolver_t *res, dns_name_t *name,
       
   611  
       
   612  	if (fctx == NULL) {
       
   613  		result = fctx_create(res, name, type, domain, nameservers,
       
   614 -				     options, bucketnum, &fctx);
       
   615 +				     options, bucketnum, depth, qc, &fctx);
       
   616  		if (result != ISC_R_SUCCESS)
       
   617  			goto unlock;
       
   618  		new_fctx = ISC_TRUE;
       
   619 -	}
       
   620 +	} else if (fctx->depth > depth)
       
   621 +		fctx->depth = depth;
       
   622  
       
   623  	result = fctx_join(fctx, task, client, id, action, arg,
       
   624  			   rdataset, sigrdataset, fetch);
       
   625 @@ -8637,3 +8715,27 @@ dns_resolver_getoptions(dns_resolver_t *resolver) {
       
   626  
       
   627  	return (resolver->options);
       
   628  }
       
   629 +
       
   630 +void
       
   631 +dns_resolver_setmaxdepth(dns_resolver_t *resolver, unsigned int maxdepth) {
       
   632 +	REQUIRE(VALID_RESOLVER(resolver));
       
   633 +	resolver->maxdepth = maxdepth;
       
   634 +}
       
   635 +
       
   636 +unsigned int
       
   637 +dns_resolver_getmaxdepth(dns_resolver_t *resolver) {
       
   638 +	REQUIRE(VALID_RESOLVER(resolver));
       
   639 +	return (resolver->maxdepth);
       
   640 +}
       
   641 +
       
   642 +void
       
   643 +dns_resolver_setmaxqueries(dns_resolver_t *resolver, unsigned int queries) {
       
   644 +	REQUIRE(VALID_RESOLVER(resolver));
       
   645 +	resolver->maxqueries = queries;
       
   646 +}
       
   647 +
       
   648 +unsigned int
       
   649 +dns_resolver_getmaxqueries(dns_resolver_t *resolver) {
       
   650 +	REQUIRE(VALID_RESOLVER(resolver));
       
   651 +	return (resolver->maxqueries);
       
   652 +}
       
   653 diff --git a/lib/isc/Makefile.in b/lib/isc/Makefile.in
       
   654 index 0b4020b..afc19ee 100644
       
   655 --- a/lib/isc/Makefile.in
       
   656 +++ b/lib/isc/Makefile.in
       
   657 @@ -13,8 +13,6 @@
       
   658  # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
       
   659  # PERFORMANCE OF THIS SOFTWARE.
       
   660  
       
   661 -# $Id$
       
   662 -
       
   663  srcdir =	@srcdir@
       
   664  VPATH =		@srcdir@
       
   665  top_srcdir =	@top_srcdir@
       
   666 @@ -53,7 +51,7 @@ WIN32OBJS = 	win32/condition.@O@ win32/dir.@O@ win32/file.@O@ \
       
   667  # Alphabetically
       
   668  OBJS =		@ISC_EXTRA_OBJS@ \
       
   669  		assertions.@O@ base32.@O@ base64.@O@ bitstring.@O@ buffer.@O@ \
       
   670 -		bufferlist.@O@ commandline.@O@ error.@O@ event.@O@ \
       
   671 +		bufferlist.@O@ commandline.@O@ counter.@O@ error.@O@ event.@O@ \
       
   672  		hash.@O@ heap.@O@ hex.@O@ hmacmd5.@O@ hmacsha.@O@ \
       
   673  		httpd.@O@ inet_aton.@O@ iterated_hash.@O@ \
       
   674  		lex.@O@ lfsr.@O@ lib.@O@ log.@O@ \
       
   675 @@ -68,7 +66,7 @@ OBJS =		@ISC_EXTRA_OBJS@ \
       
   676  # Alphabetically
       
   677  SRCS =		@ISC_EXTRA_SRCS@ \
       
   678  		assertions.c base32.c base64.c bitstring.c buffer.c \
       
   679 -		bufferlist.c commandline.c error.c event.c \
       
   680 +		bufferlist.c commandline.c counter.c error.c event.c \
       
   681  		heap.c hex.c hmacmd5.c hmacsha.c \
       
   682  		httpd.c inet_aton.c iterated_hash.c \
       
   683  		lex.c lfsr.c lib.c log.c \
       
   684 diff --git a/lib/isc/include/isc/Makefile.in b/lib/isc/include/isc/Makefile.in
       
   685 index 9adca3e..1cfbbd1 100644
       
   686 --- a/lib/isc/include/isc/Makefile.in
       
   687 +++ b/lib/isc/include/isc/Makefile.in
       
   688 @@ -13,8 +13,6 @@
       
   689  # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
       
   690  # PERFORMANCE OF THIS SOFTWARE.
       
   691  
       
   692 -# $Id$
       
   693 -
       
   694  srcdir =	@srcdir@
       
   695  VPATH =		@srcdir@
       
   696  top_srcdir =	@top_srcdir@
       
   697 @@ -27,7 +25,7 @@ top_srcdir =	@top_srcdir@
       
   698  # install target below.
       
   699  #
       
   700  HEADERS =	app.h assertions.h base64.h bitstring.h boolean.h buffer.h \
       
   701 -		bufferlist.h commandline.h entropy.h error.h event.h \
       
   702 +		bufferlist.h commandline.h counter.h entropy.h error.h event.h \
       
   703  		eventclass.h file.h formatcheck.h fsaccess.h \
       
   704  		hash.h heap.h hex.h hmacmd5.h hmacsha.h \
       
   705  		httpd.h \
       
   706 diff --git a/lib/isc/include/isc/types.h b/lib/isc/include/isc/types.h
       
   707 index 8e8b08f..a646b8b 100644
       
   708 --- a/lib/isc/include/isc/types.h
       
   709 +++ b/lib/isc/include/isc/types.h
       
   710 @@ -45,6 +45,7 @@ typedef struct isc_buffer		isc_buffer_t;		/*%< Buffer */
       
   711  typedef ISC_LIST(isc_buffer_t)		isc_bufferlist_t;	/*%< Buffer List */
       
   712  typedef struct isc_constregion		isc_constregion_t;	/*%< Const region */
       
   713  typedef struct isc_consttextregion	isc_consttextregion_t;	/*%< Const Text Region */
       
   714 +typedef struct isc_counter		isc_counter_t;		/*%< Counter */
       
   715  typedef struct isc_entropy		isc_entropy_t;		/*%< Entropy */
       
   716  typedef struct isc_entropysource	isc_entropysource_t;	/*%< Entropy Source */
       
   717  typedef struct isc_event		isc_event_t;		/*%< Event */
       
   718 diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c
       
   719 index fac2633..3023dcc 100644
       
   720 --- a/lib/isccfg/namedconf.c
       
   721 +++ b/lib/isccfg/namedconf.c
       
   722 @@ -15,8 +15,6 @@
       
   723   * PERFORMANCE OF THIS SOFTWARE.
       
   724   */
       
   725  
       
   726 -/* $Id$ */
       
   727 -
       
   728  /*! \file */
       
   729  
       
   730  #include <config.h>
       
   731 @@ -830,6 +828,8 @@ view_clauses[] = {
       
   732  	{ "max-cache-ttl", &cfg_type_uint32, 0 },
       
   733  	{ "max-clients-per-query", &cfg_type_uint32, 0 },
       
   734  	{ "max-ncache-ttl", &cfg_type_uint32, 0 },
       
   735 +	{ "max-recursion-depth", &cfg_type_uint32, 0 },
       
   736 +	{ "max-recursion-queries", &cfg_type_uint32, 0 },
       
   737  	{ "max-udp-size", &cfg_type_uint32, 0 },
       
   738  	{ "min-roots", &cfg_type_uint32, CFG_CLAUSEFLAG_NOTIMP },
       
   739  	{ "minimal-responses", &cfg_type_boolean, 0 },
       
   740 diff --git a/version b/version
       
   741 index 1be3c16..2058444 100644
       
   742 --- a/version
       
   743 +++ b/version
       
   744 @@ -10,4 +10,4 @@ MINORVER=6
       
   745  PATCHVER=
       
   746  RELEASETYPE=-ESV
       
   747  RELEASEVER=-R11
       
   748 -EXTENSIONS=
       
   749 +EXTENSIONS=-P1
       
   750 diff --git a/lib/isc/counter.c b/lib/isc/counter.c
       
   751 new file mode 100644
       
   752 index 0000000..d7d187b
       
   753 --- /dev/null
       
   754 +++ b/lib/isc/counter.c
       
   755 @@ -0,0 +1,138 @@
       
   756 +/*
       
   757 + * Copyright (C) 2014  Internet Systems Consortium, Inc. ("ISC")
       
   758 + *
       
   759 + * Permission to use, copy, modify, and/or distribute this software for any
       
   760 + * purpose with or without fee is hereby granted, provided that the above
       
   761 + * copyright notice and this permission notice appear in all copies.
       
   762 + *
       
   763 + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
       
   764 + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
       
   765 + * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
       
   766 + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
       
   767 + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
       
   768 + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
       
   769 + * PERFORMANCE OF THIS SOFTWARE.
       
   770 + */
       
   771 +
       
   772 +/*! \file */
       
   773 +
       
   774 +#include <config.h>
       
   775 +
       
   776 +#include <stddef.h>
       
   777 +
       
   778 +#include <isc/counter.h>
       
   779 +#include <isc/magic.h>
       
   780 +#include <isc/mem.h>
       
   781 +#include <isc/util.h>
       
   782 +
       
   783 +#define COUNTER_MAGIC			ISC_MAGIC('C', 'n', 't', 'r')
       
   784 +#define VALID_COUNTER(r)		ISC_MAGIC_VALID(r, COUNTER_MAGIC)
       
   785 +
       
   786 +struct isc_counter {
       
   787 +	unsigned int	magic;
       
   788 +	isc_mem_t	*mctx;
       
   789 +	isc_mutex_t	lock;
       
   790 +	unsigned int	references;
       
   791 +	unsigned int	limit;
       
   792 +	unsigned int	used;
       
   793 +};
       
   794 +
       
   795 +isc_result_t
       
   796 +isc_counter_create(isc_mem_t *mctx, int limit, isc_counter_t **counterp) {
       
   797 +	isc_result_t result;
       
   798 +	isc_counter_t *counter;
       
   799 +
       
   800 +	REQUIRE(counterp != NULL && *counterp == NULL);
       
   801 +
       
   802 +	counter = isc_mem_get(mctx, sizeof(*counter));
       
   803 +	if (counter == NULL)
       
   804 +		return (ISC_R_NOMEMORY);
       
   805 +
       
   806 +	result = isc_mutex_init(&counter->lock);
       
   807 +	if (result != ISC_R_SUCCESS) {
       
   808 +		isc_mem_put(mctx, counter, sizeof(*counter));
       
   809 +		return (result);
       
   810 +	}
       
   811 +
       
   812 +	counter->mctx = NULL;
       
   813 +	isc_mem_attach(mctx, &counter->mctx);
       
   814 +
       
   815 +	counter->references = 1;
       
   816 +	counter->limit = limit;
       
   817 +	counter->used = 0;
       
   818 +
       
   819 +	counter->magic = COUNTER_MAGIC;
       
   820 +	*counterp = counter;
       
   821 +	return (ISC_R_SUCCESS);
       
   822 +}
       
   823 +
       
   824 +isc_result_t
       
   825 +isc_counter_increment(isc_counter_t *counter) {
       
   826 +	isc_result_t result = ISC_R_SUCCESS;
       
   827 +
       
   828 +	LOCK(&counter->lock);
       
   829 +	counter->used++;
       
   830 +	if (counter->limit != 0 && counter->used >= counter->limit)
       
   831 +		result = ISC_R_QUOTA;
       
   832 +	UNLOCK(&counter->lock);
       
   833 +
       
   834 +	return (result);
       
   835 +}
       
   836 +
       
   837 +unsigned int
       
   838 +isc_counter_used(isc_counter_t *counter) {
       
   839 +	REQUIRE(VALID_COUNTER(counter));
       
   840 +
       
   841 +	return (counter->used);
       
   842 +}
       
   843 +
       
   844 +void
       
   845 +isc_counter_setlimit(isc_counter_t *counter, int limit) {
       
   846 +	REQUIRE(VALID_COUNTER(counter));
       
   847 +
       
   848 +	LOCK(&counter->lock);
       
   849 +	counter->limit = limit;
       
   850 +	UNLOCK(&counter->lock);
       
   851 +}
       
   852 +
       
   853 +void
       
   854 +isc_counter_attach(isc_counter_t *source, isc_counter_t **targetp) {
       
   855 +	REQUIRE(VALID_COUNTER(source));
       
   856 +	REQUIRE(targetp != NULL && *targetp == NULL);
       
   857 +
       
   858 +	LOCK(&source->lock);
       
   859 +	source->references++;
       
   860 +	INSIST(source->references > 0);
       
   861 +	UNLOCK(&source->lock);
       
   862 +
       
   863 +	*targetp = source;
       
   864 +}
       
   865 +
       
   866 +static void
       
   867 +destroy(isc_counter_t *counter) {
       
   868 +	counter->magic = 0;
       
   869 +	isc_mutex_destroy(&counter->lock);
       
   870 +	isc_mem_putanddetach(&counter->mctx, counter, sizeof(*counter));
       
   871 +}
       
   872 +
       
   873 +void
       
   874 +isc_counter_detach(isc_counter_t **counterp) {
       
   875 +	isc_counter_t *counter;
       
   876 +	isc_boolean_t want_destroy = ISC_FALSE;
       
   877 +
       
   878 +	REQUIRE(counterp != NULL && *counterp != NULL);
       
   879 +	counter = *counterp;
       
   880 +	REQUIRE(VALID_COUNTER(counter));
       
   881 +
       
   882 +	*counterp = NULL;
       
   883 +
       
   884 +	LOCK(&counter->lock);
       
   885 +	INSIST(counter->references > 0);
       
   886 +	counter->references--;
       
   887 +	if (counter->references == 0)
       
   888 +		want_destroy = ISC_TRUE;
       
   889 +	UNLOCK(&counter->lock);
       
   890 +
       
   891 +	if (want_destroy)
       
   892 +		destroy(counter);
       
   893 +}
       
   894 diff --git a/lib/isc/include/isc/counter.h b/lib/isc/include/isc/counter.h
       
   895 new file mode 100644
       
   896 index 0000000..e7ebd25
       
   897 --- /dev/null
       
   898 +++ b/lib/isc/include/isc/counter.h
       
   899 @@ -0,0 +1,90 @@
       
   900 +/*
       
   901 + * Copyright (C) 2014  Internet Systems Consortium, Inc. ("ISC")
       
   902 + *
       
   903 + * Permission to use, copy, modify, and/or distribute this software for any
       
   904 + * purpose with or without fee is hereby granted, provided that the above
       
   905 + * copyright notice and this permission notice appear in all copies.
       
   906 + *
       
   907 + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
       
   908 + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
       
   909 + * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
       
   910 + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
       
   911 + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
       
   912 + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
       
   913 + * PERFORMANCE OF THIS SOFTWARE.
       
   914 + */
       
   915 +
       
   916 +#ifndef ISC_COUNTER_H
       
   917 +#define ISC_COUNTER_H 1
       
   918 +
       
   919 +/*****
       
   920 + ***** Module Info
       
   921 + *****/
       
   922 +
       
   923 +/*! \file isc/counter.h
       
   924 + *
       
   925 + * \brief The isc_counter_t object is a simplified version of the
       
   926 + * isc_quota_t object; it tracks the consumption of limited
       
   927 + * resources, returning an error condition when the quota is
       
   928 + * exceeded.  However, unlike isc_quota_t, attaching and detaching
       
   929 + * from a counter object does not increment or decrement the counter.
       
   930 + */
       
   931 +
       
   932 +/***
       
   933 + *** Imports.
       
   934 + ***/
       
   935 +
       
   936 +#include <isc/lang.h>
       
   937 +#include <isc/mutex.h>
       
   938 +#include <isc/types.h>
       
   939 +
       
   940 +/*****
       
   941 + ***** Types.
       
   942 + *****/
       
   943 +
       
   944 +ISC_LANG_BEGINDECLS
       
   945 +
       
   946 +isc_result_t
       
   947 +isc_counter_create(isc_mem_t *mctx, int limit, isc_counter_t **counterp);
       
   948 +/*%<
       
   949 + * Allocate and initialize a counter object.
       
   950 + */
       
   951 +
       
   952 +isc_result_t
       
   953 +isc_counter_increment(isc_counter_t *counter);
       
   954 +/*%<
       
   955 + * Increment the counter.
       
   956 + *
       
   957 + * If the counter limit is nonzero and has been reached, then
       
   958 + * return ISC_R_QUOTA, otherwise ISC_R_SUCCESS. (The counter is
       
   959 + * incremented regardless of return value.)
       
   960 + */
       
   961 +
       
   962 +unsigned int
       
   963 +isc_counter_used(isc_counter_t *counter);
       
   964 +/*%<
       
   965 + * Return the current counter value.
       
   966 + */
       
   967 +
       
   968 +void
       
   969 +isc_counter_setlimit(isc_counter_t *counter, int limit);
       
   970 +/*%<
       
   971 + * Set the counter limit.
       
   972 + */
       
   973 +
       
   974 +void
       
   975 +isc_counter_attach(isc_counter_t *source, isc_counter_t **targetp);
       
   976 +/*%<
       
   977 + * Attach to a counter object, increasing its reference counter.
       
   978 + */
       
   979 +
       
   980 +void
       
   981 +isc_counter_detach(isc_counter_t **counterp);
       
   982 +/*%<
       
   983 + * Detach (and destroy if reference counter has dropped to zero)
       
   984 + * a counter object.
       
   985 + */
       
   986 +
       
   987 +ISC_LANG_ENDDECLS
       
   988 +
       
   989 +#endif /* ISC_COUNTER_H */
       
   990 diff --git a/lib/isc/tests/counter_test.c b/lib/isc/tests/counter_test.c
       
   991 new file mode 100644
       
   992 index 0000000..a7a1997
       
   993 --- /dev/null
       
   994 +++ b/lib/isc/tests/counter_test.c
       
   995 @@ -0,0 +1,69 @@
       
   996 +/*
       
   997 + * Copyright (C) 2014  Internet Systems Consortium, Inc. ("ISC")
       
   998 + *
       
   999 + * Permission to use, copy, modify, and/or distribute this software for any
       
  1000 + * purpose with or without fee is hereby granted, provided that the above
       
  1001 + * copyright notice and this permission notice appear in all copies.
       
  1002 + *
       
  1003 + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
       
  1004 + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
       
  1005 + * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
       
  1006 + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
       
  1007 + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
       
  1008 + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
       
  1009 + * PERFORMANCE OF THIS SOFTWARE.
       
  1010 + */
       
  1011 +
       
  1012 +#include <config.h>
       
  1013 +#include <stdlib.h>
       
  1014 +
       
  1015 +#include <atf-c.h>
       
  1016 +
       
  1017 +#include <isc/counter.h>
       
  1018 +#include <isc/result.h>
       
  1019 +
       
  1020 +#include "isctest.h"
       
  1021 +
       
  1022 +ATF_TC(isc_counter);
       
  1023 +ATF_TC_HEAD(isc_counter, tc) {
       
  1024 +	atf_tc_set_md_var(tc, "descr", "isc counter object");
       
  1025 +}
       
  1026 +ATF_TC_BODY(isc_counter, tc) {
       
  1027 +	isc_result_t result;
       
  1028 +	isc_counter_t *counter = NULL;
       
  1029 +	int i;
       
  1030 +
       
  1031 +	result = isc_test_begin(NULL, ISC_TRUE);
       
  1032 +	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
       
  1033 +
       
  1034 +	result = isc_counter_create(mctx, 0, &counter);
       
  1035 +	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
       
  1036 +
       
  1037 +	for (i = 0; i < 10; i++) {
       
  1038 +		result = isc_counter_increment(counter);
       
  1039 +		ATF_CHECK_EQ(result, ISC_R_SUCCESS);
       
  1040 +	}
       
  1041 +
       
  1042 +	ATF_CHECK_EQ(isc_counter_used(counter), 10);
       
  1043 +
       
  1044 +	isc_counter_setlimit(counter, 15);
       
  1045 +	for (i = 0; i < 10; i++) {
       
  1046 +		result = isc_counter_increment(counter);
       
  1047 +		if (result != ISC_R_SUCCESS)
       
  1048 +			break;
       
  1049 +	}
       
  1050 +
       
  1051 +	ATF_CHECK_EQ(isc_counter_used(counter), 15);
       
  1052 +
       
  1053 +	isc_counter_detach(&counter);
       
  1054 +	isc_test_end();
       
  1055 +}
       
  1056 +
       
  1057 +/*
       
  1058 + * Main
       
  1059 + */
       
  1060 +ATF_TP_ADD_TCS(tp) {
       
  1061 +	ATF_TP_ADD_TC(tp, isc_counter);
       
  1062 +	return (atf_no_error());
       
  1063 +}
       
  1064 +