572 lines
		
	
	
	
		
			17 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			572 lines
		
	
	
	
		
			17 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
Index: contrib/bind9/CHANGES
 | 
						|
===================================================================
 | 
						|
--- contrib/bind9/CHANGES.orig
 | 
						|
+++ contrib/bind9/CHANGES
 | 
						|
@@ -1,3 +1,14 @@
 | 
						|
+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.  [RT #35780]
 | 
						|
+
 | 
						|
 	--- 9.9.5 released ---
 | 
						|
 
 | 
						|
 	--- 9.9.5rc2 released ---
 | 
						|
Index: contrib/bind9/bin/named/config.c
 | 
						|
===================================================================
 | 
						|
--- contrib/bind9/bin/named/config.c.orig
 | 
						|
+++ contrib/bind9/bin/named/config.c
 | 
						|
@@ -15,8 +15,6 @@
 | 
						|
  * PERFORMANCE OF THIS SOFTWARE.
 | 
						|
  */
 | 
						|
 
 | 
						|
-/* $Id: config.c,v 1.123 2012/01/06 23:46:41 tbox Exp $ */
 | 
						|
-
 | 
						|
 /*! \file */
 | 
						|
 
 | 
						|
 #include <config.h>
 | 
						|
@@ -160,6 +158,7 @@
 | 
						|
 	dnssec-accept-expired no;\n\
 | 
						|
 	clients-per-query 10;\n\
 | 
						|
 	max-clients-per-query 100;\n\
 | 
						|
+	max-recursion-depth 7;\n\
 | 
						|
 	zero-no-soa-ttl-cache no;\n\
 | 
						|
 	nsec3-test-zone no;\n\
 | 
						|
 	allow-new-zones no;\n\
 | 
						|
Index: contrib/bind9/bin/named/query.c
 | 
						|
===================================================================
 | 
						|
--- contrib/bind9/bin/named/query.c.orig
 | 
						|
+++ contrib/bind9/bin/named/query.c
 | 
						|
@@ -3872,12 +3872,11 @@
 | 
						|
 		peeraddr = &client->peeraddr;
 | 
						|
 	else
 | 
						|
 		peeraddr = NULL;
 | 
						|
-	result = dns_resolver_createfetch2(client->view->resolver,
 | 
						|
+	result = dns_resolver_createfetch3(client->view->resolver,
 | 
						|
 					   qname, qtype, qdomain, nameservers,
 | 
						|
 					   NULL, peeraddr, client->message->id,
 | 
						|
-					   client->query.fetchoptions,
 | 
						|
-					   client->task,
 | 
						|
-					   query_resume, client,
 | 
						|
+					   client->query.fetchoptions, 0,
 | 
						|
+					   client->task, query_resume, client,
 | 
						|
 					   rdataset, sigrdataset,
 | 
						|
 					   &client->query.fetch);
 | 
						|
 
 | 
						|
Index: contrib/bind9/bin/named/server.c
 | 
						|
===================================================================
 | 
						|
--- contrib/bind9/bin/named/server.c.orig
 | 
						|
+++ contrib/bind9/bin/named/server.c
 | 
						|
@@ -3141,6 +3141,11 @@
 | 
						|
 					cfg_obj_asuint32(obj),
 | 
						|
 					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));
 | 
						|
+
 | 
						|
 #ifdef ALLOW_FILTER_AAAA_ON_V4
 | 
						|
 	obj = NULL;
 | 
						|
 	result = ns_config_get(maps, "filter-aaaa-on-v4", &obj);
 | 
						|
Index: contrib/bind9/lib/dns/adb.c
 | 
						|
===================================================================
 | 
						|
--- contrib/bind9/lib/dns/adb.c.orig
 | 
						|
+++ contrib/bind9/lib/dns/adb.c
 | 
						|
@@ -201,6 +201,7 @@
 | 
						|
 	unsigned int                    magic;
 | 
						|
 	dns_fetch_t                    *fetch;
 | 
						|
 	dns_rdataset_t                  rdataset;
 | 
						|
+	unsigned int			depth;
 | 
						|
 };
 | 
						|
 
 | 
						|
 /*%
 | 
						|
@@ -301,7 +302,7 @@
 | 
						|
 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);
 | 
						|
+				isc_uint32_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);
 | 
						|
@@ -309,7 +310,7 @@
 | 
						|
 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,
 | 
						|
-			       dns_rdatatype_t);
 | 
						|
+			       unsigned int, dns_rdatatype_t);
 | 
						|
 static inline void check_exit(dns_adb_t *);
 | 
						|
 static void destroy(dns_adb_t *);
 | 
						|
 static isc_boolean_t shutdown_names(dns_adb_t *);
 | 
						|
@@ -982,7 +983,7 @@
 | 
						|
 	 * Clean up the name's various lists.  These two are destructive
 | 
						|
 	 * in that they will always empty the list.
 | 
						|
 	 */
 | 
						|
-	clean_finds_at_name(name, ev, DNS_ADBFIND_ADDRESSMASK);
 | 
						|
+	clean_finds_at_name(name, ev, 0, DNS_ADBFIND_ADDRESSMASK);
 | 
						|
 	result4 = clean_namehooks(adb, &name->v4);
 | 
						|
 	result6 = clean_namehooks(adb, &name->v6);
 | 
						|
 	clean_target(adb, &name->target);
 | 
						|
@@ -1407,7 +1408,7 @@
 | 
						|
  */
 | 
						|
 static void
 | 
						|
 clean_finds_at_name(dns_adbname_t *name, isc_eventtype_t evtype,
 | 
						|
-		    unsigned int addrs)
 | 
						|
+		    isc_uint32_t qtotal, unsigned int addrs)
 | 
						|
 {
 | 
						|
 	isc_event_t *ev;
 | 
						|
 	isc_task_t *task;
 | 
						|
@@ -1467,6 +1468,7 @@
 | 
						|
 			ev->ev_sender = find;
 | 
						|
 			find->result_v4 = find_err_map[name->fetch_err];
 | 
						|
 			find->result_v6 = find_err_map[name->fetch6_err];
 | 
						|
+			find->qtotal += qtotal;
 | 
						|
 			ev->ev_type = evtype;
 | 
						|
 			ev->ev_destroy = event_free;
 | 
						|
 			ev->ev_destroy_arg = find;
 | 
						|
@@ -1821,6 +1823,7 @@
 | 
						|
 	h->flags = 0;
 | 
						|
 	h->result_v4 = ISC_R_UNEXPECTED;
 | 
						|
 	h->result_v6 = ISC_R_UNEXPECTED;
 | 
						|
+	h->qtotal = 0;
 | 
						|
 	ISC_LINK_INIT(h, publink);
 | 
						|
 	ISC_LINK_INIT(h, plink);
 | 
						|
 	ISC_LIST_INIT(h->list);
 | 
						|
@@ -2770,6 +2773,19 @@
 | 
						|
 		   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, 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,
 | 
						|
+		    dns_adbfind_t **findp)
 | 
						|
+{
 | 
						|
 	dns_adbfind_t *find;
 | 
						|
 	dns_adbname_t *adbname;
 | 
						|
 	int bucket;
 | 
						|
@@ -3000,7 +3016,7 @@
 | 
						|
 		 * Start V4.
 | 
						|
 		 */
 | 
						|
 		if (WANT_INET(wanted_fetches) &&
 | 
						|
-		    fetch_name(adbname, start_at_zone,
 | 
						|
+		    fetch_name(adbname, start_at_zone, depth,
 | 
						|
 			       dns_rdatatype_a) == ISC_R_SUCCESS) {
 | 
						|
 			DP(DEF_LEVEL,
 | 
						|
 			   "dns_adb_createfind: started A fetch for name %p",
 | 
						|
@@ -3011,7 +3027,7 @@
 | 
						|
 		 * Start V6.
 | 
						|
 		 */
 | 
						|
 		if (WANT_INET6(wanted_fetches) &&
 | 
						|
-		    fetch_name(adbname, start_at_zone,
 | 
						|
+		    fetch_name(adbname, start_at_zone, depth,
 | 
						|
 			       dns_rdatatype_aaaa) == ISC_R_SUCCESS) {
 | 
						|
 			DP(DEF_LEVEL,
 | 
						|
 			   "dns_adb_createfind: "
 | 
						|
@@ -3627,6 +3643,7 @@
 | 
						|
 	isc_result_t result;
 | 
						|
 	unsigned int address_type;
 | 
						|
 	isc_boolean_t want_check_exit = ISC_FALSE;
 | 
						|
+	isc_uint32_t qtotal = 0;
 | 
						|
 
 | 
						|
 	UNUSED(task);
 | 
						|
 
 | 
						|
@@ -3637,6 +3654,8 @@
 | 
						|
 	adb = name->adb;
 | 
						|
 	INSIST(DNS_ADB_VALID(adb));
 | 
						|
 
 | 
						|
+	qtotal = dev->qtotal;
 | 
						|
+
 | 
						|
 	bucket = name->lock_bucket;
 | 
						|
 	LOCK(&adb->namelocks[bucket]);
 | 
						|
 
 | 
						|
@@ -3754,6 +3773,12 @@
 | 
						|
 		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);
 | 
						|
@@ -3785,15 +3810,14 @@
 | 
						|
 	free_adbfetch(adb, &fetch);
 | 
						|
 	isc_event_free(&ev);
 | 
						|
 
 | 
						|
-	clean_finds_at_name(name, ev_status, address_type);
 | 
						|
+	clean_finds_at_name(name, ev_status, qtotal, address_type);
 | 
						|
 
 | 
						|
 	UNLOCK(&adb->namelocks[bucket]);
 | 
						|
 }
 | 
						|
 
 | 
						|
 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, dns_rdatatype_t type)
 | 
						|
 {
 | 
						|
 	isc_result_t result;
 | 
						|
 	dns_adbfetch_t *fetch = NULL;
 | 
						|
@@ -3838,12 +3862,14 @@
 | 
						|
 		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, adb->task,
 | 
						|
+					   fetch_callback, adbname,
 | 
						|
+					   &fetch->rdataset, NULL,
 | 
						|
+					   &fetch->fetch);
 | 
						|
 	if (result != ISC_R_SUCCESS)
 | 
						|
 		goto cleanup;
 | 
						|
 
 | 
						|
Index: contrib/bind9/lib/dns/include/dns/adb.h
 | 
						|
===================================================================
 | 
						|
--- contrib/bind9/lib/dns/include/dns/adb.h.orig
 | 
						|
+++ contrib/bind9/lib/dns/include/dns/adb.h
 | 
						|
@@ -118,6 +118,8 @@
 | 
						|
 	isc_result_t			result_v6;	/*%< RO: v6 result */
 | 
						|
 	ISC_LINK(dns_adbfind_t)		publink;	/*%< RW: client use */
 | 
						|
 
 | 
						|
+	isc_uint32_t			qtotal;
 | 
						|
+
 | 
						|
 	/* Private */
 | 
						|
 	isc_mutex_t			lock;		/* locks all below */
 | 
						|
 	in_port_t			port;
 | 
						|
@@ -334,6 +336,12 @@
 | 
						|
 		   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, 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
 | 
						|
Index: contrib/bind9/lib/dns/include/dns/resolver.h
 | 
						|
===================================================================
 | 
						|
--- contrib/bind9/lib/dns/include/dns/resolver.h.orig
 | 
						|
+++ contrib/bind9/lib/dns/include/dns/resolver.h
 | 
						|
@@ -82,6 +82,7 @@
 | 
						|
 	isc_sockaddr_t *		client;
 | 
						|
 	dns_messageid_t			id;
 | 
						|
 	isc_result_t			vresult;
 | 
						|
+	isc_uint32_t 			qtotal;
 | 
						|
 } dns_fetchevent_t;
 | 
						|
 
 | 
						|
 /*
 | 
						|
@@ -274,6 +275,18 @@
 | 
						|
 			  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_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.
 | 
						|
  *
 | 
						|
@@ -575,6 +588,18 @@
 | 
						|
  * \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.
 | 
						|
+ */
 | 
						|
+
 | 
						|
 ISC_LANG_ENDDECLS
 | 
						|
 
 | 
						|
 #endif /* DNS_RESOLVER_H */
 | 
						|
Index: contrib/bind9/lib/dns/resolver.c
 | 
						|
===================================================================
 | 
						|
--- contrib/bind9/lib/dns/resolver.c.orig
 | 
						|
+++ contrib/bind9/lib/dns/resolver.c
 | 
						|
@@ -131,6 +131,16 @@
 | 
						|
 #define MAXIMUM_QUERY_TIMEOUT 30 /* The maximum time in seconds for the whole query to live. */
 | 
						|
 #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.
 | 
						|
  */
 | 
						|
@@ -297,6 +307,7 @@
 | 
						|
 	isc_uint64_t			duration;
 | 
						|
 	isc_boolean_t			logged;
 | 
						|
 	unsigned int			querysent;
 | 
						|
+	unsigned int			totalqueries;
 | 
						|
 	unsigned int			referrals;
 | 
						|
 	unsigned int			lamecount;
 | 
						|
 	unsigned int			neterr;
 | 
						|
@@ -307,6 +318,7 @@
 | 
						|
 	isc_boolean_t			timeout;
 | 
						|
 	dns_adbaddrinfo_t 		*addrinfo;
 | 
						|
 	isc_sockaddr_t			*client;
 | 
						|
+	unsigned int			depth;
 | 
						|
 };
 | 
						|
 
 | 
						|
 #define FCTX_MAGIC			ISC_MAGIC('F', '!', '!', '!')
 | 
						|
@@ -418,6 +430,7 @@
 | 
						|
 	isc_timer_t *			spillattimer;
 | 
						|
 	isc_boolean_t			zero_no_soa_ttl;
 | 
						|
 	unsigned int			query_timeout;
 | 
						|
+	unsigned int			maxdepth;
 | 
						|
 
 | 
						|
 	/* Locked by lock. */
 | 
						|
 	unsigned int			references;
 | 
						|
@@ -1093,6 +1106,7 @@
 | 
						|
 			       event->result == DNS_R_NCACHENXRRSET);
 | 
						|
 		}
 | 
						|
 
 | 
						|
+		event->qtotal = fctx->totalqueries;
 | 
						|
 		isc_task_sendanddetach(&task, ISC_EVENT_PTR(&event));
 | 
						|
 		count++;
 | 
						|
 	}
 | 
						|
@@ -1533,7 +1547,9 @@
 | 
						|
 		if (result != ISC_R_SUCCESS)
 | 
						|
 			goto cleanup_dispatch;
 | 
						|
 	}
 | 
						|
+
 | 
						|
 	fctx->querysent++;
 | 
						|
+	fctx->totalqueries++;
 | 
						|
 
 | 
						|
 	ISC_LIST_APPEND(fctx->queries, query, link);
 | 
						|
 	query->fctx->nqueries++;
 | 
						|
@@ -2186,9 +2202,10 @@
 | 
						|
 		 */
 | 
						|
 		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 {
 | 
						|
+			fctx->totalqueries += find->qtotal;
 | 
						|
+		} else {
 | 
						|
 			fctx->findfail++;
 | 
						|
 			if (fctx->pending == 0) {
 | 
						|
 				/*
 | 
						|
@@ -2471,12 +2488,13 @@
 | 
						|
 	 * 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, &find);
 | 
						|
 	if (result != ISC_R_SUCCESS) {
 | 
						|
 		if (result == DNS_R_ALIAS) {
 | 
						|
 			/*
 | 
						|
@@ -2584,6 +2602,11 @@
 | 
						|
 
 | 
						|
 	res = fctx->res;
 | 
						|
 
 | 
						|
+	if (fctx->depth > res->maxdepth) {
 | 
						|
+		FCTXTRACE("too much NS indirection");
 | 
						|
+		return (DNS_R_SERVFAIL);
 | 
						|
+	}
 | 
						|
+
 | 
						|
 	/*
 | 
						|
 	 * Forwarders.
 | 
						|
 	 */
 | 
						|
@@ -3022,6 +3045,9 @@
 | 
						|
 
 | 
						|
 	REQUIRE(!ADDRWAIT(fctx));
 | 
						|
 
 | 
						|
+	if (fctx->totalqueries > DEFAULT_MAX_QUERIES)
 | 
						|
+		fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
 | 
						|
+
 | 
						|
 	addrinfo = fctx_nextaddress(fctx);
 | 
						|
 	if (addrinfo == NULL) {
 | 
						|
 		/*
 | 
						|
@@ -3380,6 +3406,7 @@
 | 
						|
 		 * Normal fctx startup.
 | 
						|
 		 */
 | 
						|
 		fctx->state = fetchstate_active;
 | 
						|
+		fctx->totalqueries = 0;
 | 
						|
 		/*
 | 
						|
 		 * Reset the control event for later use in shutting down
 | 
						|
 		 * the fctx.
 | 
						|
@@ -3449,6 +3476,7 @@
 | 
						|
 	event->fetch = fetch;
 | 
						|
 	event->client = client;
 | 
						|
 	event->id = id;
 | 
						|
+	event->qtotal = 0;
 | 
						|
 	dns_fixedname_init(&event->foundname);
 | 
						|
 
 | 
						|
 	/*
 | 
						|
@@ -3485,7 +3513,8 @@
 | 
						|
 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,
 | 
						|
+	    fetchctx_t **fctxp)
 | 
						|
 {
 | 
						|
 	fetchctx_t *fctx;
 | 
						|
 	isc_result_t result;
 | 
						|
@@ -3537,6 +3566,7 @@
 | 
						|
 	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);
 | 
						|
@@ -3555,6 +3585,7 @@
 | 
						|
 	fctx->pending = 0;
 | 
						|
 	fctx->restarts = 0;
 | 
						|
 	fctx->querysent = 0;
 | 
						|
+	fctx->totalqueries = 0;
 | 
						|
 	fctx->referrals = 0;
 | 
						|
 	TIME_NOW(&fctx->start);
 | 
						|
 	fctx->timeouts = 0;
 | 
						|
@@ -7731,6 +7762,7 @@
 | 
						|
 	res->spillattimer = NULL;
 | 
						|
 	res->zero_no_soa_ttl = ISC_FALSE;
 | 
						|
 	res->query_timeout = DEFAULT_QUERY_TIMEOUT;
 | 
						|
+	res->maxdepth = DEFAULT_RECURSION_DEPTH;
 | 
						|
 	res->nbuckets = ntasks;
 | 
						|
 	res->activebuckets = ntasks;
 | 
						|
 	res->buckets = isc_mem_get(view->mctx,
 | 
						|
@@ -8169,9 +8201,9 @@
 | 
						|
 			 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, task, action, arg,
 | 
						|
 					  rdataset, sigrdataset, fetchp));
 | 
						|
 }
 | 
						|
 
 | 
						|
@@ -8187,6 +8219,25 @@
 | 
						|
 			  dns_rdataset_t *sigrdataset,
 | 
						|
 			  dns_fetch_t **fetchp)
 | 
						|
 {
 | 
						|
+	return (dns_resolver_createfetch3(res, name, type, domain,
 | 
						|
+					  nameservers, forwarders, client, id,
 | 
						|
+					  options, 0, 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_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;
 | 
						|
@@ -8273,11 +8324,12 @@
 | 
						|
 
 | 
						|
 	if (fctx == NULL) {
 | 
						|
 		result = fctx_create(res, name, type, domain, nameservers,
 | 
						|
-				     options, bucketnum, &fctx);
 | 
						|
+				     options, bucketnum, depth, &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);
 | 
						|
@@ -9049,3 +9101,15 @@
 | 
						|
 
 | 
						|
 	resolver->query_timeout = seconds;
 | 
						|
 }
 | 
						|
+
 | 
						|
+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);
 | 
						|
+}
 | 
						|
Index: contrib/bind9/lib/isccfg/namedconf.c
 | 
						|
===================================================================
 | 
						|
--- contrib/bind9/lib/isccfg/namedconf.c.orig
 | 
						|
+++ contrib/bind9/lib/isccfg/namedconf.c
 | 
						|
@@ -1419,6 +1419,7 @@
 | 
						|
 	{ "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-udp-size", &cfg_type_uint32, 0 },
 | 
						|
 	{ "min-roots", &cfg_type_uint32, CFG_CLAUSEFLAG_NOTIMP },
 | 
						|
 	{ "minimal-responses", &cfg_type_boolean, 0 },
 |