Tomas Hozza 574dc4
diff -r -u bin/named/client.c-orig bin/named/client.c
Tomas Hozza 574dc4
--- bin/named/client.c-orig	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
+++ bin/named/client.c	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
@@ -994,6 +994,11 @@
Adam Tkac b62a53
 	}
Adam Tkac b62a53
 	if (result != ISC_R_SUCCESS)
Adam Tkac b62a53
 		goto done;
Adam Tkac b62a53
+	/*
Adam Tkac b62a53
+	 * Stop after the question if TC was set for rate limiting.
Adam Tkac b62a53
+	 */
Adam Tkac b62a53
+	if ((client->message->flags & DNS_MESSAGEFLAG_TC) != 0)
Adam Tkac b62a53
+		goto renderend;
Adam Tkac b62a53
 	result = dns_message_rendersection(client->message,
Adam Tkac b62a53
 					   DNS_SECTION_ANSWER,
Adam Tkac b62a53
 					   DNS_MESSAGERENDER_PARTIAL |
Tomas Hozza 574dc4
@@ -1134,6 +1139,51 @@
Adam Tkac b62a53
 #endif
Adam Tkac b62a53
 
Adam Tkac b62a53
 	/*
Adam Tkac b62a53
+	 * Try to rate limit error responses.
Adam Tkac b62a53
+	 */
Adam Tkac b62a53
+	if (client->view != NULL && client->view->rrl != NULL) {
Adam Tkac b62a53
+		isc_boolean_t wouldlog;
Adam Tkac b62a53
+		char log_buf[DNS_RRL_LOG_BUF_LEN];
Adam Tkac b62a53
+		dns_rrl_result_t rrl_result;
Adam Tkac b62a53
+
Adam Tkac b62a53
+		INSIST(rcode != dns_rcode_noerror &&
Adam Tkac b62a53
+		       rcode != dns_rcode_nxdomain);
Tomas Hozza 574dc4
+		wouldlog = isc_log_wouldlog(ns_g_lctx, DNS_RRL_LOG_DROP);
Adam Tkac b62a53
+		rrl_result = dns_rrl(client->view, &client->peeraddr,
Adam Tkac b62a53
+				     TCP_CLIENT(client),
Adam Tkac b62a53
+				     dns_rdataclass_in, dns_rdatatype_none,
Adam Tkac b62a53
+				     NULL, result, client->now,
Adam Tkac b62a53
+				     wouldlog, log_buf, sizeof(log_buf));
Adam Tkac b62a53
+		if (rrl_result != DNS_RRL_RESULT_OK) {
Adam Tkac b62a53
+			/*
Adam Tkac b62a53
+			 * Log dropped errors in the query category
Adam Tkac b62a53
+			 * so that they are not lost in silence.
Adam Tkac b62a53
+			 * Starts of rate-limited bursts are logged in
Adam Tkac b62a53
+			 * NS_LOGCATEGORY_RRL.
Adam Tkac b62a53
+			 */
Adam Tkac b62a53
+			if (wouldlog) {
Tomas Hozza 574dc4
+				ns_client_log(client,
Tomas Hozza 574dc4
+					      NS_LOGCATEGORY_QUERY_EERRORS,
Adam Tkac b62a53
+					      NS_LOGMODULE_CLIENT,
Adam Tkac b62a53
+					      DNS_RRL_LOG_DROP,
Adam Tkac b62a53
+					      "%s", log_buf);
Adam Tkac b62a53
+			}
Adam Tkac b62a53
+			/*
Adam Tkac b62a53
+			 * Some error responses cannot be 'slipped',
Tomas Hozza 574dc4
+			 * so don't try to slip any error responses.
Adam Tkac b62a53
+			 */
Adam Tkac b62a53
+			if (!client->view->rrl->log_only) {
Tomas Hozza 574dc4
+				isc_stats_increment(ns_g_server->nsstats,
Tomas Hozza 574dc4
+						dns_nsstatscounter_ratedropped);
Tomas Hozza 574dc4
+				isc_stats_increment(ns_g_server->nsstats,
Tomas Hozza 574dc4
+						dns_nsstatscounter_dropped);
Adam Tkac b62a53
+				ns_client_next(client, DNS_R_DROP);
Adam Tkac b62a53
+				return;
Adam Tkac b62a53
+			}
Adam Tkac b62a53
+		}
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+
Adam Tkac b62a53
+	/*
Adam Tkac b62a53
 	 * Message may be an in-progress reply that we had trouble
Adam Tkac b62a53
 	 * with, in which case QR will be set.  We need to clear QR before
Adam Tkac b62a53
 	 * calling dns_message_reply() to avoid triggering an assertion.
Tomas Hozza 574dc4
diff -r -u bin/named/config.c-orig bin/named/config.c
Tomas Hozza 574dc4
--- bin/named/config.c-orig	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
+++ bin/named/config.c	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
@@ -228,6 +228,13 @@
Adam Tkac b62a53
 	notify no;\n\
Adam Tkac b62a53
 	allow-new-zones no;\n\
Adam Tkac b62a53
 \n\
Adam Tkac b62a53
+	# Prevent use of this zone in DNS amplified reflection DoS attacks\n\
Adam Tkac b62a53
+	rate-limit {\n\
Adam Tkac b62a53
+		responses-per-second 3;\n\
Adam Tkac b62a53
+		slip 0;\n\
Adam Tkac b62a53
+		min-table-size 10;\n\
Adam Tkac b62a53
+	};\n\
Adam Tkac b62a53
+\n\
Adam Tkac b62a53
 	zone \"version.bind\" chaos {\n\
Adam Tkac b62a53
 		type master;\n\
Adam Tkac b62a53
 		database \"_builtin version\";\n\
Tomas Hozza 574dc4
diff -r -u bin/named/include/named/query.h-orig bin/named/include/named/query.h
Tomas Hozza 574dc4
--- bin/named/include/named/query.h-orig	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
+++ bin/named/include/named/query.h	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
@@ -85,6 +85,7 @@
Adam Tkac b62a53
 #define NS_QUERYATTR_CACHEACLOK		0x2000
Adam Tkac b62a53
 #define NS_QUERYATTR_DNS64		0x4000
Adam Tkac b62a53
 #define NS_QUERYATTR_DNS64EXCLUDE	0x8000
Adam Tkac b62a53
+#define NS_QUERYATTR_RRL_CHECKED	0x10000
Adam Tkac b62a53
 
Adam Tkac b62a53
 
Adam Tkac b62a53
 isc_result_t
Tomas Hozza 574dc4
diff -r -u bin/named/include/named/server.h-orig bin/named/include/named/server.h
Tomas Hozza 574dc4
--- bin/named/include/named/server.h-orig	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
+++ bin/named/include/named/server.h	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
@@ -167,7 +167,10 @@
Adam Tkac b62a53
 
Tomas Hozza 574dc4
 	dns_nsstatscounter_rpz_rewrites = 36,
Tomas Hozza 574dc4
 
Tomas Hozza 574dc4
-	dns_nsstatscounter_max = 37
Tomas Hozza 574dc4
+	dns_nsstatscounter_ratedropped = 37,
Tomas Hozza 574dc4
+	dns_nsstatscounter_rateslipped = 38,
Adam Tkac b62a53
+
Tomas Hozza 574dc4
+	dns_nsstatscounter_max = 39
Adam Tkac b62a53
 };
Adam Tkac b62a53
 
Adam Tkac b62a53
 void
Tomas Hozza 574dc4
diff -r -u bin/named/query.c-orig bin/named/query.c
Tomas Hozza 574dc4
--- bin/named/query.c-orig	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
+++ bin/named/query.c	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
@@ -193,7 +193,7 @@
Tomas Hozza 574dc4
 #ifdef NEWSTATS
Tomas Hozza 574dc4
 	/* Do query type statistics
Tomas Hozza 574dc4
 	 *
Tomas Hozza 574dc4
-	 * We only increment per-type if we're using the authoriative
Tomas Hozza 574dc4
+	 * We only increment per-type if we're using the authoritative
Tomas Hozza 574dc4
 	 * answer counter, preventing double-counting.
Tomas Hozza 574dc4
 	 */
Tomas Hozza 574dc4
 	if (counter == dns_nsstatscounter_authans) {
Tomas Hozza 612f0b
@@ -5865,6 +5865,131 @@
Adam Tkac b62a53
  resume:
Adam Tkac b62a53
 	CTRACE("query_find: resume");
Adam Tkac b62a53
 
Adam Tkac b62a53
+	/*
Adam Tkac b62a53
+	 * Rate limit these responses to this client.
Tomas Hozza 574dc4
+	 * Do not delay counting and handling obvious referrals,
Tomas Hozza 574dc4
+	 *	since those won't come here again.
Tomas Hozza 574dc4
+	 * Delay handling delegations for which we are certain to recurse and
Tomas Hozza 574dc4
+	 *	return here (DNS_R_DELEGATION, not a child of one of our
Tomas Hozza 574dc4
+	 *	own zones, and recursion enabled)
Tomas Hozza 612f0b
+	 * Don't mess with responses rewritten by RPZ
Tomas Hozza 574dc4
+	 * Count each response at most once.
Adam Tkac b62a53
+	 */
Adam Tkac b62a53
+	if (client->view->rrl != NULL &&
Tomas Hozza 574dc4
+	    ((fname != NULL && dns_name_isabsolute(fname)) ||
Tomas Hozza 574dc4
+	     (result == ISC_R_NOTFOUND && !RECURSIONOK(client))) &&
Tomas Hozza 574dc4
+	    !(result == DNS_R_DELEGATION && !is_zone && RECURSIONOK(client)) &&
Tomas Hozza 612f0b
+	    (client->query.rpz_st == NULL ||
Tomas Hozza 612f0b
+	     (client->query.rpz_st->state & DNS_RPZ_REWRITTEN) == 0)&&
Adam Tkac b62a53
+	    (client->query.attributes & NS_QUERYATTR_RRL_CHECKED) == 0) {
Adam Tkac b62a53
+		dns_rdataset_t nc_rdataset;
Adam Tkac b62a53
+		isc_boolean_t wouldlog;
Adam Tkac b62a53
+		char log_buf[DNS_RRL_LOG_BUF_LEN];
Tomas Hozza 574dc4
+		isc_result_t nc_result, resp_result;
Adam Tkac b62a53
+		dns_rrl_result_t rrl_result;
Adam Tkac b62a53
+
Adam Tkac b62a53
+		client->query.attributes |= NS_QUERYATTR_RRL_CHECKED;
Adam Tkac b62a53
+
Adam Tkac b62a53
+		wouldlog = isc_log_wouldlog(ns_g_lctx, DNS_RRL_LOG_DROP);
Adam Tkac b62a53
+		tname = fname;
Adam Tkac b62a53
+		if (result == DNS_R_NXDOMAIN) {
Adam Tkac b62a53
+			/*
Adam Tkac b62a53
+			 * Use the database origin name to rate limit NXDOMAIN
Adam Tkac b62a53
+			 */
Adam Tkac b62a53
+			if (db != NULL)
Adam Tkac b62a53
+				tname = dns_db_origin(db);
Tomas Hozza 574dc4
+			resp_result = result;
Adam Tkac b62a53
+		} else if (result == DNS_R_NCACHENXDOMAIN &&
Adam Tkac b62a53
+			   rdataset != NULL &&
Adam Tkac b62a53
+			   dns_rdataset_isassociated(rdataset) &&
Adam Tkac b62a53
+			   (rdataset->attributes &
Adam Tkac b62a53
+			    DNS_RDATASETATTR_NEGATIVE) != 0) {
Adam Tkac b62a53
+			/*
Adam Tkac b62a53
+			 * Try to use owner name in the negative cache SOA.
Adam Tkac b62a53
+			 */
Adam Tkac b62a53
+			dns_fixedname_init(&fixed);
Adam Tkac b62a53
+			dns_rdataset_init(&nc_rdataset);
Adam Tkac b62a53
+			for (nc_result = dns_rdataset_first(rdataset);
Adam Tkac b62a53
+			     nc_result == ISC_R_SUCCESS;
Adam Tkac b62a53
+			     nc_result = dns_rdataset_next(rdataset)) {
Adam Tkac b62a53
+				dns_ncache_current(rdataset,
Adam Tkac b62a53
+						   dns_fixedname_name(&fixed),
Adam Tkac b62a53
+						   &nc_rdataset);
Adam Tkac b62a53
+				if (nc_rdataset.type == dns_rdatatype_soa) {
Adam Tkac b62a53
+					dns_rdataset_disassociate(&nc_rdataset);
Adam Tkac b62a53
+					tname = dns_fixedname_name(&fixed);
Adam Tkac b62a53
+					break;
Adam Tkac b62a53
+				}
Adam Tkac b62a53
+				dns_rdataset_disassociate(&nc_rdataset);
Adam Tkac b62a53
+			}
Tomas Hozza 574dc4
+			resp_result = DNS_R_NXDOMAIN;
Tomas Hozza 574dc4
+		} else if (result == DNS_R_NXRRSET ||
Tomas Hozza 574dc4
+			   result == DNS_R_EMPTYNAME) {
Tomas Hozza 574dc4
+			resp_result = DNS_R_NXRRSET;
Adam Tkac b62a53
+		} else if (result == DNS_R_DELEGATION) {
Tomas Hozza 574dc4
+			resp_result = result;
Tomas Hozza 574dc4
+		} else if (result == ISC_R_NOTFOUND) {
Tomas Hozza 574dc4
+			/*
Tomas Hozza 574dc4
+			 * Handle referral to ".", including when recursion
Tomas Hozza 574dc4
+			 * is off or not requested and the hints have not
Tomas Hozza 574dc4
+			 * been loaded or we have "additional-from-cache no".
Tomas Hozza 574dc4
+			 */
Tomas Hozza 574dc4
+			tname = dns_rootname;
Tomas Hozza 574dc4
+			resp_result = DNS_R_DELEGATION;
Adam Tkac b62a53
+		} else {
Tomas Hozza 574dc4
+			resp_result = ISC_R_SUCCESS;
Adam Tkac b62a53
+		}
Adam Tkac b62a53
+		rrl_result = dns_rrl(client->view, &client->peeraddr,
Adam Tkac b62a53
+				     ISC_TF((client->attributes
Adam Tkac b62a53
+					     & NS_CLIENTATTR_TCP) != 0),
Adam Tkac b62a53
+				     client->message->rdclass, qtype, tname,
Tomas Hozza 574dc4
+				     resp_result, client->now,
Adam Tkac b62a53
+				     wouldlog, log_buf, sizeof(log_buf));
Adam Tkac b62a53
+		if (rrl_result != DNS_RRL_RESULT_OK) {
Adam Tkac b62a53
+			/*
Adam Tkac b62a53
+			 * Log dropped or slipped responses in the query
Adam Tkac b62a53
+			 * category so that requests are not silently lost.
Adam Tkac b62a53
+			 * Starts of rate-limited bursts are logged in
Adam Tkac b62a53
+			 * DNS_LOGCATEGORY_RRL.
Adam Tkac b62a53
+			 *
Adam Tkac b62a53
+			 * Dropped responses are counted with dropped queries
Adam Tkac b62a53
+			 * in QryDropped while slipped responses are counted
Adam Tkac b62a53
+			 * with other truncated responses in RespTruncated.
Adam Tkac b62a53
+			 */
Tomas Hozza 574dc4
+			if (wouldlog) {
Tomas Hozza 574dc4
+				ns_client_log(client,
Tomas Hozza 574dc4
+					      NS_LOGCATEGORY_QUERY_EERRORS,
Tomas Hozza 574dc4
+					      NS_LOGMODULE_QUERY,
Adam Tkac b62a53
+					      DNS_RRL_LOG_DROP,
Adam Tkac b62a53
+					      "%s", log_buf);
Adam Tkac b62a53
+			}
Adam Tkac b62a53
+			if (!client->view->rrl->log_only) {
Adam Tkac b62a53
+				if (rrl_result == DNS_RRL_RESULT_DROP) {
Adam Tkac b62a53
+					/*
Adam Tkac b62a53
+					 * These will also be counted in
Adam Tkac b62a53
+					 * dns_nsstatscounter_dropped
Adam Tkac b62a53
+					 */
Adam Tkac b62a53
+					inc_stats(client,
Adam Tkac b62a53
+						dns_nsstatscounter_ratedropped);
Adam Tkac b62a53
+					QUERY_ERROR(DNS_R_DROP);
Adam Tkac b62a53
+				} else {
Adam Tkac b62a53
+					/*
Adam Tkac b62a53
+					 * These will also be counted in
Adam Tkac b62a53
+					 * dns_nsstatscounter_truncatedresp
Adam Tkac b62a53
+					 */
Adam Tkac b62a53
+					inc_stats(client,
Adam Tkac b62a53
+						dns_nsstatscounter_rateslipped);
Adam Tkac b62a53
+					client->message->flags |=
Adam Tkac b62a53
+						DNS_MESSAGEFLAG_TC;
Tomas Hozza 574dc4
+					if (resp_result == DNS_R_NXDOMAIN)
Tomas Hozza 574dc4
+						client->message->rcode =
Tomas Hozza 574dc4
+							dns_rcode_nxdomain;
Adam Tkac b62a53
+				}
Adam Tkac b62a53
+				goto cleanup;
Adam Tkac b62a53
+			}
Adam Tkac b62a53
+		}
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+
Adam Tkac b62a53
 	if (!ISC_LIST_EMPTY(client->view->rpz_zones) &&
Adam Tkac b62a53
 	    (RECURSIONOK(client) || !client->view->rpz_recursive_only) &&
Adam Tkac b62a53
 	    rpz_ck_dnssec(client, result, rdataset, sigrdataset) &&
Tomas Hozza 612f0b
@@ -7318,12 +7443,14 @@
Adam Tkac b62a53
 	}
Adam Tkac b62a53
 
Adam Tkac b62a53
 	if (eresult != ISC_R_SUCCESS &&
Adam Tkac b62a53
-	    (!PARTIALANSWER(client) || WANTRECURSION(client))) {
Adam Tkac b62a53
+	    (!PARTIALANSWER(client) || WANTRECURSION(client)
Adam Tkac b62a53
+	     || eresult == DNS_R_DROP)) {
Adam Tkac b62a53
 		if (eresult == DNS_R_DUPLICATE || eresult == DNS_R_DROP) {
Adam Tkac b62a53
 			/*
Adam Tkac b62a53
 			 * This was a duplicate query that we are
Adam Tkac b62a53
-			 * recursing on.  Don't send a response now.
Adam Tkac b62a53
-			 * The original query will still cause a response.
Adam Tkac b62a53
+			 * recursing on or the result of rate limiting.
Adam Tkac b62a53
+			 * Don't send a response now for a duplicate query,
Adam Tkac b62a53
+			 * because the original will still cause a response.
Adam Tkac b62a53
 			 */
Adam Tkac b62a53
 			query_next(client, eresult);
Adam Tkac b62a53
 		} else {
Tomas Hozza 574dc4
diff -r -u bin/named/server.c-orig bin/named/server.c
Tomas Hozza 574dc4
--- bin/named/server.c-orig	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
+++ bin/named/server.c	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
@@ -1639,6 +1639,168 @@
Tomas Hozza 574dc4
 	return (ISC_R_SUCCESS);
Adam Tkac b62a53
 }
Adam Tkac b62a53
 
Tomas Hozza 574dc4
+#define CHECK_RRL(cond, pat, val1, val2)				\
Adam Tkac b62a53
+	do {								\
Adam Tkac b62a53
+		if (!(cond)) {						\
Adam Tkac b62a53
+			cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR,	\
Adam Tkac b62a53
+				    pat, val1, val2);			\
Adam Tkac b62a53
+			result = ISC_R_RANGE;				\
Adam Tkac b62a53
+			goto cleanup;					\
Adam Tkac b62a53
+		    }							\
Adam Tkac b62a53
+	} while (0)
Adam Tkac b62a53
+
Tomas Hozza 574dc4
+#define CHECK_RRL_RATE(rate, def, max_rate, name)			\
Tomas Hozza 574dc4
+	do {								\
Tomas Hozza 574dc4
+		obj = NULL;						\
Tomas Hozza 574dc4
+		rrl->rate.str = name;					\
Tomas Hozza 574dc4
+		result = cfg_map_get(map, name, &obj);			\
Tomas Hozza 574dc4
+		if (result == ISC_R_SUCCESS) {				\
Tomas Hozza 574dc4
+			rrl->rate.r = cfg_obj_asuint32(obj);		\
Tomas Hozza 574dc4
+			CHECK_RRL(rrl->rate.r <= max_rate,		\
Tomas Hozza 574dc4
+				  name" %d > %d",			\
Tomas Hozza 574dc4
+				  rrl->rate.r, max_rate);		\
Tomas Hozza 574dc4
+		} else {						\
Tomas Hozza 574dc4
+			rrl->rate.r = def;				\
Tomas Hozza 574dc4
+		}							\
Tomas Hozza 574dc4
+		rrl->rate.scaled = rrl->rate.r;				\
Tomas Hozza 574dc4
+	} while (0)
Tomas Hozza 574dc4
+
Adam Tkac b62a53
+static isc_result_t
Adam Tkac b62a53
+configure_rrl(dns_view_t *view, const cfg_obj_t *config, const cfg_obj_t *map) {
Adam Tkac b62a53
+	const cfg_obj_t *obj;
Adam Tkac b62a53
+	dns_rrl_t *rrl;
Adam Tkac b62a53
+	isc_result_t result;
Adam Tkac b62a53
+ 	int min_entries, i, j;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	/*
Adam Tkac b62a53
+	 * Most DNS servers have few clients, but intentinally open
Adam Tkac b62a53
+	 * recursive and authoritative servers often have many.
Adam Tkac b62a53
+	 * So start with a small number of entries unless told otherwise
Adam Tkac b62a53
+	 * to reduce cold-start costs.
Adam Tkac b62a53
+	 */
Adam Tkac b62a53
+	min_entries = 500;
Adam Tkac b62a53
+	obj = NULL;
Adam Tkac b62a53
+	result = cfg_map_get(map, "min-table-size", &obj);
Adam Tkac b62a53
+	if (result == ISC_R_SUCCESS) {
Adam Tkac b62a53
+		min_entries = cfg_obj_asuint32(obj);
Adam Tkac b62a53
+		if (min_entries < 1)
Adam Tkac b62a53
+			min_entries = 1;
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+	result = dns_rrl_init(&rrl, view, min_entries);
Adam Tkac b62a53
+	if (result != ISC_R_SUCCESS)
Adam Tkac b62a53
+		return (result);
Adam Tkac b62a53
+
Adam Tkac b62a53
+	i = ISC_MAX(20000, min_entries);
Adam Tkac b62a53
+	obj = NULL;
Adam Tkac b62a53
+	result = cfg_map_get(map, "max-table-size", &obj);
Adam Tkac b62a53
+	if (result == ISC_R_SUCCESS) {
Adam Tkac b62a53
+		i = cfg_obj_asuint32(obj);
Tomas Hozza 574dc4
+		CHECK_RRL(i >= min_entries,
Adam Tkac b62a53
+			  "max-table-size %d < min-table-size %d",
Adam Tkac b62a53
+			  i, min_entries);
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+	rrl->max_entries = i;
Adam Tkac b62a53
+
Tomas Hozza 574dc4
+	CHECK_RRL_RATE(responses_per_second, 0, DNS_RRL_MAX_RATE,
Tomas Hozza 574dc4
+		       "responses-per-second");
Tomas Hozza 574dc4
+	CHECK_RRL_RATE(referrals_per_second,
Tomas Hozza 574dc4
+		       rrl->responses_per_second.r, DNS_RRL_MAX_RATE,
Tomas Hozza 574dc4
+		       "referrals-per-second");
Tomas Hozza 574dc4
+	CHECK_RRL_RATE(nodata_per_second,
Tomas Hozza 574dc4
+		       rrl->responses_per_second.r, DNS_RRL_MAX_RATE,
Tomas Hozza 574dc4
+		       "nodata-per-second");
Tomas Hozza 574dc4
+	CHECK_RRL_RATE(nxdomains_per_second,
Tomas Hozza 574dc4
+		       rrl->responses_per_second.r, DNS_RRL_MAX_RATE,
Tomas Hozza 574dc4
+		       "nxdomains-per-second");
Tomas Hozza 574dc4
+	CHECK_RRL_RATE(errors_per_second,
Tomas Hozza 574dc4
+		       rrl->responses_per_second.r, DNS_RRL_MAX_RATE,
Tomas Hozza 574dc4
+		       "errors-per-second");
Tomas Hozza 574dc4
+
Tomas Hozza 574dc4
+	CHECK_RRL_RATE(all_per_second, 0, DNS_RRL_MAX_RATE,
Tomas Hozza 574dc4
+		       "all-per-second");
Tomas Hozza 574dc4
+
Tomas Hozza 574dc4
+	CHECK_RRL_RATE(slip, 2, DNS_RRL_MAX_SLIP,
Tomas Hozza 574dc4
+		       "slip");
Adam Tkac b62a53
+
Adam Tkac b62a53
+	i = 15;
Adam Tkac b62a53
+	obj = NULL;
Adam Tkac b62a53
+	result = cfg_map_get(map, "window", &obj);
Adam Tkac b62a53
+	if (result == ISC_R_SUCCESS) {
Adam Tkac b62a53
+		i = cfg_obj_asuint32(obj);
Tomas Hozza 574dc4
+		CHECK_RRL(i >= 1 && i <= DNS_RRL_MAX_WINDOW,
Adam Tkac b62a53
+			  "window %d < 1 or > %d", i, DNS_RRL_MAX_WINDOW);
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+	rrl->window = i;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	i = 0;
Adam Tkac b62a53
+	obj = NULL;
Adam Tkac b62a53
+	result = cfg_map_get(map, "qps-scale", &obj);
Adam Tkac b62a53
+	if (result == ISC_R_SUCCESS) {
Adam Tkac b62a53
+		i = cfg_obj_asuint32(obj);
Tomas Hozza 574dc4
+		CHECK_RRL(i >= 1, "invalid 'qps-scale %d'%s", i, "");
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+	rrl->qps_scale = i;
Adam Tkac b62a53
+	rrl->qps = 1.0;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	i = 24;
Adam Tkac b62a53
+	obj = NULL;
Tomas Hozza 574dc4
+	result = cfg_map_get(map, "ipv4-prefix-length", &obj);
Adam Tkac b62a53
+	if (result == ISC_R_SUCCESS) {
Adam Tkac b62a53
+		i = cfg_obj_asuint32(obj);
Tomas Hozza 574dc4
+		CHECK_RRL(i >= 8 && i <= 32,
Tomas Hozza 574dc4
+			  "invalid 'ipv4-prefix-length %d'%s", i, "");
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+	rrl->ipv4_prefixlen = i;
Adam Tkac b62a53
+	if (i == 32)
Adam Tkac b62a53
+		rrl->ipv4_mask = 0xffffffff;
Adam Tkac b62a53
+	else
Adam Tkac b62a53
+		rrl->ipv4_mask = htonl(0xffffffff << (32-i));
Adam Tkac b62a53
+
Adam Tkac b62a53
+	i = 56;
Adam Tkac b62a53
+	obj = NULL;
Tomas Hozza 574dc4
+	result = cfg_map_get(map, "ipv6-prefix-length", &obj);
Adam Tkac b62a53
+	if (result == ISC_R_SUCCESS) {
Adam Tkac b62a53
+		i = cfg_obj_asuint32(obj);
Tomas Hozza 574dc4
+		CHECK_RRL(i >= 16 && i <= DNS_RRL_MAX_PREFIX,
Tomas Hozza 574dc4
+			  "ipv6-prefix-length %d < 16 or > %d",
Adam Tkac b62a53
+			  i, DNS_RRL_MAX_PREFIX);
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+	rrl->ipv6_prefixlen = i;
Adam Tkac b62a53
+	for (j = 0; j < 4; ++j) {
Adam Tkac b62a53
+		if (i <= 0) {
Adam Tkac b62a53
+			rrl->ipv6_mask[j] = 0;
Adam Tkac b62a53
+		} else if (i < 32) {
Adam Tkac b62a53
+			rrl->ipv6_mask[j] = htonl(0xffffffff << (32-i));
Adam Tkac b62a53
+		} else {
Adam Tkac b62a53
+			rrl->ipv6_mask[j] = 0xffffffff;
Adam Tkac b62a53
+		}
Adam Tkac b62a53
+		i -= 32;
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+
Adam Tkac b62a53
+	obj = NULL;
Adam Tkac b62a53
+	result = cfg_map_get(map, "exempt-clients", &obj);
Adam Tkac b62a53
+	if (result == ISC_R_SUCCESS) {
Adam Tkac b62a53
+		result = cfg_acl_fromconfig(obj, config, ns_g_lctx,
Adam Tkac b62a53
+					    ns_g_aclconfctx, ns_g_mctx,
Adam Tkac b62a53
+					    0, &rrl->exempt);
Tomas Hozza 574dc4
+		CHECK_RRL(result == ISC_R_SUCCESS,
Adam Tkac b62a53
+			  "invalid %s%s", "address match list", "");
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+
Adam Tkac b62a53
+	obj = NULL;
Adam Tkac b62a53
+	result = cfg_map_get(map, "log-only", &obj);
Adam Tkac b62a53
+	if (result == ISC_R_SUCCESS && cfg_obj_asboolean(obj))
Adam Tkac b62a53
+		rrl->log_only = ISC_TRUE;
Adam Tkac b62a53
+	else
Adam Tkac b62a53
+		rrl->log_only = ISC_FALSE;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	return (ISC_R_SUCCESS);
Adam Tkac b62a53
+
Adam Tkac b62a53
+ cleanup:
Adam Tkac b62a53
+	dns_rrl_view_destroy(view);
Adam Tkac b62a53
+	return (result);
Adam Tkac b62a53
+}
Adam Tkac b62a53
+
Adam Tkac b62a53
 /*
Adam Tkac b62a53
  * Configure 'view' according to 'vconfig', taking defaults from 'config'
Adam Tkac b62a53
  * where values are missing in 'vconfig'.
Tomas Hozza 574dc4
@@ -3043,6 +3205,14 @@
Adam Tkac b62a53
 		}
Adam Tkac b62a53
 	}
Adam Tkac b62a53
 
Adam Tkac b62a53
+	obj = NULL;
Adam Tkac b62a53
+	result = ns_config_get(maps, "rate-limit", &obj);
Adam Tkac b62a53
+	if (result == ISC_R_SUCCESS) {
Adam Tkac b62a53
+		result = configure_rrl(view, config, obj);
Adam Tkac b62a53
+		if (result != ISC_R_SUCCESS)
Adam Tkac b62a53
+			goto cleanup;
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+
Adam Tkac b62a53
 	result = ISC_R_SUCCESS;
Adam Tkac b62a53
 
Adam Tkac b62a53
  cleanup:
Tomas Hozza 574dc4
diff -r -u bin/named/statschannel.c-orig bin/named/statschannel.c
Tomas Hozza 574dc4
--- bin/named/statschannel.c-orig	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
+++ bin/named/statschannel.c	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
@@ -206,6 +206,10 @@
Adam Tkac b62a53
 	SET_NSSTATDESC(updatebadprereq,
Adam Tkac b62a53
 		       "updates rejected due to prerequisite failure",
Adam Tkac b62a53
 		       "UpdateBadPrereq");
Adam Tkac b62a53
+	SET_NSSTATDESC(ratedropped, "responses dropped for rate limits",
Adam Tkac b62a53
+		       "RateDropped");
Adam Tkac b62a53
+	SET_NSSTATDESC(rateslipped, "responses truncated for rate limits",
Adam Tkac b62a53
+		       "RateSlipped");
Tomas Hozza 574dc4
 	SET_NSSTATDESC(rpz_rewrites, "response policy zone rewrites",
Tomas Hozza 574dc4
 		       "RPZRewrites");
Adam Tkac b62a53
 	INSIST(i == dns_nsstatscounter_max);
Tomas Hozza 574dc4
diff -r -u bin/tests/system/README-orig bin/tests/system/README
Tomas Hozza 574dc4
--- bin/tests/system/README-orig	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
+++ bin/tests/system/README	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
@@ -17,6 +17,7 @@
Tomas Hozza 35a4e4
   nsupdate/	Dynamic update and IXFR tests
Tomas Hozza 35a4e4
   resolver/     Regression tests for resolver bugs that have been fixed
Tomas Hozza 35a4e4
 		(not a complete resolver test suite)
Tomas Hozza 35a4e4
+  rrl/		query rate limiting
Tomas Hozza 35a4e4
   rpz/		Tests of response policy zone (RPZ) rewriting
Tomas Hozza 35a4e4
   stub/		Tests of stub zone functionality
Tomas Hozza 35a4e4
   unknown/	Unknown type and class tests
Tomas Hozza 574dc4
diff -r -u bin/tests/system/conf.sh.in-orig bin/tests/system/conf.sh.in
Tomas Hozza 574dc4
--- bin/tests/system/conf.sh.in-orig	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
+++ bin/tests/system/conf.sh.in	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
@@ -62,7 +62,7 @@
Tomas Hozza 574dc4
          database dlv dlvauto dlz dlzexternal dname dns64 dnssec ecdsa
Tomas Hozza 574dc4
          formerr forward glue gost ixfr inline limits logfileconfig
Tomas Hozza 574dc4
          lwresd masterfile masterformat metadata notify nsupdate pending
Tomas Hozza 574dc4
-	 pkcs11 redirect resolver rndc rpz rrsetorder rsabigexponent
Tomas Hozza 574dc4
+	 pkcs11 redirect resolver rndc rpz rrl rrsetorder rsabigexponent
Tomas Hozza 574dc4
 	 smartsign sortlist spf staticstub stub tkey tsig tsiggss unknown
Tomas Hozza 574dc4
 	 upforwd verify views wildcard xfer xferquota zonechecks"
Tomas Hozza 574dc4
 
Tomas Hozza 574dc4
diff -r -u bin/tests/system/rrl/clean.sh-orig bin/tests/system/rrl/clean.sh
Tomas Hozza 574dc4
--- bin/tests/system/rrl/clean.sh-orig	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
+++ bin/tests/system/rrl/clean.sh	2004-01-01 00:00:00.000000000 +0000
Adam Tkac b62a53
@@ -0,0 +1,21 @@
Tomas Hozza 574dc4
+# Copyright (C) 2012, 2013  Internet Systems Consortium, Inc. ("ISC")
Adam Tkac b62a53
+#
Adam Tkac b62a53
+# Permission to use, copy, modify, and/or distribute this software for any
Adam Tkac b62a53
+# purpose with or without fee is hereby granted, provided that the above
Adam Tkac b62a53
+# copyright notice and this permission notice appear in all copies.
Adam Tkac b62a53
+#
Adam Tkac b62a53
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
Adam Tkac b62a53
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
Adam Tkac b62a53
+# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
Adam Tkac b62a53
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
Adam Tkac b62a53
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
Adam Tkac b62a53
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
Adam Tkac b62a53
+# PERFORMANCE OF THIS SOFTWARE.
Adam Tkac b62a53
+
Adam Tkac b62a53
+
Adam Tkac b62a53
+
Adam Tkac b62a53
+# Clean up after rrl tests.
Adam Tkac b62a53
+
Adam Tkac b62a53
+rm -f dig.out*
Tomas Hozza 574dc4
+rm -f  */named.memstats */named.run */named.stats */log-* */session.key
Adam Tkac b62a53
+rm -f ns3/bl*.db */*.jnl */*.core */*.pid
Tomas Hozza 574dc4
diff -r -u bin/tests/system/rrl/ns1/named.conf-orig bin/tests/system/rrl/ns1/named.conf
Tomas Hozza 574dc4
--- bin/tests/system/rrl/ns1/named.conf-orig	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
+++ bin/tests/system/rrl/ns1/named.conf	2004-01-01 00:00:00.000000000 +0000
Adam Tkac b62a53
@@ -0,0 +1,32 @@
Adam Tkac b62a53
+/*
Tomas Hozza 574dc4
+ * Copyright (C) 2012, 2013  Internet Systems Consortium, Inc. ("ISC")
Adam Tkac b62a53
+ *
Adam Tkac b62a53
+ * Permission to use, copy, modify, and/or distribute this software for any
Adam Tkac b62a53
+ * purpose with or without fee is hereby granted, provided that the above
Adam Tkac b62a53
+ * copyright notice and this permission notice appear in all copies.
Adam Tkac b62a53
+ *
Adam Tkac b62a53
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
Adam Tkac b62a53
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
Adam Tkac b62a53
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
Adam Tkac b62a53
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
Adam Tkac b62a53
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
Adam Tkac b62a53
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
Adam Tkac b62a53
+ * PERFORMANCE OF THIS SOFTWARE.
Adam Tkac b62a53
+ */
Adam Tkac b62a53
+
Adam Tkac b62a53
+
Adam Tkac b62a53
+controls { /* empty */ };
Adam Tkac b62a53
+
Adam Tkac b62a53
+options {
Adam Tkac b62a53
+	query-source address 10.53.0.1;
Adam Tkac b62a53
+	notify-source 10.53.0.1;
Adam Tkac b62a53
+	transfer-source 10.53.0.1;
Adam Tkac b62a53
+	port 5300;
Adam Tkac b62a53
+	session-keyfile "session.key";
Adam Tkac b62a53
+	pid-file "named.pid";
Adam Tkac b62a53
+	listen-on { 10.53.0.1; };
Adam Tkac b62a53
+	listen-on-v6 { none; };
Adam Tkac b62a53
+	notify no;
Adam Tkac b62a53
+};
Adam Tkac b62a53
+
Adam Tkac b62a53
+zone "." {type master; file "root.db";};
Tomas Hozza 574dc4
diff -r -u bin/tests/system/rrl/ns1/root.db-orig bin/tests/system/rrl/ns1/root.db
Tomas Hozza 574dc4
--- bin/tests/system/rrl/ns1/root.db-orig	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
+++ bin/tests/system/rrl/ns1/root.db	2004-01-01 00:00:00.000000000 +0000
Adam Tkac b62a53
@@ -0,0 +1,31 @@
Tomas Hozza 574dc4
+; Copyright (C) 2012, 2013  Internet Systems Consortium, Inc. ("ISC")
Adam Tkac b62a53
+;
Adam Tkac b62a53
+; Permission to use, copy, modify, and/or distribute this software for any
Adam Tkac b62a53
+; purpose with or without fee is hereby granted, provided that the above
Adam Tkac b62a53
+; copyright notice and this permission notice appear in all copies.
Adam Tkac b62a53
+;
Adam Tkac b62a53
+; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
Adam Tkac b62a53
+; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
Adam Tkac b62a53
+; AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
Adam Tkac b62a53
+; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
Adam Tkac b62a53
+; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
Adam Tkac b62a53
+; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
Adam Tkac b62a53
+; PERFORMANCE OF THIS SOFTWARE.
Adam Tkac b62a53
+
Adam Tkac b62a53
+
Adam Tkac b62a53
+$TTL	120
Adam Tkac b62a53
+@		SOA	ns. hostmaster.ns. ( 1 3600 1200 604800 60 )
Adam Tkac b62a53
+@		NS	ns.
Adam Tkac b62a53
+ns.		A	10.53.0.1
Adam Tkac b62a53
+.		A	10.53.0.1
Adam Tkac b62a53
+
Adam Tkac b62a53
+; limit responses from here
Adam Tkac b62a53
+tld2.		NS	ns.tld2.
Adam Tkac b62a53
+ns.tld2.	A	10.53.0.2
Adam Tkac b62a53
+
Adam Tkac b62a53
+; limit recursion to here
Adam Tkac b62a53
+tld3.		NS	ns.tld3.
Adam Tkac b62a53
+ns.tld3.	A	10.53.0.3
Adam Tkac b62a53
+
Adam Tkac b62a53
+; generate SERVFAIL
Adam Tkac b62a53
+tld4.		NS	ns.tld3.
Tomas Hozza 574dc4
diff -r -u bin/tests/system/rrl/ns2/hints-orig bin/tests/system/rrl/ns2/hints
Tomas Hozza 574dc4
--- bin/tests/system/rrl/ns2/hints-orig	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
+++ bin/tests/system/rrl/ns2/hints	2004-01-01 00:00:00.000000000 +0000
Adam Tkac b62a53
@@ -0,0 +1,18 @@
Tomas Hozza 574dc4
+; Copyright (C) 2012, 2013  Internet Systems Consortium, Inc. ("ISC")
Adam Tkac b62a53
+;
Adam Tkac b62a53
+; Permission to use, copy, modify, and/or distribute this software for any
Adam Tkac b62a53
+; purpose with or without fee is hereby granted, provided that the above
Adam Tkac b62a53
+; copyright notice and this permission notice appear in all copies.
Adam Tkac b62a53
+;
Adam Tkac b62a53
+; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
Adam Tkac b62a53
+; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
Adam Tkac b62a53
+; AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
Adam Tkac b62a53
+; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
Adam Tkac b62a53
+; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
Adam Tkac b62a53
+; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
Adam Tkac b62a53
+; PERFORMANCE OF THIS SOFTWARE.
Adam Tkac b62a53
+
Adam Tkac b62a53
+
Adam Tkac b62a53
+
Adam Tkac b62a53
+.	0	NS	ns1.
Adam Tkac b62a53
+ns1.	0	A	10.53.0.1
Tomas Hozza 574dc4
diff -r -u bin/tests/system/rrl/ns2/named.conf-orig bin/tests/system/rrl/ns2/named.conf
Tomas Hozza 574dc4
--- bin/tests/system/rrl/ns2/named.conf-orig	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
+++ bin/tests/system/rrl/ns2/named.conf	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
@@ -0,0 +1,71 @@
Adam Tkac b62a53
+/*
Tomas Hozza 574dc4
+ * Copyright (C) 2012, 2013  Internet Systems Consortium, Inc. ("ISC")
Adam Tkac b62a53
+ *
Adam Tkac b62a53
+ * Permission to use, copy, modify, and/or distribute this software for any
Adam Tkac b62a53
+ * purpose with or without fee is hereby granted, provided that the above
Adam Tkac b62a53
+ * copyright notice and this permission notice appear in all copies.
Adam Tkac b62a53
+ *
Adam Tkac b62a53
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
Adam Tkac b62a53
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
Adam Tkac b62a53
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
Adam Tkac b62a53
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
Adam Tkac b62a53
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
Adam Tkac b62a53
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
Adam Tkac b62a53
+ * PERFORMANCE OF THIS SOFTWARE.
Adam Tkac b62a53
+ */
Adam Tkac b62a53
+
Adam Tkac b62a53
+
Adam Tkac b62a53
+controls { /* empty */ };
Adam Tkac b62a53
+
Adam Tkac b62a53
+options {
Adam Tkac b62a53
+	query-source address 10.53.0.2;
Adam Tkac b62a53
+	notify-source 10.53.0.2;
Adam Tkac b62a53
+	transfer-source 10.53.0.2;
Adam Tkac b62a53
+	port 5300;
Adam Tkac b62a53
+	session-keyfile "session.key";
Adam Tkac b62a53
+	pid-file "named.pid";
Adam Tkac b62a53
+	statistics-file	"named.stats";
Adam Tkac b62a53
+	listen-on { 10.53.0.2; };
Adam Tkac b62a53
+	listen-on-v6 { none; };
Adam Tkac b62a53
+	notify no;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	rate-limit {
Adam Tkac b62a53
+	    responses-per-second 2;
Tomas Hozza 574dc4
+	    all-per-second 50;
Adam Tkac b62a53
+	    slip 3;
Adam Tkac b62a53
+	    exempt-clients { 10.53.0.7; };
Tomas Hozza 574dc4
+
Tomas Hozza 574dc4
+	    // small enough to force a table expansion
Tomas Hozza 574dc4
+	    min-table-size 75;
Adam Tkac b62a53
+	};
Tomas Hozza 574dc4
+
Tomas Hozza 574dc4
+	additional-from-cache no;
Adam Tkac b62a53
+};
Adam Tkac b62a53
+
Adam Tkac b62a53
+key rndc_key {
Adam Tkac b62a53
+	secret "1234abcd8765";
Adam Tkac b62a53
+	algorithm hmac-md5;
Adam Tkac b62a53
+};
Adam Tkac b62a53
+controls {
Adam Tkac b62a53
+	inet 10.53.0.2 port 9953 allow { any; } keys { rndc_key; };
Adam Tkac b62a53
+};
Adam Tkac b62a53
+
Adam Tkac b62a53
+/*
Adam Tkac b62a53
+ * These log settings have no effect unless "-g" is removed from ../../start.pl
Adam Tkac b62a53
+ */
Adam Tkac b62a53
+logging {
Adam Tkac b62a53
+	channel debug {
Adam Tkac b62a53
+	    file "log-debug";
Adam Tkac b62a53
+	    print-category yes; print-severity yes; severity debug 10;
Adam Tkac b62a53
+	};
Adam Tkac b62a53
+	channel queries {
Adam Tkac b62a53
+	    file "log-queries";
Adam Tkac b62a53
+	    print-category yes; print-severity yes; severity info;
Adam Tkac b62a53
+	};
Adam Tkac b62a53
+	category rate-limit { debug; queries; };
Adam Tkac b62a53
+	category queries { debug; queries; };
Adam Tkac b62a53
+};
Adam Tkac b62a53
+
Adam Tkac b62a53
+zone "." { type hint; file "hints"; };
Adam Tkac b62a53
+
Adam Tkac b62a53
+zone "tld2."{ type master; file "tld2.db"; };
Tomas Hozza 574dc4
diff -r -u bin/tests/system/rrl/ns2/tld2.db-orig bin/tests/system/rrl/ns2/tld2.db
Tomas Hozza 574dc4
--- bin/tests/system/rrl/ns2/tld2.db-orig	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
+++ bin/tests/system/rrl/ns2/tld2.db	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
@@ -0,0 +1,47 @@
Tomas Hozza 574dc4
+; Copyright (C) 2012, 2013  Internet Systems Consortium, Inc. ("ISC")
Adam Tkac b62a53
+;
Adam Tkac b62a53
+; Permission to use, copy, modify, and/or distribute this software for any
Adam Tkac b62a53
+; purpose with or without fee is hereby granted, provided that the above
Adam Tkac b62a53
+; copyright notice and this permission notice appear in all copies.
Adam Tkac b62a53
+;
Adam Tkac b62a53
+; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
Adam Tkac b62a53
+; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
Adam Tkac b62a53
+; AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
Adam Tkac b62a53
+; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
Adam Tkac b62a53
+; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
Adam Tkac b62a53
+; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
Adam Tkac b62a53
+; PERFORMANCE OF THIS SOFTWARE.
Adam Tkac b62a53
+
Adam Tkac b62a53
+
Adam Tkac b62a53
+
Adam Tkac b62a53
+; rate limit response from this zone
Adam Tkac b62a53
+
Adam Tkac b62a53
+$TTL	120
Adam Tkac b62a53
+@		SOA	tld2.  hostmaster.ns.tld2. ( 1 3600 1200 604800 60 )
Adam Tkac b62a53
+		NS	ns
Adam Tkac b62a53
+		NS	.
Adam Tkac b62a53
+ns		A	10.53.0.2
Adam Tkac b62a53
+
Tomas Hozza 574dc4
+; basic rate limiting
Tomas Hozza 574dc4
+a1		A	192.0.2.1
Adam Tkac b62a53
+
Tomas Hozza 574dc4
+; wildcards
Tomas Hozza 574dc4
+*.a2		A	192.0.2.2
Adam Tkac b62a53
+
Adam Tkac b62a53
+; a3 is in tld3
Adam Tkac b62a53
+
Adam Tkac b62a53
+; a4 does not exist to give NXDOMAIN
Adam Tkac b62a53
+
Adam Tkac b62a53
+; a5 for TCP requests
Tomas Hozza 574dc4
+a5		A	192.0.2.5
Adam Tkac b62a53
+
Adam Tkac b62a53
+; a6 for whitelisted clients
Tomas Hozza 574dc4
+a6		A	192.0.2.6
Adam Tkac b62a53
+
Adam Tkac b62a53
+; a7 for SERVFAIL
Adam Tkac b62a53
+
Tomas Hozza 574dc4
+; a8 for NODATA
Tomas Hozza 574dc4
+a8		A	192.0.2.8
Tomas Hozza 574dc4
+
Tomas Hozza 574dc4
+; a9 for all-per-second limit
Tomas Hozza 574dc4
+$GENERATE 101-180 all$.a9 A 192.0.2.8
Tomas Hozza 574dc4
diff -r -u bin/tests/system/rrl/ns3/hints-orig bin/tests/system/rrl/ns3/hints
Tomas Hozza 574dc4
--- bin/tests/system/rrl/ns3/hints-orig	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
+++ bin/tests/system/rrl/ns3/hints	2004-01-01 00:00:00.000000000 +0000
Adam Tkac b62a53
@@ -0,0 +1,18 @@
Tomas Hozza 574dc4
+; Copyright (C) 2012, 2013  Internet Systems Consortium, Inc. ("ISC")
Adam Tkac b62a53
+;
Adam Tkac b62a53
+; Permission to use, copy, modify, and/or distribute this software for any
Adam Tkac b62a53
+; purpose with or without fee is hereby granted, provided that the above
Adam Tkac b62a53
+; copyright notice and this permission notice appear in all copies.
Adam Tkac b62a53
+;
Adam Tkac b62a53
+; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
Adam Tkac b62a53
+; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
Adam Tkac b62a53
+; AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
Adam Tkac b62a53
+; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
Adam Tkac b62a53
+; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
Adam Tkac b62a53
+; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
Adam Tkac b62a53
+; PERFORMANCE OF THIS SOFTWARE.
Adam Tkac b62a53
+
Adam Tkac b62a53
+
Adam Tkac b62a53
+
Adam Tkac b62a53
+.	0	NS	ns1.
Adam Tkac b62a53
+ns1.	0	A	10.53.0.1
Tomas Hozza 574dc4
diff -r -u bin/tests/system/rrl/ns3/named.conf-orig bin/tests/system/rrl/ns3/named.conf
Tomas Hozza 574dc4
--- bin/tests/system/rrl/ns3/named.conf-orig	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
+++ bin/tests/system/rrl/ns3/named.conf	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
@@ -0,0 +1,50 @@
Adam Tkac b62a53
+/*
Tomas Hozza 574dc4
+ * Copyright (C) 2012, 2013  Internet Systems Consortium, Inc. ("ISC")
Adam Tkac b62a53
+ *
Adam Tkac b62a53
+ * Permission to use, copy, modify, and/or distribute this software for any
Adam Tkac b62a53
+ * purpose with or without fee is hereby granted, provided that the above
Adam Tkac b62a53
+ * copyright notice and this permission notice appear in all copies.
Adam Tkac b62a53
+ *
Adam Tkac b62a53
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
Adam Tkac b62a53
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
Adam Tkac b62a53
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
Adam Tkac b62a53
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
Adam Tkac b62a53
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
Adam Tkac b62a53
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
Adam Tkac b62a53
+ * PERFORMANCE OF THIS SOFTWARE.
Adam Tkac b62a53
+ */
Adam Tkac b62a53
+
Adam Tkac b62a53
+
Adam Tkac b62a53
+controls { /* empty */ };
Adam Tkac b62a53
+
Adam Tkac b62a53
+options {
Adam Tkac b62a53
+	query-source address 10.53.0.3;
Adam Tkac b62a53
+	notify-source 10.53.0.3;
Adam Tkac b62a53
+	transfer-source 10.53.0.3;
Adam Tkac b62a53
+	port 5300;
Adam Tkac b62a53
+	session-keyfile "session.key";
Adam Tkac b62a53
+	pid-file "named.pid";
Adam Tkac b62a53
+	listen-on { 10.53.0.3; };
Adam Tkac b62a53
+	listen-on-v6 { none; };
Adam Tkac b62a53
+	notify no;
Tomas Hozza 574dc4
+
Tomas Hozza 574dc4
+	// check that all of the options are parsed without limiting anything
Tomas Hozza 574dc4
+	rate-limit {
Tomas Hozza 574dc4
+	    responses-per-second 200;
Tomas Hozza 574dc4
+	    referrals-per-second 220;
Tomas Hozza 574dc4
+	    nodata-per-second 230;
Tomas Hozza 574dc4
+	    nxdomains-per-second 240;
Tomas Hozza 574dc4
+	    errors-per-second 250;
Tomas Hozza 574dc4
+	    all-per-second 700;
Tomas Hozza 574dc4
+	    ipv4-prefix-length 24;
Tomas Hozza 574dc4
+	    ipv6-prefix-length 64;
Tomas Hozza 574dc4
+	    qps-scale 10;
Tomas Hozza 574dc4
+	    window 1;
Tomas Hozza 574dc4
+	    max-table-size 1000;
Tomas Hozza 574dc4
+	};
Tomas Hozza 574dc4
+
Adam Tkac b62a53
+};
Adam Tkac b62a53
+
Adam Tkac b62a53
+zone "." { type hint; file "hints"; };
Adam Tkac b62a53
+
Adam Tkac b62a53
+zone "tld3."{ type master; file "tld3.db"; };
Tomas Hozza 574dc4
diff -r -u bin/tests/system/rrl/ns3/tld3.db-orig bin/tests/system/rrl/ns3/tld3.db
Tomas Hozza 574dc4
--- bin/tests/system/rrl/ns3/tld3.db-orig	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
+++ bin/tests/system/rrl/ns3/tld3.db	2004-01-01 00:00:00.000000000 +0000
Adam Tkac b62a53
@@ -0,0 +1,25 @@
Tomas Hozza 574dc4
+; Copyright (C) 2012, 2013  Internet Systems Consortium, Inc. ("ISC")
Adam Tkac b62a53
+;
Adam Tkac b62a53
+; Permission to use, copy, modify, and/or distribute this software for any
Adam Tkac b62a53
+; purpose with or without fee is hereby granted, provided that the above
Adam Tkac b62a53
+; copyright notice and this permission notice appear in all copies.
Adam Tkac b62a53
+;
Adam Tkac b62a53
+; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
Adam Tkac b62a53
+; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
Adam Tkac b62a53
+; AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
Adam Tkac b62a53
+; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
Adam Tkac b62a53
+; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
Adam Tkac b62a53
+; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
Adam Tkac b62a53
+; PERFORMANCE OF THIS SOFTWARE.
Adam Tkac b62a53
+
Adam Tkac b62a53
+
Adam Tkac b62a53
+
Adam Tkac b62a53
+; rate limit response from this zone
Adam Tkac b62a53
+
Adam Tkac b62a53
+$TTL	120
Adam Tkac b62a53
+@		SOA	tld3.  hostmaster.ns.tld3. ( 1 3600 1200 604800 60 )
Adam Tkac b62a53
+		NS	ns
Adam Tkac b62a53
+		NS	.
Adam Tkac b62a53
+ns		A	10.53.0.3
Adam Tkac b62a53
+
Tomas Hozza 574dc4
+*.a3		A	192.0.3.3
Tomas Hozza 574dc4
diff -r -u bin/tests/system/rrl/setup.sh-orig bin/tests/system/rrl/setup.sh
Tomas Hozza 574dc4
--- bin/tests/system/rrl/setup.sh-orig	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
+++ bin/tests/system/rrl/setup.sh	2004-01-01 00:00:00.000000000 +0000
Adam Tkac b62a53
@@ -0,0 +1,21 @@
Adam Tkac b62a53
+#!/bin/sh
Adam Tkac b62a53
+#
Tomas Hozza 574dc4
+# Copyright (C) 2012, 2013  Internet Systems Consortium, Inc. ("ISC")
Adam Tkac b62a53
+#
Adam Tkac b62a53
+# Permission to use, copy, modify, and/or distribute this software for any
Adam Tkac b62a53
+# purpose with or without fee is hereby granted, provided that the above
Adam Tkac b62a53
+# copyright notice and this permission notice appear in all copies.
Adam Tkac b62a53
+#
Adam Tkac b62a53
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
Adam Tkac b62a53
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
Adam Tkac b62a53
+# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
Adam Tkac b62a53
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
Adam Tkac b62a53
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
Adam Tkac b62a53
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
Adam Tkac b62a53
+# PERFORMANCE OF THIS SOFTWARE.
Adam Tkac b62a53
+
Adam Tkac b62a53
+
Adam Tkac b62a53
+SYSTEMTESTTOP=..
Adam Tkac b62a53
+. $SYSTEMTESTTOP/conf.sh
Adam Tkac b62a53
+. ./clean.sh
Adam Tkac b62a53
+
Tomas Hozza 574dc4
diff -r -u bin/tests/system/rrl/tests.sh-orig bin/tests/system/rrl/tests.sh
Tomas Hozza 574dc4
--- bin/tests/system/rrl/tests.sh-orig	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
+++ bin/tests/system/rrl/tests.sh	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
@@ -0,0 +1,258 @@
Tomas Hozza 574dc4
+# Copyright (C) 2012, 2013  Internet Systems Consortium, Inc. ("ISC")
Adam Tkac b62a53
+#
Adam Tkac b62a53
+# Permission to use, copy, modify, and/or distribute this software for any
Adam Tkac b62a53
+# purpose with or without fee is hereby granted, provided that the above
Adam Tkac b62a53
+# copyright notice and this permission notice appear in all copies.
Adam Tkac b62a53
+#
Adam Tkac b62a53
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
Adam Tkac b62a53
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
Adam Tkac b62a53
+# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
Adam Tkac b62a53
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
Adam Tkac b62a53
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
Adam Tkac b62a53
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
Adam Tkac b62a53
+# PERFORMANCE OF THIS SOFTWARE.
Adam Tkac b62a53
+
Adam Tkac b62a53
+
Adam Tkac b62a53
+# test response rate limiting
Adam Tkac b62a53
+
Adam Tkac b62a53
+SYSTEMTESTTOP=..
Adam Tkac b62a53
+. $SYSTEMTESTTOP/conf.sh
Adam Tkac b62a53
+
Adam Tkac b62a53
+#set -x
Adam Tkac b62a53
+
Adam Tkac b62a53
+ns1=10.53.0.1			    # root, defining the others
Adam Tkac b62a53
+ns2=10.53.0.2			    # test server
Adam Tkac b62a53
+ns3=10.53.0.3			    # secondary test server
Adam Tkac b62a53
+ns7=10.53.0.7			    # whitelisted client
Adam Tkac b62a53
+
Adam Tkac b62a53
+USAGE="$0: [-x]"
Adam Tkac b62a53
+while getopts "x" c; do
Adam Tkac b62a53
+    case $c in
Adam Tkac b62a53
+	x) set -x;;
Adam Tkac b62a53
+	*) echo "$USAGE" 1>&2; exit 1;;
Adam Tkac b62a53
+    esac
Adam Tkac b62a53
+done
Adam Tkac b62a53
+shift `expr $OPTIND - 1 || true`
Adam Tkac b62a53
+if test "$#" -ne 0; then
Adam Tkac b62a53
+    echo "$USAGE" 1>&2
Adam Tkac b62a53
+    exit 1
Adam Tkac b62a53
+fi
Adam Tkac b62a53
+# really quit on control-C
Adam Tkac b62a53
+trap 'exit 1' 1 2 15
Adam Tkac b62a53
+
Adam Tkac b62a53
+
Adam Tkac b62a53
+ret=0
Adam Tkac b62a53
+setret () {
Adam Tkac b62a53
+    ret=1
Adam Tkac b62a53
+    echo "$*"
Adam Tkac b62a53
+}
Adam Tkac b62a53
+
Adam Tkac b62a53
+
Adam Tkac b62a53
+# Wait until soon after the start of a second to make results consistent.
Adam Tkac b62a53
+#   The start of a second credits a rate limit.
Adam Tkac b62a53
+#   This would be far easier in C or by assuming a modern version of perl.
Adam Tkac b62a53
+sec_start () {
Adam Tkac b62a53
+    START=`date`
Adam Tkac b62a53
+    while true; do
Adam Tkac b62a53
+	NOW=`date`
Adam Tkac b62a53
+	if test "$START" != "$NOW"; then
Adam Tkac b62a53
+	    return
Adam Tkac b62a53
+	fi
Adam Tkac b62a53
+	$PERL -e 'select(undef, undef, undef, 0.05)' || true
Adam Tkac b62a53
+    done
Adam Tkac b62a53
+}
Adam Tkac b62a53
+
Adam Tkac b62a53
+
Tomas Hozza 574dc4
+# turn off ${HOME}/.digrc
Tomas Hozza 574dc4
+HOME=/dev/null; export HOME
Tomas Hozza 574dc4
+
Adam Tkac b62a53
+#   $1=result name  $2=domain name  $3=dig options
Adam Tkac b62a53
+digcmd () {
Adam Tkac b62a53
+    OFILE=$1; shift
Adam Tkac b62a53
+    DIG_DOM=$1; shift
Tomas Hozza 574dc4
+    ARGS="+nosearch +time=1 +tries=1 +ignore -p 5300 $* $DIG_DOM @$ns2"
Adam Tkac b62a53
+    #echo I:dig $ARGS 1>&2
Adam Tkac b62a53
+    START=`date +%y%m%d%H%M.%S`
Adam Tkac b62a53
+    RESULT=`$DIG $ARGS 2>&1 | tee $OFILE=TEMP				\
Tomas Hozza 574dc4
+	    | sed -n -e '/^;; AUTHORITY/,/^$/d'				\
Tomas Hozza 574dc4
+		-e '/^;; ADDITIONAL/,/^$/d'				\
Tomas Hozza 574dc4
+		-e  's/^[^;].*	\([^	 ]\{1,\}\)$/\1/p'		\
Adam Tkac b62a53
+		-e 's/;; flags.* tc .*/TC/p'				\
Adam Tkac b62a53
+		-e 's/;; .* status: NXDOMAIN.*/NXDOMAIN/p'		\
Adam Tkac b62a53
+		-e 's/;; .* status: SERVFAIL.*/SERVFAIL/p'		\
Adam Tkac b62a53
+		-e 's/;; connection timed out.*/drop/p'			\
Adam Tkac b62a53
+		-e 's/;; communications error to.*/drop/p'		\
Adam Tkac b62a53
+	    | tr -d '\n'`
Adam Tkac b62a53
+    mv "$OFILE=TEMP" "$OFILE=$RESULT"
Adam Tkac b62a53
+    touch -t $START "$OFILE=$RESULT"
Adam Tkac b62a53
+}
Adam Tkac b62a53
+
Adam Tkac b62a53
+
Adam Tkac b62a53
+#   $1=number of tests  $2=target domain  $3=dig options
Tomas Hozza 574dc4
+QNUM=1
Adam Tkac b62a53
+burst () {
Adam Tkac b62a53
+    BURST_LIMIT=$1; shift
Adam Tkac b62a53
+    BURST_DOM_BASE="$1"; shift
Adam Tkac b62a53
+    while test "$BURST_LIMIT" -ge 1; do
Tomas Hozza 574dc4
+	CNT=`expr "00$QNUM" : '.*\(...\)'`
Adam Tkac b62a53
+	eval BURST_DOM="$BURST_DOM_BASE"
Adam Tkac b62a53
+	FILE="dig.out-$BURST_DOM-$CNT"
Adam Tkac b62a53
+	digcmd $FILE $BURST_DOM $* &
Tomas Hozza 574dc4
+	QNUM=`expr $QNUM + 1`
Adam Tkac b62a53
+	BURST_LIMIT=`expr "$BURST_LIMIT" - 1`
Adam Tkac b62a53
+    done
Adam Tkac b62a53
+}
Adam Tkac b62a53
+
Adam Tkac b62a53
+
Adam Tkac b62a53
+#   $1=domain  $2=IP address  $3=# of IP addresses  $4=TC  $5=drop
Adam Tkac b62a53
+#	$6=NXDOMAIN  $7=SERVFAIL or other errors
Adam Tkac b62a53
+ck_result() {
Adam Tkac b62a53
+    BAD=
Adam Tkac b62a53
+    wait
Tomas Hozza 574dc4
+    ADDRS=`ls dig.out-$1-*=$2				2>/dev/null | wc -l`
Tomas Hozza 574dc4
+    # count simple truncated and truncated NXDOMAIN as TC
Tomas Hozza 574dc4
+    TC=`ls dig.out-$1-*=TC dig.out-$1-*=NXDOMAINTC	2>/dev/null | wc -l`
Tomas Hozza 574dc4
+    DROP=`ls dig.out-$1-*=drop				2>/dev/null | wc -l`
Tomas Hozza 574dc4
+    # count NXDOMAIN and truncated NXDOMAIN as NXDOMAIN
Tomas Hozza 574dc4
+    NXDOMAIN=`ls dig.out-$1-*=NXDOMAIN  dig.out-$1-*=NXDOMAINTC	2>/dev/null \
Tomas Hozza 574dc4
+							| wc -l`
Tomas Hozza 574dc4
+    SERVFAIL=`ls dig.out-$1-*=SERVFAIL			2>/dev/null | wc -l`
Adam Tkac b62a53
+    if test $ADDRS -ne "$3"; then
Tomas Hozza 574dc4
+	setret "I:"$ADDRS" instead of $3 '$2' responses for $1"
Adam Tkac b62a53
+	BAD=yes
Adam Tkac b62a53
+    fi
Adam Tkac b62a53
+    if test $TC -ne "$4"; then
Tomas Hozza 574dc4
+	setret "I:"$TC" instead of $4 truncation responses for $1"
Adam Tkac b62a53
+	BAD=yes
Adam Tkac b62a53
+    fi
Adam Tkac b62a53
+    if test $DROP -ne "$5"; then
Tomas Hozza 574dc4
+	setret "I:"$DROP" instead of $5 dropped responses for $1"
Adam Tkac b62a53
+	BAD=yes
Adam Tkac b62a53
+    fi
Adam Tkac b62a53
+    if test $NXDOMAIN -ne "$6"; then
Tomas Hozza 574dc4
+	setret "I:"$NXDOMAIN" instead of $6 NXDOMAIN responses for $1"
Adam Tkac b62a53
+	BAD=yes
Adam Tkac b62a53
+    fi
Adam Tkac b62a53
+    if test $SERVFAIL -ne "$7"; then
Tomas Hozza 574dc4
+	setret "I:"$SERVFAIL" instead of $7 error responses for $1"
Adam Tkac b62a53
+	BAD=yes
Adam Tkac b62a53
+    fi
Adam Tkac b62a53
+    if test -z "$BAD"; then
Adam Tkac b62a53
+	rm -f dig.out-$1-*
Adam Tkac b62a53
+    fi
Adam Tkac b62a53
+}
Adam Tkac b62a53
+
Adam Tkac b62a53
+
Tomas Hozza 574dc4
+ckstats () {
Tomas Hozza 574dc4
+    LABEL="$1"; shift
Tomas Hozza 574dc4
+    TYPE="$1"; shift
Tomas Hozza 574dc4
+    EXPECTED="$1"; shift
Tomas Hozza 574dc4
+    C=`sed -n -e "s/[	 ]*\([0-9]*\).responses $TYPE for rate limits.*/\1/p"  \
Tomas Hozza 574dc4
+	    ns2/named.stats | tail -1`
Tomas Hozza 574dc4
+    C=`expr 0$C + 0`
Tomas Hozza 574dc4
+    if test "$C" -ne $EXPECTED; then
Tomas Hozza 574dc4
+	setret "I:wrong $LABEL $TYPE statistics of $C instead of $EXPECTED"
Tomas Hozza 574dc4
+    fi
Tomas Hozza 574dc4
+}
Tomas Hozza 574dc4
+
Tomas Hozza 574dc4
+
Adam Tkac b62a53
+#########
Adam Tkac b62a53
+sec_start
Adam Tkac b62a53
+
Tomas Hozza 574dc4
+# Tests of referrals to "." must be done before the hints are loaded
Tomas Hozza 574dc4
+#   or with "additional-from-cache no"
Tomas Hozza 574dc4
+burst 5 a1.tld3 +norec
Adam Tkac b62a53
+# basic rate limiting
Adam Tkac b62a53
+burst 3 a1.tld2
Adam Tkac b62a53
+# 1 second delay allows an additional response.
Adam Tkac b62a53
+sleep 1
Tomas Hozza 574dc4
+burst 10 a1.tld2
Tomas Hozza 574dc4
+# Request 30 different qnames to try a wildcard.
Adam Tkac b62a53
+burst 30 'x$CNT.a2.tld2'
Tomas Hozza 574dc4
+# These should be counted and limited but are not.  See RT33138.
Tomas Hozza 574dc4
+burst 10 'y.x$CNT.a2.tld2'
Adam Tkac b62a53
+
Adam Tkac b62a53
+#					IP      TC      drop  NXDOMAIN SERVFAIL
Tomas Hozza 574dc4
+# referrals to "."
Tomas Hozza 574dc4
+ck_result   a1.tld3	''		2	1	2	0	0
Tomas Hozza 574dc4
+# check 13 results including 1 second delay that allows an additional response
Tomas Hozza 574dc4
+ck_result   a1.tld2	192.0.2.1	3	4	6	0	0
Adam Tkac b62a53
+
Adam Tkac b62a53
+# Check the wild card answers.
Adam Tkac b62a53
+# The parent name of the 30 requests is counted.
Tomas Hozza 574dc4
+ck_result 'x*.a2.tld2'	192.0.2.2	2	10	18	0	0
Adam Tkac b62a53
+
Tomas Hozza 574dc4
+# These should be limited but are not.  See RT33138.
Tomas Hozza 574dc4
+ck_result 'y.x*.a2.tld2' 192.0.2.2	10	0	0	0	0
Adam Tkac b62a53
+
Adam Tkac b62a53
+#########
Adam Tkac b62a53
+sec_start
Adam Tkac b62a53
+
Tomas Hozza 574dc4
+burst 10 'x.a3.tld3'
Tomas Hozza 574dc4
+burst 10 'y$CNT.a3.tld3'
Tomas Hozza 574dc4
+burst 10 'z$CNT.a4.tld2'
Adam Tkac b62a53
+
Tomas Hozza 574dc4
+# 10 identical recursive responses are limited
Tomas Hozza 574dc4
+ck_result 'x.a3.tld3'	192.0.3.3	2	3	5	0	0
Adam Tkac b62a53
+
Tomas Hozza 574dc4
+# 10 different recursive responses are not limited
Tomas Hozza 574dc4
+ck_result 'y*.a3.tld3'	192.0.3.3	10	0	0	0	0
Tomas Hozza 574dc4
+
Tomas Hozza 574dc4
+# 10 different NXDOMAIN responses are limited based on the parent name.
Tomas Hozza 574dc4
+#   We count 13 responses because we count truncated NXDOMAIN responses
Tomas Hozza 574dc4
+#   as both truncated and NXDOMAIN.
Tomas Hozza 574dc4
+ck_result 'z*.a4.tld2'	x		0	3	5	5	0
Tomas Hozza 574dc4
+
Tomas Hozza 574dc4
+$RNDC -c $SYSTEMTESTTOP/common/rndc.conf -p 9953 -s $ns2 stats
Tomas Hozza 574dc4
+ckstats first dropped 36
Tomas Hozza 574dc4
+ckstats first truncated 21
Adam Tkac b62a53
+
Adam Tkac b62a53
+
Adam Tkac b62a53
+#########
Adam Tkac b62a53
+sec_start
Adam Tkac b62a53
+
Tomas Hozza 574dc4
+burst 10 a5.tld2 +tcp
Tomas Hozza 574dc4
+burst 10 a6.tld2 -b $ns7
Tomas Hozza 574dc4
+burst 10 a7.tld4
Tomas Hozza 574dc4
+burst 2 a8.tld2 AAAA
Tomas Hozza 574dc4
+burst 2 a8.tld2 TXT
Tomas Hozza 574dc4
+burst 2 a8.tld2 SPF
Adam Tkac b62a53
+
Tomas Hozza 574dc4
+#					IP      TC      drop  NXDOMAIN SERVFAIL
Adam Tkac b62a53
+# TCP responses are not rate limited
Tomas Hozza 574dc4
+ck_result a5.tld2	192.0.2.5	10	0	0	0	0
Adam Tkac b62a53
+
Adam Tkac b62a53
+# whitelisted client is not rate limited
Tomas Hozza 574dc4
+ck_result a6.tld2	192.0.2.6	10	0	0	0	0
Adam Tkac b62a53
+
Tomas Hozza 574dc4
+# Errors such as SERVFAIL are rate limited.
Tomas Hozza 574dc4
+ck_result a7.tld4	x		0	0	8	0	2
Tomas Hozza 574dc4
+
Tomas Hozza 574dc4
+# NODATA responses are counted as the same regardless of qtype.
Tomas Hozza 574dc4
+ck_result a8.tld2	''		2	2	2	0	0
Tomas Hozza 574dc4
+
Tomas Hozza 574dc4
+$RNDC -c $SYSTEMTESTTOP/common/rndc.conf -p 9953 -s $ns2 stats
Tomas Hozza 574dc4
+ckstats second dropped 46
Tomas Hozza 574dc4
+ckstats second truncated 23
Adam Tkac b62a53
+
Adam Tkac b62a53
+
Adam Tkac b62a53
+#########
Adam Tkac b62a53
+sec_start
Adam Tkac b62a53
+
Tomas Hozza 574dc4
+#					IP      TC      drop  NXDOMAIN SERVFAIL
Adam Tkac b62a53
+# all-per-second
Adam Tkac b62a53
+#   The qnames are all unique but the client IP address is constant.
Tomas Hozza 574dc4
+QNUM=101
Tomas Hozza 574dc4
+burst 60 'all$CNT.a9.tld2'
Adam Tkac b62a53
+
Tomas Hozza 574dc4
+ck_result 'a*.a9.tld2'	192.0.2.8	50	0	10	0	0
Adam Tkac b62a53
+
Adam Tkac b62a53
+$RNDC -c $SYSTEMTESTTOP/common/rndc.conf -p 9953 -s $ns2 stats
Tomas Hozza 574dc4
+ckstats final dropped 56
Tomas Hozza 574dc4
+ckstats final truncated 23
Adam Tkac b62a53
+
Adam Tkac b62a53
+
Adam Tkac b62a53
+echo "I:exit status: $ret"
Tomas Hozza 574dc4
+# exit $ret
Tomas Hozza 574dc4
+[ $ret -ne 0 ] && echo "I:test failure overridden"
Tomas Hozza 574dc4
+exit 0
Tomas Hozza 574dc4
diff -r -u doc/arm/Bv9ARM-book.xml-orig doc/arm/Bv9ARM-book.xml
Tomas Hozza 574dc4
--- doc/arm/Bv9ARM-book.xml-orig	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
+++ doc/arm/Bv9ARM-book.xml	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
@@ -4818,6 +4818,32 @@
Adam Tkac b62a53
 		    </para>
Adam Tkac b62a53
 		  </entry>
Adam Tkac b62a53
 		</row>
Adam Tkac b62a53
+                <row rowsep="0">
Adam Tkac b62a53
+                  <entry colname="1">
Adam Tkac b62a53
+                    <para><command>rate-limit</command></para>
Adam Tkac b62a53
+                  </entry>
Adam Tkac b62a53
+		  <entry colname="2">
Adam Tkac b62a53
+		    <para>
Adam Tkac b62a53
+		      The start, periodic, and final notices of the
Adam Tkac b62a53
+		      rate limiting of a stream of responses are logged at
Adam Tkac b62a53
+		      <command>info</command> severity in this category.
Adam Tkac b62a53
+		      These messages include a hash value of the domain name
Adam Tkac b62a53
+		      of the response and the name itself,
Adam Tkac b62a53
+		      except when there is insufficient memory to record
Adam Tkac b62a53
+		      the name for the final notice
Adam Tkac b62a53
+		      The final notice is normally delayed until about one
Adam Tkac b62a53
+		      minute after rate limit stops.
Adam Tkac b62a53
+		      A lack of memory can hurry the final notice,
Adam Tkac b62a53
+		      in which case it starts with an asterisk (*).
Adam Tkac b62a53
+		      Various internal events are logged at debug 1 level
Adam Tkac b62a53
+		      and higher.
Adam Tkac b62a53
+		    </para>
Adam Tkac b62a53
+		    <para>
Adam Tkac b62a53
+		      Rate limiting of individual requests
Tomas Hozza 574dc4
+		      is logged in the <command>query-errors</command> category.
Adam Tkac b62a53
+		    </para>
Adam Tkac b62a53
+		  </entry>
Adam Tkac b62a53
+		</row>
Adam Tkac b62a53
 	      
Adam Tkac b62a53
 	    </tgroup>
Adam Tkac b62a53
 	  </informaltable>
Tomas Hozza 574dc4
@@ -5318,7 +5344,7 @@
Tomas Hozza 574dc4
     <optional> match-mapped-addresses <replaceable>yes_or_no</replaceable>; </optional>
Tomas Hozza 574dc4
     <optional> filter-aaaa-on-v4 ( <replaceable>yes_or_no</replaceable> | <replaceable>break-dnssec</replaceable> ); </optional>
Tomas Hozza 574dc4
     <optional> filter-aaaa { <replaceable>address_match_list</replaceable> }; </optional>
Tomas Hozza 574dc4
-    <optional> dns64 <replaceable>IPv6-prefix</replaceable> {
Tomas Hozza 574dc4
+    <optional> dns64 <replaceable>ipv6-prefix</replaceable> {
Tomas Hozza 574dc4
 	<optional> clients { <replaceable>address_match_list</replaceable> }; </optional>
Tomas Hozza 574dc4
 	<optional> mapped { <replaceable>address_match_list</replaceable> }; </optional>
Tomas Hozza 574dc4
         <optional> exclude { <replaceable>address_match_list</replaceable> }; </optional>
Tomas Hozza 574dc4
@@ -5351,6 +5377,23 @@
Adam Tkac b62a53
     <optional> resolver-query-timeout <replaceable>number</replaceable> ; </optional>
Adam Tkac b62a53
     <optional> deny-answer-addresses { <replaceable>address_match_list</replaceable> } <optional> except-from { <replaceable>namelist</replaceable> } </optional>;</optional>
Adam Tkac b62a53
     <optional> deny-answer-aliases { <replaceable>namelist</replaceable> } <optional> except-from { <replaceable>namelist</replaceable> } </optional>;</optional>
Adam Tkac b62a53
+    <optional> rate-limit {
Adam Tkac b62a53
+	<optional> responses-per-second <replaceable>number</replaceable> ; </optional>
Tomas Hozza 574dc4
+	<optional> referrals-per-second <replaceable>number</replaceable> ; </optional>
Tomas Hozza 574dc4
+	<optional> nodata-per-second <replaceable>number</replaceable> ; </optional>
Adam Tkac b62a53
+	<optional> nxdomains-per-second <replaceable>number</replaceable> ; </optional>
Tomas Hozza 574dc4
+	<optional> errors-per-second <replaceable>number</replaceable> ; </optional>
Adam Tkac b62a53
+	<optional> all-per-second <replaceable>number</replaceable> ; </optional>
Adam Tkac b62a53
+	<optional> window <replaceable>number</replaceable> ; </optional>
Adam Tkac b62a53
+	<optional> log-only <replaceable>yes_or_no</replaceable> ; </optional>
Adam Tkac b62a53
+	<optional> qps-scale <replaceable>number</replaceable> ; </optional>
Tomas Hozza 574dc4
+	<optional> ipv4-prefix-length <replaceable>number</replaceable> ; </optional>
Tomas Hozza 574dc4
+	<optional> ipv6-prefix-length <replaceable>number</replaceable> ; </optional>
Adam Tkac b62a53
+	<optional> slip <replaceable>number</replaceable> ; </optional>
Adam Tkac b62a53
+	<optional> exempt-clients  { <replaceable>address_match_list</replaceable> } ; </optional>
Adam Tkac b62a53
+	<optional> max-table-size <replaceable>number</replaceable> ; </optional>
Adam Tkac b62a53
+	<optional> min-table-size <replaceable>number</replaceable> ; </optional>
Adam Tkac b62a53
+      } ; </optional>
Adam Tkac b62a53
     <optional> response-policy { <replaceable>zone_name</replaceable>
Adam Tkac b62a53
 	<optional> policy given | disabled | passthru | nxdomain | nodata | cname <replaceable>domain</replaceable> </optional>
Adam Tkac b62a53
 	<optional> recursive-only <replaceable>yes_or_no</replaceable> </optional> <optional> max-policy-ttl <replaceable>number</replaceable> </optional> ;
Tomas Hozza 574dc4
@@ -9897,6 +9940,223 @@
Tomas Hozza 574dc4
             <command>RPZRewrites</command> statistics.
Tomas Hozza 574dc4
           </para>
Adam Tkac b62a53
         </sect3>
Adam Tkac b62a53
+
Adam Tkac b62a53
+	<sect3>
Tomas Hozza 574dc4
+	  <title>Response Rate Limiting</title>
Adam Tkac b62a53
+	  <para>
Tomas Hozza 574dc4
+	    Excessive almost-identical UDP <emphasis>responses</emphasis>
Tomas Hozza 574dc4
+	    can be controlled by configuring a
Adam Tkac b62a53
+	    <command>rate-limit</command> clause in an
Tomas Hozza 574dc4
+	    <command>options</command> or <command>view</command> statement.
Tomas Hozza 574dc4
+	    This mechanism keeps authoritative BIND 9 from being used
Tomas Hozza 574dc4
+	    in amplifying reflection denial of service (DoS) attacks.
Tomas Hozza 574dc4
+	    Short truncated (TC=1) responses can be sent to provide
Tomas Hozza 574dc4
+	    rate-limited responses to legitimate clients within
Tomas Hozza 574dc4
+	    a range of forged, attacked IP addresses.
Tomas Hozza 574dc4
+	    Legitimate clients react to dropped or truncated response
Tomas Hozza 574dc4
+	    by retrying with UDP or with TCP respectively.
Adam Tkac b62a53
+	  </para>
Adam Tkac b62a53
+
Adam Tkac b62a53
+	  <para>
Tomas Hozza 574dc4
+	    This mechanism is intended for authoritative DNS servers.
Tomas Hozza 574dc4
+	    It can be used on recursive servers but can slow
Tomas Hozza 574dc4
+	    applications such as SMTP servers (mail receivers) and
Tomas Hozza 574dc4
+	    HTTP clients (web browsers) that repeatedly request the
Tomas Hozza 574dc4
+	    same domains.
Tomas Hozza 574dc4
+	    When possible, closing "open" recursive servers is better.
Adam Tkac b62a53
+	  </para>
Adam Tkac b62a53
+
Adam Tkac b62a53
+	  <para>
Tomas Hozza 574dc4
+	    Response rate limiting uses a "credit" or "token bucket" scheme.
Tomas Hozza 574dc4
+	    Each combination of identical response and client
Tomas Hozza 574dc4
+	    has a conceptual account that earns a specified number
Tomas Hozza 574dc4
+	    of credits every second.
Tomas Hozza 574dc4
+	    A prospective response debits its account by one.
Tomas Hozza 574dc4
+	    Responses are dropped or truncated
Tomas Hozza 574dc4
+	    while the account is negative.
Tomas Hozza 574dc4
+            Responses are tracked within a rolling window of time
Tomas Hozza 574dc4
+            which defaults to 15 seconds, but can be configured with
Tomas Hozza 574dc4
+            the <command>window</command> option to any value from
Tomas Hozza 574dc4
+            1 to 3600 seconds (1 hour).
Tomas Hozza 574dc4
+	    The account cannot become more positive than
Tomas Hozza 574dc4
+	    the per-second limit
Tomas Hozza 574dc4
+	    or more negative than <command>window</command>
Tomas Hozza 574dc4
+	    times the per-second limit.
Tomas Hozza 574dc4
+            When the specified number of credits for a class of
Tomas Hozza 574dc4
+            responses is set to 0, those responses are not rate limited.
Adam Tkac b62a53
+	  </para>
Adam Tkac b62a53
+
Adam Tkac b62a53
+	  <para>
Tomas Hozza 574dc4
+	    The notions of "identical response" and "DNS client"
Tomas Hozza 574dc4
+	    for rate limiting are not simplistic.
Tomas Hozza 574dc4
+	    All responses to an address block are counted as if to a
Tomas Hozza 574dc4
+	    single client.
Tomas Hozza 574dc4
+	    The prefix lengths of addresses blocks are
Tomas Hozza 574dc4
+	    specified with <command>ipv4-prefix-length</command> (default 24)
Tomas Hozza 574dc4
+	    and <command>ipv6-prefix-length</command> (default 56).
Adam Tkac b62a53
+	  </para>
Adam Tkac b62a53
+
Adam Tkac b62a53
+	  <para>
Tomas Hozza 574dc4
+	    All non-empty responses for a valid domain name (qname)
Tomas Hozza 574dc4
+	    and record type (qtype) are identical and have a limit specified
Tomas Hozza 574dc4
+	    with <command>responses-per-second</command>
Tomas Hozza 574dc4
+	    (default 0 or no limit).
Tomas Hozza 574dc4
+	    All empty (NODATA) responses for a valid domain,
Tomas Hozza 574dc4
+	    regardless of query type, are identical.
Tomas Hozza 574dc4
+	    Responses in the NODATA class are limited by
Tomas Hozza 574dc4
+	    <command>nodata-per-second</command>
Tomas Hozza 574dc4
+	    (default <command>responses-per-second</command>).
Tomas Hozza 574dc4
+	    Requests for any and all undefined subdomains of a given
Tomas Hozza 574dc4
+            valid domain result in NXDOMAIN errors, and are identical
Tomas Hozza 574dc4
+            regardless of query type.
Tomas Hozza 574dc4
+	    They are limited by <command>nxdomain-per-second</command>
Tomas Hozza 574dc4
+	    (default <command>responses-per-second</command>).
Tomas Hozza 574dc4
+	    This controls some attacks using random names, but
Tomas Hozza 574dc4
+	    can be relaxed or turned off (set to 0)
Tomas Hozza 574dc4
+	    on servers that expect many legitimate
Tomas Hozza 574dc4
+	    NXDOMAIN responses, such as from anti-spam blacklists.
Tomas Hozza 574dc4
+	    Referrals or delegations to the server of a given
Tomas Hozza 574dc4
+	    domain are identical and are limited by
Tomas Hozza 574dc4
+	    <command>referrals-per-second</command>
Tomas Hozza 574dc4
+	    (default <command>responses-per-second</command>).
Adam Tkac b62a53
+	  </para>
Adam Tkac b62a53
+
Adam Tkac b62a53
+	  <para>
Adam Tkac b62a53
+	    Responses generated from local wildcards are counted and limited
Adam Tkac b62a53
+	    as if they were for the parent domain name.
Tomas Hozza 574dc4
+	    This controls flooding using random.wild.example.com.
Tomas Hozza 574dc4
+	  </para>
Tomas Hozza 574dc4
+
Tomas Hozza 574dc4
+	  <para>
Tomas Hozza 574dc4
+            All requests that result in DNS errors other
Tomas Hozza 574dc4
+	    than NXDOMAIN, such as SERVFAIL and FORMERR, are identical
Tomas Hozza 574dc4
+            regardless of requested name (qname) or record type (qtype).
Tomas Hozza 574dc4
+	    This controls attacks using invalid requests or distant,
Tomas Hozza 574dc4
+	    broken authoritative servers.
Tomas Hozza 574dc4
+	    By default the limit on errors is the same as the
Tomas Hozza 574dc4
+	    <command>responses-per-second</command> value,
Tomas Hozza 574dc4
+	    but it can be set separately with
Tomas Hozza 574dc4
+	    <command>errors-per-second</command>.
Adam Tkac b62a53
+	  </para>
Adam Tkac b62a53
+
Adam Tkac b62a53
+	  <para>
Adam Tkac b62a53
+	    Many attacks using DNS involve UDP requests with forged source
Adam Tkac b62a53
+	    addresses.
Adam Tkac b62a53
+	    Rate limiting prevents the use of BIND 9 to flood a network
Adam Tkac b62a53
+	    with responses to requests with forged source addresses,
Adam Tkac b62a53
+	    but could let a third party block responses to legitimate requests.
Adam Tkac b62a53
+	    There is a mechanism that can answer some legitimate
Adam Tkac b62a53
+	    requests from a client whose address is being forged in a flood.
Adam Tkac b62a53
+	    Setting <command>slip</command> to 2 (its default) causes every
Tomas Hozza 574dc4
+	    other UDP request to be answered with a small truncated (TC=1)
Tomas Hozza 574dc4
+	    response.
Tomas Hozza 574dc4
+	    The small size and reduced frequency, and so lack of
Tomas Hozza 574dc4
+	    amplification, of "slipped" responses make them unattractive
Tomas Hozza 574dc4
+	    for reflection DoS attacks.
Tomas Hozza 574dc4
+	    <command>slip</command> must be between 0 and 10.
Tomas Hozza 574dc4
+	    A value of 0 does not "slip";
Tomas Hozza 574dc4
+	    no truncated responses are sent due to rate limiting.
Tomas Hozza 574dc4
+	    Some error responses including REFUSED and SERVFAIL
Adam Tkac b62a53
+	    cannot be replaced with truncated responses and are instead
Adam Tkac b62a53
+	    leaked at the <command>slip</command> rate.
Adam Tkac b62a53
+	  </para>
Adam Tkac b62a53
+
Adam Tkac b62a53
+	  <para>
Adam Tkac b62a53
+	    When the approximate query per second rate exceeds
Adam Tkac b62a53
+	    the <command>qps-scale</command> value,
Adam Tkac b62a53
+	    then the <command>responses-per-second</command>,
Adam Tkac b62a53
+	    <command>errors-per-second</command>,
Adam Tkac b62a53
+	    <command>nxdomains-per-second</command> and
Adam Tkac b62a53
+	    <command>all-per-second</command> values are reduced by the
Adam Tkac b62a53
+	    ratio of the current rate to the <command>qps-scale</command> value.
Adam Tkac b62a53
+	    This feature can tighten defenses during attacks.
Adam Tkac b62a53
+	    For example, with
Adam Tkac b62a53
+	    <command>qps-scale 250; responses-per-second 20;</command> and
Adam Tkac b62a53
+	    a total query rate of 1000 queries/second for all queries from
Adam Tkac b62a53
+	    all DNS clients including via TCP,
Adam Tkac b62a53
+	    then the effective responses/second limit changes to
Adam Tkac b62a53
+	    (250/1000)*20 or 5.
Adam Tkac b62a53
+	    Responses sent via TCP are not limited
Adam Tkac b62a53
+	    but are counted to compute the query per second rate.
Adam Tkac b62a53
+	  </para>
Adam Tkac b62a53
+
Adam Tkac b62a53
+	  <para>
Adam Tkac b62a53
+	    Communities of DNS clients can be given their own parameters or no
Adam Tkac b62a53
+	    rate limiting by putting
Adam Tkac b62a53
+	    <command>rate-limit</command> statements in <command>view</command>
Adam Tkac b62a53
+	    statements instead of the global <command>option</command>
Adam Tkac b62a53
+	    statement.
Tomas Hozza 574dc4
+	    A <command>rate-limit</command> statement in a view replaces,
Tomas Hozza 574dc4
+	    rather than supplementing, a <command>rate-limit</command>
Adam Tkac b62a53
+	    statement among the main options.
Adam Tkac b62a53
+	    DNS clients within a view can be exempted from rate limits
Adam Tkac b62a53
+	    with the <command>exempt-clients</command> clause.
Adam Tkac b62a53
+	  </para>
Adam Tkac b62a53
+
Adam Tkac b62a53
+	  <para>
Adam Tkac b62a53
+	    UDP responses of all kinds can be limited with the
Adam Tkac b62a53
+	    <command>all-per-second</command> phrase.
Adam Tkac b62a53
+	    This rate limiting is unlike the rate limiting provided by
Adam Tkac b62a53
+	    <command>responses-per-second</command>,
Adam Tkac b62a53
+	    <command>errors-per-second</command>, and
Adam Tkac b62a53
+	    <command>nxdomains-per-second</command> on a DNS server
Adam Tkac b62a53
+	    which are often invisible to the victim of a DNS reflection attack.
Adam Tkac b62a53
+	    Unless the forged requests of the attack are the same as the
Adam Tkac b62a53
+	    legitimate requests of the victim, the victim's requests are
Adam Tkac b62a53
+	    not affected.
Adam Tkac b62a53
+	    Responses affected by an <command>all-per-second</command> limit
Adam Tkac b62a53
+	    are always dropped; the <command>slip</command> value has no
Adam Tkac b62a53
+	    effect.
Adam Tkac b62a53
+	    An <command>all-per-second</command> limit should be
Adam Tkac b62a53
+	    at least 4 times as large as the other limits,
Adam Tkac b62a53
+	    because single DNS clients often send bursts of legitimate
Adam Tkac b62a53
+	    requests.
Adam Tkac b62a53
+	    For example, the receipt of a single mail message can prompt
Adam Tkac b62a53
+	    requests from an SMTP server for NS, PTR, A, and AAAA records
Adam Tkac b62a53
+	    as the incoming SMTP/TCP/IP connection is considered.
Adam Tkac b62a53
+	    The SMTP server can need additional NS, A, AAAA, MX, TXT, and SPF
Adam Tkac b62a53
+	    records as it considers the STMP <command>Mail From</command>
Adam Tkac b62a53
+	    command.
Adam Tkac b62a53
+	    Web browsers often repeatedly resolve the same names that
Adam Tkac b62a53
+	    are repeated in HTML <IMG> tags in a page.
Adam Tkac b62a53
+	    <command>All-per-second</command> is similar to the
Adam Tkac b62a53
+	    rate limiting offered by firewalls but often inferior.
Adam Tkac b62a53
+	    Attacks that justify ignoring the
Adam Tkac b62a53
+	    contents of DNS responses are likely to be attacks on the
Adam Tkac b62a53
+	    DNS server itself.
Adam Tkac b62a53
+	    They usually should be discarded before the DNS server
Adam Tkac b62a53
+	    spends resources make TCP connections or parsing DNS requesets,
Adam Tkac b62a53
+	    but that rate limiting must be done before the
Adam Tkac b62a53
+	    DNS server sees the requests.
Adam Tkac b62a53
+	  </para>
Adam Tkac b62a53
+
Adam Tkac b62a53
+	  <para>
Adam Tkac b62a53
+	    The maximum size of the table used to track requests and
Adam Tkac b62a53
+	    rate limit responses is set with <command>max-table-size</command>.
Adam Tkac b62a53
+	    Each entry in the table is between 40 and 80 bytes.
Adam Tkac b62a53
+	    The table needs approximately as many entries as the number
Adam Tkac b62a53
+	    of requests received per second.
Adam Tkac b62a53
+	    The default is 20,000.
Adam Tkac b62a53
+	    To reduce the cold start of growing the table,
Adam Tkac b62a53
+	    <command>min-table-size</command> (default 500)
Adam Tkac b62a53
+	    can set the minimum table size.
Adam Tkac b62a53
+	    Enable <command>rate-limit</command> category logging to monitor
Adam Tkac b62a53
+	    expansions of the table and inform
Adam Tkac b62a53
+	    choices for the initial and maximum table size.
Adam Tkac b62a53
+	  </para>
Adam Tkac b62a53
+
Adam Tkac b62a53
+	  <para>
Adam Tkac b62a53
+	    Use <command>log-only yes</command> to test rate limiting parameters
Adam Tkac b62a53
+	    without actually dropping any requests.
Adam Tkac b62a53
+	  </para>
Adam Tkac b62a53
+
Adam Tkac b62a53
+	  <para>
Adam Tkac b62a53
+	    Responses dropped by rate limits are included in the
Adam Tkac b62a53
+	    <command>RateDropped</command> and <command>QryDropped</command>
Adam Tkac b62a53
+	    statistics.
Adam Tkac b62a53
+	    Responses that truncated by rate limits are included in
Adam Tkac b62a53
+	    <command>RateSlipped</command> and <command>RespTruncated</command>.
Adam Tkac b62a53
+	</sect3>
Adam Tkac b62a53
       </sect2>
Adam Tkac b62a53
 
Adam Tkac b62a53
       <sect2 id="server_statement_grammar">
Tomas Hozza 574dc4
@@ -14649,6 +14909,32 @@
Adam Tkac b62a53
 		      </para>
Adam Tkac b62a53
 		    </entry>
Adam Tkac b62a53
 		  </row>
Adam Tkac b62a53
+		  <row rowsep="0">
Adam Tkac b62a53
+		    <entry colname="1">
Adam Tkac b62a53
+		      <para><command>RateDropped</command></para>
Adam Tkac b62a53
+		    </entry>
Adam Tkac b62a53
+		    <entry colname="2">
Adam Tkac b62a53
+		      <para><command></command></para>
Adam Tkac b62a53
+		    </entry>
Adam Tkac b62a53
+		    <entry colname="3">
Adam Tkac b62a53
+		      <para>
Adam Tkac b62a53
+			Responses dropped by rate limits.
Adam Tkac b62a53
+		      </para>
Adam Tkac b62a53
+		    </entry>
Adam Tkac b62a53
+		  </row>
Adam Tkac b62a53
+		  <row rowsep="0">
Adam Tkac b62a53
+		    <entry colname="1">
Adam Tkac b62a53
+		      <para><command>RateSlipped</command></para>
Adam Tkac b62a53
+		    </entry>
Adam Tkac b62a53
+		    <entry colname="2">
Adam Tkac b62a53
+		      <para><command></command></para>
Adam Tkac b62a53
+		    </entry>
Adam Tkac b62a53
+		    <entry colname="3">
Adam Tkac b62a53
+		      <para>
Adam Tkac b62a53
+			Responses truncated by rate limits.
Adam Tkac b62a53
+		      </para>
Adam Tkac b62a53
+		    </entry>
Adam Tkac b62a53
+		  </row>
Adam Tkac b62a53
 		
Adam Tkac b62a53
               </tgroup>
Adam Tkac b62a53
             </informaltable>
Tomas Hozza 574dc4
diff -r -u lib/dns/Makefile.in-orig lib/dns/Makefile.in
Tomas Hozza 574dc4
--- lib/dns/Makefile.in-orig	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
+++ lib/dns/Makefile.in	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
@@ -67,8 +67,8 @@
Tomas Hozza 574dc4
 		portlist.@O@ private.@O@ \
Tomas Hozza 574dc4
 		rbt.@O@ rbtdb.@O@ rbtdb64.@O@ rcode.@O@ rdata.@O@ \
Tomas Hozza 574dc4
 		rdatalist.@O@ rdataset.@O@ rdatasetiter.@O@ rdataslab.@O@ \
Tomas Hozza 574dc4
-		request.@O@ resolver.@O@ result.@O@ rootns.@O@ rpz.@O@ \
Tomas Hozza 574dc4
-		rriterator.@O@ sdb.@O@ \
Tomas Hozza 574dc4
+		request.@O@ resolver.@O@ result.@O@ rootns.@O@ \
Tomas Hozza 574dc4
+		rpz.@O@ rrl.@O@ rriterator.@O@ sdb.@O@ \
Tomas Hozza 574dc4
 		sdlz.@O@ soa.@O@ ssu.@O@ ssu_external.@O@ \
Tomas Hozza 574dc4
 		stats.@O@ tcpmsg.@O@ time.@O@ timer.@O@ tkey.@O@ \
Tomas Hozza 574dc4
 		tsec.@O@ tsig.@O@ ttl.@O@ update.@O@ validator.@O@ \
Tomas Hozza 574dc4
@@ -95,7 +95,7 @@
Tomas Hozza 574dc4
 		name.c ncache.c nsec.c nsec3.c order.c peer.c portlist.c \
Tomas Hozza 574dc4
 		rbt.c rbtdb.c rbtdb64.c rcode.c rdata.c rdatalist.c \
Tomas Hozza 574dc4
 		rdataset.c rdatasetiter.c rdataslab.c request.c \
Tomas Hozza 574dc4
-		resolver.c result.c rootns.c rpz.c rriterator.c \
Tomas Hozza 574dc4
+		resolver.c result.c rootns.c rpz.c rrl.c rriterator.c \
Tomas Hozza 574dc4
 		sdb.c sdlz.c soa.c ssu.c ssu_external.c \
Tomas Hozza 574dc4
 		stats.c tcpmsg.c time.c timer.c tkey.c \
Tomas Hozza 574dc4
 		tsec.c tsig.c ttl.c update.c validator.c \
Tomas Hozza 574dc4
diff -r -u lib/dns/include/dns/log.h-orig lib/dns/include/dns/log.h
Tomas Hozza 574dc4
--- lib/dns/include/dns/log.h-orig	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
+++ lib/dns/include/dns/log.h	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
@@ -43,6 +43,7 @@
Adam Tkac b62a53
 #define DNS_LOGCATEGORY_DELEGATION_ONLY	(&dns_categories[10])
Adam Tkac b62a53
 #define DNS_LOGCATEGORY_EDNS_DISABLED	(&dns_categories[11])
Adam Tkac b62a53
 #define DNS_LOGCATEGORY_RPZ		(&dns_categories[12])
Adam Tkac b62a53
+#define DNS_LOGCATEGORY_RRL		(&dns_categories[13])
Adam Tkac b62a53
 
Adam Tkac b62a53
 /* Backwards compatibility. */
Adam Tkac b62a53
 #define DNS_LOGCATEGORY_GENERAL		ISC_LOGCATEGORY_GENERAL
Tomas Hozza 574dc4
diff -r -u lib/dns/include/dns/rrl.h-orig lib/dns/include/dns/rrl.h
Tomas Hozza 574dc4
--- lib/dns/include/dns/rrl.h-orig	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
+++ lib/dns/include/dns/rrl.h	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
@@ -0,0 +1,278 @@
Adam Tkac b62a53
+/*
Tomas Hozza 574dc4
+ * Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
Adam Tkac b62a53
+ *
Adam Tkac b62a53
+ * Permission to use, copy, modify, and/or distribute this software for any
Adam Tkac b62a53
+ * purpose with or without fee is hereby granted, provided that the above
Adam Tkac b62a53
+ * copyright notice and this permission notice appear in all copies.
Adam Tkac b62a53
+ *
Adam Tkac b62a53
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
Adam Tkac b62a53
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
Adam Tkac b62a53
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
Adam Tkac b62a53
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
Adam Tkac b62a53
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
Adam Tkac b62a53
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
Adam Tkac b62a53
+ * PERFORMANCE OF THIS SOFTWARE.
Adam Tkac b62a53
+ */
Adam Tkac b62a53
+
Adam Tkac b62a53
+
Adam Tkac b62a53
+#ifndef DNS_RRL_H
Adam Tkac b62a53
+#define DNS_RRL_H 1
Adam Tkac b62a53
+
Adam Tkac b62a53
+/*
Adam Tkac b62a53
+ * Rate limit DNS responses.
Adam Tkac b62a53
+ */
Adam Tkac b62a53
+
Adam Tkac b62a53
+#include <isc/lang.h>
Adam Tkac b62a53
+
Adam Tkac b62a53
+#include <dns/fixedname.h>
Adam Tkac b62a53
+#include <dns/rdata.h>
Adam Tkac b62a53
+#include <dns/types.h>
Adam Tkac b62a53
+
Adam Tkac b62a53
+ISC_LANG_BEGINDECLS
Adam Tkac b62a53
+
Adam Tkac b62a53
+
Adam Tkac b62a53
+/*
Adam Tkac b62a53
+ * Memory allocation or other failures.
Adam Tkac b62a53
+ */
Adam Tkac b62a53
+#define DNS_RRL_LOG_FAIL	ISC_LOG_WARNING
Adam Tkac b62a53
+/*
Adam Tkac b62a53
+ * dropped or slipped responses.
Adam Tkac b62a53
+ */
Adam Tkac b62a53
+#define DNS_RRL_LOG_DROP	ISC_LOG_INFO
Adam Tkac b62a53
+/*
Adam Tkac b62a53
+ * Major events in dropping or slipping.
Adam Tkac b62a53
+ */
Adam Tkac b62a53
+#define DNS_RRL_LOG_DEBUG1	ISC_LOG_DEBUG(3)
Adam Tkac b62a53
+/*
Adam Tkac b62a53
+ * Limit computations.
Adam Tkac b62a53
+ */
Adam Tkac b62a53
+#define DNS_RRL_LOG_DEBUG2	ISC_LOG_DEBUG(4)
Adam Tkac b62a53
+/*
Adam Tkac b62a53
+ * Even less interesting.
Adam Tkac b62a53
+ */
Adam Tkac b62a53
+#define DNS_RRL_LOG_DEBUG3	ISC_LOG_DEBUG(9)
Adam Tkac b62a53
+
Adam Tkac b62a53
+
Adam Tkac b62a53
+#define DNS_RRL_LOG_ERR_LEN	64
Adam Tkac b62a53
+#define DNS_RRL_LOG_BUF_LEN	(sizeof("would continue limiting") +	\
Adam Tkac b62a53
+				 DNS_RRL_LOG_ERR_LEN +			\
Adam Tkac b62a53
+				 sizeof(" responses to ") +		\
Adam Tkac b62a53
+				 ISC_NETADDR_FORMATSIZE +		\
Adam Tkac b62a53
+				 sizeof("/128 for IN ") +		\
Adam Tkac b62a53
+				 DNS_RDATATYPE_FORMATSIZE +		\
Adam Tkac b62a53
+				 DNS_NAME_FORMATSIZE)
Adam Tkac b62a53
+
Adam Tkac b62a53
+
Adam Tkac b62a53
+typedef struct dns_rrl_hash dns_rrl_hash_t;
Adam Tkac b62a53
+
Adam Tkac b62a53
+/*
Adam Tkac b62a53
+ * Response types.
Adam Tkac b62a53
+ */
Adam Tkac b62a53
+typedef enum {
Adam Tkac b62a53
+	DNS_RRL_RTYPE_FREE = 0,
Adam Tkac b62a53
+	DNS_RRL_RTYPE_QUERY,
Tomas Hozza 574dc4
+	DNS_RRL_RTYPE_REFERRAL,
Tomas Hozza 574dc4
+	DNS_RRL_RTYPE_NODATA,
Adam Tkac b62a53
+	DNS_RRL_RTYPE_NXDOMAIN,
Adam Tkac b62a53
+	DNS_RRL_RTYPE_ERROR,
Adam Tkac b62a53
+	DNS_RRL_RTYPE_ALL,
Adam Tkac b62a53
+	DNS_RRL_RTYPE_TCP,
Adam Tkac b62a53
+} dns_rrl_rtype_t;
Adam Tkac b62a53
+
Adam Tkac b62a53
+/*
Adam Tkac b62a53
+ * A rate limit bucket key.
Adam Tkac b62a53
+ * This should be small to limit the total size of the database.
Adam Tkac b62a53
+ * The hash of the qname should be wide enough to make the probability
Adam Tkac b62a53
+ * of collisions among requests from a single IP address block less than 50%.
Adam Tkac b62a53
+ * We need a 32-bit hash value for 10000 qps (e.g. random qnames forged
Adam Tkac b62a53
+ * by attacker) to collide with legitimate qnames from the target with
Adam Tkac b62a53
+ * probability at most 1%.
Adam Tkac b62a53
+ */
Adam Tkac b62a53
+#define DNS_RRL_MAX_PREFIX  64
Adam Tkac b62a53
+typedef union dns_rrl_key dns_rrl_key_t;
Adam Tkac b62a53
+union dns_rrl_key {
Adam Tkac b62a53
+	struct {
Adam Tkac b62a53
+		isc_uint32_t	    ip[DNS_RRL_MAX_PREFIX/32];
Adam Tkac b62a53
+		isc_uint32_t	    qname_hash;
Adam Tkac b62a53
+		dns_rdatatype_t	    qtype;
Adam Tkac b62a53
+		isc_uint8_t	    qclass;
Tomas Hozza 574dc4
+		dns_rrl_rtype_t	    rtype   :4; /* 3 bits + sign bit */
Adam Tkac b62a53
+		isc_boolean_t	    ipv6    :1;
Adam Tkac b62a53
+	} s;
Adam Tkac b62a53
+	isc_uint16_t	w[1];
Adam Tkac b62a53
+};
Adam Tkac b62a53
+
Adam Tkac b62a53
+/*
Adam Tkac b62a53
+ * A rate-limit entry.
Adam Tkac b62a53
+ * This should be small to limit the total size of the table of entries.
Adam Tkac b62a53
+ */
Adam Tkac b62a53
+typedef struct dns_rrl_entry dns_rrl_entry_t;
Adam Tkac b62a53
+typedef ISC_LIST(dns_rrl_entry_t) dns_rrl_bin_t;
Adam Tkac b62a53
+struct dns_rrl_entry {
Adam Tkac b62a53
+	ISC_LINK(dns_rrl_entry_t) lru;
Adam Tkac b62a53
+	ISC_LINK(dns_rrl_entry_t) hlink;
Adam Tkac b62a53
+	dns_rrl_key_t	key;
Adam Tkac b62a53
+# define DNS_RRL_RESPONSE_BITS	24
Adam Tkac b62a53
+	signed int	responses   :DNS_RRL_RESPONSE_BITS;
Adam Tkac b62a53
+# define DNS_RRL_QNAMES_BITS	8
Adam Tkac b62a53
+	unsigned int	log_qname   :DNS_RRL_QNAMES_BITS;
Adam Tkac b62a53
+
Adam Tkac b62a53
+# define DNS_RRL_TS_GEN_BITS	2
Adam Tkac b62a53
+	unsigned int	ts_gen	    :DNS_RRL_TS_GEN_BITS;
Adam Tkac b62a53
+	isc_boolean_t	ts_valid    :1;
Adam Tkac b62a53
+# define DNS_RRL_HASH_GEN_BITS	1
Adam Tkac b62a53
+	unsigned int	hash_gen    :DNS_RRL_HASH_GEN_BITS;
Adam Tkac b62a53
+	isc_boolean_t	logged	    :1;
Adam Tkac b62a53
+# define DNS_RRL_LOG_BITS	11
Adam Tkac b62a53
+	unsigned int	log_secs    :DNS_RRL_LOG_BITS;
Adam Tkac b62a53
+
Adam Tkac b62a53
+# define DNS_RRL_TS_BITS	12
Adam Tkac b62a53
+	unsigned int	ts	    :DNS_RRL_TS_BITS;
Adam Tkac b62a53
+
Adam Tkac b62a53
+# define DNS_RRL_MAX_SLIP	10
Adam Tkac b62a53
+	unsigned int	slip_cnt    :4;
Adam Tkac b62a53
+};
Adam Tkac b62a53
+
Adam Tkac b62a53
+#define DNS_RRL_MAX_TIME_TRAVEL	5
Adam Tkac b62a53
+#define DNS_RRL_FOREVER		(1<
Adam Tkac b62a53
+#define DNS_RRL_MAX_TS		(DNS_RRL_FOREVER - 1)
Adam Tkac b62a53
+
Adam Tkac b62a53
+#define DNS_RRL_MAX_RESPONSES	((1<<(DNS_RRL_RESPONSE_BITS-1))-1)
Adam Tkac b62a53
+#define DNS_RRL_MAX_WINDOW	3600
Adam Tkac b62a53
+#if DNS_RRL_MAX_WINDOW >= DNS_RRL_MAX_TS
Adam Tkac b62a53
+#error "DNS_RRL_MAX_WINDOW is too large"
Adam Tkac b62a53
+#endif
Adam Tkac b62a53
+#define DNS_RRL_MAX_RATE	1000
Adam Tkac b62a53
+#if DNS_RRL_MAX_RATE >= (DNS_RRL_MAX_RESPONSES / DNS_RRL_MAX_WINDOW)
Adam Tkac b62a53
+#error "DNS_RRL_MAX_rate is too large"
Adam Tkac b62a53
+#endif
Adam Tkac b62a53
+
Adam Tkac b62a53
+#if (1<<DNS_RRL_LOG_BITS) >= DNS_RRL_FOREVER
Adam Tkac b62a53
+#error DNS_RRL_LOG_BITS is too big
Adam Tkac b62a53
+#endif
Adam Tkac b62a53
+#define DNS_RRL_MAX_LOG_SECS	1800
Adam Tkac b62a53
+#if DNS_RRL_MAX_LOG_SECS >= (1<
Adam Tkac b62a53
+#error "DNS_RRL_MAX_LOG_SECS is too large"
Adam Tkac b62a53
+#endif
Adam Tkac b62a53
+#define DNS_RRL_STOP_LOG_SECS	60
Adam Tkac b62a53
+#if DNS_RRL_STOP_LOG_SECS >= (1<
Adam Tkac b62a53
+#error "DNS_RRL_STOP_LOG_SECS is too large"
Adam Tkac b62a53
+#endif
Adam Tkac b62a53
+
Adam Tkac b62a53
+
Adam Tkac b62a53
+/*
Adam Tkac b62a53
+ * A hash table of rate-limit entries.
Adam Tkac b62a53
+ */
Adam Tkac b62a53
+struct dns_rrl_hash {
Adam Tkac b62a53
+	isc_stdtime_t	check_time;
Adam Tkac b62a53
+	unsigned int	gen	    :DNS_RRL_HASH_GEN_BITS;
Adam Tkac b62a53
+	int		length;
Adam Tkac b62a53
+	dns_rrl_bin_t	bins[1];
Adam Tkac b62a53
+};
Adam Tkac b62a53
+
Adam Tkac b62a53
+/*
Adam Tkac b62a53
+ * A block of rate-limit entries.
Adam Tkac b62a53
+ */
Adam Tkac b62a53
+typedef struct dns_rrl_block dns_rrl_block_t;
Adam Tkac b62a53
+struct dns_rrl_block {
Adam Tkac b62a53
+	ISC_LINK(dns_rrl_block_t) link;
Adam Tkac b62a53
+	int		size;
Adam Tkac b62a53
+	dns_rrl_entry_t	entries[1];
Adam Tkac b62a53
+};
Adam Tkac b62a53
+
Adam Tkac b62a53
+/*
Adam Tkac b62a53
+ * A rate limited qname buffer.
Adam Tkac b62a53
+ */
Adam Tkac b62a53
+typedef struct dns_rrl_qname_buf dns_rrl_qname_buf_t;
Adam Tkac b62a53
+struct dns_rrl_qname_buf {
Adam Tkac b62a53
+	ISC_LINK(dns_rrl_qname_buf_t) link;
Adam Tkac b62a53
+	const dns_rrl_entry_t *e;
Adam Tkac b62a53
+	unsigned int	    index;
Adam Tkac b62a53
+	dns_fixedname_t	    qname;
Adam Tkac b62a53
+};
Adam Tkac b62a53
+
Tomas Hozza 574dc4
+typedef struct dns_rrl_rate dns_rrl_rate_t;
Tomas Hozza 574dc4
+struct dns_rrl_rate {
Tomas Hozza 574dc4
+	int	    r;
Tomas Hozza 574dc4
+	int	    scaled;
Tomas Hozza 574dc4
+	const char  *str;
Tomas Hozza 574dc4
+};
Tomas Hozza 574dc4
+
Adam Tkac b62a53
+/*
Adam Tkac b62a53
+ * Per-view query rate limit parameters and a pointer to database.
Adam Tkac b62a53
+ */
Adam Tkac b62a53
+typedef struct dns_rrl dns_rrl_t;
Adam Tkac b62a53
+struct dns_rrl {
Adam Tkac b62a53
+	isc_mutex_t	lock;
Adam Tkac b62a53
+	isc_mem_t	*mctx;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	isc_boolean_t	log_only;
Tomas Hozza 574dc4
+	dns_rrl_rate_t	responses_per_second;
Tomas Hozza 574dc4
+	dns_rrl_rate_t	referrals_per_second;
Tomas Hozza 574dc4
+	dns_rrl_rate_t	nodata_per_second;
Tomas Hozza 574dc4
+	dns_rrl_rate_t	nxdomains_per_second;
Tomas Hozza 574dc4
+	dns_rrl_rate_t	errors_per_second;
Tomas Hozza 574dc4
+	dns_rrl_rate_t	all_per_second;
Tomas Hozza 574dc4
+	dns_rrl_rate_t	slip;
Adam Tkac b62a53
+	int		window;
Adam Tkac b62a53
+	double		qps_scale;
Adam Tkac b62a53
+	int		max_entries;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	dns_acl_t	*exempt;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	int		num_entries;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	int		qps_responses;
Adam Tkac b62a53
+	isc_stdtime_t	qps_time;
Adam Tkac b62a53
+	double		qps;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	unsigned int	probes;
Adam Tkac b62a53
+	unsigned int	searches;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	ISC_LIST(dns_rrl_block_t) blocks;
Adam Tkac b62a53
+	ISC_LIST(dns_rrl_entry_t) lru;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	dns_rrl_hash_t	*hash;
Adam Tkac b62a53
+	dns_rrl_hash_t	*old_hash;
Adam Tkac b62a53
+	unsigned int	hash_gen;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	unsigned int	ts_gen;
Adam Tkac b62a53
+# define DNS_RRL_TS_BASES   (1<
Adam Tkac b62a53
+	isc_stdtime_t	ts_bases[DNS_RRL_TS_BASES];
Adam Tkac b62a53
+
Adam Tkac b62a53
+	int		ipv4_prefixlen;
Adam Tkac b62a53
+	isc_uint32_t	ipv4_mask;
Adam Tkac b62a53
+	int		ipv6_prefixlen;
Adam Tkac b62a53
+	isc_uint32_t	ipv6_mask[4];
Adam Tkac b62a53
+
Adam Tkac b62a53
+	isc_stdtime_t	log_stops_time;
Adam Tkac b62a53
+	dns_rrl_entry_t	*last_logged;
Adam Tkac b62a53
+	int		num_logged;
Adam Tkac b62a53
+	int		num_qnames;
Adam Tkac b62a53
+	ISC_LIST(dns_rrl_qname_buf_t) qname_free;
Adam Tkac b62a53
+# define DNS_RRL_QNAMES	    (1<
Adam Tkac b62a53
+	dns_rrl_qname_buf_t *qnames[DNS_RRL_QNAMES];
Adam Tkac b62a53
+};
Adam Tkac b62a53
+
Adam Tkac b62a53
+typedef enum {
Adam Tkac b62a53
+	DNS_RRL_RESULT_OK,
Adam Tkac b62a53
+	DNS_RRL_RESULT_DROP,
Adam Tkac b62a53
+	DNS_RRL_RESULT_SLIP,
Adam Tkac b62a53
+} dns_rrl_result_t;
Adam Tkac b62a53
+
Adam Tkac b62a53
+dns_rrl_result_t
Adam Tkac b62a53
+dns_rrl(dns_view_t *view,
Adam Tkac b62a53
+	const isc_sockaddr_t *client_addr, isc_boolean_t is_tcp,
Adam Tkac b62a53
+	dns_rdataclass_t rdclass, dns_rdatatype_t qtype,
Adam Tkac b62a53
+	dns_name_t *qname, isc_result_t resp_result, isc_stdtime_t now,
Adam Tkac b62a53
+	isc_boolean_t wouldlog, char *log_buf, unsigned int log_buf_len);
Adam Tkac b62a53
+
Adam Tkac b62a53
+void
Adam Tkac b62a53
+dns_rrl_view_destroy(dns_view_t *view);
Adam Tkac b62a53
+
Adam Tkac b62a53
+isc_result_t
Adam Tkac b62a53
+dns_rrl_init(dns_rrl_t **rrlp, dns_view_t *view, int min_entries);
Adam Tkac b62a53
+
Adam Tkac b62a53
+ISC_LANG_ENDDECLS
Adam Tkac b62a53
+
Adam Tkac b62a53
+#endif /* DNS_RRL_H */
Tomas Hozza 574dc4
diff -r -u lib/dns/include/dns/view.h-orig lib/dns/include/dns/view.h
Tomas Hozza 574dc4
--- lib/dns/include/dns/view.h-orig	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
+++ lib/dns/include/dns/view.h	2004-01-01 00:00:00.000000000 +0000
Adam Tkac b62a53
@@ -73,6 +73,7 @@
Adam Tkac b62a53
 
Adam Tkac b62a53
 #include <dns/acl.h>
Adam Tkac b62a53
 #include <dns/fixedname.h>
Adam Tkac b62a53
+#include <dns/rrl.h>
Adam Tkac b62a53
 #include <dns/rdatastruct.h>
Adam Tkac b62a53
 #include <dns/rpz.h>
Adam Tkac b62a53
 #include <dns/types.h>
Tomas Hozza 574dc4
@@ -142,6 +143,7 @@
Adam Tkac b62a53
 	dns_rbt_t *			answeracl_exclude;
Adam Tkac b62a53
 	dns_rbt_t *			denyanswernames;
Adam Tkac b62a53
 	dns_rbt_t *			answernames_exclude;
Adam Tkac b62a53
+	dns_rrl_t *			rrl;
Adam Tkac b62a53
 	isc_boolean_t			provideixfr;
Adam Tkac b62a53
 	isc_boolean_t			requestnsid;
Adam Tkac b62a53
 	dns_ttl_t			maxcachettl;
Tomas Hozza 574dc4
diff -r -u lib/dns/log.c-orig lib/dns/log.c
Tomas Hozza 574dc4
--- lib/dns/log.c-orig	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
+++ lib/dns/log.c	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
@@ -45,6 +45,7 @@
Adam Tkac b62a53
 	{ "delegation-only", 0 },
Adam Tkac b62a53
 	{ "edns-disabled", 0 },
Adam Tkac b62a53
 	{ "rpz",	0 },
Adam Tkac b62a53
+	{ "rate-limit",	0 },
Adam Tkac b62a53
 	{ NULL, 	0 }
Adam Tkac b62a53
 };
Adam Tkac b62a53
 
Tomas Hozza 574dc4
diff -r -u lib/dns/rrl.c-orig lib/dns/rrl.c
Tomas Hozza 574dc4
--- lib/dns/rrl.c-orig	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
+++ lib/dns/rrl.c	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
@@ -0,0 +1,1324 @@
Adam Tkac b62a53
+/*
Tomas Hozza 574dc4
+ * Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
Adam Tkac b62a53
+ *
Adam Tkac b62a53
+ * Permission to use, copy, modify, and/or distribute this software for any
Adam Tkac b62a53
+ * purpose with or without fee is hereby granted, provided that the above
Adam Tkac b62a53
+ * copyright notice and this permission notice appear in all copies.
Adam Tkac b62a53
+ *
Adam Tkac b62a53
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
Adam Tkac b62a53
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
Adam Tkac b62a53
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
Adam Tkac b62a53
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
Adam Tkac b62a53
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
Adam Tkac b62a53
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
Adam Tkac b62a53
+ * PERFORMANCE OF THIS SOFTWARE.
Adam Tkac b62a53
+ */
Adam Tkac b62a53
+
Adam Tkac b62a53
+/*! \file */
Adam Tkac b62a53
+
Adam Tkac b62a53
+/*
Adam Tkac b62a53
+ * Rate limit DNS responses.
Adam Tkac b62a53
+ */
Adam Tkac b62a53
+
Adam Tkac b62a53
+/* #define ISC_LIST_CHECKINIT */
Adam Tkac b62a53
+
Adam Tkac b62a53
+#include <config.h>
Adam Tkac b62a53
+#include <isc/mem.h>
Adam Tkac b62a53
+#include <isc/net.h>
Adam Tkac b62a53
+#include <isc/netaddr.h>
Tomas Hozza 574dc4
+#include <isc/print.h>
Adam Tkac b62a53
+
Adam Tkac b62a53
+#include <dns/result.h>
Adam Tkac b62a53
+#include <dns/rcode.h>
Adam Tkac b62a53
+#include <dns/rdatatype.h>
Adam Tkac b62a53
+#include <dns/rdataclass.h>
Adam Tkac b62a53
+#include <dns/log.h>
Adam Tkac b62a53
+#include <dns/rrl.h>
Adam Tkac b62a53
+#include <dns/view.h>
Adam Tkac b62a53
+
Adam Tkac b62a53
+static void
Adam Tkac b62a53
+log_end(dns_rrl_t *rrl, dns_rrl_entry_t *e, isc_boolean_t early,
Adam Tkac b62a53
+	char *log_buf, unsigned int log_buf_len);
Adam Tkac b62a53
+
Adam Tkac b62a53
+/*
Adam Tkac b62a53
+ * Get a modulus for a hash function that is tolerably likely to be
Adam Tkac b62a53
+ * relatively prime to most inputs.  Of course, we get a prime for for initial
Adam Tkac b62a53
+ * values not larger than the square of the last prime.  We often get a prime
Adam Tkac b62a53
+ * after that.
Adam Tkac b62a53
+ * This works well in practice for hash tables up to at least 100
Adam Tkac b62a53
+ * times the square of the last prime and better than a multiplicative hash.
Adam Tkac b62a53
+ */
Adam Tkac b62a53
+static int
Adam Tkac b62a53
+hash_divisor(unsigned int initial) {
Adam Tkac b62a53
+	static isc_uint16_t primes[] = {
Adam Tkac b62a53
+		  3,   5,   7,  11,  13,  17,  19,  23,  29,  31,  37,  41,
Adam Tkac b62a53
+		 43,  47,  53,  59,  61,  67,  71,  73,  79,  83,  89,  97,
Adam Tkac b62a53
+#if 0
Adam Tkac b62a53
+		101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157,
Adam Tkac b62a53
+		163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227,
Adam Tkac b62a53
+		229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283,
Adam Tkac b62a53
+		293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367,
Adam Tkac b62a53
+		373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439,
Adam Tkac b62a53
+		443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509,
Adam Tkac b62a53
+		521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599,
Adam Tkac b62a53
+		601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661,
Adam Tkac b62a53
+		673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751,
Adam Tkac b62a53
+		757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829,
Adam Tkac b62a53
+		839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919,
Adam Tkac b62a53
+		929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997,1009,
Adam Tkac b62a53
+#endif
Adam Tkac b62a53
+	};
Adam Tkac b62a53
+	int divisions, tries;
Adam Tkac b62a53
+	unsigned int result;
Adam Tkac b62a53
+	isc_uint16_t *pp, p;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	result = initial;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	if (primes[sizeof(primes)/sizeof(primes[0])-1] >= result) {
Adam Tkac b62a53
+		pp = primes;
Adam Tkac b62a53
+		while (*pp < result)
Adam Tkac b62a53
+			++pp;
Adam Tkac b62a53
+		return (*pp);
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+
Adam Tkac b62a53
+	if ((result & 1) == 0)
Adam Tkac b62a53
+		++result;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	divisions = 0;
Adam Tkac b62a53
+	tries = 1;
Adam Tkac b62a53
+	pp = primes;
Adam Tkac b62a53
+	do {
Adam Tkac b62a53
+		p = *pp++;
Adam Tkac b62a53
+		++divisions;
Adam Tkac b62a53
+		if ((result % p) == 0) {
Adam Tkac b62a53
+			++tries;
Adam Tkac b62a53
+			result += 2;
Adam Tkac b62a53
+			pp = primes;
Adam Tkac b62a53
+		}
Tomas Hozza 574dc4
+	} while (pp < &primes[sizeof(primes) / sizeof(primes[0])]);
Adam Tkac b62a53
+
Adam Tkac b62a53
+	if (isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DEBUG3))
Adam Tkac b62a53
+		isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
Adam Tkac b62a53
+			      DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_DEBUG3,
Adam Tkac b62a53
+			      "%d hash_divisor() divisions in %d tries"
Adam Tkac b62a53
+			      " to get %d from %d",
Adam Tkac b62a53
+			      divisions, tries, result, initial);
Adam Tkac b62a53
+
Adam Tkac b62a53
+	return (result);
Adam Tkac b62a53
+}
Adam Tkac b62a53
+
Adam Tkac b62a53
+/*
Adam Tkac b62a53
+ * Convert a timestamp to a number of seconds in the past.
Adam Tkac b62a53
+ */
Adam Tkac b62a53
+static inline int
Adam Tkac b62a53
+delta_rrl_time(isc_stdtime_t ts, isc_stdtime_t now) {
Adam Tkac b62a53
+	int delta;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	delta = now - ts;
Adam Tkac b62a53
+	if (delta >= 0)
Adam Tkac b62a53
+		return (delta);
Adam Tkac b62a53
+
Adam Tkac b62a53
+	/*
Adam Tkac b62a53
+	 * The timestamp is in the future.  That future might result from
Adam Tkac b62a53
+	 * re-ordered requests, because we use timestamps on requests
Adam Tkac b62a53
+	 * instead of consulting a clock.  Timestamps in the distant future are
Adam Tkac b62a53
+	 * assumed to result from clock changes.  When the clock changes to
Adam Tkac b62a53
+	 * the past, make existing timestamps appear to be in the past.
Adam Tkac b62a53
+	 */
Adam Tkac b62a53
+	if (delta < -DNS_RRL_MAX_TIME_TRAVEL)
Adam Tkac b62a53
+		return (DNS_RRL_FOREVER);
Adam Tkac b62a53
+	return (0);
Adam Tkac b62a53
+}
Adam Tkac b62a53
+
Adam Tkac b62a53
+static inline int
Adam Tkac b62a53
+get_age(const dns_rrl_t *rrl, const dns_rrl_entry_t *e, isc_stdtime_t now) {
Adam Tkac b62a53
+	if (!e->ts_valid)
Adam Tkac b62a53
+		return (DNS_RRL_FOREVER);
Adam Tkac b62a53
+	return (delta_rrl_time(e->ts + rrl->ts_bases[e->ts_gen], now));
Adam Tkac b62a53
+}
Adam Tkac b62a53
+
Adam Tkac b62a53
+static inline void
Adam Tkac b62a53
+set_age(dns_rrl_t *rrl, dns_rrl_entry_t *e, isc_stdtime_t now) {
Adam Tkac b62a53
+	dns_rrl_entry_t *e_old;
Adam Tkac b62a53
+	unsigned int ts_gen;
Adam Tkac b62a53
+	int i, ts;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	ts_gen = rrl->ts_gen;
Adam Tkac b62a53
+	ts = now - rrl->ts_bases[ts_gen];
Adam Tkac b62a53
+	if (ts < 0) {
Adam Tkac b62a53
+		if (ts < -DNS_RRL_MAX_TIME_TRAVEL)
Adam Tkac b62a53
+			ts = DNS_RRL_FOREVER;
Adam Tkac b62a53
+		else
Adam Tkac b62a53
+			ts = 0;
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+
Adam Tkac b62a53
+	/*
Adam Tkac b62a53
+	 * Make a new timestamp base if the current base is too old.
Adam Tkac b62a53
+	 * All entries older than DNS_RRL_MAX_WINDOW seconds are ancient,
Adam Tkac b62a53
+	 * useless history.  Their timestamps can be treated as if they are
Adam Tkac b62a53
+	 * all the same.
Adam Tkac b62a53
+	 * We only do arithmetic on more recent timestamps, so bases for
Adam Tkac b62a53
+	 * older timestamps can be recycled provided the old timestamps are
Adam Tkac b62a53
+	 * marked as ancient history.
Adam Tkac b62a53
+	 * This loop is almost always very short because most entries are
Adam Tkac b62a53
+	 * recycled after one second and any entries that need to be marked
Adam Tkac b62a53
+	 * are older than (DNS_RRL_TS_BASES)*DNS_RRL_MAX_TS seconds.
Adam Tkac b62a53
+	 */
Adam Tkac b62a53
+	if (ts >= DNS_RRL_MAX_TS) {
Tomas Hozza 574dc4
+		ts_gen = (ts_gen + 1) % DNS_RRL_TS_BASES;
Adam Tkac b62a53
+		for (e_old = ISC_LIST_TAIL(rrl->lru), i = 0;
Tomas Hozza 574dc4
+		     e_old != NULL && (e_old->ts_gen == ts_gen ||
Tomas Hozza 574dc4
+				       !ISC_LINK_LINKED(e_old, hlink));
Tomas Hozza 574dc4
+		     e_old = ISC_LIST_PREV(e_old, lru), ++i)
Tomas Hozza 574dc4
+		{
Tomas Hozza 574dc4
+			e_old->ts_valid = ISC_FALSE;
Adam Tkac b62a53
+		}
Adam Tkac b62a53
+		if (i != 0)
Adam Tkac b62a53
+			isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
Adam Tkac b62a53
+				      DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_DEBUG1,
Adam Tkac b62a53
+				      "rrl new time base scanned %d entries"
Adam Tkac b62a53
+				      " at %d for %d %d %d %d",
Adam Tkac b62a53
+				      i, now, rrl->ts_bases[ts_gen],
Tomas Hozza 574dc4
+				      rrl->ts_bases[(ts_gen + 1) %
Tomas Hozza 574dc4
+					DNS_RRL_TS_BASES],
Tomas Hozza 574dc4
+				      rrl->ts_bases[(ts_gen + 2) %
Tomas Hozza 574dc4
+					DNS_RRL_TS_BASES],
Tomas Hozza 574dc4
+				      rrl->ts_bases[(ts_gen + 3) %
Tomas Hozza 574dc4
+					DNS_RRL_TS_BASES]);
Adam Tkac b62a53
+		rrl->ts_gen = ts_gen;
Adam Tkac b62a53
+		rrl->ts_bases[ts_gen] = now;
Adam Tkac b62a53
+		ts = 0;
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+
Adam Tkac b62a53
+	e->ts_gen = ts_gen;
Adam Tkac b62a53
+	e->ts = ts;
Adam Tkac b62a53
+	e->ts_valid = ISC_TRUE;
Adam Tkac b62a53
+}
Adam Tkac b62a53
+
Adam Tkac b62a53
+static isc_result_t
Adam Tkac b62a53
+expand_entries(dns_rrl_t *rrl, int new) {
Adam Tkac b62a53
+	unsigned int bsize;
Adam Tkac b62a53
+	dns_rrl_block_t *b;
Adam Tkac b62a53
+	dns_rrl_entry_t *e;
Adam Tkac b62a53
+	double rate;
Adam Tkac b62a53
+	int i;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	if (rrl->num_entries+new >= rrl->max_entries && rrl->max_entries != 0) {
Adam Tkac b62a53
+		if (rrl->num_entries >= rrl->max_entries)
Adam Tkac b62a53
+			return (ISC_R_SUCCESS);
Adam Tkac b62a53
+		new = rrl->max_entries - rrl->num_entries;
Adam Tkac b62a53
+		if (new <= 0)
Adam Tkac b62a53
+			return (ISC_R_NOMEMORY);
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+
Adam Tkac b62a53
+	/*
Adam Tkac b62a53
+	 * Log expansions so that the user can tune max-table-size
Adam Tkac b62a53
+	 * and min-table-size.
Adam Tkac b62a53
+	 */
Adam Tkac b62a53
+	if (isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DROP) &&
Adam Tkac b62a53
+	    rrl->hash != NULL) {
Adam Tkac b62a53
+		rate = rrl->probes;
Adam Tkac b62a53
+		if (rrl->searches != 0)
Adam Tkac b62a53
+			rate /= rrl->searches;
Adam Tkac b62a53
+		isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
Adam Tkac b62a53
+			      DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_DROP,
Adam Tkac b62a53
+			      "increase from %d to %d RRL entries with"
Adam Tkac b62a53
+			      " %d bins; average search length %.1f",
Adam Tkac b62a53
+			      rrl->num_entries, rrl->num_entries+new,
Adam Tkac b62a53
+			      rrl->hash->length, rate);
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+
Adam Tkac b62a53
+	bsize = sizeof(dns_rrl_block_t) + (new-1)*sizeof(dns_rrl_entry_t);
Adam Tkac b62a53
+	b = isc_mem_get(rrl->mctx, bsize);
Adam Tkac b62a53
+	if (b == NULL) {
Adam Tkac b62a53
+		isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
Adam Tkac b62a53
+			      DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_FAIL,
Adam Tkac b62a53
+			      "isc_mem_get(%d) failed for RRL entries",
Adam Tkac b62a53
+			      bsize);
Adam Tkac b62a53
+		return (ISC_R_NOMEMORY);
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+	memset(b, 0, bsize);
Adam Tkac b62a53
+	b->size = bsize;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	e = b->entries;
Adam Tkac b62a53
+	for (i = 0; i < new; ++i, ++e) {
Adam Tkac b62a53
+		ISC_LINK_INIT(e, hlink);
Adam Tkac b62a53
+		ISC_LIST_INITANDAPPEND(rrl->lru, e, lru);
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+	rrl->num_entries += new;
Adam Tkac b62a53
+	ISC_LIST_INITANDAPPEND(rrl->blocks, b, link);
Adam Tkac b62a53
+
Adam Tkac b62a53
+	return (ISC_R_SUCCESS);
Adam Tkac b62a53
+}
Adam Tkac b62a53
+
Adam Tkac b62a53
+static inline dns_rrl_bin_t *
Adam Tkac b62a53
+get_bin(dns_rrl_hash_t *hash, unsigned int hval) {
Adam Tkac b62a53
+	return (&hash->bins[hval % hash->length]);
Adam Tkac b62a53
+}
Adam Tkac b62a53
+
Adam Tkac b62a53
+static void
Adam Tkac b62a53
+free_old_hash(dns_rrl_t *rrl) {
Adam Tkac b62a53
+	dns_rrl_hash_t *old_hash;
Adam Tkac b62a53
+	dns_rrl_bin_t *old_bin;
Adam Tkac b62a53
+	dns_rrl_entry_t *e, *e_next;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	old_hash = rrl->old_hash;
Adam Tkac b62a53
+	for (old_bin = &old_hash->bins[0];
Adam Tkac b62a53
+	     old_bin < &old_hash->bins[old_hash->length];
Tomas Hozza 574dc4
+	     ++old_bin)
Tomas Hozza 574dc4
+	{
Adam Tkac b62a53
+		for (e = ISC_LIST_HEAD(*old_bin); e != NULL; e = e_next) {
Adam Tkac b62a53
+			e_next = ISC_LIST_NEXT(e, hlink);
Adam Tkac b62a53
+			ISC_LINK_INIT(e, hlink);
Adam Tkac b62a53
+		}
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+
Adam Tkac b62a53
+	isc_mem_put(rrl->mctx, old_hash,
Adam Tkac b62a53
+		    sizeof(*old_hash)
Tomas Hozza 574dc4
+		      + (old_hash->length - 1) * sizeof(old_hash->bins[0]));
Adam Tkac b62a53
+	rrl->old_hash = NULL;
Adam Tkac b62a53
+}
Adam Tkac b62a53
+
Adam Tkac b62a53
+static isc_result_t
Adam Tkac b62a53
+expand_rrl_hash(dns_rrl_t *rrl, isc_stdtime_t now) {
Adam Tkac b62a53
+	dns_rrl_hash_t *hash;
Adam Tkac b62a53
+	int old_bins, new_bins, hsize;
Adam Tkac b62a53
+	double rate;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	if (rrl->old_hash != NULL)
Adam Tkac b62a53
+		free_old_hash(rrl);
Adam Tkac b62a53
+
Adam Tkac b62a53
+	/*
Adam Tkac b62a53
+	 * Most searches fail and so go to the end of the chain.
Adam Tkac b62a53
+	 * Use a small hash table load factor.
Adam Tkac b62a53
+	 */
Adam Tkac b62a53
+	old_bins = (rrl->hash == NULL) ? 0 : rrl->hash->length;
Adam Tkac b62a53
+	new_bins = old_bins/8 + old_bins;
Adam Tkac b62a53
+	if (new_bins < rrl->num_entries)
Adam Tkac b62a53
+		new_bins = rrl->num_entries;
Adam Tkac b62a53
+	new_bins = hash_divisor(new_bins);
Adam Tkac b62a53
+
Adam Tkac b62a53
+	hsize = sizeof(dns_rrl_hash_t) + (new_bins-1)*sizeof(hash->bins[0]);
Adam Tkac b62a53
+	hash = isc_mem_get(rrl->mctx, hsize);
Adam Tkac b62a53
+	if (hash == NULL) {
Adam Tkac b62a53
+		isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
Adam Tkac b62a53
+			      DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_FAIL,
Adam Tkac b62a53
+			      "isc_mem_get(%d) failed for"
Adam Tkac b62a53
+			      " RRL hash table",
Adam Tkac b62a53
+			      hsize);
Adam Tkac b62a53
+		return (ISC_R_NOMEMORY);
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+	memset(hash, 0, hsize);
Adam Tkac b62a53
+	hash->length = new_bins;
Adam Tkac b62a53
+	rrl->hash_gen ^= 1;
Adam Tkac b62a53
+	hash->gen = rrl->hash_gen;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	if (isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DROP) && old_bins != 0) {
Adam Tkac b62a53
+		rate = rrl->probes;
Adam Tkac b62a53
+		if (rrl->searches != 0)
Adam Tkac b62a53
+			rate /= rrl->searches;
Adam Tkac b62a53
+		isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
Adam Tkac b62a53
+			      DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_DROP,
Adam Tkac b62a53
+			      "increase from %d to %d RRL bins for"
Adam Tkac b62a53
+			      " %d entries; average search length %.1f",
Adam Tkac b62a53
+			      old_bins, new_bins, rrl->num_entries, rate);
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+
Adam Tkac b62a53
+	rrl->old_hash = rrl->hash;
Adam Tkac b62a53
+	if (rrl->old_hash != NULL)
Adam Tkac b62a53
+		rrl->old_hash->check_time = now;
Adam Tkac b62a53
+	rrl->hash = hash;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	return (ISC_R_SUCCESS);
Adam Tkac b62a53
+}
Adam Tkac b62a53
+
Adam Tkac b62a53
+static void
Adam Tkac b62a53
+ref_entry(dns_rrl_t *rrl, dns_rrl_entry_t *e, int probes, isc_stdtime_t now) {
Adam Tkac b62a53
+	/*
Adam Tkac b62a53
+	 * Make the entry most recently used.
Adam Tkac b62a53
+	 */
Adam Tkac b62a53
+	if (ISC_LIST_HEAD(rrl->lru) != e) {
Adam Tkac b62a53
+		if (e == rrl->last_logged)
Adam Tkac b62a53
+			rrl->last_logged = ISC_LIST_PREV(e, lru);
Adam Tkac b62a53
+		ISC_LIST_UNLINK(rrl->lru, e, lru);
Adam Tkac b62a53
+		ISC_LIST_PREPEND(rrl->lru, e, lru);
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+
Adam Tkac b62a53
+	/*
Adam Tkac b62a53
+	 * Expand the hash table if it is time and necessary.
Adam Tkac b62a53
+	 * This will leave the newly referenced entry in a chain in the
Adam Tkac b62a53
+	 * old hash table.  It will migrate to the new hash table the next
Adam Tkac b62a53
+	 * time it is used or be cut loose when the old hash table is destroyed.
Adam Tkac b62a53
+	 */
Adam Tkac b62a53
+	rrl->probes += probes;
Adam Tkac b62a53
+	++rrl->searches;
Adam Tkac b62a53
+	if (rrl->searches > 100 &&
Adam Tkac b62a53
+	    delta_rrl_time(rrl->hash->check_time, now) > 1) {
Adam Tkac b62a53
+		if (rrl->probes/rrl->searches > 2)
Adam Tkac b62a53
+			expand_rrl_hash(rrl, now);
Adam Tkac b62a53
+		rrl->hash->check_time = now;
Adam Tkac b62a53
+		rrl->probes = 0;
Adam Tkac b62a53
+		rrl->searches = 0;
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+}
Adam Tkac b62a53
+
Adam Tkac b62a53
+static inline isc_boolean_t
Adam Tkac b62a53
+key_cmp(const dns_rrl_key_t *a, const dns_rrl_key_t *b) {
Adam Tkac b62a53
+	if (memcmp(a, b, sizeof(dns_rrl_key_t)) == 0)
Adam Tkac b62a53
+		return (ISC_TRUE);
Adam Tkac b62a53
+	return (ISC_FALSE);
Adam Tkac b62a53
+}
Adam Tkac b62a53
+
Adam Tkac b62a53
+static inline isc_uint32_t
Adam Tkac b62a53
+hash_key(const dns_rrl_key_t *key) {
Adam Tkac b62a53
+	isc_uint32_t hval;
Adam Tkac b62a53
+	int i;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	hval = key->w[0];
Tomas Hozza 574dc4
+	for (i = sizeof(*key) / sizeof(key->w[0]) - 1; i >= 0; --i) {
Adam Tkac b62a53
+		hval = key->w[i] + (hval<<1);
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+	return (hval);
Adam Tkac b62a53
+}
Adam Tkac b62a53
+
Adam Tkac b62a53
+/*
Adam Tkac b62a53
+ * Construct the hash table key.
Adam Tkac b62a53
+ * Use a hash of the DNS query name to save space in the database.
Adam Tkac b62a53
+ * Collisions result in legitimate rate limiting responses for one
Adam Tkac b62a53
+ * query name also limiting responses for other names to the
Adam Tkac b62a53
+ * same client.  This is rare and benign enough given the large
Adam Tkac b62a53
+ * space costs compared to keeping the entire name in the database
Adam Tkac b62a53
+ * entry or the time costs of dynamic allocation.
Adam Tkac b62a53
+ */
Adam Tkac b62a53
+static void
Adam Tkac b62a53
+make_key(const dns_rrl_t *rrl, dns_rrl_key_t *key,
Adam Tkac b62a53
+	 const isc_sockaddr_t *client_addr,
Adam Tkac b62a53
+	 dns_rdatatype_t qtype, dns_name_t *qname, dns_rdataclass_t qclass,
Adam Tkac b62a53
+	 dns_rrl_rtype_t rtype)
Adam Tkac b62a53
+{
Adam Tkac b62a53
+	dns_name_t base;
Adam Tkac b62a53
+	dns_offsets_t base_offsets;
Adam Tkac b62a53
+	int labels, i;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	memset(key, 0, sizeof(*key));
Adam Tkac b62a53
+
Adam Tkac b62a53
+	key->s.rtype = rtype;
Tomas Hozza 574dc4
+	if (rtype == DNS_RRL_RTYPE_QUERY) {
Adam Tkac b62a53
+		key->s.qtype = qtype;
Tomas Hozza 574dc4
+		key->s.qclass = qclass & 0xff;
Tomas Hozza 574dc4
+	} else if (rtype == DNS_RRL_RTYPE_REFERRAL ||
Tomas Hozza 574dc4
+		   rtype == DNS_RRL_RTYPE_NODATA) {
Tomas Hozza 574dc4
+		/*
Tomas Hozza 574dc4
+		 * Because there is no qtype in the empty answer sections of
Tomas Hozza 574dc4
+		 * referral and NODATA responses, count them as the same.
Tomas Hozza 574dc4
+		 */
Tomas Hozza 574dc4
+		key->s.qclass = qclass & 0xff;
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+
Adam Tkac b62a53
+	if (qname != NULL && qname->labels != 0) {
Adam Tkac b62a53
+		/*
Adam Tkac b62a53
+		 * Ignore the first label of wildcards.
Adam Tkac b62a53
+		 */
Adam Tkac b62a53
+		if ((qname->attributes & DNS_NAMEATTR_WILDCARD) != 0 &&
Tomas Hozza 574dc4
+		    (labels = dns_name_countlabels(qname)) > 1)
Tomas Hozza 574dc4
+		{
Adam Tkac b62a53
+			dns_name_init(&base, base_offsets);
Adam Tkac b62a53
+			dns_name_getlabelsequence(qname, 1, labels-1, &base);
Adam Tkac b62a53
+			key->s.qname_hash = dns_name_hashbylabel(&base,
Adam Tkac b62a53
+							ISC_FALSE);
Adam Tkac b62a53
+		} else {
Adam Tkac b62a53
+			key->s.qname_hash = dns_name_hashbylabel(qname,
Adam Tkac b62a53
+							ISC_FALSE);
Adam Tkac b62a53
+		}
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+
Adam Tkac b62a53
+	switch (client_addr->type.sa.sa_family) {
Adam Tkac b62a53
+	case AF_INET:
Adam Tkac b62a53
+		key->s.ip[0] = (client_addr->type.sin.sin_addr.s_addr &
Adam Tkac b62a53
+			      rrl->ipv4_mask);
Adam Tkac b62a53
+		break;
Adam Tkac b62a53
+	case AF_INET6:
Adam Tkac b62a53
+		key->s.ipv6 = ISC_TRUE;
Adam Tkac b62a53
+		memcpy(key->s.ip, &client_addr->type.sin6.sin6_addr,
Adam Tkac b62a53
+		       sizeof(key->s.ip));
Adam Tkac b62a53
+		for (i = 0; i < DNS_RRL_MAX_PREFIX/32; ++i)
Adam Tkac b62a53
+			key->s.ip[i] &= rrl->ipv6_mask[i];
Adam Tkac b62a53
+		break;
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+}
Adam Tkac b62a53
+
Tomas Hozza 574dc4
+static inline dns_rrl_rate_t *
Tomas Hozza 574dc4
+get_rate(dns_rrl_t *rrl, dns_rrl_rtype_t rtype) {
Tomas Hozza 574dc4
+	switch (rtype) {
Tomas Hozza 574dc4
+	case DNS_RRL_RTYPE_QUERY:
Tomas Hozza 574dc4
+		return (&rrl->responses_per_second);
Tomas Hozza 574dc4
+	case DNS_RRL_RTYPE_REFERRAL:
Tomas Hozza 574dc4
+		return (&rrl->referrals_per_second);
Tomas Hozza 574dc4
+	case DNS_RRL_RTYPE_NODATA:
Tomas Hozza 574dc4
+		return (&rrl->nodata_per_second);
Tomas Hozza 574dc4
+	case DNS_RRL_RTYPE_NXDOMAIN:
Tomas Hozza 574dc4
+		return (&rrl->nxdomains_per_second);
Tomas Hozza 574dc4
+	case DNS_RRL_RTYPE_ERROR:
Tomas Hozza 574dc4
+		return (&rrl->errors_per_second);
Tomas Hozza 574dc4
+	case DNS_RRL_RTYPE_ALL:
Tomas Hozza 574dc4
+		return (&rrl->all_per_second);
Tomas Hozza 574dc4
+	default:
Tomas Hozza 574dc4
+		INSIST(0);
Tomas Hozza 574dc4
+	}
Tomas Hozza 574dc4
+	return (NULL);
Tomas Hozza 574dc4
+}
Tomas Hozza 574dc4
+
Tomas Hozza 574dc4
+static int
Tomas Hozza 574dc4
+response_balance(dns_rrl_t *rrl, const dns_rrl_entry_t *e, int age) {
Tomas Hozza 574dc4
+	dns_rrl_rate_t *ratep;
Adam Tkac b62a53
+	int balance, rate;
Adam Tkac b62a53
+
Tomas Hozza 574dc4
+	if (e->key.s.rtype == DNS_RRL_RTYPE_TCP) {
Tomas Hozza 574dc4
+		rate = 1;
Tomas Hozza 574dc4
+	} else {
Tomas Hozza 574dc4
+		ratep = get_rate(rrl, e->key.s.rtype);
Tomas Hozza 574dc4
+		rate = ratep->scaled;
Adam Tkac b62a53
+	}
Tomas Hozza 574dc4
+
Tomas Hozza 574dc4
+	balance = e->responses + age * rate;
Adam Tkac b62a53
+	if (balance > rate)
Adam Tkac b62a53
+		balance = rate;
Adam Tkac b62a53
+	return (balance);
Adam Tkac b62a53
+}
Adam Tkac b62a53
+
Adam Tkac b62a53
+/*
Adam Tkac b62a53
+ * Search for an entry for a response and optionally create it.
Adam Tkac b62a53
+ */
Adam Tkac b62a53
+static dns_rrl_entry_t *
Adam Tkac b62a53
+get_entry(dns_rrl_t *rrl, const isc_sockaddr_t *client_addr,
Adam Tkac b62a53
+	  dns_rdataclass_t qclass, dns_rdatatype_t qtype, dns_name_t *qname,
Adam Tkac b62a53
+	  dns_rrl_rtype_t rtype, isc_stdtime_t now, isc_boolean_t create,
Adam Tkac b62a53
+	  char *log_buf, unsigned int log_buf_len)
Adam Tkac b62a53
+{
Adam Tkac b62a53
+	dns_rrl_key_t key;
Adam Tkac b62a53
+	isc_uint32_t hval;
Adam Tkac b62a53
+	dns_rrl_entry_t *e;
Adam Tkac b62a53
+	dns_rrl_hash_t *hash;
Adam Tkac b62a53
+	dns_rrl_bin_t *new_bin, *old_bin;
Adam Tkac b62a53
+	int probes, age;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	make_key(rrl, &key, client_addr, qtype, qname, qclass, rtype);
Adam Tkac b62a53
+	hval = hash_key(&key);
Adam Tkac b62a53
+
Adam Tkac b62a53
+	/*
Adam Tkac b62a53
+	 * Look for the entry in the current hash table.
Adam Tkac b62a53
+	 */
Adam Tkac b62a53
+	new_bin = get_bin(rrl->hash, hval);
Adam Tkac b62a53
+	probes = 1;
Adam Tkac b62a53
+	e = ISC_LIST_HEAD(*new_bin);
Adam Tkac b62a53
+	while (e != NULL) {
Adam Tkac b62a53
+		if (key_cmp(&e->key, &key)) {
Adam Tkac b62a53
+			ref_entry(rrl, e, probes, now);
Adam Tkac b62a53
+			return (e);
Adam Tkac b62a53
+		}
Adam Tkac b62a53
+		++probes;
Adam Tkac b62a53
+		e = ISC_LIST_NEXT(e, hlink);
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+
Adam Tkac b62a53
+	/*
Adam Tkac b62a53
+	 * Look in the old hash table.
Adam Tkac b62a53
+	 */
Adam Tkac b62a53
+	if (rrl->old_hash != NULL) {
Adam Tkac b62a53
+		old_bin = get_bin(rrl->old_hash, hval);
Adam Tkac b62a53
+		e = ISC_LIST_HEAD(*old_bin);
Adam Tkac b62a53
+		while (e != NULL) {
Adam Tkac b62a53
+			if (key_cmp(&e->key, &key)) {
Adam Tkac b62a53
+				ISC_LIST_UNLINK(*old_bin, e, hlink);
Adam Tkac b62a53
+				ISC_LIST_PREPEND(*new_bin, e, hlink);
Adam Tkac b62a53
+				e->hash_gen = rrl->hash_gen;
Adam Tkac b62a53
+				ref_entry(rrl, e, probes, now);
Adam Tkac b62a53
+				return (e);
Adam Tkac b62a53
+			}
Tomas Hozza 574dc4
+			e = ISC_LIST_NEXT(e, hlink);
Adam Tkac b62a53
+		}
Adam Tkac b62a53
+
Adam Tkac b62a53
+		/*
Adam Tkac b62a53
+		 * Discard prevous hash table when all of its entries are old.
Adam Tkac b62a53
+		 */
Adam Tkac b62a53
+		age = delta_rrl_time(rrl->old_hash->check_time, now);
Adam Tkac b62a53
+		if (age > rrl->window)
Adam Tkac b62a53
+			free_old_hash(rrl);
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+
Adam Tkac b62a53
+	if (!create)
Adam Tkac b62a53
+		return (NULL);
Adam Tkac b62a53
+
Adam Tkac b62a53
+	/*
Adam Tkac b62a53
+	 * The entry does not exist, so create it by finding a free entry.
Adam Tkac b62a53
+	 * Keep currently penalized and logged entries.
Adam Tkac b62a53
+	 * Try to make more entries if none are idle.
Adam Tkac b62a53
+	 * Steal the oldest entry if we cannot create more.
Adam Tkac b62a53
+	 */
Tomas Hozza 574dc4
+	for (e = ISC_LIST_TAIL(rrl->lru);
Tomas Hozza 574dc4
+	     e != NULL;
Tomas Hozza 574dc4
+	     e = ISC_LIST_PREV(e, lru))
Tomas Hozza 574dc4
+	{
Adam Tkac b62a53
+		if (!ISC_LINK_LINKED(e, hlink))
Adam Tkac b62a53
+			break;
Adam Tkac b62a53
+		age = get_age(rrl, e, now);
Adam Tkac b62a53
+		if (age <= 1) {
Adam Tkac b62a53
+			e = NULL;
Adam Tkac b62a53
+			break;
Adam Tkac b62a53
+		}
Tomas Hozza 574dc4
+		if (!e->logged && response_balance(rrl, e, age) > 0)
Adam Tkac b62a53
+			break;
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+	if (e == NULL) {
Adam Tkac b62a53
+		expand_entries(rrl, ISC_MIN((rrl->num_entries+1)/2, 1000));
Adam Tkac b62a53
+		e = ISC_LIST_TAIL(rrl->lru);
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+	if (e->logged)
Adam Tkac b62a53
+		log_end(rrl, e, ISC_TRUE, log_buf, log_buf_len);
Adam Tkac b62a53
+	if (ISC_LINK_LINKED(e, hlink)) {
Adam Tkac b62a53
+		if (e->hash_gen == rrl->hash_gen)
Adam Tkac b62a53
+			hash = rrl->hash;
Adam Tkac b62a53
+		else
Adam Tkac b62a53
+			hash = rrl->old_hash;
Adam Tkac b62a53
+		old_bin = get_bin(hash, hash_key(&e->key));
Adam Tkac b62a53
+		ISC_LIST_UNLINK(*old_bin, e, hlink);
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+	ISC_LIST_PREPEND(*new_bin, e, hlink);
Adam Tkac b62a53
+	e->hash_gen = rrl->hash_gen;
Adam Tkac b62a53
+	e->key = key;
Adam Tkac b62a53
+	e->ts_valid = ISC_FALSE;
Adam Tkac b62a53
+	ref_entry(rrl, e, probes, now);
Adam Tkac b62a53
+	return (e);
Adam Tkac b62a53
+}
Adam Tkac b62a53
+
Adam Tkac b62a53
+static void
Adam Tkac b62a53
+debit_log(const dns_rrl_entry_t *e, int age, const char *action) {
Adam Tkac b62a53
+	char buf[sizeof("age=12345678")];
Adam Tkac b62a53
+	const char *age_str;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	if (age == DNS_RRL_FOREVER) {
Adam Tkac b62a53
+		age_str = "";
Adam Tkac b62a53
+	} else {
Adam Tkac b62a53
+		snprintf(buf, sizeof(buf), "age=%d", age);
Adam Tkac b62a53
+		age_str = buf;
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+	isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
Adam Tkac b62a53
+		      DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_DEBUG3,
Adam Tkac b62a53
+		      "rrl %08x %6s  responses=%-3d %s",
Adam Tkac b62a53
+		      hash_key(&e->key), age_str, e->responses, action);
Adam Tkac b62a53
+}
Adam Tkac b62a53
+
Adam Tkac b62a53
+static inline dns_rrl_result_t
Adam Tkac b62a53
+debit_rrl_entry(dns_rrl_t *rrl, dns_rrl_entry_t *e, double qps, double scale,
Adam Tkac b62a53
+		const isc_sockaddr_t *client_addr, isc_stdtime_t now,
Adam Tkac b62a53
+		char *log_buf, unsigned int log_buf_len)
Adam Tkac b62a53
+{
Tomas Hozza 574dc4
+	int rate, new_rate, slip, new_slip, age, log_secs, min;
Tomas Hozza 574dc4
+	dns_rrl_rate_t *ratep;
Adam Tkac b62a53
+	dns_rrl_entry_t const *credit_e;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	/*
Adam Tkac b62a53
+	 * Pick the rate counter.
Adam Tkac b62a53
+	 * Optionally adjust the rate by the estimated query/second rate.
Adam Tkac b62a53
+	 */
Tomas Hozza 574dc4
+	ratep = get_rate(rrl, e->key.s.rtype);
Tomas Hozza 574dc4
+	rate = ratep->r;
Adam Tkac b62a53
+	if (rate == 0)
Adam Tkac b62a53
+		return (DNS_RRL_RESULT_OK);
Adam Tkac b62a53
+
Adam Tkac b62a53
+	if (scale < 1.0) {
Adam Tkac b62a53
+		/*
Adam Tkac b62a53
+		 * The limit for clients that have used TCP is not scaled.
Adam Tkac b62a53
+		 */
Adam Tkac b62a53
+		credit_e = get_entry(rrl, client_addr,
Adam Tkac b62a53
+				     0, dns_rdatatype_none, NULL,
Adam Tkac b62a53
+				     DNS_RRL_RTYPE_TCP, now, ISC_FALSE,
Adam Tkac b62a53
+				     log_buf, log_buf_len);
Adam Tkac b62a53
+		if (credit_e != NULL) {
Adam Tkac b62a53
+			age = get_age(rrl, e, now);
Adam Tkac b62a53
+			if (age < rrl->window)
Adam Tkac b62a53
+				scale = 1.0;
Adam Tkac b62a53
+		}
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+	if (scale < 1.0) {
Tomas Hozza 574dc4
+		new_rate = (int) (rate * scale);
Adam Tkac b62a53
+		if (new_rate < 1)
Adam Tkac b62a53
+			new_rate = 1;
Tomas Hozza 574dc4
+		if (ratep->scaled != new_rate) {
Tomas Hozza 574dc4
+			isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
Tomas Hozza 574dc4
+				      DNS_LOGMODULE_REQUEST,
Tomas Hozza 574dc4
+				      DNS_RRL_LOG_DEBUG1,
Tomas Hozza 574dc4
+				      "%d qps scaled %s by %.2f"
Tomas Hozza 574dc4
+				      " from %d to %d",
Tomas Hozza 574dc4
+				      (int)qps, ratep->str, scale,
Tomas Hozza 574dc4
+				      rate, new_rate);
Adam Tkac b62a53
+			rate = new_rate;
Tomas Hozza 574dc4
+			ratep->scaled = rate;
Adam Tkac b62a53
+		}
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+
Adam Tkac b62a53
+	min = -rrl->window * rate;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	/*
Adam Tkac b62a53
+	 * Treat time jumps into the recent past as no time.
Adam Tkac b62a53
+	 * Treat entries older than the window as if they were just created
Adam Tkac b62a53
+	 * Credit other entries.
Adam Tkac b62a53
+	 */
Adam Tkac b62a53
+	age = get_age(rrl, e, now);
Adam Tkac b62a53
+	if (age > 0) {
Adam Tkac b62a53
+		/*
Adam Tkac b62a53
+		 * Credit tokens earned during elapsed time.
Adam Tkac b62a53
+		 */
Adam Tkac b62a53
+		if (age > rrl->window) {
Adam Tkac b62a53
+			e->responses = rate;
Adam Tkac b62a53
+			e->slip_cnt = 0;
Adam Tkac b62a53
+		} else {
Adam Tkac b62a53
+			e->responses += rate*age;
Adam Tkac b62a53
+			if (e->responses > rate) {
Adam Tkac b62a53
+				e->responses = rate;
Adam Tkac b62a53
+				e->slip_cnt = 0;
Adam Tkac b62a53
+			}
Adam Tkac b62a53
+		}
Adam Tkac b62a53
+		/*
Adam Tkac b62a53
+		 * Find the seconds since last log message without overflowing
Adam Tkac b62a53
+		 * small counter.  This counter is reset when an entry is
Adam Tkac b62a53
+		 * created.  It is not necessarily reset when some requests
Adam Tkac b62a53
+		 * are answered provided other requests continue to be dropped
Adam Tkac b62a53
+		 * or slipped.  This can happen when the request rate is just
Adam Tkac b62a53
+		 * at the limit.
Adam Tkac b62a53
+		 */
Adam Tkac b62a53
+		if (e->logged) {
Adam Tkac b62a53
+			log_secs = e->log_secs;
Adam Tkac b62a53
+			log_secs += age;
Adam Tkac b62a53
+			if (log_secs > DNS_RRL_MAX_LOG_SECS || log_secs < 0)
Adam Tkac b62a53
+				log_secs = DNS_RRL_MAX_LOG_SECS;
Adam Tkac b62a53
+			e->log_secs = log_secs;
Adam Tkac b62a53
+		}
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+	set_age(rrl, e, now);
Adam Tkac b62a53
+
Adam Tkac b62a53
+	/*
Adam Tkac b62a53
+	 * Debit the entry for this response.
Adam Tkac b62a53
+	 */
Adam Tkac b62a53
+	if (--e->responses >= 0) {
Adam Tkac b62a53
+		if (isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DEBUG3))
Adam Tkac b62a53
+			debit_log(e, age, "");
Adam Tkac b62a53
+		return (DNS_RRL_RESULT_OK);
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+
Adam Tkac b62a53
+	if (e->responses < min)
Adam Tkac b62a53
+		e->responses = min;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	/*
Adam Tkac b62a53
+	 * Drop this response unless it should slip or leak.
Adam Tkac b62a53
+	 */
Tomas Hozza 574dc4
+	slip = rrl->slip.r;
Adam Tkac b62a53
+	if (slip > 2 && scale < 1.0) {
Tomas Hozza 574dc4
+		new_slip = (int) (slip * scale);
Adam Tkac b62a53
+		if (new_slip < 2)
Adam Tkac b62a53
+			new_slip = 2;
Tomas Hozza 574dc4
+		if (rrl->slip.scaled != new_slip) {
Tomas Hozza 574dc4
+			isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
Tomas Hozza 574dc4
+				      DNS_LOGMODULE_REQUEST,
Tomas Hozza 574dc4
+				      DNS_RRL_LOG_DEBUG1,
Tomas Hozza 574dc4
+				      "%d qps scaled slip"
Tomas Hozza 574dc4
+				      " by %.2f from %d to %d",
Tomas Hozza 574dc4
+				      (int)qps, scale,
Tomas Hozza 574dc4
+				      slip, new_slip);
Adam Tkac b62a53
+			slip = new_slip;
Tomas Hozza 574dc4
+			rrl->slip.scaled = slip;
Adam Tkac b62a53
+		}
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+	if (slip != 0 && e->key.s.rtype != DNS_RRL_RTYPE_ALL) {
Adam Tkac b62a53
+		if (e->slip_cnt++ == 0) {
Tomas Hozza 574dc4
+			if ((int) e->slip_cnt >= slip)
Tomas Hozza 574dc4
+				e->slip_cnt = 0;
Adam Tkac b62a53
+			if (isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DEBUG3))
Adam Tkac b62a53
+				debit_log(e, age, "slip");
Adam Tkac b62a53
+			return (DNS_RRL_RESULT_SLIP);
Tomas Hozza 574dc4
+		} else if ((int) e->slip_cnt >= slip) {
Adam Tkac b62a53
+			e->slip_cnt = 0;
Adam Tkac b62a53
+		}
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+
Adam Tkac b62a53
+	if (isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DEBUG3))
Adam Tkac b62a53
+		debit_log(e, age, "drop");
Adam Tkac b62a53
+	return (DNS_RRL_RESULT_DROP);
Adam Tkac b62a53
+}
Adam Tkac b62a53
+
Adam Tkac b62a53
+static inline dns_rrl_qname_buf_t *
Adam Tkac b62a53
+get_qname(dns_rrl_t *rrl, const dns_rrl_entry_t *e) {
Adam Tkac b62a53
+	dns_rrl_qname_buf_t *qbuf;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	qbuf = rrl->qnames[e->log_qname];
Tomas Hozza 574dc4
+	if (qbuf == NULL || qbuf->e != e)
Adam Tkac b62a53
+		return (NULL);
Adam Tkac b62a53
+	return (qbuf);
Adam Tkac b62a53
+}
Adam Tkac b62a53
+
Adam Tkac b62a53
+static inline void
Adam Tkac b62a53
+free_qname(dns_rrl_t *rrl, dns_rrl_entry_t *e) {
Adam Tkac b62a53
+	dns_rrl_qname_buf_t *qbuf;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	qbuf = get_qname(rrl, e);
Adam Tkac b62a53
+	if (qbuf != NULL) {
Adam Tkac b62a53
+		qbuf->e = NULL;
Adam Tkac b62a53
+		ISC_LIST_APPEND(rrl->qname_free, qbuf, link);
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+}
Adam Tkac b62a53
+
Adam Tkac b62a53
+static void
Tomas Hozza 574dc4
+add_log_str(isc_buffer_t *lb, const char *str, unsigned int str_len) {
Adam Tkac b62a53
+	isc_region_t region;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	isc_buffer_availableregion(lb, &region);
Adam Tkac b62a53
+	if (str_len >= region.length) {
Adam Tkac b62a53
+		if (region.length <= 0)
Adam Tkac b62a53
+			return;
Adam Tkac b62a53
+		str_len = region.length;
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+	memcpy(region.base, str, str_len);
Adam Tkac b62a53
+	isc_buffer_add(lb, str_len);
Adam Tkac b62a53
+}
Adam Tkac b62a53
+
Adam Tkac b62a53
+#define ADD_LOG_CSTR(eb, s) add_log_str(eb, s, sizeof(s)-1)
Adam Tkac b62a53
+
Adam Tkac b62a53
+/*
Adam Tkac b62a53
+ * Build strings for the logs
Adam Tkac b62a53
+ */
Adam Tkac b62a53
+static void
Adam Tkac b62a53
+make_log_buf(dns_rrl_t *rrl, dns_rrl_entry_t *e,
Adam Tkac b62a53
+	     const char *str1, const char *str2, isc_boolean_t plural,
Adam Tkac b62a53
+	     dns_name_t *qname, isc_boolean_t save_qname,
Adam Tkac b62a53
+	     dns_rrl_result_t rrl_result, isc_result_t resp_result,
Adam Tkac b62a53
+	     char *log_buf, unsigned int log_buf_len)
Adam Tkac b62a53
+{
Adam Tkac b62a53
+	isc_buffer_t lb;
Adam Tkac b62a53
+	dns_rrl_qname_buf_t *qbuf;
Adam Tkac b62a53
+	isc_netaddr_t cidr;
Adam Tkac b62a53
+	char strbuf[ISC_MAX(sizeof("/123"), sizeof("  (12345678)"))];
Adam Tkac b62a53
+	const char *rstr;
Adam Tkac b62a53
+	isc_result_t msg_result;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	if (log_buf_len <= 1) {
Adam Tkac b62a53
+		if (log_buf_len == 1)
Adam Tkac b62a53
+			log_buf[0] = '\0';
Adam Tkac b62a53
+		return;
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+	isc_buffer_init(&lb, log_buf, log_buf_len-1);
Adam Tkac b62a53
+
Adam Tkac b62a53
+	if (str1 != NULL)
Adam Tkac b62a53
+		add_log_str(&lb, str1, strlen(str1));
Adam Tkac b62a53
+	if (str2 != NULL)
Adam Tkac b62a53
+		add_log_str(&lb, str2, strlen(str2));
Adam Tkac b62a53
+
Adam Tkac b62a53
+	switch (rrl_result) {
Adam Tkac b62a53
+	case DNS_RRL_RESULT_OK:
Adam Tkac b62a53
+		break;
Adam Tkac b62a53
+	case DNS_RRL_RESULT_DROP:
Adam Tkac b62a53
+		ADD_LOG_CSTR(&lb, "drop ");
Adam Tkac b62a53
+		break;
Adam Tkac b62a53
+	case DNS_RRL_RESULT_SLIP:
Adam Tkac b62a53
+		ADD_LOG_CSTR(&lb, "slip ");
Adam Tkac b62a53
+		break;
Adam Tkac b62a53
+	default:
Adam Tkac b62a53
+		INSIST(0);
Adam Tkac b62a53
+		break;
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+
Adam Tkac b62a53
+	switch (e->key.s.rtype) {
Adam Tkac b62a53
+	case DNS_RRL_RTYPE_QUERY:
Adam Tkac b62a53
+		break;
Tomas Hozza 574dc4
+	case DNS_RRL_RTYPE_REFERRAL:
Tomas Hozza 574dc4
+		ADD_LOG_CSTR(&lb, "referral ");
Tomas Hozza 574dc4
+		break;
Tomas Hozza 574dc4
+	case DNS_RRL_RTYPE_NODATA:
Tomas Hozza 574dc4
+		ADD_LOG_CSTR(&lb, "NODATA ");
Adam Tkac b62a53
+		break;
Adam Tkac b62a53
+	case DNS_RRL_RTYPE_NXDOMAIN:
Tomas Hozza 574dc4
+		ADD_LOG_CSTR(&lb, "NXDOMAIN ");
Adam Tkac b62a53
+		break;
Adam Tkac b62a53
+	case DNS_RRL_RTYPE_ERROR:
Adam Tkac b62a53
+		if (resp_result == ISC_R_SUCCESS) {
Tomas Hozza 574dc4
+			ADD_LOG_CSTR(&lb, "error ");
Adam Tkac b62a53
+		} else {
Adam Tkac b62a53
+			rstr = isc_result_totext(resp_result);
Tomas Hozza 574dc4
+			add_log_str(&lb, rstr, strlen(rstr));
Tomas Hozza 574dc4
+			ADD_LOG_CSTR(&lb, " error ");
Adam Tkac b62a53
+		}
Adam Tkac b62a53
+		break;
Adam Tkac b62a53
+	case DNS_RRL_RTYPE_ALL:
Tomas Hozza 574dc4
+		ADD_LOG_CSTR(&lb, "all ");
Adam Tkac b62a53
+		break;
Adam Tkac b62a53
+	default:
Adam Tkac b62a53
+		INSIST(0);
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+
Adam Tkac b62a53
+	if (plural)
Tomas Hozza 574dc4
+		ADD_LOG_CSTR(&lb, "responses to ");
Adam Tkac b62a53
+	else
Tomas Hozza 574dc4
+		ADD_LOG_CSTR(&lb, "response to ");
Adam Tkac b62a53
+
Adam Tkac b62a53
+	memset(&cidr, 0, sizeof(cidr));
Adam Tkac b62a53
+	if (e->key.s.ipv6) {
Adam Tkac b62a53
+		snprintf(strbuf, sizeof(strbuf), "/%d", rrl->ipv6_prefixlen);
Adam Tkac b62a53
+		cidr.family = AF_INET6;
Adam Tkac b62a53
+		memset(&cidr.type.in6, 0,  sizeof(cidr.type.in6));
Adam Tkac b62a53
+		memcpy(&cidr.type.in6, e->key.s.ip, sizeof(e->key.s.ip));
Adam Tkac b62a53
+	} else {
Adam Tkac b62a53
+		snprintf(strbuf, sizeof(strbuf), "/%d", rrl->ipv4_prefixlen);
Adam Tkac b62a53
+		cidr.family = AF_INET;
Adam Tkac b62a53
+		cidr.type.in.s_addr = e->key.s.ip[0];
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+	msg_result = isc_netaddr_totext(&cidr, &lb);
Adam Tkac b62a53
+	if (msg_result != ISC_R_SUCCESS)
Adam Tkac b62a53
+		ADD_LOG_CSTR(&lb, "?");
Adam Tkac b62a53
+	add_log_str(&lb, strbuf, strlen(strbuf));
Adam Tkac b62a53
+
Adam Tkac b62a53
+	if (e->key.s.rtype == DNS_RRL_RTYPE_QUERY ||
Tomas Hozza 574dc4
+	    e->key.s.rtype == DNS_RRL_RTYPE_REFERRAL ||
Tomas Hozza 574dc4
+	    e->key.s.rtype == DNS_RRL_RTYPE_NODATA ||
Adam Tkac b62a53
+	    e->key.s.rtype == DNS_RRL_RTYPE_NXDOMAIN) {
Adam Tkac b62a53
+		qbuf = get_qname(rrl, e);
Adam Tkac b62a53
+		if (save_qname && qbuf == NULL &&
Adam Tkac b62a53
+		    qname != NULL && dns_name_isabsolute(qname)) {
Adam Tkac b62a53
+			/*
Adam Tkac b62a53
+			 * Capture the qname for the "stop limiting" message.
Adam Tkac b62a53
+			 */
Adam Tkac b62a53
+			qbuf = ISC_LIST_TAIL(rrl->qname_free);
Adam Tkac b62a53
+			if (qbuf != NULL) {
Adam Tkac b62a53
+				ISC_LIST_UNLINK(rrl->qname_free, qbuf, link);
Adam Tkac b62a53
+			} else if (rrl->num_qnames < DNS_RRL_QNAMES) {
Adam Tkac b62a53
+				qbuf = isc_mem_get(rrl->mctx, sizeof(*qbuf));
Adam Tkac b62a53
+				if (qbuf != NULL) {
Adam Tkac b62a53
+					memset(qbuf, 0, sizeof(*qbuf));
Tomas Hozza 574dc4
+					ISC_LINK_INIT(qbuf, link);
Adam Tkac b62a53
+					qbuf->index = rrl->num_qnames;
Adam Tkac b62a53
+					rrl->qnames[rrl->num_qnames++] = qbuf;
Adam Tkac b62a53
+				} else {
Adam Tkac b62a53
+					isc_log_write(dns_lctx,
Adam Tkac b62a53
+						      DNS_LOGCATEGORY_RRL,
Adam Tkac b62a53
+						      DNS_LOGMODULE_REQUEST,
Adam Tkac b62a53
+						      DNS_RRL_LOG_FAIL,
Adam Tkac b62a53
+						      "isc_mem_get(%d)"
Adam Tkac b62a53
+						      " failed for RRL qname",
Adam Tkac b62a53
+						      (int)sizeof(*qbuf));
Adam Tkac b62a53
+				}
Adam Tkac b62a53
+			}
Adam Tkac b62a53
+			if (qbuf != NULL) {
Adam Tkac b62a53
+				e->log_qname = qbuf->index;
Adam Tkac b62a53
+				qbuf->e = e;
Adam Tkac b62a53
+				dns_fixedname_init(&qbuf->qname);
Adam Tkac b62a53
+				dns_name_copy(qname,
Adam Tkac b62a53
+					      dns_fixedname_name(&qbuf->qname),
Adam Tkac b62a53
+					      NULL);
Adam Tkac b62a53
+			}
Adam Tkac b62a53
+		}
Adam Tkac b62a53
+		if (qbuf != NULL)
Adam Tkac b62a53
+			qname = dns_fixedname_name(&qbuf->qname);
Adam Tkac b62a53
+		if (qname != NULL) {
Adam Tkac b62a53
+			ADD_LOG_CSTR(&lb, " for ");
Tomas Hozza 574dc4
+			(void)dns_name_totext(qname, ISC_TRUE, &lb);
Adam Tkac b62a53
+		} else {
Adam Tkac b62a53
+			ADD_LOG_CSTR(&lb, " for (?)");
Adam Tkac b62a53
+		}
Adam Tkac b62a53
+		if (e->key.s.rtype != DNS_RRL_RTYPE_NXDOMAIN) {
Adam Tkac b62a53
+			ADD_LOG_CSTR(&lb, " ");
Tomas Hozza 574dc4
+			(void)dns_rdataclass_totext(e->key.s.qclass, &lb);
Tomas Hozza 574dc4
+			if (e->key.s.rtype == DNS_RRL_RTYPE_QUERY) {
Tomas Hozza 574dc4
+				ADD_LOG_CSTR(&lb, " ");
Tomas Hozza 574dc4
+				(void)dns_rdatatype_totext(e->key.s.qtype, &lb);
Tomas Hozza 574dc4
+			}
Adam Tkac b62a53
+		}
Adam Tkac b62a53
+		snprintf(strbuf, sizeof(strbuf), "  (%08x)",
Adam Tkac b62a53
+			 e->key.s.qname_hash);
Adam Tkac b62a53
+		add_log_str(&lb, strbuf, strlen(strbuf));
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+
Adam Tkac b62a53
+	/*
Adam Tkac b62a53
+	 * We saved room for '\0'.
Adam Tkac b62a53
+	 */
Adam Tkac b62a53
+	log_buf[isc_buffer_usedlength(&lb)] = '\0';
Adam Tkac b62a53
+}
Adam Tkac b62a53
+
Adam Tkac b62a53
+static void
Adam Tkac b62a53
+log_end(dns_rrl_t *rrl, dns_rrl_entry_t *e, isc_boolean_t early,
Adam Tkac b62a53
+	char *log_buf, unsigned int log_buf_len)
Adam Tkac b62a53
+{
Adam Tkac b62a53
+	if (e->logged) {
Adam Tkac b62a53
+		make_log_buf(rrl, e,
Adam Tkac b62a53
+			     early ? "*" : NULL,
Adam Tkac b62a53
+			     rrl->log_only ? "would stop limiting "
Adam Tkac b62a53
+					   : "stop limiting ",
Adam Tkac b62a53
+			     ISC_TRUE, NULL, ISC_FALSE,
Adam Tkac b62a53
+			     DNS_RRL_RESULT_OK, ISC_R_SUCCESS,
Adam Tkac b62a53
+			     log_buf, log_buf_len);
Adam Tkac b62a53
+		isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
Adam Tkac b62a53
+			      DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_DROP,
Adam Tkac b62a53
+			      "%s", log_buf);
Adam Tkac b62a53
+		free_qname(rrl, e);
Adam Tkac b62a53
+		e->logged = ISC_FALSE;
Adam Tkac b62a53
+		--rrl->num_logged;
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+}
Adam Tkac b62a53
+
Adam Tkac b62a53
+/*
Adam Tkac b62a53
+ * Log messages for streams that have stopped being rate limited.
Adam Tkac b62a53
+ */
Adam Tkac b62a53
+static void
Adam Tkac b62a53
+log_stops(dns_rrl_t *rrl, isc_stdtime_t now, int limit,
Adam Tkac b62a53
+	  char *log_buf, unsigned int log_buf_len)
Adam Tkac b62a53
+{
Adam Tkac b62a53
+	dns_rrl_entry_t *e;
Adam Tkac b62a53
+	int age;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	for (e = rrl->last_logged; e != NULL; e = ISC_LIST_PREV(e, lru)) {
Adam Tkac b62a53
+		if (!e->logged)
Adam Tkac b62a53
+			continue;
Adam Tkac b62a53
+		if (now != 0) {
Adam Tkac b62a53
+			age = get_age(rrl, e, now);
Adam Tkac b62a53
+			if (age < DNS_RRL_STOP_LOG_SECS ||
Adam Tkac b62a53
+			    response_balance(rrl, e, age) < 0)
Adam Tkac b62a53
+				break;
Adam Tkac b62a53
+		}
Adam Tkac b62a53
+
Adam Tkac b62a53
+		log_end(rrl, e, now == 0, log_buf, log_buf_len);
Adam Tkac b62a53
+		if (rrl->num_logged <= 0)
Adam Tkac b62a53
+			break;
Adam Tkac b62a53
+
Adam Tkac b62a53
+		/*
Adam Tkac b62a53
+		 * Too many messages could stall real work.
Adam Tkac b62a53
+		 */
Adam Tkac b62a53
+		if (--limit < 0) {
Adam Tkac b62a53
+			rrl->last_logged = ISC_LIST_PREV(e, lru);
Adam Tkac b62a53
+			return;
Adam Tkac b62a53
+		}
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+	if (e == NULL) {
Adam Tkac b62a53
+		INSIST(rrl->num_logged == 0);
Adam Tkac b62a53
+		rrl->log_stops_time = now;
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+	rrl->last_logged = e;
Adam Tkac b62a53
+}
Adam Tkac b62a53
+
Adam Tkac b62a53
+/*
Adam Tkac b62a53
+ * Main rate limit interface.
Adam Tkac b62a53
+ */
Adam Tkac b62a53
+dns_rrl_result_t
Adam Tkac b62a53
+dns_rrl(dns_view_t *view,
Adam Tkac b62a53
+	const isc_sockaddr_t *client_addr, isc_boolean_t is_tcp,
Adam Tkac b62a53
+	dns_rdataclass_t qclass, dns_rdatatype_t qtype,
Adam Tkac b62a53
+	dns_name_t *qname, isc_result_t resp_result, isc_stdtime_t now,
Adam Tkac b62a53
+	isc_boolean_t wouldlog, char *log_buf, unsigned int log_buf_len)
Adam Tkac b62a53
+{
Adam Tkac b62a53
+	dns_rrl_t *rrl;
Adam Tkac b62a53
+	dns_rrl_rtype_t rtype;
Adam Tkac b62a53
+	dns_rrl_entry_t *e;
Adam Tkac b62a53
+	isc_netaddr_t netclient;
Adam Tkac b62a53
+	int secs;
Adam Tkac b62a53
+	double qps, scale;
Adam Tkac b62a53
+	int exempt_match;
Adam Tkac b62a53
+	isc_result_t result;
Adam Tkac b62a53
+	dns_rrl_result_t rrl_result;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	INSIST(log_buf != NULL && log_buf_len > 0);
Adam Tkac b62a53
+
Adam Tkac b62a53
+	rrl = view->rrl;
Adam Tkac b62a53
+	if (rrl->exempt != NULL) {
Adam Tkac b62a53
+		isc_netaddr_fromsockaddr(&netclient, client_addr);
Adam Tkac b62a53
+		result = dns_acl_match(&netclient, NULL, rrl->exempt,
Adam Tkac b62a53
+				       &view->aclenv, &exempt_match, NULL);
Adam Tkac b62a53
+		if (result == ISC_R_SUCCESS && exempt_match > 0)
Adam Tkac b62a53
+			return (DNS_RRL_RESULT_OK);
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+
Adam Tkac b62a53
+	LOCK(&rrl->lock);
Adam Tkac b62a53
+
Adam Tkac b62a53
+	/*
Adam Tkac b62a53
+	 * Estimate total query per second rate when scaling by qps.
Adam Tkac b62a53
+	 */
Adam Tkac b62a53
+	if (rrl->qps_scale == 0) {
Adam Tkac b62a53
+		qps = 0.0;
Adam Tkac b62a53
+		scale = 1.0;
Adam Tkac b62a53
+	} else {
Adam Tkac b62a53
+		++rrl->qps_responses;
Adam Tkac b62a53
+		secs = delta_rrl_time(rrl->qps_time, now);
Adam Tkac b62a53
+		if (secs <= 0) {
Adam Tkac b62a53
+			qps = rrl->qps;
Adam Tkac b62a53
+		} else {
Adam Tkac b62a53
+			qps = (1.0*rrl->qps_responses) / secs;
Adam Tkac b62a53
+			if (secs >= rrl->window) {
Adam Tkac b62a53
+				if (isc_log_wouldlog(dns_lctx,
Adam Tkac b62a53
+						     DNS_RRL_LOG_DEBUG3))
Adam Tkac b62a53
+					isc_log_write(dns_lctx,
Adam Tkac b62a53
+						      DNS_LOGCATEGORY_RRL,
Adam Tkac b62a53
+						      DNS_LOGMODULE_REQUEST,
Adam Tkac b62a53
+						      DNS_RRL_LOG_DEBUG3,
Adam Tkac b62a53
+						      "%d responses/%d seconds"
Adam Tkac b62a53
+						      " = %d qps",
Adam Tkac b62a53
+						      rrl->qps_responses, secs,
Adam Tkac b62a53
+						      (int)qps);
Adam Tkac b62a53
+				rrl->qps = qps;
Adam Tkac b62a53
+				rrl->qps_responses = 0;
Adam Tkac b62a53
+				rrl->qps_time = now;
Adam Tkac b62a53
+			} else if (qps < rrl->qps) {
Adam Tkac b62a53
+				qps = rrl->qps;
Adam Tkac b62a53
+			}
Adam Tkac b62a53
+		}
Adam Tkac b62a53
+		scale = rrl->qps_scale / qps;
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+
Adam Tkac b62a53
+	/*
Adam Tkac b62a53
+	 * Do maintenance once per second.
Adam Tkac b62a53
+	 */
Adam Tkac b62a53
+	if (rrl->num_logged > 0 && rrl->log_stops_time != now)
Adam Tkac b62a53
+		log_stops(rrl, now, 8, log_buf, log_buf_len);
Adam Tkac b62a53
+
Adam Tkac b62a53
+	/*
Adam Tkac b62a53
+	 * Notice TCP responses when scaling limits by qps.
Adam Tkac b62a53
+	 * Do not try to rate limit TCP responses.
Adam Tkac b62a53
+	 */
Adam Tkac b62a53
+	if (is_tcp) {
Adam Tkac b62a53
+		if (scale < 1.0) {
Adam Tkac b62a53
+			e = get_entry(rrl, client_addr,
Adam Tkac b62a53
+				      0, dns_rdatatype_none, NULL,
Adam Tkac b62a53
+				      DNS_RRL_RTYPE_TCP, now, ISC_TRUE,
Adam Tkac b62a53
+				      log_buf, log_buf_len);
Adam Tkac b62a53
+			if (e != NULL) {
Adam Tkac b62a53
+				e->responses = -(rrl->window+1);
Adam Tkac b62a53
+				set_age(rrl, e, now);
Adam Tkac b62a53
+			}
Adam Tkac b62a53
+		}
Adam Tkac b62a53
+		UNLOCK(&rrl->lock);
Adam Tkac b62a53
+		return (ISC_R_SUCCESS);
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+
Adam Tkac b62a53
+	/*
Adam Tkac b62a53
+	 * Find the right kind of entry, creating it if necessary.
Adam Tkac b62a53
+	 * If that is impossible, then nothing more can be done
Adam Tkac b62a53
+	 */
Tomas Hozza 574dc4
+	switch (resp_result) {
Tomas Hozza 574dc4
+	case ISC_R_SUCCESS:
Adam Tkac b62a53
+		rtype = DNS_RRL_RTYPE_QUERY;
Tomas Hozza 574dc4
+		break;
Tomas Hozza 574dc4
+	case DNS_R_DELEGATION:
Tomas Hozza 574dc4
+		rtype = DNS_RRL_RTYPE_REFERRAL;
Tomas Hozza 574dc4
+		break;
Tomas Hozza 574dc4
+	case DNS_R_NXRRSET:
Tomas Hozza 574dc4
+		rtype = DNS_RRL_RTYPE_NODATA;
Tomas Hozza 574dc4
+		break;
Tomas Hozza 574dc4
+	case DNS_R_NXDOMAIN:
Adam Tkac b62a53
+		rtype = DNS_RRL_RTYPE_NXDOMAIN;
Tomas Hozza 574dc4
+		break;
Tomas Hozza 574dc4
+	default:
Adam Tkac b62a53
+		rtype = DNS_RRL_RTYPE_ERROR;
Tomas Hozza 574dc4
+		break;
Tomas Hozza 574dc4
+	}
Adam Tkac b62a53
+	e = get_entry(rrl, client_addr, qclass, qtype, qname, rtype,
Adam Tkac b62a53
+		      now, ISC_TRUE, log_buf, log_buf_len);
Adam Tkac b62a53
+	if (e == NULL) {
Adam Tkac b62a53
+		UNLOCK(&rrl->lock);
Adam Tkac b62a53
+		return (DNS_RRL_RESULT_OK);
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+
Adam Tkac b62a53
+	if (isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DEBUG1)) {
Adam Tkac b62a53
+		/*
Adam Tkac b62a53
+		 * Do not worry about speed or releasing the lock.
Adam Tkac b62a53
+		 * This message appears before messages from debit_rrl_entry().
Adam Tkac b62a53
+		 */
Adam Tkac b62a53
+		make_log_buf(rrl, e, "consider limiting ", NULL, ISC_FALSE,
Adam Tkac b62a53
+			     qname, ISC_FALSE, DNS_RRL_RESULT_OK, resp_result,
Adam Tkac b62a53
+			     log_buf, log_buf_len);
Adam Tkac b62a53
+		isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
Adam Tkac b62a53
+			      DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_DEBUG1,
Adam Tkac b62a53
+			      "%s", log_buf);
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+
Adam Tkac b62a53
+	rrl_result = debit_rrl_entry(rrl, e, qps, scale, client_addr, now,
Adam Tkac b62a53
+				     log_buf, log_buf_len);
Adam Tkac b62a53
+
Tomas Hozza 574dc4
+	if (rrl->all_per_second.r != 0) {
Adam Tkac b62a53
+		/*
Adam Tkac b62a53
+		 * We must debit the all-per-second token bucket if we have
Adam Tkac b62a53
+		 * an all-per-second limit for the IP address.
Adam Tkac b62a53
+		 * The all-per-second limit determines the log message
Adam Tkac b62a53
+		 * when both limits are hit.
Adam Tkac b62a53
+		 * The response limiting must continue if the
Adam Tkac b62a53
+		 * all-per-second limiting lapses.
Adam Tkac b62a53
+		 */
Adam Tkac b62a53
+		dns_rrl_entry_t *e_all;
Adam Tkac b62a53
+		dns_rrl_result_t rrl_all_result;
Adam Tkac b62a53
+
Adam Tkac b62a53
+		e_all = get_entry(rrl, client_addr,
Adam Tkac b62a53
+				  0, dns_rdatatype_none, NULL,
Adam Tkac b62a53
+				  DNS_RRL_RTYPE_ALL, now, ISC_TRUE,
Adam Tkac b62a53
+				  log_buf, log_buf_len);
Adam Tkac b62a53
+		if (e_all == NULL) {
Adam Tkac b62a53
+			UNLOCK(&rrl->lock);
Adam Tkac b62a53
+			return (DNS_RRL_RESULT_OK);
Adam Tkac b62a53
+		}
Adam Tkac b62a53
+		rrl_all_result = debit_rrl_entry(rrl, e_all, qps, scale,
Adam Tkac b62a53
+						 client_addr, now,
Adam Tkac b62a53
+						 log_buf, log_buf_len);
Adam Tkac b62a53
+		if (rrl_all_result != DNS_RRL_RESULT_OK) {
Adam Tkac b62a53
+			int level;
Adam Tkac b62a53
+
Adam Tkac b62a53
+			e = e_all;
Adam Tkac b62a53
+			rrl_result = rrl_all_result;
Adam Tkac b62a53
+			if (rrl_result == DNS_RRL_RESULT_OK)
Adam Tkac b62a53
+				level = DNS_RRL_LOG_DEBUG2;
Adam Tkac b62a53
+			else
Adam Tkac b62a53
+				level = DNS_RRL_LOG_DEBUG1;
Adam Tkac b62a53
+			if (isc_log_wouldlog(dns_lctx, level)) {
Adam Tkac b62a53
+				make_log_buf(rrl, e,
Adam Tkac b62a53
+					     "prefer all-per-second limiting ",
Adam Tkac b62a53
+					     NULL, ISC_TRUE, qname, ISC_FALSE,
Adam Tkac b62a53
+					     DNS_RRL_RESULT_OK, resp_result,
Adam Tkac b62a53
+					     log_buf, log_buf_len);
Adam Tkac b62a53
+				isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
Adam Tkac b62a53
+					      DNS_LOGMODULE_REQUEST, level,
Adam Tkac b62a53
+					      "%s", log_buf);
Adam Tkac b62a53
+			}
Adam Tkac b62a53
+		}
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+
Adam Tkac b62a53
+	if (rrl_result == DNS_RRL_RESULT_OK) {
Adam Tkac b62a53
+		UNLOCK(&rrl->lock);
Adam Tkac b62a53
+		return (DNS_RRL_RESULT_OK);
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+
Adam Tkac b62a53
+	/*
Adam Tkac b62a53
+	 * Log occassionally in the rate-limit category.
Adam Tkac b62a53
+	 */
Adam Tkac b62a53
+	if ((!e->logged || e->log_secs >= DNS_RRL_MAX_LOG_SECS) &&
Adam Tkac b62a53
+	    isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DROP)) {
Adam Tkac b62a53
+		make_log_buf(rrl, e, rrl->log_only ? "would " : NULL,
Adam Tkac b62a53
+			     e->logged ? "continue limiting " : "limit ",
Adam Tkac b62a53
+			     ISC_TRUE, qname, ISC_TRUE,
Adam Tkac b62a53
+			     DNS_RRL_RESULT_OK, resp_result,
Adam Tkac b62a53
+			     log_buf, log_buf_len);
Adam Tkac b62a53
+		if (!e->logged) {
Adam Tkac b62a53
+			e->logged = ISC_TRUE;
Adam Tkac b62a53
+			if (++rrl->num_logged <= 1)
Adam Tkac b62a53
+				rrl->last_logged = e;
Adam Tkac b62a53
+		}
Adam Tkac b62a53
+		e->log_secs = 0;
Tomas Hozza 574dc4
+
Adam Tkac b62a53
+		/*
Adam Tkac b62a53
+		 * Avoid holding the lock.
Adam Tkac b62a53
+		 */
Adam Tkac b62a53
+		if (!wouldlog) {
Adam Tkac b62a53
+			UNLOCK(&rrl->lock);
Adam Tkac b62a53
+			e = NULL;
Adam Tkac b62a53
+		}
Adam Tkac b62a53
+		isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
Adam Tkac b62a53
+			      DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_DROP,
Adam Tkac b62a53
+			      "%s", log_buf);
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+
Adam Tkac b62a53
+	/*
Adam Tkac b62a53
+	 * Make a log message for the caller.
Adam Tkac b62a53
+	 */
Adam Tkac b62a53
+	if (wouldlog)
Tomas Hozza 574dc4
+		make_log_buf(rrl, e,
Tomas Hozza 574dc4
+			     rrl->log_only ? "would rate limit " : "rate limit ",
Adam Tkac b62a53
+			     NULL, ISC_FALSE, qname, ISC_FALSE,
Adam Tkac b62a53
+			     rrl_result, resp_result, log_buf, log_buf_len);
Adam Tkac b62a53
+
Adam Tkac b62a53
+	if (e != NULL) {
Adam Tkac b62a53
+		/*
Tomas Hozza 574dc4
+		 * Do not save the qname unless we might need it for
Adam Tkac b62a53
+		 * the ending log message.
Adam Tkac b62a53
+		 */
Adam Tkac b62a53
+		if (!e->logged)
Adam Tkac b62a53
+			free_qname(rrl, e);
Adam Tkac b62a53
+		UNLOCK(&rrl->lock);
Adam Tkac b62a53
+	}
Tomas Hozza 574dc4
+
Adam Tkac b62a53
+	return (rrl_result);
Adam Tkac b62a53
+}
Adam Tkac b62a53
+
Adam Tkac b62a53
+void
Adam Tkac b62a53
+dns_rrl_view_destroy(dns_view_t *view) {
Adam Tkac b62a53
+	dns_rrl_t *rrl;
Adam Tkac b62a53
+	dns_rrl_block_t *b;
Adam Tkac b62a53
+	dns_rrl_hash_t *h;
Adam Tkac b62a53
+	char log_buf[DNS_RRL_LOG_BUF_LEN];
Adam Tkac b62a53
+	int i;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	rrl = view->rrl;
Adam Tkac b62a53
+	if (rrl == NULL)
Adam Tkac b62a53
+		return;
Adam Tkac b62a53
+	view->rrl = NULL;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	/*
Adam Tkac b62a53
+	 * Assume the caller takes care of locking the view and anything else.
Adam Tkac b62a53
+	 */
Adam Tkac b62a53
+
Adam Tkac b62a53
+	if (rrl->num_logged > 0)
Adam Tkac b62a53
+		log_stops(rrl, 0, ISC_INT32_MAX, log_buf, sizeof(log_buf));
Adam Tkac b62a53
+
Adam Tkac b62a53
+	for (i = 0; i < DNS_RRL_QNAMES; ++i) {
Adam Tkac b62a53
+		if (rrl->qnames[i] == NULL)
Adam Tkac b62a53
+			break;
Adam Tkac b62a53
+		isc_mem_put(rrl->mctx, rrl->qnames[i], sizeof(*rrl->qnames[i]));
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+
Adam Tkac b62a53
+	if (rrl->exempt != NULL)
Adam Tkac b62a53
+		dns_acl_detach(&rrl->exempt);
Adam Tkac b62a53
+
Adam Tkac b62a53
+	DESTROYLOCK(&rrl->lock);
Adam Tkac b62a53
+
Adam Tkac b62a53
+	while (!ISC_LIST_EMPTY(rrl->blocks)) {
Adam Tkac b62a53
+		b = ISC_LIST_HEAD(rrl->blocks);
Adam Tkac b62a53
+		ISC_LIST_UNLINK(rrl->blocks, b, link);
Adam Tkac b62a53
+		isc_mem_put(rrl->mctx, b, b->size);
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+
Adam Tkac b62a53
+	h = rrl->hash;
Adam Tkac b62a53
+	if (h != NULL)
Adam Tkac b62a53
+		isc_mem_put(rrl->mctx, h,
Tomas Hozza 574dc4
+			    sizeof(*h) + (h->length - 1) * sizeof(h->bins[0]));
Adam Tkac b62a53
+
Adam Tkac b62a53
+	h = rrl->old_hash;
Adam Tkac b62a53
+	if (h != NULL)
Adam Tkac b62a53
+		isc_mem_put(rrl->mctx, h,
Tomas Hozza 574dc4
+			    sizeof(*h) + (h->length - 1) * sizeof(h->bins[0]));
Adam Tkac b62a53
+
Tomas Hozza 574dc4
+	isc_mem_putanddetach(&rrl->mctx, rrl, sizeof(*rrl));
Adam Tkac b62a53
+}
Adam Tkac b62a53
+
Adam Tkac b62a53
+isc_result_t
Adam Tkac b62a53
+dns_rrl_init(dns_rrl_t **rrlp, dns_view_t *view, int min_entries) {
Adam Tkac b62a53
+	dns_rrl_t *rrl;
Adam Tkac b62a53
+	isc_result_t result;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	*rrlp = NULL;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	rrl = isc_mem_get(view->mctx, sizeof(*rrl));
Adam Tkac b62a53
+	if (rrl == NULL)
Adam Tkac b62a53
+		return (ISC_R_NOMEMORY);
Adam Tkac b62a53
+	memset(rrl, 0, sizeof(*rrl));
Tomas Hozza 574dc4
+	isc_mem_attach(view->mctx, &rrl->mctx);
Adam Tkac b62a53
+	result = isc_mutex_init(&rrl->lock);
Adam Tkac b62a53
+	if (result != ISC_R_SUCCESS) {
Tomas Hozza 574dc4
+		isc_mem_putanddetach(&rrl->mctx, rrl, sizeof(*rrl));
Adam Tkac b62a53
+		return (result);
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+	isc_stdtime_get(&rrl->ts_bases[0]);
Adam Tkac b62a53
+
Adam Tkac b62a53
+	view->rrl = rrl;
Adam Tkac b62a53
+
Adam Tkac b62a53
+	result = expand_entries(rrl, min_entries);
Adam Tkac b62a53
+	if (result != ISC_R_SUCCESS) {
Adam Tkac b62a53
+		dns_rrl_view_destroy(view);
Adam Tkac b62a53
+		return (result);
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+	result = expand_rrl_hash(rrl, 0);
Adam Tkac b62a53
+	if (result != ISC_R_SUCCESS) {
Adam Tkac b62a53
+		dns_rrl_view_destroy(view);
Adam Tkac b62a53
+		return (result);
Adam Tkac b62a53
+	}
Adam Tkac b62a53
+
Adam Tkac b62a53
+	*rrlp = rrl;
Adam Tkac b62a53
+	return (ISC_R_SUCCESS);
Adam Tkac b62a53
+}
Tomas Hozza 574dc4
diff -r -u lib/dns/view.c-orig lib/dns/view.c
Tomas Hozza 574dc4
--- lib/dns/view.c-orig	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
+++ lib/dns/view.c	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
@@ -49,6 +49,7 @@
Adam Tkac b62a53
 #include <dns/masterdump.h>
Adam Tkac b62a53
 #include <dns/order.h>
Adam Tkac b62a53
 #include <dns/peer.h>
Adam Tkac b62a53
+#include <dns/rrl.h>
Adam Tkac b62a53
 #include <dns/rbt.h>
Adam Tkac b62a53
 #include <dns/rdataset.h>
Adam Tkac b62a53
 #include <dns/request.h>
Tomas Hozza 574dc4
@@ -184,6 +185,7 @@
Adam Tkac b62a53
 	view->answeracl_exclude = NULL;
Adam Tkac b62a53
 	view->denyanswernames = NULL;
Adam Tkac b62a53
 	view->answernames_exclude = NULL;
Adam Tkac b62a53
+	view->rrl = NULL;
Adam Tkac b62a53
 	view->provideixfr = ISC_TRUE;
Adam Tkac b62a53
 	view->maxcachettl = 7 * 24 * 3600;
Adam Tkac b62a53
 	view->maxncachettl = 3 * 3600;
Tomas Hozza 574dc4
@@ -335,9 +337,11 @@
Adam Tkac b62a53
 		dns_acache_detach(&view->acache);
Adam Tkac b62a53
 	}
Adam Tkac b62a53
 	dns_rpz_view_destroy(view);
Adam Tkac b62a53
+	dns_rrl_view_destroy(view);
Adam Tkac b62a53
 #else
Adam Tkac b62a53
 	INSIST(view->acache == NULL);
Adam Tkac b62a53
 	INSIST(ISC_LIST_EMPTY(view->rpz_zones));
Adam Tkac b62a53
+	INSIST(view->rrl == NULL);
Adam Tkac b62a53
 #endif
Adam Tkac b62a53
 	if (view->requestmgr != NULL)
Adam Tkac b62a53
 		dns_requestmgr_detach(&view->requestmgr);
Tomas Hozza 574dc4
diff -r -u lib/dns/win32/libdns.def-orig lib/dns/win32/libdns.def
Tomas Hozza 574dc4
--- lib/dns/win32/libdns.def-orig	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
+++ lib/dns/win32/libdns.def	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
@@ -657,6 +657,9 @@
Adam Tkac b62a53
 dns_rriterator_next
Adam Tkac b62a53
 dns_rriterator_nextrrset
Adam Tkac b62a53
 dns_rriterator_pause
Adam Tkac b62a53
+dns_rrl
Adam Tkac b62a53
+dns_rrl_init
Adam Tkac b62a53
+dns_rrl_view_destroy
Adam Tkac b62a53
 dns_sdb_putnamedrr
Adam Tkac b62a53
 dns_sdb_putrdata
Adam Tkac b62a53
 dns_sdb_putrr
Tomas Hozza 574dc4
diff -r -u lib/dns/win32/libdns.dsp-orig lib/dns/win32/libdns.dsp
Tomas Hozza 574dc4
--- lib/dns/win32/libdns.dsp-orig	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
+++ lib/dns/win32/libdns.dsp	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
@@ -346,6 +346,10 @@
Adam Tkac b62a53
 # End Source File
Adam Tkac b62a53
 # Begin Source File
Adam Tkac b62a53
 
Adam Tkac b62a53
+SOURCE=..\include\dns\rrl.h
Adam Tkac b62a53
+# End Source File
Adam Tkac b62a53
+# Begin Source File
Adam Tkac b62a53
+
Adam Tkac b62a53
 SOURCE=..\include\dns\rriterator.h
Adam Tkac b62a53
 # End Source File
Adam Tkac b62a53
 # Begin Source File
Tomas Hozza 574dc4
@@ -650,6 +654,10 @@
Adam Tkac b62a53
 # End Source File
Adam Tkac b62a53
 # Begin Source File
Adam Tkac b62a53
 
Adam Tkac b62a53
+SOURCE=..\rrl.c
Adam Tkac b62a53
+# End Source File
Adam Tkac b62a53
+# Begin Source File
Adam Tkac b62a53
+
Adam Tkac b62a53
 SOURCE=..\rriterator.c
Adam Tkac b62a53
 # End Source File
Adam Tkac b62a53
 # Begin Source File
Tomas Hozza 574dc4
diff -r -u lib/dns/win32/libdns.mak-orig lib/dns/win32/libdns.mak
Tomas Hozza 574dc4
--- lib/dns/win32/libdns.mak-orig	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
+++ lib/dns/win32/libdns.mak	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
@@ -184,6 +184,7 @@
Adam Tkac b62a53
 	-@erase "$(INTDIR)\result.obj"
Adam Tkac b62a53
 	-@erase "$(INTDIR)\rootns.obj"
Adam Tkac b62a53
 	-@erase "$(INTDIR)\rpz.obj"
Adam Tkac b62a53
+	-@erase "$(INTDIR)\rrl.obj"
Adam Tkac b62a53
 	-@erase "$(INTDIR)\sdb.obj"
Adam Tkac b62a53
 	-@erase "$(INTDIR)\sdlz.obj"
Adam Tkac b62a53
 	-@erase "$(INTDIR)\soa.obj"
Tomas Hozza 574dc4
@@ -309,6 +310,7 @@
Adam Tkac b62a53
 	"$(INTDIR)\result.obj" \
Adam Tkac b62a53
 	"$(INTDIR)\rootns.obj" \
Adam Tkac b62a53
 	"$(INTDIR)\rpz.obj" \
Adam Tkac b62a53
+	"$(INTDIR)\rrl.obj" \
Adam Tkac b62a53
 	"$(INTDIR)\rriterator.obj" \
Adam Tkac b62a53
 	"$(INTDIR)\sdb.obj" \
Adam Tkac b62a53
 	"$(INTDIR)\sdlz.obj" \
Tomas Hozza 574dc4
@@ -505,6 +507,8 @@
Adam Tkac b62a53
 	-@erase "$(INTDIR)\rootns.sbr"
Adam Tkac b62a53
 	-@erase "$(INTDIR)\rpz.obj"
Adam Tkac b62a53
 	-@erase "$(INTDIR)\rpz.sbr"
Adam Tkac b62a53
+	-@erase "$(INTDIR)\rrl.obj"
Adam Tkac b62a53
+	-@erase "$(INTDIR)\rrl.sbr"
Adam Tkac b62a53
 	-@erase "$(INTDIR)\rriterator.obj"
Adam Tkac b62a53
 	-@erase "$(INTDIR)\rriterator.sbr"
Adam Tkac b62a53
 	-@erase "$(INTDIR)\sdb.obj"
Tomas Hozza 574dc4
@@ -651,6 +655,7 @@
Adam Tkac b62a53
 	"$(INTDIR)\result.sbr" \
Adam Tkac b62a53
 	"$(INTDIR)\rootns.sbr" \
Adam Tkac b62a53
 	"$(INTDIR)\rpz.sbr" \
Adam Tkac b62a53
+	"$(INTDIR)\rrl.sbr" \
Adam Tkac b62a53
 	"$(INTDIR)\rriterator.sbr" \
Adam Tkac b62a53
 	"$(INTDIR)\sdb.sbr" \
Adam Tkac b62a53
 	"$(INTDIR)\sdlz.sbr" \
Tomas Hozza 574dc4
@@ -748,6 +753,7 @@
Adam Tkac b62a53
 	"$(INTDIR)\result.obj" \
Adam Tkac b62a53
 	"$(INTDIR)\rootns.obj" \
Adam Tkac b62a53
 	"$(INTDIR)\rpz.obj" \
Adam Tkac b62a53
+	"$(INTDIR)\rrl.obj" \
Adam Tkac b62a53
 	"$(INTDIR)\rriterator.obj" \
Adam Tkac b62a53
 	"$(INTDIR)\sdb.obj" \
Adam Tkac b62a53
 	"$(INTDIR)\sdlz.obj" \
Tomas Hozza 574dc4
@@ -1726,6 +1732,24 @@
Adam Tkac b62a53
 
Tomas Hozza 574dc4
 !ENDIF 
Adam Tkac b62a53
 
Adam Tkac b62a53
+SOURCE=..\rrl.c
Adam Tkac b62a53
+
Adam Tkac b62a53
+!IF  "$(CFG)" == "libdns - Win32 Release"
Adam Tkac b62a53
+
Adam Tkac b62a53
+
Adam Tkac b62a53
+"$(INTDIR)\rrl.obj" : $(SOURCE) "$(INTDIR)"
Adam Tkac b62a53
+	$(CPP) $(CPP_PROJ) $(SOURCE)
Adam Tkac b62a53
+
Adam Tkac b62a53
+
Adam Tkac b62a53
+!ELSEIF  "$(CFG)" == "libdns - Win32 Debug"
Adam Tkac b62a53
+
Adam Tkac b62a53
+
Adam Tkac b62a53
+"$(INTDIR)\rrl.obj"	"$(INTDIR)\rrl.sbr" : $(SOURCE) "$(INTDIR)"
Adam Tkac b62a53
+	$(CPP) $(CPP_PROJ) $(SOURCE)
Adam Tkac b62a53
+
Adam Tkac b62a53
+
Tomas Hozza 574dc4
+!ENDIF 
Tomas Hozza 574dc4
+
Tomas Hozza 35a4e4
 SOURCE=..\rriterator.c
Tomas Hozza 574dc4
 
Tomas Hozza 574dc4
 !IF  "$(CFG)" == "libdns - Win32 Release"
Tomas Hozza 574dc4
diff -r -u lib/isccfg/namedconf.c-orig lib/isccfg/namedconf.c
Tomas Hozza 574dc4
--- lib/isccfg/namedconf.c-orig	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
+++ lib/isccfg/namedconf.c	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
@@ -1270,6 +1270,40 @@
Adam Tkac b62a53
 };
Adam Tkac b62a53
 
Adam Tkac b62a53
 
Adam Tkac b62a53
+/*
Adam Tkac b62a53
+ * rate-limit
Adam Tkac b62a53
+ */
Adam Tkac b62a53
+static cfg_clausedef_t rrl_clauses[] = {
Adam Tkac b62a53
+	{ "responses-per-second", &cfg_type_uint32, 0 },
Tomas Hozza 574dc4
+	{ "referrals-per-second", &cfg_type_uint32, 0 },
Tomas Hozza 574dc4
+	{ "nodata-per-second", &cfg_type_uint32, 0 },
Adam Tkac b62a53
+	{ "nxdomains-per-second", &cfg_type_uint32, 0 },
Tomas Hozza 574dc4
+	{ "errors-per-second", &cfg_type_uint32, 0 },
Adam Tkac b62a53
+	{ "all-per-second", &cfg_type_uint32, 0 },
Adam Tkac b62a53
+	{ "slip", &cfg_type_uint32, 0 },
Adam Tkac b62a53
+	{ "window", &cfg_type_uint32, 0 },
Adam Tkac b62a53
+	{ "log-only", &cfg_type_boolean, 0 },
Adam Tkac b62a53
+	{ "qps-scale", &cfg_type_uint32, 0 },
Tomas Hozza 574dc4
+	{ "ipv4-prefix-length", &cfg_type_uint32, 0 },
Tomas Hozza 574dc4
+	{ "ipv6-prefix-length", &cfg_type_uint32, 0 },
Adam Tkac b62a53
+	{ "exempt-clients", &cfg_type_bracketed_aml, 0 },
Adam Tkac b62a53
+	{ "max-table-size", &cfg_type_uint32, 0 },
Adam Tkac b62a53
+	{ "min-table-size", &cfg_type_uint32, 0 },
Adam Tkac b62a53
+	{ NULL, NULL, 0 }
Adam Tkac b62a53
+};
Adam Tkac b62a53
+
Adam Tkac b62a53
+static cfg_clausedef_t *rrl_clausesets[] = {
Adam Tkac b62a53
+	rrl_clauses,
Adam Tkac b62a53
+	NULL
Adam Tkac b62a53
+};
Adam Tkac b62a53
+
Adam Tkac b62a53
+static cfg_type_t cfg_type_rrl = {
Adam Tkac b62a53
+	"rate-limit", cfg_parse_map, cfg_print_map, cfg_doc_map,
Adam Tkac b62a53
+	&cfg_rep_map, rrl_clausesets
Adam Tkac b62a53
+};
Adam Tkac b62a53
+
Adam Tkac b62a53
+
Adam Tkac b62a53
+
Adam Tkac b62a53
 /*%
Adam Tkac b62a53
  * dnssec-lookaside
Adam Tkac b62a53
  */
Tomas Hozza 574dc4
@@ -1423,6 +1457,7 @@
Adam Tkac b62a53
 	   CFG_CLAUSEFLAG_NOTCONFIGURED },
Adam Tkac b62a53
 #endif
Adam Tkac b62a53
 	{ "response-policy", &cfg_type_rpz, 0 },
Adam Tkac b62a53
+	{ "rate-limit", &cfg_type_rrl, 0 },
Adam Tkac b62a53
 	{ NULL, NULL, 0 }
Adam Tkac b62a53
 };
Adam Tkac b62a53
 
Tomas Hozza 574dc4
diff -r -u version-orig version
Tomas Hozza 574dc4
--- version-orig	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
+++ version	2004-01-01 00:00:00.000000000 +0000
Tomas Hozza 574dc4
@@ -7,6 +7,6 @@
Tomas Hozza 574dc4
 DESCRIPTION="(Extended Support Version)"
Adam Tkac b62a53
 MAJORVER=9
Adam Tkac b62a53
 MINORVER=9
Tomas Hozza 574dc4
-PATCHVER=3
Tomas Hozza 612f0b
+PATCHVER=3-rl.13207.22
Tomas Hozza acf65a
 RELEASETYPE=-P
Tomas Hozza 612f0b
 RELEASEVER=2