diff -uNrbB bind-9.9.2-orig/bin/named/client.c bind-9.9.2/bin/named/client.c
--- bind-9.9.2-orig/bin/named/client.c	2012-09-27 02:35:19.000000000 +0200
+++ bind-9.9.2/bin/named/client.c	2012-10-13 23:30:52.000000000 +0200
@@ -883,6 +883,11 @@
 		goto done;
 	}
 
+        if (client->view != NULL && client->view->dampening != NULL)
+		dns_dampening_score_size(client->view->dampening,
+					 &client->peeraddr,
+					 client->now, mr->length);
+
 	result = client_allocsendbuf(client, &buffer, NULL, mr->length,
 				     sendbuf, &data);
 	if (result != ISC_R_SUCCESS)
diff -uNrbB bind-9.9.2-orig/bin/named/include/named/server.h bind-9.9.2/bin/named/include/named/server.h
--- bind-9.9.2-orig/bin/named/include/named/server.h	2012-09-27 02:35:19.000000000 +0200
+++ bind-9.9.2/bin/named/include/named/server.h	2012-10-13 23:30:52.000000000 +0200
@@ -165,7 +165,9 @@
 	dns_nsstatscounter_updatefail = 34,
 	dns_nsstatscounter_updatebadprereq = 35,
 
-	dns_nsstatscounter_max = 36
+	dns_nsstatscounter_dampened = 36,
+     
+	dns_nsstatscounter_max = 37
 };
 
 void
diff -uNrbB bind-9.9.2-orig/bin/named/query.c bind-9.9.2/bin/named/query.c
--- bind-9.9.2-orig/bin/named/query.c	2012-09-27 02:35:19.000000000 +0200
+++ bind-9.9.2/bin/named/query.c	2012-10-14 14:29:35.000000000 +0200
@@ -7228,11 +7228,12 @@
 }
 
 static inline void
-log_query(ns_client_t *client, unsigned int flags, unsigned int extflags) {
+log_query(ns_client_t *client, unsigned int flags, unsigned int extflags, int penalty) {
 	char namebuf[DNS_NAME_FORMATSIZE];
 	char typename[DNS_RDATATYPE_FORMATSIZE];
 	char classname[DNS_RDATACLASS_FORMATSIZE];
 	char onbuf[ISC_NETADDR_FORMATSIZE];
+   	char penbuf[sizeof("65536")+1];
 	dns_rdataset_t *rdataset;
 	int level = ISC_LOG_INFO;
 
@@ -7245,9 +7246,10 @@
 	dns_rdataclass_format(rdataset->rdclass, classname, sizeof(classname));
 	dns_rdatatype_format(rdataset->type, typename, sizeof(typename));
 	isc_netaddr_format(&client->destaddr, onbuf, sizeof(onbuf));
+   	snprintf(penbuf, sizeof(penbuf), "%d", penalty);
 
 	ns_client_log(client, NS_LOGCATEGORY_QUERIES, NS_LOGMODULE_QUERY,
-		      level, "query: %s %s %s %s%s%s%s%s%s (%s)", namebuf,
+		      level, "query: %s %s %s %s%s%s%s%s%s (%s) %s", namebuf,
 		      classname, typename, WANTRECURSION(client) ? "+" : "-",
 		      (client->signer != NULL) ? "S": "",
 		      (client->opt != NULL) ? "E" : "",
@@ -7255,7 +7257,8 @@
 				 "T" : "",
 		      ((extflags & DNS_MESSAGEEXTFLAG_DO) != 0) ? "D" : "",
 		      ((flags & DNS_MESSAGEFLAG_CD) != 0) ? "C" : "",
-		      onbuf);
+		      onbuf,
+		      penalty < -1 ? "" : penalty < 0 ? "-" : penbuf);
 }
 
 static inline void
@@ -7310,6 +7313,7 @@
 	unsigned int saved_extflags = client->extflags;
 	unsigned int saved_flags = client->message->flags;
 	isc_boolean_t want_ad;
+   	int client_penalty = -2;
 
 	CTRACE("ns_query_start");
 
@@ -7364,6 +7368,22 @@
 	}
 
 	/*
+	 * Dampening for UDP packets only. Early processing to prevent error
+	 * message reflector attacks.
+	 */
+	if ((client->attributes & NS_CLIENTATTR_TCP) == 0 &&
+	    client->view != NULL && client->view->dampening != NULL) {
+	   	if( DNS_DAMPENING_STATE_SUPPRESS ==
+		    dns_dampening_query(client->view->dampening,
+					&client->peeraddr, client->now,
+					&client_penalty) ) {
+		   	inc_stats(client, dns_nsstatscounter_dampened);
+			query_next(client, DNS_R_DROP);
+			return;
+		}
+	}
+
+	/*
 	 * Get the question name.
 	 */
 	result = dns_message_firstname(message, DNS_SECTION_QUESTION);
@@ -7388,7 +7408,7 @@
 	}
 
 	if (ns_g_server->log_queries)
-		log_query(client, saved_flags, saved_extflags);
+		log_query(client, saved_flags, saved_extflags, client_penalty);
 
 	/*
 	 * Check for multiple question queries, since edns1 is dead.
@@ -7405,6 +7425,10 @@
 	INSIST(rdataset != NULL);
 	qtype = rdataset->type;
 	dns_rdatatypestats_increment(ns_g_server->rcvquerystats, qtype);
+   	if (client->view != NULL && client->view->dampening != NULL)
+		dns_dampening_score_qtype(client->view->dampening,
+					  &client->peeraddr, client->now,
+					  client->message->id, qtype);
 	if (dns_rdatatype_ismeta(qtype)) {
 		switch (qtype) {
 		case dns_rdatatype_any:
diff -uNrbB bind-9.9.2-orig/bin/named/server.c bind-9.9.2/bin/named/server.c
--- bind-9.9.2-orig/bin/named/server.c	2012-09-27 02:35:19.000000000 +0200
+++ bind-9.9.2/bin/named/server.c	2012-10-14 16:18:57.000000000 +0200
@@ -156,6 +156,16 @@
 			fatal(msg, result);			  \
 	} while (0)						  \
 
+#define CHECKRANGE(obj, cond, pat, val) \
+	do {								\
+		if (!(cond)) {						\
+			cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR,	\
+				    pat, val);				\
+			result = ISC_R_RANGE;				\
+			goto cleanup;					\
+		    }							\
+	} while (0)							\
+
 /*%
  * Maximum ADB size for views that share a cache.  Use this limit to suppress
  * the total of memory footprint, which should be the main reason for sharing
@@ -1561,6 +1571,230 @@
 	return (result);
 }
 
+static isc_result_t
+configure_dampening(dns_view_t *view, const cfg_obj_t *config,
+		    const cfg_obj_t *map) {
+	const cfg_obj_t *obj;
+	isc_result_t result;
+   	int min_entries, i;
+
+	min_entries = 500;
+	obj = NULL;
+	result = cfg_map_get(map, "min-table-size", &obj);
+	if (result == ISC_R_SUCCESS) {
+		min_entries = cfg_obj_asuint32(obj);
+	   	CHECKRANGE(obj, 1 < min_entries && min_entries < ISC_UINT16_MAX,
+			   "invalid '{min-table-size %d;}'", min_entries);
+	}
+	result = dns_dampening_init(view, min_entries);
+	if (result != ISC_R_SUCCESS)
+		goto cleanup;
+
+	i = ISC_MAX(min_entries, 1000);
+	obj = NULL;
+	result = cfg_map_get(map, "max-table-size", &obj);
+	if (result == ISC_R_SUCCESS) {
+		i = cfg_obj_asuint32(obj);
+		CHECKRANGE(obj, min_entries <= i && i < ISC_UINT16_MAX,
+			   "invalid '{max-table-size %d;}'", i);
+	}
+	view->dampening->max_entries = i;
+
+   	i = 600;
+	obj = NULL;
+	result = cfg_map_get(map, "halflife", &obj);
+	if (result == ISC_R_SUCCESS) {
+		i = cfg_obj_asuint32(obj);
+		CHECKRANGE(obj, 10 < i,
+			   "invalid '{halflife %d;}'", i);
+	}
+	view->dampening->decay.halflife = i;
+
+   	i = ISC_MAX(1 , view->dampening->decay.halflife / 100);
+	obj = NULL;
+	result = cfg_map_get(map, "update-delay", &obj);
+	if (result == ISC_R_SUCCESS) {
+		i = cfg_obj_asuint32(obj);
+		CHECKRANGE(obj, i <= view->dampening->decay.halflife,
+			   "invalid '{update-delay %d;}'", i);
+	}
+	view->dampening->decay.updatedelay = i;
+
+   	i = 32000;
+	obj = NULL;
+	result = cfg_map_get(map, "limit-maximum", &obj);
+	if (result == ISC_R_SUCCESS) {
+		i = cfg_obj_asuint32(obj);
+		CHECKRANGE(obj, 1000 <= i && i <= ISC_UINT16_MAX,
+			   "invalid '{limit-maximum %d;}'", i);
+	}
+	view->dampening->limit.maximum = i;
+
+   	i = 0.8 * view->dampening->limit.maximum;
+	obj = NULL;
+	result = cfg_map_get(map, "limit-enable-dampening", &obj);
+	if (result == ISC_R_SUCCESS) {
+		i = cfg_obj_asuint32(obj);
+		CHECKRANGE(obj, 0.3 * view->dampening->limit.maximum < i && i < view->dampening->limit.maximum,
+			   "invalid '{limit-enable-dampening %d;}'", i);
+	}
+	view->dampening->limit.enable_dampening = i;
+
+   	i = 0.3 * view->dampening->limit.maximum;
+	obj = NULL;
+	result = cfg_map_get(map, "limit-disable-dampening", &obj);
+	if (result == ISC_R_SUCCESS) {
+		i = cfg_obj_asuint32(obj);
+		CHECKRANGE(obj, 0.1 * view->dampening->limit.maximum < i && i < view->dampening->limit.enable_dampening,
+			   "invalid '{limit-disable-dampening %d;}'", i);
+	}
+	view->dampening->limit.disable_dampening = i;
+
+   	i = 100;
+	obj = NULL;
+	result = cfg_map_get(map, "limit-irrelevant", &obj);
+	if (result == ISC_R_SUCCESS) {
+		i = cfg_obj_asuint32(obj);
+		CHECKRANGE(obj, 100 < i && i < view->dampening->limit.disable_dampening,
+			   "invalid '{limit-irrelevant %d;}'", i);
+	}
+	view->dampening->limit.irrelevant = i;
+
+   	i = 10;
+	obj = NULL;
+	result = cfg_map_get(map, "score-first-query", &obj);
+	if (result == ISC_R_SUCCESS) {
+		i = cfg_obj_asuint32(obj);
+		CHECKRANGE(obj, i < view->dampening->limit.enable_dampening,
+			   "invalid '{score-first-query %d;}'", i);
+	}
+	view->dampening->score.first_query = i;
+
+   	i = 1;
+	obj = NULL;
+	result = cfg_map_get(map, "score-per-query", &obj);
+	if (result == ISC_R_SUCCESS) {
+		i = cfg_obj_asuint32(obj);
+		CHECKRANGE(obj, i < view->dampening->limit.maximum,
+			   "invalid '{score-per-query %d;}'", i);
+	}
+	view->dampening->score.per_query = i;
+
+   	i = 100;
+	obj = NULL;
+	result = cfg_map_get(map, "score-qtype-any", &obj);
+	if (result == ISC_R_SUCCESS) {
+		i = cfg_obj_asuint32(obj);
+		CHECKRANGE(obj, i < view->dampening->limit.maximum,
+			   "invalid '{score-qtype-any %d;}'", i);
+	}
+	view->dampening->score.qtype_any = i;
+
+   	i = 500;
+	obj = NULL;
+	result = cfg_map_get(map, "minimum-score-size", &obj);
+	if (result == ISC_R_SUCCESS) {
+		i = cfg_obj_asuint32(obj);
+		CHECKRANGE(obj, i < ISC_UINT16_MAX / 4,
+			   "invalid '{minimum-score-size %d;}'", i);
+	}
+	view->dampening->score.minimum_size = i;
+
+   	i = ISC_MAX(4000,4*view->dampening->score.minimum_size);
+	obj = NULL;
+	result = cfg_map_get(map, "maximum-score-size", &obj);
+	if (result == ISC_R_SUCCESS) {
+		i = cfg_obj_asuint32(obj);
+		CHECKRANGE(obj, i > view->dampening->score.minimum_size && i <= ISC_UINT16_MAX,
+			   "invalid '{maximum-score-size %d;}'", i);
+	}
+	view->dampening->score.maximum_size = i;
+
+   	i = 100;
+	obj = NULL;
+	result = cfg_map_get(map, "score-size", &obj);
+	if (result == ISC_R_SUCCESS) {
+		i = cfg_obj_asuint32(obj);
+		CHECKRANGE(obj, i < view->dampening->limit.maximum,
+			   "invalid '{score-size %d;}'", i);
+	}
+	view->dampening->score.size_penalty = i;
+
+   	i = 100;
+	obj = NULL;
+	result = cfg_map_get(map, "score-duplicates", &obj);
+	if (result == ISC_R_SUCCESS) {
+		i = cfg_obj_asuint32(obj);
+		CHECKRANGE(obj, i < view->dampening->limit.maximum,
+			   "invalid '{score-duplicates %d;}'", i);
+	}
+	view->dampening->score.duplicates = i;
+
+	i = 24;
+	obj = NULL;
+	result = cfg_map_get(map, "IPv4-prefix-length", &obj);
+	if (result == ISC_R_SUCCESS) {
+		i = cfg_obj_asuint32(obj);
+		CHECKRANGE(obj, i >= 8 && i <= 32,
+			   "invalid '{IPv4-prefix-length %d;}'", i);
+	}
+	view->dampening->prefixlen.ipv4 = i;
+
+	i = 48;
+	obj = NULL;
+	result = cfg_map_get(map, "IPv6-prefix-length", &obj);
+	if (result == ISC_R_SUCCESS) {
+		i = cfg_obj_asuint32(obj);
+		CHECKRANGE(obj, i >= 16 && i <= 128,
+			   "invalid '{IPv6-prefix-length %d;}'", i);
+	}
+	view->dampening->prefixlen.ipv6 = i;
+
+	obj = NULL;
+	result = cfg_map_get(map, "exempt-clients", &obj);
+	if (result == ISC_R_SUCCESS) {
+		result = cfg_acl_fromconfig(obj, config, ns_g_lctx,
+					    ns_g_aclconfctx, ns_g_mctx,
+					    0, &view->dampening->exempt);
+		CHECKRANGE(obj, result == ISC_R_SUCCESS,
+			   "invalid %s", "address_match_list");
+	}
+
+   	i = 0;
+	obj = NULL;
+	result = cfg_map_get(map, "report-interval", &obj);
+	if (result == ISC_R_SUCCESS) {
+		i = cfg_obj_asuint32(obj);
+	}
+	view->dampening->statistics.report_interval = i;
+
+	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+		      NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+		      "Dampening configured to max_entries=%d prefixlen{ipv4=%d ipv6=%d} decay{halflife=%d updatedelay=%d} limit{max=%d enable=%d disable=%d irrelevant=%d} score{first=%d each=%d any=%d dup=%d size=%d mins=%d maxs=%d} report=%d",
+		      view->dampening->max_entries,
+		      view->dampening->prefixlen.ipv4,
+		      view->dampening->prefixlen.ipv6,
+		      view->dampening->decay.halflife,
+		      view->dampening->decay.updatedelay,
+		      view->dampening->limit.maximum,
+		      view->dampening->limit.enable_dampening,
+		      view->dampening->limit.disable_dampening,
+		      view->dampening->limit.irrelevant,
+		      view->dampening->score.first_query,
+		      view->dampening->score.per_query,
+		      view->dampening->score.qtype_any,
+		      view->dampening->score.duplicates,
+		      view->dampening->score.size_penalty,
+		      view->dampening->score.minimum_size,
+		      view->dampening->score.maximum_size,
+		      view->dampening->statistics.report_interval
+		      );
+	return (ISC_R_SUCCESS);
+cleanup:
+	dns_dampening_destroy(view);
+	return (result);
+}
+
 /*
  * Configure 'view' according to 'vconfig', taking defaults from 'config'
  * where values are missing in 'vconfig'.
@@ -2925,6 +3159,17 @@
 		}
 	}
 
+	/*
+	 * Activate dampening
+	 */
+	obj = NULL;
+	result = ns_config_get(maps, "dampening", &obj);
+	if (result == ISC_R_SUCCESS) {
+		result = configure_dampening(view, config, obj);
+		if (result != ISC_R_SUCCESS)
+			goto cleanup;
+	}
+
 	result = ISC_R_SUCCESS;
 
  cleanup:
diff -uNrbB bind-9.9.2-orig/bin/named/statschannel.c bind-9.9.2/bin/named/statschannel.c
--- bind-9.9.2-orig/bin/named/statschannel.c	2012-09-27 02:35:19.000000000 +0200
+++ bind-9.9.2/bin/named/statschannel.c	2012-10-15 23:29:20.000000000 +0200
@@ -202,6 +202,9 @@
 	SET_NSSTATDESC(updatebadprereq,
 		       "updates rejected due to prerequisite failure",
 		       "UpdateBadPrereq");
+	SET_NSSTATDESC(dampened,
+		       "query dropped due to dampening",
+		       "Dampened");
 	INSIST(i == dns_nsstatscounter_max);
 
 	/* Initialize resolver statistics */
diff -uNrbB bind-9.9.2-orig/doc/arm/Bv9ARM-book.xml bind-9.9.2/doc/arm/Bv9ARM-book.xml
--- bind-9.9.2-orig/doc/arm/Bv9ARM-book.xml	2012-09-27 02:35:19.000000000 +0200
+++ bind-9.9.2/doc/arm/Bv9ARM-book.xml	2012-10-15 23:28:19.000000000 +0200
@@ -4803,6 +4803,22 @@
 		    </para>
 		  </entry>
 		</row>
+                <row rowsep="0">
+                  <entry colname="1">
+                    <para><command>dampening</command></para>
+                  </entry>
+		  <entry colname="2">
+		    <para>
+		     The start and periodic notices of a penalty based
+		     suppression of incoming queries are logged at
+		     <command>info</command> severity in this category.
+		     Dampening events for prefixes as well as  
+		     periodic performance data comes into the same severity.
+		     Individual penalty values per query are logged in the
+		     <command>queries</command> category.
+		    </para>
+		  </entry>
+		</row>
 	      </tbody>
 	    </tgroup>
 	  </informaltable>
@@ -5339,6 +5355,27 @@
 	<optional> recursive-only <replaceable>yes_or_no</replaceable> </optional> <optional> max-policy-ttl <replaceable>number</replaceable> </optional> ;
     } <optional> recursive-only <replaceable>yes_or_no</replaceable> </optional> <optional> max-policy-ttl <replaceable>number</replaceable> </optional>
 	<optional> break-dnssec <replaceable>yes_or_no</replaceable> </optional> ; </optional>
+    <optional> dampening {
+	<optional> halflife <replaceable>number</replaceable> ; </optional>
+	<optional> update-delay <replaceable>number</replaceable> ; </optional>
+	<optional> limit-maximum <replaceable>number</replaceable> ; </optional>
+	<optional> limit-enable-dampening <replaceable>number</replaceable> ; </optional>
+	<optional> limit-disable-dampening <replaceable>number</replaceable> ; </optional>
+	<optional> limit-irrelevant <replaceable>number</replaceable> ; </optional>
+	<optional> score-first-query <replaceable>number</replaceable> ; </optional>
+	<optional> score-per-query <replaceable>number</replaceable> ; </optional>
+	<optional> score-qtype-any <replaceable>number</replaceable> ; </optional>
+	<optional> minimum-score-size <replaceable>number</replaceable> ; </optional>
+	<optional> maximum-score-size <replaceable>number</replaceable> ; </optional>
+	<optional> score-size <replaceable>number</replaceable> ; </optional>
+	<optional> score-duplicates <replaceable>number</replaceable> ; </optional>
+	<optional> report-interval <replaceable>number</replaceable> ; </optional>
+	<optional> IPv4-prefix-length <replaceable>number</replaceable> ; </optional>
+	<optional> IPv6-prefix-length <replaceable>number</replaceable> ; </optional>
+	<optional> exempt-clients  { <replaceable>address_match_list</replaceable> } ; </optional>
+	<optional> max-table-size <replaceable>number</replaceable> ; </optional>
+	<optional> min-table-size <replaceable>number</replaceable> ; </optional>
+      } ; </optional>
 };
 </programlisting>
 
@@ -9737,6 +9774,91 @@
 48.zz.2.2001.rpz-nsip       CNAME   .
 </programlisting>
         </sect3>
+	
+	<sect3>
+	  <title>Dampening</title>
+	  <para>
+	    Spoofed UDP queries are a major security threat especially when
+	    reflected and possibly amplified by DNS servers. Dampening is a
+	    dynamically adapting method for dealing with such kinds of misuse.
+	    So dampening a about spoofing, hence TCP connections are not
+	    affected at all.
+	  </para>
+
+	  <para>
+	    Every piece of a query and its response is associated with
+	    specific penalty points. Those points aggregate over all queries
+	    from netblocks defined by <command>IPv6-prefix-length</command> and
+	    <command>IPv4-prefix-length</command>. Over the time, the penalty
+	    decrease exponentially (determined by <command>halflife</command>).
+	    So each netblock gains a penalty over time characteristics.
+	  </para>
+
+	  <para>
+	    In order to allow local clients, which should be protected by
+	    anti-spoof techniques at the edge of the operated network, to
+	    send any amount of queries without fear the risks of being blocked,
+	    push them into a seperate view with other or no dampening rules or
+	    add them to <command>exempt-clients</command>.
+	  </para>
+
+	  <para>
+	    If the penalty exeeds the <command>limit-enable-dampening</command>,
+	    the netblock gets dampened. No further query processing will occur.
+	    Every further query is dropped silently before inspecting the
+	    content of the query itself. So the penalty value can decrease
+	    slowly, if the sender collect not quickly enough further
+	    <command>score-per-query</command> points. Please note, that
+	    penalty values can't exceed <command>limit-maximum</command>.
+	  </para>
+
+	  <para>
+	    If the penalty drops below <command>limit-disable-dampening</command>,
+	    query processing for the netblock switchs back to normal. If
+	    the penalty drops as low as <command>limit-irrelevant</command>,
+	    the netblock is removed from the statistics. The next query
+	    gains <command>score-first-query</command> for this netblock
+	    and reenteres the collection.
+	  </para>
+
+	  <para>
+	    Comment current attacks gain a huge amplification from ANY query
+	    to DNSSEC signed zones. So each ANY query is charged with
+	    additional <command>score-qtype-any</command> points. Furthermore
+	    current attackers repeat the same packet over and over again, so
+	    keeping the same id in each query. Such repetitions are recognised
+	    and give <command>score-duplicates</command> points times the
+	    repetition of this id.
+	  </para>
+
+	  <para>
+	    Amplification attacks aims to huge answers, so the next penalty
+	    comes from the response size. The penalty increased lineary from
+	    zero for responses up to <command>minimum-score-size</command>
+	    bytes. The maximum penalty of <command>score-size</command> is
+	    reached when the response reaches or exceeds
+	    <command>maximum-score-size</command> bytes.
+	  </para>
+
+	  <para>
+	    In order to minimize expensive calulations for each query, the
+	    penalty decay is delayed by at least
+	    <command>update-delay</command> seconds. The statistics table
+	    has space for <command>min-table-size</command> elements but
+	    might increase to <command>max-table-size</command> elements if
+	    necessary.
+	  </para>
+	  
+	  </para>
+	    In order to compare various storage models, which are activated
+	    at compile time, regular statistics can by extracted by setting
+	    <command>report-interval</command> to the seconds between reports.
+	    Those statistic lines contain the numerical index of the
+	    implementation <command>Stats for #...</command>, the number of
+	    queries processed, dampened, and exempted <command>queries ././.</command>,
+	    and aggregated time spend in the various functions.
+	  <para>
+	</sect3>
       </sect2>
 
       <sect2 id="server_statement_grammar">
@@ -14385,6 +14507,19 @@
 		      </para>
 		    </entry>
 		  </row>
+		  <row rowsep="0">
+		    <entry colname="1">
+		      <para><command>Dampened</command></para>
+		    </entry>
+		    <entry colname="2">
+		      <para><command></command></para>
+		    </entry>
+		    <entry colname="3">
+		      <para>
+			Queries dropped due to dampening
+		      </para>
+		    </entry>
+		  </row>
 		</tbody>
               </tgroup>
             </informaltable>
diff -uNrbB bind-9.9.2-orig/lib/dns/dampening.c bind-9.9.2/lib/dns/dampening.c
--- bind-9.9.2-orig/lib/dns/dampening.c	1970-01-01 01:00:00.000000000 +0100
+++ bind-9.9.2/lib/dns/dampening.c	2012-10-15 23:39:46.000000000 +0200
@@ -0,0 +1,974 @@
+/*
+ * (C) 2012 Lutz Donnerhacke
+ */
+
+#include <config.h>
+#include <math.h>
+#include <sys/time.h>
+
+/*
+#include <isc/buffer.h>
+#include <isc/net.h>
+#include <isc/netaddr.h>
+#include <isc/print.h>
+#include <isc/stdlib.h>
+#include <isc/string.h>
+#include <isc/util.h>
+#include <dns/db.h>
+#include <dns/fixedname.h>
+#include <dns/rdata.h>
+#include <dns/rdataset.h>
+#include <dns/rdatastruct.h>
+#include <dns/result.h>
+#include <dns/rpz.h>
+#include <dns/view.h>
+ */
+
+#include <isc/mem.h>
+#include <dns/dampening.h>
+#include <dns/log.h>
+#include <dns/view.h>
+
+#define DAMPENING_STATISTICS_DO(impl, field, command)	do { \
+   struct timeval before, after, diff; \
+   gettimeofday(&before, NULL); \
+   command; \
+   gettimeofday(&after, NULL); \
+   timersub(&after, &before, &diff); \
+   timeradd(&diff, &((impl)->statistics.field), &after); \
+   (impl)->statistics.field = after; \
+}  while(0)
+#define DAMPENING_STATISTICS_INC(impl, field)	do { (impl)->statistics.field++; } while(0)
+
+
+static isc_result_t heap_init (isc_mem_t *, dns_dampening_implementation_t *, dns_dampening_t *, uint16_t);
+static isc_result_t queue_init(isc_mem_t *, dns_dampening_implementation_t *, dns_dampening_t *, uint16_t);
+
+static isc_result_t
+(*(implementations[])) (isc_mem_t *, dns_dampening_implementation_t *,
+			dns_dampening_t *, uint16_t) = {
+	heap_init , queue_init
+};
+   
+
+
+static void
+extract_prefix(isc_netaddr_t * prefix, const isc_netaddr_t * addr, const struct dns_dampening_prefix * prefixlen) {
+   int bytes, bits, len, max = -1;
+   unsigned char *p = NULL;
+   
+   INSIST(prefix != NULL);
+   INSIST(addr != NULL);
+   INSIST(prefixlen != NULL);
+   
+   memcpy(prefix, addr, sizeof(*prefix));
+   
+   switch(addr->family) {
+    case AF_INET:
+      p = (unsigned char *)&(prefix->type.in);
+      max = 32;
+      len = prefixlen->ipv4;
+      break;
+    case AF_INET6:
+      p = (unsigned char *)&(prefix->type.in6);
+      max = 128;
+      len = prefixlen->ipv6;
+      break;
+    default:
+      return;
+   }
+
+   INSIST(0 <= len && len <= max);
+   INSIST(p != NULL);
+   
+   bytes = len / 8;
+   bits  = len % 8;
+   
+   if(bits > 0)
+     p[bytes++] &= (0xFF << (8-bits)) & 0xFF;
+
+   for(max /= 8; bytes < max; bytes++)
+     p[bytes] = 0;
+
+}
+
+static void
+log_dampening(const dns_dampening_t * conf, const isc_netaddr_t * prefix, int enabled) {
+   char pb[ISC_NETADDR_FORMATSIZE];
+   int len;
+   
+   INSIST(conf != NULL);
+   INSIST(prefix != NULL);
+   
+   switch(prefix->family) {
+    case AF_INET : len = conf->prefixlen.ipv4; break;
+    case AF_INET6: len = conf->prefixlen.ipv6; break;
+    default      : return;
+   }
+
+   if(isc_log_wouldlog(dns_lctx, ISC_LOG_INFO)) {
+      isc_netaddr_format(prefix, pb, sizeof(pb));
+      isc_log_write(dns_lctx, DNS_LOGCATEGORY_DAMPENING,
+		    DNS_LOGMODULE_REQUEST, ISC_LOG_INFO,
+		    "%s/%d dampening %s.",pb, len,
+		    enabled ? "activated" : "removed");
+   }
+}
+
+/*
+ * Decay the penalty value, if necessary and add the new points.
+ * Return zero if the entry is below the drop limit. Caller should remove it
+ * from the table. Otherwise the caller has to update the structure to reflect
+ * the modified values.
+ */
+static int
+update_penalty(const dns_dampening_t * conf, dns_dampening_entry_t * entry,
+	       uint16_t points, isc_stdtime_t now) {
+   int timediff;
+   
+   INSIST(conf != NULL);
+   INSIST(entry != NULL);
+   
+   timediff = now - entry->last_updated;
+   if(timediff > conf->decay.updatedelay) {
+      float penalty = entry->penalty;
+      penalty *= exp(-(0.693*timediff)/conf->decay.halflife);
+      entry->penalty = penalty;
+      entry->last_updated = now;
+   }
+   if(entry->penalty >= conf->limit.maximum - points)
+     entry->penalty = conf->limit.maximum;
+   else
+     entry->penalty += points;
+
+   if(entry->dampening == 1 && entry->penalty < conf->limit.disable_dampening) {
+      entry->dampening = 0;
+      log_dampening(conf, &entry->netaddr, entry->dampening);
+   }
+   
+   if(entry->dampening == 0 && entry->penalty > conf->limit.enable_dampening) {
+      entry->dampening = 1;
+      log_dampening(conf, &entry->netaddr, entry->dampening);
+   }
+   
+   if(entry->penalty < conf->limit.irrelevant &&
+      timediff > conf->decay.updatedelay) {
+      return 0;
+   } else {
+      return 1;
+   }
+}
+
+dns_dampening_state_t
+dns_dampening_query(dns_dampening_t * damp, const isc_sockaddr_t * addr,
+		    isc_stdtime_t now, int * penalty) {
+   isc_netaddr_t netaddr, prefix;
+   dns_dampening_state_t final_state = DNS_DAMPENING_STATE_NORMAL, state = DNS_DAMPENING_STATE_NORMAL;
+   dns_dampening_entry_t * entry;
+   dns_dampening_implementation_t *impl;
+   int max_penalty = -2;
+
+   RUNTIME_CHECK( damp != NULL );
+   RUNTIME_CHECK( addr != NULL );
+
+   isc_netaddr_fromsockaddr(&netaddr, addr);
+   extract_prefix(&prefix, &netaddr, &(damp->prefixlen));
+   
+   for(impl = damp->workers;
+       impl - damp->workers < damp->workers_count;
+       impl++) {
+      
+      if(damp->exempt != NULL) {
+	 int match;
+	 
+	 if (ISC_R_SUCCESS == dns_acl_match(&netaddr, NULL, damp->exempt,
+					    NULL, &match, NULL) &&
+	     match > 0) {
+	    max_penalty = ISC_MAX(max_penalty, -1);
+	    DAMPENING_STATISTICS_INC(impl,skipped);
+	    continue;
+	 }
+      }
+      
+      DAMPENING_STATISTICS_DO(impl, lock, LOCK(&impl->lock));
+      
+      if(damp->statistics.report_interval > 0 &&
+	 damp->statistics.report_interval + impl->statistics.last_report <= now) {
+	 if(isc_log_wouldlog(dns_lctx, ISC_LOG_INFO))
+	   isc_log_write(dns_lctx, DNS_LOGCATEGORY_DAMPENING,
+			 DNS_LOGMODULE_REQUEST, ISC_LOG_INFO,
+			 "Stats for #%d: queries %u/%u/%u: lock=%ld.%06ld, search=%ld.%06ld, update=%ld.%06ld, add=%ld.%06ld",
+			 impl - damp->workers,
+			 impl->statistics.allowed, impl->statistics.denied, impl->statistics.skipped,
+			 impl->statistics.lock.tv_sec, impl->statistics.lock.tv_usec,
+			 impl->statistics.search.tv_sec, impl->statistics.search.tv_usec,
+			 impl->statistics.update.tv_sec, impl->statistics.update.tv_usec,
+			 impl->statistics.add.tv_sec, impl->statistics.add.tv_usec);
+	 memset(&impl->statistics, 0, sizeof(impl->statistics));
+	 impl->statistics.last_report = now;
+      }
+      
+      DAMPENING_STATISTICS_DO(impl, search, entry = impl->search(impl->data, &prefix));
+      if(entry == NULL) {
+	 state = DNS_DAMPENING_STATE_NORMAL;
+	 DAMPENING_STATISTICS_DO(impl, add, impl->add(impl->data, &prefix, damp->score.first_query, now));
+	 max_penalty = ISC_MAX(max_penalty, 0);
+      } else {
+	 state = entry->dampening == 1
+	   ? DNS_DAMPENING_STATE_SUPPRESS
+	   : DNS_DAMPENING_STATE_NORMAL;
+	 max_penalty = ISC_MAX(max_penalty, entry->penalty);
+	 DAMPENING_STATISTICS_DO(impl, update, impl->update(impl->data, &entry, damp->score.per_query, now));
+      }
+      
+      if(state == DNS_DAMPENING_STATE_NORMAL) {
+	 DAMPENING_STATISTICS_INC(impl, allowed);
+      } else {
+	 DAMPENING_STATISTICS_INC(impl, denied);
+	 final_state = state;	       /* any dampening suffice */
+      }
+
+      UNLOCK(&impl->lock);
+   }
+   
+   if(penalty != NULL) *penalty = max_penalty;
+   return final_state;
+}
+
+void dns_dampening_score_qtype(dns_dampening_t * damp,
+			       const isc_sockaddr_t * addr,
+			       isc_stdtime_t now,
+			       dns_messageid_t message_id,
+			       int qtype) {
+   isc_netaddr_t netaddr, prefix;
+   dns_dampening_entry_t * entry;
+   uint16_t points;
+   dns_dampening_implementation_t *impl;
+   
+   RUNTIME_CHECK( damp != NULL );
+   RUNTIME_CHECK( addr != NULL );
+   
+   isc_netaddr_fromsockaddr(&netaddr, addr);
+   extract_prefix(&prefix, &netaddr, &(damp->prefixlen));
+  
+   for(impl = damp->workers;
+       impl - damp->workers < damp->workers_count;
+       impl++) {
+
+      if(damp->exempt != NULL) {
+	 int match;
+	 
+	 if (ISC_R_SUCCESS == dns_acl_match(&netaddr, NULL, damp->exempt,
+					    NULL, &match, NULL) &&
+	     match > 0) {
+	    DAMPENING_STATISTICS_INC(impl,skipped);
+	    continue;
+	 }
+      }
+      
+      DAMPENING_STATISTICS_DO(impl, lock, LOCK(&impl->lock));
+      DAMPENING_STATISTICS_DO(impl, search, entry = impl->search(impl->data, &prefix));
+      
+      if(entry != NULL) {
+	 switch(qtype) {
+	  case dns_rdatatype_any: points = damp->score.qtype_any; break;
+	  default               : points = 0;                     break;
+	 }
+	 
+	 if(entry->last_id == message_id) {
+	    points += (entry->last_id_count++)*damp->score.duplicates;
+	 } else {
+	    entry->last_id = message_id;
+	    entry->last_id_count = 1;
+	 }
+
+	 DAMPENING_STATISTICS_DO(impl, update, impl->update(impl->data, &entry, points, now));
+      }
+
+      UNLOCK(&impl->lock);
+   }
+}
+
+void dns_dampening_score_size(dns_dampening_t * damp, const isc_sockaddr_t * addr, isc_stdtime_t now, int length) {
+   isc_netaddr_t netaddr, prefix;
+   dns_dampening_entry_t * entry;
+   uint16_t points;
+   dns_dampening_implementation_t *impl;
+   
+   RUNTIME_CHECK( damp != NULL );
+   RUNTIME_CHECK( addr != NULL );
+
+   isc_netaddr_fromsockaddr(&netaddr, addr);
+   extract_prefix(&prefix, &netaddr, &(damp->prefixlen));
+   
+   for(impl = damp->workers;
+       impl - damp->workers < damp->workers_count;
+       impl++) {
+   
+      if(damp->exempt != NULL) {
+	 int match;
+	 
+	 if (ISC_R_SUCCESS == dns_acl_match(&netaddr, NULL, damp->exempt,
+					    NULL, &match, NULL) &&
+	     match > 0) {
+	    DAMPENING_STATISTICS_INC(impl,skipped);
+	    continue;
+	 }
+      }
+   
+      DAMPENING_STATISTICS_DO(impl, lock, LOCK(&impl->lock));
+      DAMPENING_STATISTICS_DO(impl, search, entry = impl->search(impl->data, &prefix));
+      if(entry != NULL) {
+	 length = ISC_MAX(length, damp->score.minimum_size);
+	 length = ISC_MIN(length, damp->score.maximum_size);
+	 points = damp->score.size_penalty
+	   * (length - damp->score.minimum_size)
+	   / (damp->score.maximum_size - damp->score.minimum_size);
+	 DAMPENING_STATISTICS_DO(impl, update, impl->update(impl->data, &entry, points, now));
+      }
+
+      UNLOCK(&impl->lock);
+   }
+}
+
+isc_result_t dns_dampening_init(dns_view_t * view, int initial_size) {
+   isc_result_t result;
+   int i, num_workers = sizeof(implementations)/sizeof(*implementations);
+   
+   INSIST( view != NULL );
+   INSIST( view->dampening == NULL );
+   RUNTIME_CHECK( 0 < initial_size && initial_size <= ISC_UINT16_MAX );
+
+   view->dampening = isc_mem_get(view->mctx, sizeof(*(view->dampening)));
+   if( view->dampening == NULL ) {
+      result = ISC_R_NOMEMORY;
+      goto cleanup;
+   }
+   memset( view->dampening, 0, sizeof(*(view->dampening)) );
+
+   view->dampening->workers = isc_mem_get(view->mctx, num_workers * sizeof(*(view->dampening->workers)));
+   if( view->dampening->workers == NULL ) {
+      result = ISC_R_NOMEMORY;
+      goto cleanup;
+   }
+   memset( view->dampening->workers, 0, num_workers * sizeof(*(view->dampening->workers)) );
+   
+   for(i = 0; i < num_workers; i++) {
+      result = implementations[i](view->mctx,
+				  view->dampening->workers + i,
+				  view->dampening, initial_size);
+      if( ISC_R_SUCCESS != result) {
+	 dns_dampening_destroy( view );
+	 goto cleanup;
+      }
+   
+      result = isc_mutex_init(&view->dampening->workers[i].lock);
+      if( result != ISC_R_SUCCESS ) {
+	 dns_dampening_destroy( view );
+	 goto cleanup;
+      }
+   }
+   view->dampening->workers_count = num_workers;
+   INSIST( view->dampening != NULL );
+
+   result = ISC_R_SUCCESS;
+
+cleanup:
+   return result;
+}
+
+void dns_dampening_destroy(dns_view_t * view) {
+   int i, num_workers = sizeof(implementations)/sizeof(*implementations);
+
+   INSIST( view != NULL );
+   INSIST( view->dampening != NULL );
+   
+   if(view->dampening->exempt != NULL)
+     dns_acl_detach(&view->dampening->exempt);
+
+   for( i = view->dampening->workers_count; i-- > 0; ) {
+      DESTROYLOCK(&view->dampening->workers[i].lock);
+      view->dampening->workers[i].destroy(&(view->dampening->workers[i].data));
+   }
+   view->dampening->workers_count = 0;
+   
+   isc_mem_put(view->mctx, view->dampening->workers, num_workers * sizeof(*(view->dampening->workers)));
+   view->dampening->workers = NULL;
+   
+   isc_mem_put(view->mctx, view->dampening, sizeof(*(view->dampening)));
+   view->dampening = NULL;
+	       
+   INSIST( view->dampening == NULL );
+}
+
+
+/********************************************************
+ * Heap
+ ********************************************************/
+
+/*
+ * Heap-Implementation
+ * ~~~~~~~~~~~~~~~~~~~
+ * 
+ * The assumed access pattern is that high penalty clients will be cause
+ * most of the queries. The used data structure is therefore a heap.
+ * 
+ * The following operations are implemented:
+ * 
+ *  a) Searching an IP by travering the heap array from front (high values)
+ *     to the end (beware of a partially filled heap).
+ * 
+ *  b) Adding a new IP (if not found) by overwriting the very last entry
+ *     of the heap, if the heap is full. This will cause one of the low
+ *     penality entries to be lost.
+ * 
+ *  c) If a searched IP was found, the last updated timestamp is used to
+ *     recalculate the penality value (expotential decrease plus new
+ *     points), update the state, and rebalanced the heap for this modified
+ *     entry only. If the penality value falls below a certain limit, the
+ *     entry is removed from the heap.
+ * 
+ *  d) The top, last, and a random node are recalculated and rebalanced
+ *     after each update or insertion, if the nodes were not updated
+ *     recently.
+ * 
+ * Using this approach, the operations on the heap will be O(log n) on
+ * each access. There is no need for a regular maintainence activity.
+ * 
+ * See: http://lutz.donnerhacke.de/eng/Blog/DNS-Dampening
+ * 
+ */
+
+typedef struct {
+   dns_dampening_entry_t * heap;
+   uint16_t last, length;
+   isc_mem_t * mctx;
+   dns_dampening_t * conf;
+} heap_t;
+
+
+static int
+heap_below_top(heap_t * d, uint16_t entry) {
+   RUNTIME_CHECK(entry > 0 && entry <= d->last);
+   return entry <= 1 ||
+     (d->heap[entry].penalty <= d->heap[entry/2].penalty);
+}
+
+static void
+heap_swap(heap_t * d, uint16_t a, uint16_t b) {
+   RUNTIME_CHECK(a > 0 && a <= d->last);
+   RUNTIME_CHECK(b > 0 && b <= d->last);
+   RUNTIME_CHECK(a != b);
+   
+   memcpy(d->heap + 0, d->heap + a, sizeof(*(d->heap)));
+   memcpy(d->heap + a, d->heap + b, sizeof(*(d->heap)));
+   memcpy(d->heap + b, d->heap + 0, sizeof(*(d->heap)));
+}
+
+static void
+heap_up(heap_t * d, uint16_t entry) {
+   uint16_t e;
+   
+   RUNTIME_CHECK(entry > 0 && entry <= d->last);
+   
+   for(e = entry/2; e > 0; entry = e, e = entry/2) {
+      if(d->heap[entry].penalty <= d->heap[e].penalty)
+	break;
+      heap_swap(d, entry, e);
+   }
+}
+
+static void
+heap_down(heap_t * d, uint16_t entry) {
+   uint16_t e;
+   
+   RUNTIME_CHECK(entry > 0 && entry <= d->last);
+   
+   for(e = 2*entry; e <= d->last; entry = e, e = 2*entry) {
+      if(e+1 <= d->last &&
+	 d->heap[e].penalty <= d->heap[e+1].penalty)
+	e = e+1;
+
+      if(d->heap[e].penalty <= d->heap[entry].penalty)
+	break;
+
+      heap_swap(d, entry, e);
+   }
+}
+
+
+
+/*
+ * Simple search though the heap, exploiting the feature, that the heap
+ * is mapped to a continiously filled, linear array. High penalty values
+ * occur first, so they are searched first.
+ * Returns DAMPENING_NOTHING if no match.
+ */
+static dns_dampening_entry_t * heap_search(void * data, const isc_netaddr_t * netaddr) {
+   heap_t * d = data;
+   uint16_t i;
+   
+   INSIST(d != NULL);
+   REQUIRE(netaddr != NULL);
+
+   for(i = 1; i <= d->last; i++) {
+      if(ISC_TRUE == isc_netaddr_equal(&d->heap[i].netaddr, netaddr))
+	return &d->heap[i];
+   }
+   return NULL;
+}
+
+/*
+ * Update an existing entry with new points
+ */
+static void
+heap_update1(void * data, dns_dampening_entry_t ** pentry, uint16_t points, isc_stdtime_t now) {
+   heap_t * d = data;
+   uint16_t e;
+   
+   INSIST(d != NULL);
+   REQUIRE(pentry != NULL && *pentry != NULL);
+   e = *pentry - d->heap;
+   RUNTIME_CHECK(0 < e && e <= d->last);
+   
+   if(update_penalty(d->conf, *pentry, points, now) == 0) {
+      /* Shrink heap */
+      if(e < d->last) {
+	 heap_swap(d, e, d->last);
+      } else {
+	 e = 0;
+      }
+      d->last--;
+   }
+   *pentry = NULL;		       /* prevent further use */
+   
+   if(e > 0) {
+      if(heap_below_top(d, e))
+	heap_down(d, e);
+      else
+	heap_up(d, e);
+   }
+}
+
+/*
+ * Expire penalties
+ */
+static void
+heap_expire(void * data, isc_stdtime_t now) {
+   heap_t * d = data;
+   uint16_t poi[3] = { 1, d->last, 1+(random()%(d->last)) };
+   unsigned int i;
+   
+   for(i=0; i<sizeof(poi)/sizeof(*poi); i++) {
+      dns_dampening_entry_t * entry = d->heap + poi[i];
+      
+      if(0 < poi[i] && poi[i] <= d->last &&
+	 now > entry->last_updated + d->conf->decay.updatedelay)
+	heap_update1(data, &entry, 0, now);
+   }
+}
+
+/*
+ * Update an existing entry and expire
+ */
+static void
+heap_update(void * data, dns_dampening_entry_t ** pentry, uint16_t points, isc_stdtime_t now) {
+   heap_update1(data, pentry, points, now);
+   heap_expire(data, now);
+}
+
+
+/*
+ * Add an element to the end (eventually overwrite the last one)
+ */
+static void
+heap_add(void * data, const isc_netaddr_t * netaddr, uint16_t points, isc_stdtime_t now) {
+   heap_t * d = data;
+   dns_dampening_entry_t * entry;
+
+   INSIST(d != NULL);
+   REQUIRE(netaddr != NULL);
+
+   entry = d->heap + d->last;
+   if(d->last < d->length - 1) {
+      d->last++;
+      entry++;
+   } else {
+      update_penalty(d->conf, entry, 0, now);
+      if(entry->penalty > points) {
+	 if(entry->penalty > d->conf->limit.irrelevant) {
+	    if(isc_log_wouldlog(dns_lctx, ISC_LOG_WARNING))
+	      isc_log_write(dns_lctx, DNS_LOGCATEGORY_DAMPENING,
+			    DNS_LOGMODULE_REQUEST, ISC_LOG_WARNING,
+			    "Dampening heap table overflow! Restarting with empty table.");
+	    d->last = 1;
+	    entry = d->heap + d->last;
+	 } else
+	   return;		       /* don't add */
+      }
+   }
+
+   memset(entry, 0, sizeof(*entry));
+   memcpy(&entry->netaddr, netaddr, sizeof(entry->netaddr));
+   entry->last_updated = now;
+
+   heap_update(data, &entry, points, now);
+}
+
+static void
+heap_destroy(void ** pdata) {
+   INSIST(pdata != NULL);
+
+   if(*pdata != NULL) {
+      heap_t * d = *pdata;
+
+      if(d->heap != NULL)
+	isc_mem_put(d->mctx, d->heap, d->length * sizeof(*(d->heap)));
+      
+      isc_mem_put(d->mctx, d, sizeof(*d));
+      *pdata = NULL;
+   }
+   
+   INSIST(*pdata == NULL);
+}
+
+
+/*
+ * Set up the heap: allocate the memory range. Change of size is possible,
+ * if heap_last is untouched.
+ */
+isc_result_t heap_init(isc_mem_t * mctx, dns_dampening_implementation_t * impl, dns_dampening_t * conf, uint16_t size) {
+   heap_t * d;
+
+   INSIST(mctx != NULL);
+   INSIST(impl != NULL);
+   INSIST(size > 0);
+   
+   impl->destroy = heap_destroy;
+   
+   impl->data = d = isc_mem_get(mctx, sizeof(*d));
+   if(d == NULL)
+     return ISC_R_NOMEMORY;
+   memset(d, 0, sizeof(*d));
+   
+   d->length = size + 1;
+   d->heap = isc_mem_get(mctx, d->length * sizeof(*(d->heap)));
+   d->last = 0;
+   d->mctx = mctx;
+   d->conf = conf;
+   if(d->heap == NULL) {
+      impl->destroy(&impl->data);
+      return ISC_R_NOMEMORY;
+   }
+   
+   impl->search  = heap_search;
+   impl->add     = heap_add;
+   impl->update  = heap_update;
+   
+   if(isc_log_wouldlog(dns_lctx, ISC_LOG_INFO))
+     isc_log_write(dns_lctx, DNS_LOGCATEGORY_DAMPENING,
+		   DNS_LOGMODULE_REQUEST, ISC_LOG_INFO,
+		   "Heap initialized to %d entries: %u bytes.",
+		   size, d->length * sizeof(*(d->heap)));
+   
+   return ISC_R_SUCCESS;
+}
+
+/********************************************************
+ * Quere
+ ********************************************************/
+
+/*
+ * Queue-Implementation
+ * ~~~~~+~~~~~~~~~~~~~~
+ * 
+ * Any client operation should be performed in constant time, unless really
+ * strange condtions occur. So the natural data structure is a hash.
+ * 
+ * The following operations are implemented:
+ * 
+ *  a) Searching an IP by hashing and travering a linked list in the case of
+ *     duplicates. In order to catch attacker IPs quickly, the least recent
+ *     entry is always moved to the front of the list.
+ * 
+ *     Measurements show, that a ordered list is strictly slower than the LRU
+ *     approach. Even if unknown entries require a full search of the hash
+ *     duplicates.
+ * 
+ *  b) If the searched IP was not found, a new entry is allocated and linked
+ *     into the front of the associeated hash list. If the storage is full,
+ *     the least used entry is recalculated using the last update timestamp
+ *     and the highest penalty (old or new) is moved to the front of a
+ *     decay queue. So the least used entry is always at the rear of the queue.
+ * 
+ *  c) If a searched IP was found, the penalty is recalculated and moved to
+ *     the front of the decay queue. If the penality value falls below a
+ *     certain limit, the entry is removed from the queue and the hash list.
+ *     This operation requires a double linked list for the queue.
+ * 
+ * Using this approach, the operations on the data structure will be O(1) on
+ * each access. There is no need for a regular maintainence activity.
+ * 
+ * The free space is maintained by reusing the single link fields of the hash
+ * linked list. The very first entry is used as a sentiel node for all lists.
+ * 
+ * This algorithms was developed by my collegue Jens.
+ * 
+ */
+
+typedef struct {
+   dns_dampening_entry_t entry;
+   uint16_t list_next, queue_next, queue_prev;
+} queue_entry_t;
+
+typedef struct {
+   queue_entry_t * field;
+   uint16_t * hash;
+   uint16_t length;
+   isc_mem_t * mctx;
+   dns_dampening_t * conf;
+} queue_t;
+
+#define QUEUE_AVAIL(d)	((d)->field[0].list_next)
+#define QUEUE_FRONT(d)	((d)->field[0].queue_next)
+#define QUEUE_REAR(d)	((d)->field[0].queue_prev)
+
+/*
+ * Hashing using a variant of the Adler32 algorithm. I had touble
+ * with generic hash functions, the full content of netaddr varies
+ * between queries for the same IP.
+ */
+static uint16_t
+queue_makehash(const queue_t * d, const isc_netaddr_t * netaddr) {
+   uint16_t a = 0, b = 0;
+   const unsigned char * buff = (const unsigned char*)&netaddr->type;
+   unsigned int i;
+
+   INSIST(d != NULL);
+   RUNTIME_CHECK(netaddr != NULL);
+   
+   for(i= netaddr->family == AF_INET  ? sizeof(netaddr->type.in ) :
+          netaddr->family == AF_INET6 ? sizeof(netaddr->type.in6) :
+                                        sizeof(netaddr->type    ) ;
+       i-->0; ) {
+      a = a + buff[i];
+      b = b + a;
+   }
+   
+   return ((b << 8) + a) % d->length;
+}
+
+/*
+ * Searching the entry by hashing the address and searching the link list
+ * of duplicates. If found, move the entry to the front.
+ */
+static dns_dampening_entry_t *
+queue_search(void * data, const isc_netaddr_t * netaddr) {
+   queue_t * d = data;
+   uint16_t h = queue_makehash(data, netaddr), i, j=0;
+
+   INSIST(data != NULL);
+   INSIST(h < d->length);
+   
+   for(i = d->hash[h]; i > 0; i = d->field[j=i].list_next) {
+      INSIST(0 < i && i < d->length);
+      
+      if(ISC_TRUE == isc_netaddr_equal(netaddr, &d->field[i].entry.netaddr)) {
+	 if(j>0) {
+	    /* Move to front */
+	    d->field[j].list_next = d->field[i].list_next;
+	    d->field[i].list_next = d->hash[h];
+	    d->hash[h] = i;
+	 }
+	 return &(d->field[i].entry);
+      }
+   }
+   return NULL;
+}
+
+/*
+ * Delete an entry from the quere and the hash.
+ */
+static void
+queue_delete(queue_t * d, uint16_t entry) {
+   uint16_t h, i, j=0;
+
+   INSIST(d != NULL);
+   RUNTIME_CHECK(0 < entry && entry < d->length);
+   h = queue_makehash(d, &d->field[entry].entry.netaddr);
+   INSIST(h < d->length);
+   
+   for(i = d->hash[h]; i != entry; i = d->field[j=i].list_next) {
+      INSIST(0 < i && i < d->length);
+   }
+   
+   /* Remove from hash */
+   if(j>0)
+     d->field[j].list_next = d->field[i].list_next;
+   else
+     d->hash[h] = d->field[i].list_next;
+   
+   /* Remove from queue */
+   d->field[d->field[i].queue_next].queue_prev = d->field[i].queue_prev;
+   d->field[d->field[i].queue_prev].queue_next = d->field[i].queue_next;
+
+   /* Back to free space */
+   d->field[i].list_next = QUEUE_AVAIL(d);
+   QUEUE_AVAIL(d) = i;
+}
+
+/*
+ * Recalculate the penaly by expotential decay and new points. If the new
+ * penalty is high enough, move it to the front of the queue, otherwise
+ * remove it completely.
+ */
+static void
+queue_update(void * data, dns_dampening_entry_t ** entry, uint16_t points, isc_stdtime_t now) {
+   queue_t * d = data;
+   uint16_t e;
+   
+   INSIST(data != NULL);
+   INSIST(entry != NULL);
+   e = (queue_entry_t*)*entry - d->field;
+   RUNTIME_CHECK(0 < e && e < d->length);
+   
+   if(update_penalty(d->conf, *entry, points, now) == 0)
+     queue_delete(d, e);
+   else if(QUEUE_FRONT(d) != e) {
+      /* Move to front */
+      d->field[e].queue_prev = d->field[QUEUE_FRONT(d)].queue_prev;
+      d->field[QUEUE_FRONT(d)].queue_prev = e;
+      d->field[e].queue_next = QUEUE_FRONT(d);
+      QUEUE_FRONT(d) = e;
+   }
+}
+
+/*
+ * Add a new element by inserting it into the front of the hash list
+ * and the decay queue. Recalculate the oldest element to get free space
+ * if necessary.
+ */
+static void
+queue_add(void * data, const isc_netaddr_t * netaddr,
+	  uint16_t points, isc_stdtime_t now) {
+   queue_t * d = data;
+   uint16_t n, h = queue_makehash(data,netaddr);
+
+   INSIST(data != NULL);
+   INSIST(h < d->length);
+   
+   if(QUEUE_AVAIL(d) == 0) {   /* full */
+      dns_dampening_entry_t * e;
+      
+      /* Check against least used element */
+      INSIST(QUEUE_REAR(d) != 0);
+      e = &(d->field[QUEUE_REAR(d)].entry);
+      update_penalty(d->conf, e, 0, now);
+
+      if(e->penalty > points) {
+	 queue_update(d, &e, 0, now);
+	 return;
+      } else 
+	queue_delete(d, QUEUE_REAR(d));
+   }
+   
+   INSIST(QUEUE_AVAIL(d) != 0);
+   
+   /* Allocate n */
+   n = QUEUE_AVAIL(d);
+   QUEUE_AVAIL(d) = d->field[n].list_next;
+   
+   /* Link to hash */
+   d->field[n].list_next = d->hash[h];
+   d->hash[h] = n;
+   
+   /* Place in front */
+   d->field[n].queue_prev = d->field[QUEUE_FRONT(d)].queue_prev;
+   d->field[QUEUE_FRONT(d)].queue_prev = n;
+   d->field[n].queue_next = QUEUE_FRONT(d);
+   QUEUE_FRONT(d) = n;
+   
+   /* Setup */
+   memset(&d->field[n].entry, 0, sizeof(d->field[n].entry));
+   memcpy(&d->field[n].entry.netaddr, netaddr, sizeof(d->field[n].entry.netaddr));
+   d->field[n].entry.penalty       = points;
+   d->field[n].entry.last_updated  = now;
+}
+
+/*
+ * Free the memory for this data structure.
+ */
+static void
+queue_destroy(void ** pdata) {
+   
+   INSIST(pdata != NULL);
+   if(*pdata != NULL) {
+      queue_t * d = *pdata;
+
+      if(d->hash != NULL)
+	isc_mem_put(d->mctx, d->hash, d->length * sizeof(*(d->hash)));
+      
+      if(d->field != NULL)
+	isc_mem_put(d->mctx, d->field, d->length * sizeof(*(d->field)));
+      
+      isc_mem_put(d->mctx, d, sizeof(*d));
+      *pdata = NULL;
+   }
+   
+   INSIST(*pdata == NULL);
+}
+
+/*
+ * Allocate the memory for this data structure.
+ */
+isc_result_t queue_init(isc_mem_t * mctx, dns_dampening_implementation_t * impl,
+			dns_dampening_t * conf, uint16_t size) {
+   queue_t * d;
+   int i;
+   
+   INSIST(mctx != NULL);
+   INSIST(impl != NULL);
+   INSIST(size > 0);
+
+   impl->destroy = queue_destroy;
+   
+   impl->data = d = isc_mem_get(mctx, sizeof(*d));
+   if(d == NULL)
+          return ISC_R_NOMEMORY;
+   memset(d, 0, sizeof(*d));
+   
+   d->length = size+1;
+   d->field = isc_mem_get(mctx, d->length * sizeof(*(d->field)));
+   d->hash  = isc_mem_get(mctx, d->length * sizeof(*(d->hash)));
+   d->mctx = mctx;
+   d->conf = conf;
+   if(d->field == NULL || d->hash == NULL) {
+      impl->destroy(&impl->data);
+      return ISC_R_NOMEMORY;
+   }
+   
+   /* Initialize free space */
+   for(i=0; i < d->length - 1; i++) {
+      d->field[i].list_next = i+1;
+      d->hash[i] = 0;
+   }
+   /* last element */
+   d->field[i].list_next = 0;
+   d->hash[i] = 0;
+
+   /* Sentiel values */
+   QUEUE_FRONT(d) = QUEUE_REAR(d) = 0;
+   QUEUE_AVAIL(d) = 1;
+   
+   
+   impl->search  = queue_search;
+   impl->add     = queue_add;
+   impl->update  = queue_update;
+   
+   isc_log_write(dns_lctx, DNS_LOGCATEGORY_DAMPENING,
+		 DNS_LOGMODULE_REQUEST, ISC_LOG_INFO,
+		 "Queue initialized to %d entries and %d hash: %u bytes",
+		 size, size+1,
+		 (sizeof(*d->hash)+sizeof(*d->field))*(1+size));
+   return ISC_R_SUCCESS;
+}
diff -uNrbB bind-9.9.2-orig/lib/dns/include/dns/dampening.h bind-9.9.2/lib/dns/include/dns/dampening.h
--- bind-9.9.2-orig/lib/dns/include/dns/dampening.h	1970-01-01 01:00:00.000000000 +0100
+++ bind-9.9.2/lib/dns/include/dns/dampening.h	2012-10-15 17:47:51.000000000 +0200
@@ -0,0 +1,89 @@
+/*
+ * (C) 2012 Lutz Donnerhacke
+ */
+
+#ifndef DNS_DAMPENING_H
+#define DNS_DAMPENING_H 1
+
+#include <isc/mutex.h>
+#include <isc/stdtime.h>
+#include <dns/acl.h>
+
+typedef enum {
+   DNS_DAMPENING_STATE_NORMAL,
+   DNS_DAMPENING_STATE_SUPPRESS
+} dns_dampening_state_t;
+
+typedef struct dns_dampening_entry {
+   isc_netaddr_t netaddr;
+   isc_stdtime_t last_updated;
+   dns_messageid_t last_id;
+   unsigned int dampening : 1, last_id_count : 15, penalty : 16;
+} dns_dampening_entry_t;
+
+typedef struct dns_dampening_implementation {
+   /* Interals */
+   void * data;
+   struct dns_dampening * conf;
+   /* API */
+   void (*destroy)(void **);
+   dns_dampening_entry_t * (*search)(void *, const isc_netaddr_t * netaddr);
+   void (*add)(void *, const isc_netaddr_t * netaddr, uint16_t points, isc_stdtime_t now);
+   void (*update)(void *, dns_dampening_entry_t ** entry, uint16_t points, isc_stdtime_t now);
+   /* Used by externals */
+   isc_mutex_t lock;
+   struct {
+      struct timeval lock, search, update, add;
+      unsigned int allowed, denied, skipped;
+      isc_stdtime_t last_report;
+   } statistics;
+}  dns_dampening_implementation_t;
+
+typedef struct dns_dampening {
+   dns_acl_t	*exempt;
+   int		max_entries;
+   dns_dampening_implementation_t * workers;
+   int workers_count;
+
+   struct dns_dampening_prefix {
+      unsigned int ipv4;
+      unsigned int ipv6;
+   } prefixlen;
+   
+   struct dns_dampening_decay {
+      int halflife;
+      int updatedelay;
+   } decay;
+    
+   struct dns_dampening_limit {
+      unsigned int
+	maximum            : 16,
+	enable_dampening   : 16,
+	disable_dampening  : 16,
+	irrelevant         : 16;
+   } limit;
+   
+   struct dns_dampening_score {
+      unsigned int
+	first_query  : 16,
+	per_query    : 16,
+	duplicates   : 16,
+	qtype_any    : 16,
+	size_penalty : 16,
+	minimum_size : 16,
+	maximum_size : 16;
+   } score;
+   
+   struct dns_dampening_statistics {
+      int report_interval;
+   } statistics;
+   
+} dns_dampening_t;
+
+dns_dampening_state_t dns_dampening_query(dns_dampening_t *, const isc_sockaddr_t *, isc_stdtime_t, int *);
+void dns_dampening_score_qtype(dns_dampening_t *, const isc_sockaddr_t *, isc_stdtime_t, dns_messageid_t, int);
+void dns_dampening_score_size(dns_dampening_t *, const isc_sockaddr_t *, isc_stdtime_t, int);
+isc_result_t dns_dampening_init(dns_view_t *, int);
+void dns_dampening_destroy(dns_view_t *);
+
+#endif
diff -uNrbB bind-9.9.2-orig/lib/dns/include/dns/log.h bind-9.9.2/lib/dns/include/dns/log.h
--- bind-9.9.2-orig/lib/dns/include/dns/log.h	2012-09-27 02:35:19.000000000 +0200
+++ bind-9.9.2/lib/dns/include/dns/log.h	2012-10-13 23:30:52.000000000 +0200
@@ -43,6 +43,7 @@
 #define DNS_LOGCATEGORY_DELEGATION_ONLY	(&dns_categories[10])
 #define DNS_LOGCATEGORY_EDNS_DISABLED	(&dns_categories[11])
 #define DNS_LOGCATEGORY_RPZ		(&dns_categories[12])
+#define DNS_LOGCATEGORY_DAMPENING	(&dns_categories[13])
 
 /* Backwards compatibility. */
 #define DNS_LOGCATEGORY_GENERAL		ISC_LOGCATEGORY_GENERAL
diff -uNrbB bind-9.9.2-orig/lib/dns/include/dns/view.h bind-9.9.2/lib/dns/include/dns/view.h
--- bind-9.9.2-orig/lib/dns/include/dns/view.h	2012-09-27 02:35:19.000000000 +0200
+++ bind-9.9.2/lib/dns/include/dns/view.h	2012-10-13 23:30:52.000000000 +0200
@@ -72,6 +72,7 @@
 #include <isc/stdtime.h>
 
 #include <dns/acl.h>
+#include <dns/dampening.h>
 #include <dns/fixedname.h>
 #include <dns/rdatastruct.h>
 #include <dns/rpz.h>
@@ -142,6 +143,7 @@
 	dns_rbt_t *			answeracl_exclude;
 	dns_rbt_t *			denyanswernames;
 	dns_rbt_t *			answernames_exclude;
+	dns_dampening_t *		dampening;
 	isc_boolean_t			provideixfr;
 	isc_boolean_t			requestnsid;
 	dns_ttl_t			maxcachettl;
diff -uNrbB bind-9.9.2-orig/lib/dns/log.c bind-9.9.2/lib/dns/log.c
--- bind-9.9.2-orig/lib/dns/log.c	2012-09-27 02:35:19.000000000 +0200
+++ bind-9.9.2/lib/dns/log.c	2012-10-13 23:30:52.000000000 +0200
@@ -45,6 +45,7 @@
 	{ "delegation-only", 0 },
 	{ "edns-disabled", 0 },
 	{ "rpz",	0 },
+	{ "dampening",	0 },
 	{ NULL, 	0 }
 };
 
diff -uNrbB bind-9.9.2-orig/lib/dns/Makefile.in bind-9.9.2/lib/dns/Makefile.in
--- bind-9.9.2-orig/lib/dns/Makefile.in	2012-09-27 02:35:19.000000000 +0200
+++ bind-9.9.2/lib/dns/Makefile.in	2012-10-13 23:30:52.000000000 +0200
@@ -57,6 +57,7 @@
 # Alphabetically
 DNSOBJS =	acache.@O@ acl.@O@ adb.@O@ byaddr.@O@ \
 		cache.@O@ callbacks.@O@ clientinfo.@O@ compress.@O@ \
+		dampening.@O@ \
 		db.@O@ dbiterator.@O@ dbtable.@O@ diff.@O@ dispatch.@O@ \
 		dlz.@O@ dns64.@O@ dnssec.@O@ ds.@O@ forward.@O@ iptable.@O@ \
 		journal.@O@ keydata.@O@ keytable.@O@ \
@@ -86,6 +87,7 @@
 
 DNSSRCS =	acache.c acl.c adb.c byaddr.c \
 		cache.c callbacks.c clientinfo.c compress.c \
+		dampening.c \
 		db.c dbiterator.c dbtable.c diff.c dispatch.c \
 		dlz.c dns64.c dnssec.c ds.c forward.c iptable.c journal.c \
 		keydata.c keytable.c lib.c log.c lookup.c \
diff -uNrbB bind-9.9.2-orig/lib/dns/view.c bind-9.9.2/lib/dns/view.c
--- bind-9.9.2-orig/lib/dns/view.c	2012-09-27 02:35:19.000000000 +0200
+++ bind-9.9.2/lib/dns/view.c	2012-10-13 23:30:52.000000000 +0200
@@ -34,6 +34,7 @@
 #include <dns/acl.h>
 #include <dns/adb.h>
 #include <dns/cache.h>
+#include <dns/dampening.h>
 #include <dns/db.h>
 #include <dns/dlz.h>
 #ifdef BIND9
@@ -120,6 +121,7 @@
 	view->acache = NULL;
 	view->cache = NULL;
 	view->cachedb = NULL;
+	view->dampening = NULL;
 	view->dlzdatabase = NULL;
 	view->hints = NULL;
 	view->resolver = NULL;
@@ -355,6 +357,8 @@
 		dns_acl_detach(&view->cacheacl);
 	if (view->cacheonacl != NULL)
 		dns_acl_detach(&view->cacheonacl);
+	if (view->dampening != NULL)
+		dns_dampening_destroy(view);
 	if (view->queryacl != NULL)
 		dns_acl_detach(&view->queryacl);
 	if (view->queryonacl != NULL)
diff -uNrbB bind-9.9.2-orig/lib/isccfg/namedconf.c bind-9.9.2/lib/isccfg/namedconf.c
--- bind-9.9.2-orig/lib/isccfg/namedconf.c	2012-09-27 02:35:19.000000000 +0200
+++ bind-9.9.2/lib/isccfg/namedconf.c	2012-10-13 23:30:52.000000000 +0200
@@ -1243,6 +1243,42 @@
 	rpz_fields
 };
 
+/*
+ * dampening
+ */
+
+static cfg_clausedef_t dampening_clauses[] = {
+     { "min-table-size", &cfg_type_uint32, 0 },
+     { "max-table-size", &cfg_type_uint32, 0 },
+     { "halflife", &cfg_type_uint32, 0 },
+     { "update-delay", &cfg_type_uint32, 0 },
+     { "limit-maximum", &cfg_type_uint32, 0 },
+     { "limit-enable-dampening", &cfg_type_uint32, 0 },
+     { "limit-disable-dampening", &cfg_type_uint32, 0 },
+     { "limit-irrelevant", &cfg_type_uint32, 0 },
+     { "score-first-query", &cfg_type_uint32, 0 },
+     { "score-per-query", &cfg_type_uint32, 0 },
+     { "score-qtype-any", &cfg_type_uint32, 0 },
+     { "minimum-score-size", &cfg_type_uint32, 0 },
+     { "maximum-score-size", &cfg_type_uint32, 0 },
+     { "score-size", &cfg_type_uint32, 0 },
+     { "score-duplicates", &cfg_type_uint32, 0 },
+     { "IPv4-prefix-length", &cfg_type_uint32, 0 },
+     { "IPv6-prefix-length", &cfg_type_uint32, 0 },
+     { "report-interval", &cfg_type_uint32, 0 },
+     { "exempt-clients", &cfg_type_bracketed_aml, 0 },
+     { NULL, NULL, 0 }
+};
+
+static cfg_clausedef_t * dampening_clauseset[] = {
+     dampening_clauses,
+     NULL
+};
+
+static cfg_type_t cfg_type_dampening = {
+   "dampening", cfg_parse_map, cfg_print_map, cfg_doc_map,
+     &cfg_rep_map, dampening_clauseset
+};
 
 /*%
  * dnssec-lookaside
@@ -1397,6 +1433,7 @@
 	   CFG_CLAUSEFLAG_NOTCONFIGURED },
 #endif
 	{ "response-policy", &cfg_type_rpz, 0 },
+	{ "dampening", &cfg_type_dampening, 0 },
 	{ NULL, NULL, 0 }
 };
 
diff -uNrbB bind-9.9.2-orig/version bind-9.9.2/version
--- bind-9.9.2-orig/version	2012-09-27 02:35:19.000000000 +0200
+++ bind-9.9.2/version	2012-10-13 23:48:44.000000000 +0200
@@ -5,6 +5,6 @@
 #
 MAJORVER=9
 MINORVER=9
-PATCHVER=2
+PATCHVER=2-damp
 RELEASETYPE=
 RELEASEVER=
