[OE-core] [PATCH 2/2] bind: Security fix CVE-2016-6170

Yi Zhao yi.zhao at windriver.com
Thu Apr 13 05:20:25 UTC 2017


CVE-2016-6170: ISC BIND through 9.9.9-P1, 9.10.x through 9.10.4-P1, and
9.11.x through 9.11.0b1 allows primary DNS servers to cause a denial of
service (secondary DNS server crash) via a large AXFR response, and
possibly allows IXFR servers to cause a denial of service (IXFR client
crash) via a large IXFR response and allows remote authenticated users
to cause a denial of service (primary DNS server crash) via a large
UPDATE message.

External References:
https://nvd.nist.gov/vuln/detail/CVE-2016-6170

Patch from:
https://source.isc.org/cgi-bin/gitweb.cgi?p=bind9.git;a=commit;h=1bbcfe2fc84f57b1e4e075fb3bc2a1dd0a3a851f

Signed-off-by: Yi Zhao <yi.zhao at windriver.com>
---
 .../bind/bind/CVE-2016-6170.patch                  | 1088 ++++++++++++++++++++
 meta/recipes-connectivity/bind/bind_9.10.3-P3.bb   |    1 +
 2 files changed, 1089 insertions(+)
 create mode 100644 meta/recipes-connectivity/bind/bind/CVE-2016-6170.patch

diff --git a/meta/recipes-connectivity/bind/bind/CVE-2016-6170.patch b/meta/recipes-connectivity/bind/bind/CVE-2016-6170.patch
new file mode 100644
index 0000000..e0653bf
--- /dev/null
+++ b/meta/recipes-connectivity/bind/bind/CVE-2016-6170.patch
@@ -0,0 +1,1088 @@
+From 1bbcfe2fc84f57b1e4e075fb3bc2a1dd0a3a851f Mon Sep 17 00:00:00 2001
+From: Mark Andrews <marka at isc.org>
+Date: Wed, 2 Nov 2016 17:31:27 +1100
+Subject: [PATCH] 4504. [security] Allow the maximum number of records in a
+ zone to be specified. This provides a control for issues raised in
+ CVE-2016-6170. [RT #42143]
+
+(cherry picked from commit 5f8412a4cb5ee14a0e8cddd4107854b40ee3291e)
+
+Upstream-Status: Backport
+[https://source.isc.org/cgi-bin/gitweb.cgi?p=bind9.git;a=commit;h=1bbcfe2fc84f57b1e4e075fb3bc2a1dd0a3a851f]
+
+Signed-off-by: Yi Zhao <yi.zhao at windriver.com>
+---
+ CHANGES                                          |   4 +
+ bin/named/config.c                               |   1 +
+ bin/named/named.conf.docbook                     |   3 +
+ bin/named/update.c                               |  16 +++
+ bin/named/zoneconf.c                             |   7 ++
+ bin/tests/system/nsupdate/clean.sh               |   1 +
+ bin/tests/system/nsupdate/ns3/named.conf         |   7 ++
+ bin/tests/system/nsupdate/ns3/too-big.test.db.in |  10 ++
+ bin/tests/system/nsupdate/setup.sh               |   2 +
+ bin/tests/system/nsupdate/tests.sh               |  15 +++
+ bin/tests/system/xfer/clean.sh                   |   1 +
+ bin/tests/system/xfer/ns1/axfr-too-big.db        |  10 ++
+ bin/tests/system/xfer/ns1/ixfr-too-big.db.in     |  13 +++
+ bin/tests/system/xfer/ns1/named.conf             |  11 ++
+ bin/tests/system/xfer/ns6/named.conf             |  14 +++
+ bin/tests/system/xfer/setup.sh                   |   2 +
+ bin/tests/system/xfer/tests.sh                   |  26 +++++
+ doc/arm/Bv9ARM-book.xml                          |  21 ++++
+ doc/arm/notes.xml                                |   9 ++
+ lib/bind9/check.c                                |   2 +
+ lib/dns/db.c                                     |  13 +++
+ lib/dns/ecdb.c                                   |   3 +-
+ lib/dns/include/dns/db.h                         |  20 ++++
+ lib/dns/include/dns/rdataslab.h                  |  13 +++
+ lib/dns/include/dns/result.h                     |   6 +-
+ lib/dns/include/dns/zone.h                       |  28 ++++-
+ lib/dns/rbtdb.c                                  | 127 +++++++++++++++++++++--
+ lib/dns/rdataslab.c                              |  13 +++
+ lib/dns/result.c                                 |   9 +-
+ lib/dns/sdb.c                                    |   3 +-
+ lib/dns/sdlz.c                                   |   3 +-
+ lib/dns/xfrin.c                                  |  22 +++-
+ lib/dns/zone.c                                   |  23 +++-
+ lib/isccfg/namedconf.c                           |   1 +
+ 34 files changed, 444 insertions(+), 15 deletions(-)
+ create mode 100644 bin/tests/system/nsupdate/ns3/too-big.test.db.in
+ create mode 100644 bin/tests/system/xfer/ns1/axfr-too-big.db
+ create mode 100644 bin/tests/system/xfer/ns1/ixfr-too-big.db.in
+
+diff --git a/CHANGES b/CHANGES
+index 41cfce5..97d2e60 100644
+--- a/CHANGES
++++ b/CHANGES
+@@ -1,3 +1,7 @@
++4504.	[security]	Allow the maximum number of records in a zone to
++			be specified.  This provides a control for issues
++			raised in CVE-2016-6170. [RT #42143]
++
+ 4489.	[security]	It was possible to trigger assertions when processing
+ 			a response. (CVE-2016-8864) [RT #43465]
+ 
+diff --git a/bin/named/config.c b/bin/named/config.c
+index f06348c..c24e334 100644
+--- a/bin/named/config.c
++++ b/bin/named/config.c
+@@ -209,6 +209,7 @@ options {\n\
+ 	max-transfer-time-out 120;\n\
+ 	max-transfer-idle-in 60;\n\
+ 	max-transfer-idle-out 60;\n\
++	max-records 0;\n\
+ 	max-retry-time 1209600; /* 2 weeks */\n\
+ 	min-retry-time 500;\n\
+ 	max-refresh-time 2419200; /* 4 weeks */\n\
+diff --git a/bin/named/named.conf.docbook b/bin/named/named.conf.docbook
+index 4c99a61..c2d173a 100644
+--- a/bin/named/named.conf.docbook
++++ b/bin/named/named.conf.docbook
+@@ -338,6 +338,7 @@ options {
+ 	};
+ 
+ 	max-journal-size <replaceable>size_no_default</replaceable>;
++	max-records <replaceable>integer</replaceable>;
+ 	max-transfer-time-in <replaceable>integer</replaceable>;
+ 	max-transfer-time-out <replaceable>integer</replaceable>;
+ 	max-transfer-idle-in <replaceable>integer</replaceable>;
+@@ -527,6 +528,7 @@ view <replaceable>string</replaceable> <replaceable>optional_class</replaceable>
+ 	};
+ 
+ 	max-journal-size <replaceable>size_no_default</replaceable>;
++	max-records <replaceable>integer</replaceable>;
+ 	max-transfer-time-in <replaceable>integer</replaceable>;
+ 	max-transfer-time-out <replaceable>integer</replaceable>;
+ 	max-transfer-idle-in <replaceable>integer</replaceable>;
+@@ -624,6 +626,7 @@ zone <replaceable>string</replaceable> <replaceable>optional_class</replaceable>
+ 	};
+ 
+ 	max-journal-size <replaceable>size_no_default</replaceable>;
++	max-records <replaceable>integer</replaceable>;
+ 	max-transfer-time-in <replaceable>integer</replaceable>;
+ 	max-transfer-time-out <replaceable>integer</replaceable>;
+ 	max-transfer-idle-in <replaceable>integer</replaceable>;
+diff --git a/bin/named/update.c b/bin/named/update.c
+index 83b1a05..cc2a611 100644
+--- a/bin/named/update.c
++++ b/bin/named/update.c
+@@ -2455,6 +2455,8 @@ update_action(isc_task_t *task, isc_event_t *event) {
+ 	isc_boolean_t had_dnskey;
+ 	dns_rdatatype_t privatetype = dns_zone_getprivatetype(zone);
+ 	dns_ttl_t maxttl = 0;
++	isc_uint32_t maxrecords;
++	isc_uint64_t records;
+ 
+ 	INSIST(event->ev_type == DNS_EVENT_UPDATE);
+ 
+@@ -3138,6 +3140,20 @@ update_action(isc_task_t *task, isc_event_t *event) {
+ 			}
+ 		}
+ 
++		maxrecords = dns_zone_getmaxrecords(zone);
++		if (maxrecords != 0U) {
++			result = dns_db_getsize(db, ver, &records, NULL);
++			if (result == ISC_R_SUCCESS && records > maxrecords) {
++				update_log(client, zone, ISC_LOG_ERROR,
++					   "records in zone (%"
++					   ISC_PRINT_QUADFORMAT
++					   "u) exceeds max-records (%u)",
++					   records, maxrecords);
++				result = DNS_R_TOOMANYRECORDS;
++				goto failure;
++			}
++		}
++
+ 		journalfile = dns_zone_getjournal(zone);
+ 		if (journalfile != NULL) {
+ 			update_log(client, zone, LOGLEVEL_DEBUG,
+diff --git a/bin/named/zoneconf.c b/bin/named/zoneconf.c
+index 4ee3dfe..14dd8ce 100644
+--- a/bin/named/zoneconf.c
++++ b/bin/named/zoneconf.c
+@@ -978,6 +978,13 @@ ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig,
+ 			dns_zone_setmaxttl(raw, maxttl);
+ 	}
+ 
++	obj = NULL;
++	result = ns_config_get(maps, "max-records", &obj);
++	INSIST(result == ISC_R_SUCCESS && obj != NULL);
++	dns_zone_setmaxrecords(mayberaw, cfg_obj_asuint32(obj));
++	if (zone != mayberaw)
++		dns_zone_setmaxrecords(zone, 0);
++
+ 	if (raw != NULL && filename != NULL) {
+ #define SIGNED ".signed"
+ 		size_t signedlen = strlen(filename) + sizeof(SIGNED);
+diff --git a/bin/tests/system/nsupdate/clean.sh b/bin/tests/system/nsupdate/clean.sh
+index aaefc02..ea25545 100644
+--- a/bin/tests/system/nsupdate/clean.sh
++++ b/bin/tests/system/nsupdate/clean.sh
+@@ -32,6 +32,7 @@ rm -f ns3/example.db.jnl ns3/example.db
+ rm -f ns3/nsec3param.test.db.signed.jnl ns3/nsec3param.test.db ns3/nsec3param.test.db.signed ns3/dsset-nsec3param.test.
+ rm -f ns3/dnskey.test.db.signed.jnl ns3/dnskey.test.db ns3/dnskey.test.db.signed ns3/dsset-dnskey.test.
+ rm -f ns3/K*
++rm -f ns3/too-big.test.db
+ rm -f dig.out.*
+ rm -f jp.out.ns3.*
+ rm -f Kxxx.*
+diff --git a/bin/tests/system/nsupdate/ns3/named.conf b/bin/tests/system/nsupdate/ns3/named.conf
+index 2abd522..68ff27a 100644
+--- a/bin/tests/system/nsupdate/ns3/named.conf
++++ b/bin/tests/system/nsupdate/ns3/named.conf
+@@ -60,3 +60,10 @@ zone "dnskey.test" {
+ 	allow-update { any; };
+ 	file "dnskey.test.db.signed";
+ };
++
++zone "too-big.test" {
++	type master;
++	allow-update { any; };
++	max-records 3;
++	file "too-big.test.db";
++};
+diff --git a/bin/tests/system/nsupdate/ns3/too-big.test.db.in b/bin/tests/system/nsupdate/ns3/too-big.test.db.in
+new file mode 100644
+index 0000000..7ff1e4a
+--- /dev/null
++++ b/bin/tests/system/nsupdate/ns3/too-big.test.db.in
+@@ -0,0 +1,10 @@
++; Copyright (C) 2016  Internet Systems Consortium, Inc. ("ISC")
++;
++; This Source Code Form is subject to the terms of the Mozilla Public
++; License, v. 2.0. If a copy of the MPL was not distributed with this
++; file, You can obtain one at http://mozilla.org/MPL/2.0/.
++
++$TTL 10
++too-big.test. IN SOA too-big.test. hostmaster.too-big.test. 1 3600 900 2419200 3600
++too-big.test. IN NS too-big.test.
++too-big.test. IN A 10.53.0.3
+diff --git a/bin/tests/system/nsupdate/setup.sh b/bin/tests/system/nsupdate/setup.sh
+index 828255e..43c4094 100644
+--- a/bin/tests/system/nsupdate/setup.sh
++++ b/bin/tests/system/nsupdate/setup.sh
+@@ -27,12 +27,14 @@ test -r $RANDFILE || $GENRANDOM 400 $RANDFILE
+ rm -f ns1/*.jnl ns1/example.db ns2/*.jnl ns2/example.bk
+ rm -f ns2/update.bk ns2/update.alt.bk
+ rm -f ns3/example.db.jnl
++rm -f ns3/too-big.test.db.jnl
+ 
+ cp -f ns1/example1.db ns1/example.db
+ sed 's/example.nil/other.nil/g' ns1/example1.db > ns1/other.db
+ sed 's/example.nil/unixtime.nil/g' ns1/example1.db > ns1/unixtime.db
+ sed 's/example.nil/keytests.nil/g' ns1/example1.db > ns1/keytests.db
+ cp -f ns3/example.db.in ns3/example.db
++cp -f ns3/too-big.test.db.in ns3/too-big.test.db
+ 
+ # update_test.pl has its own zone file because it
+ # requires a specific NS record set.
+diff --git a/bin/tests/system/nsupdate/tests.sh b/bin/tests/system/nsupdate/tests.sh
+index 78d501e..0a6bbd3 100755
+--- a/bin/tests/system/nsupdate/tests.sh
++++ b/bin/tests/system/nsupdate/tests.sh
+@@ -581,5 +581,20 @@ if [ $ret -ne 0 ]; then
+     status=1
+ fi
+ 
++n=`expr $n + 1`
++echo "I:check that adding too many records is blocked ($n)"
++ret=0
++$NSUPDATE -v << EOF > nsupdate.out-$n 2>&1 && ret=1
++server 10.53.0.3 5300
++zone too-big.test.
++update add r1.too-big.test 3600 IN TXT r1.too-big.test
++send
++EOF
++grep "update failed: SERVFAIL" nsupdate.out-$n > /dev/null || ret=1
++DIG +tcp @10.53.0.3 -p 5300 r1.too-big.test TXT > dig.out.ns3.test$n
++grep "status: NXDOMAIN" dig.out.ns3.test$n > /dev/null || ret=1
++grep "records in zone (4) exceeds max-records (3)" ns3/named.run > /dev/null || ret=1
++[ $ret = 0 ] || { echo I:failed; status=1; }
++
+ echo "I:exit status: $status"
+ exit $status
+diff --git a/bin/tests/system/xfer/clean.sh b/bin/tests/system/xfer/clean.sh
+index 48aa159..da62a33 100644
+--- a/bin/tests/system/xfer/clean.sh
++++ b/bin/tests/system/xfer/clean.sh
+@@ -36,3 +36,4 @@ rm -f ns7/*.db ns7/*.bk ns7/*.jnl
+ rm -f */named.memstats
+ rm -f */named.run
+ rm -f */ans.run
++rm -f ns1/ixfr-too-big.db ns1/ixfr-too-big.db.jnl
+diff --git a/bin/tests/system/xfer/ns1/axfr-too-big.db b/bin/tests/system/xfer/ns1/axfr-too-big.db
+new file mode 100644
+index 0000000..d43760d
+--- /dev/null
++++ b/bin/tests/system/xfer/ns1/axfr-too-big.db
+@@ -0,0 +1,10 @@
++; Copyright (C) 2016  Internet Systems Consortium, Inc. ("ISC")
++;
++; This Source Code Form is subject to the terms of the Mozilla Public
++; License, v. 2.0. If a copy of the MPL was not distributed with this
++; file, You can obtain one at http://mozilla.org/MPL/2.0/.
++
++$TTL	3600
++@	IN	SOA	. . 0 0 0 0 0
++@	IN	NS	.
++$GENERATE 1-29	host$	A	1.2.3.$
+diff --git a/bin/tests/system/xfer/ns1/ixfr-too-big.db.in b/bin/tests/system/xfer/ns1/ixfr-too-big.db.in
+new file mode 100644
+index 0000000..318bb77
+--- /dev/null
++++ b/bin/tests/system/xfer/ns1/ixfr-too-big.db.in
+@@ -0,0 +1,13 @@
++; Copyright (C) 2016  Internet Systems Consortium, Inc. ("ISC")
++;
++; This Source Code Form is subject to the terms of the Mozilla Public
++; License, v. 2.0. If a copy of the MPL was not distributed with this
++; file, You can obtain one at http://mozilla.org/MPL/2.0/.
++
++$TTL	3600
++@	IN	SOA	. . 0 0 0 0 0
++@	IN	NS	ns1
++@	IN	NS	ns6
++ns1	IN	A	10.53.0.1
++ns6	IN	A	10.53.0.6
++$GENERATE 1-25	host$	A	1.2.3.$
+diff --git a/bin/tests/system/xfer/ns1/named.conf b/bin/tests/system/xfer/ns1/named.conf
+index 07dad85..1d29292 100644
+--- a/bin/tests/system/xfer/ns1/named.conf
++++ b/bin/tests/system/xfer/ns1/named.conf
+@@ -44,3 +44,14 @@ zone "slave" {
+ 	type master;
+ 	file "slave.db";
+ };
++
++zone "axfr-too-big" {
++        type master;
++        file "axfr-too-big.db";
++};
++
++zone "ixfr-too-big" {
++        type master;
++	allow-update { any; };
++        file "ixfr-too-big.db";
++};
+diff --git a/bin/tests/system/xfer/ns6/named.conf b/bin/tests/system/xfer/ns6/named.conf
+index c9421b1..a12a92c 100644
+--- a/bin/tests/system/xfer/ns6/named.conf
++++ b/bin/tests/system/xfer/ns6/named.conf
+@@ -52,3 +52,17 @@ zone "slave" {
+ 	masters { 10.53.0.1; };
+ 	file "slave.bk";
+ };
++
++zone "axfr-too-big" {
++	type slave;
++	max-records 30;
++	masters { 10.53.0.1; };
++	file "axfr-too-big.bk";
++};
++
++zone "ixfr-too-big" {
++	type slave;
++	max-records 30;
++	masters { 10.53.0.1; };
++	file "ixfr-too-big.bk";
++};
+diff --git a/bin/tests/system/xfer/setup.sh b/bin/tests/system/xfer/setup.sh
+index 56ca901..c55abf8 100644
+--- a/bin/tests/system/xfer/setup.sh
++++ b/bin/tests/system/xfer/setup.sh
+@@ -33,3 +33,5 @@ cp -f ns4/named.conf.base ns4/named.conf
+ 
+ cp ns2/slave.db.in ns2/slave.db
+ touch -t 200101010000 ns2/slave.db
++
++cp -f ns1/ixfr-too-big.db.in ns1/ixfr-too-big.db
+diff --git a/bin/tests/system/xfer/tests.sh b/bin/tests/system/xfer/tests.sh
+index 67b2a1a..fe33f0a 100644
+--- a/bin/tests/system/xfer/tests.sh
++++ b/bin/tests/system/xfer/tests.sh
+@@ -368,5 +368,31 @@ $DIGCMD nil. TXT | grep 'incorrect key AXFR' >/dev/null && {
+     status=1
+ }
+ 
++n=`expr $n + 1`
++echo "I:test that a zone with too many records is rejected (AXFR) ($n)"
++tmp=0
++grep "'axfr-too-big/IN'.*: too many records" ns6/named.run >/dev/null || tmp=1
++if test $tmp != 0 ; then echo "I:failed"; fi
++status=`expr $status + $tmp`
++
++n=`expr $n + 1`
++echo "I:test that a zone with too many records is rejected (IXFR) ($n)"
++tmp=0
++grep "'ixfr-too-big./IN.*: too many records" ns6/named.run >/dev/null && tmp=1
++$NSUPDATE << EOF
++zone ixfr-too-big
++server 10.53.0.1 5300
++update add the-31st-record.ixfr-too-big 0 TXT this is it
++send
++EOF
++for i in 1 2 3 4 5 6 7 8
++do
++    grep "'ixfr-too-big/IN'.*: too many records" ns6/named.run >/dev/null && break
++    sleep 1
++done
++grep "'ixfr-too-big/IN'.*: too many records" ns6/named.run >/dev/null || tmp=1
++if test $tmp != 0 ; then echo "I:failed"; fi
++status=`expr $status + $tmp`
++
+ echo "I:exit status: $status"
+ exit $status
+diff --git a/doc/arm/Bv9ARM-book.xml b/doc/arm/Bv9ARM-book.xml
+index 848b582..0369505 100644
+--- a/doc/arm/Bv9ARM-book.xml
++++ b/doc/arm/Bv9ARM-book.xml
+@@ -4858,6 +4858,7 @@ badresp:1,adberr:0,findfail:0,valfail:0]
+     <optional> use-queryport-pool <replaceable>yes_or_no</replaceable>; </optional>
+     <optional> queryport-pool-ports <replaceable>number</replaceable>; </optional>
+     <optional> queryport-pool-updateinterval <replaceable>number</replaceable>; </optional>
++    <optional> max-records <replaceable>number</replaceable>; </optional>
+     <optional> max-transfer-time-in <replaceable>number</replaceable>; </optional>
+     <optional> max-transfer-time-out <replaceable>number</replaceable>; </optional>
+     <optional> max-transfer-idle-in <replaceable>number</replaceable>; </optional>
+@@ -8164,6 +8165,16 @@ avoid-v6-udp-ports { 40000; range 50000 60000; };
+ 	    </varlistentry>
+ 
+ 	    <varlistentry>
++	      <term><command>max-records</command></term>
++	      <listitem>
++		<para>
++		  The maximum number of records permitted in a zone.
++		  The default is zero which means unlimited.
++	 	</para>
++	      </listitem>
++	    </varlistentry>
++
++	    <varlistentry>
+ 	      <term><command>host-statistics-max</command></term>
+ 	      <listitem>
+ 		<para>
+@@ -12056,6 +12067,16 @@ zone <replaceable>zone_name</replaceable> <optional><replaceable>class</replacea
+ 	      </varlistentry>
+ 
+ 	      <varlistentry>
++		<term><command>max-records</command></term>
++		<listitem>
++		  <para>
++		    See the description of
++		    <command>max-records</command> in <xref linkend="server_resource_limits"/>.
++		  </para>
++		</listitem>
++	      </varlistentry>
++
++	      <varlistentry>
+ 		<term><command>max-transfer-time-in</command></term>
+ 		<listitem>
+ 		  <para>
+diff --git a/doc/arm/notes.xml b/doc/arm/notes.xml
+index 095eb5b..36495e7 100644
+--- a/doc/arm/notes.xml
++++ b/doc/arm/notes.xml
+@@ -52,6 +52,15 @@
+     <itemizedlist>
+       <listitem>
+        <para>
++	  Added the ability to specify the maximum number of records
++	  permitted in a zone (max-records #;).  This provides a mechanism
++	  to block overly large zone transfers, which is a potential risk
++	  with slave zones from other parties, as described in CVE-2016-6170.
++	  [RT #42143]
++	</para>
++      </listitem>
++      <listitem>
++	<para>
+          Duplicate EDNS COOKIE options in a response could trigger
+          an assertion failure. This flaw is disclosed in CVE-2016-2088.
+          [RT #41809]
+diff --git a/lib/bind9/check.c b/lib/bind9/check.c
+index b8c05dd..edb7534 100644
+--- a/lib/bind9/check.c
++++ b/lib/bind9/check.c
+@@ -1510,6 +1510,8 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions,
+ 	  REDIRECTZONE },
+ 	{ "masters", SLAVEZONE | STUBZONE | REDIRECTZONE },
+ 	{ "max-ixfr-log-size", MASTERZONE | SLAVEZONE | STREDIRECTZONE },
++	{ "max-records", MASTERZONE | SLAVEZONE | STUBZONE | STREDIRECTZONE |
++          STATICSTUBZONE | REDIRECTZONE },
+ 	{ "max-refresh-time", SLAVEZONE | STUBZONE | STREDIRECTZONE },
+ 	{ "max-retry-time", SLAVEZONE | STUBZONE | STREDIRECTZONE },
+ 	{ "max-transfer-idle-in", SLAVEZONE | STUBZONE | STREDIRECTZONE },
+diff --git a/lib/dns/db.c b/lib/dns/db.c
+index 7e4f357..ced94a5 100644
+--- a/lib/dns/db.c
++++ b/lib/dns/db.c
+@@ -999,6 +999,19 @@ dns_db_getnsec3parameters(dns_db_t *db, dns_dbversion_t *version,
+ }
+ 
+ isc_result_t
++dns_db_getsize(dns_db_t *db, dns_dbversion_t *version, isc_uint64_t *records,
++	       isc_uint64_t *bytes)
++{
++	REQUIRE(DNS_DB_VALID(db));
++	REQUIRE(dns_db_iszone(db) == ISC_TRUE);
++
++	if (db->methods->getsize != NULL)
++		return ((db->methods->getsize)(db, version, records, bytes));
++
++	return (ISC_R_NOTFOUND);
++}
++
++isc_result_t
+ dns_db_setsigningtime(dns_db_t *db, dns_rdataset_t *rdataset,
+ 		      isc_stdtime_t resign)
+ {
+diff --git a/lib/dns/ecdb.c b/lib/dns/ecdb.c
+index 553a339..b5d04d2 100644
+--- a/lib/dns/ecdb.c
++++ b/lib/dns/ecdb.c
+@@ -587,7 +587,8 @@ static dns_dbmethods_t ecdb_methods = {
+ 	NULL,			/* findnodeext */
+ 	NULL,			/* findext */
+ 	NULL,			/* setcachestats */
+-	NULL			/* hashsize */
++	NULL,			/* hashsize */
++	NULL			/* getsize */
+ };
+ 
+ static isc_result_t
+diff --git a/lib/dns/include/dns/db.h b/lib/dns/include/dns/db.h
+index a4a4482..aff42d6 100644
+--- a/lib/dns/include/dns/db.h
++++ b/lib/dns/include/dns/db.h
+@@ -195,6 +195,8 @@ typedef struct dns_dbmethods {
+ 				   dns_rdataset_t *sigrdataset);
+ 	isc_result_t	(*setcachestats)(dns_db_t *db, isc_stats_t *stats);
+ 	unsigned int	(*hashsize)(dns_db_t *db);
++	isc_result_t	(*getsize)(dns_db_t *db, dns_dbversion_t *version,
++				   isc_uint64_t *records, isc_uint64_t *bytes);
+ } dns_dbmethods_t;
+ 
+ typedef isc_result_t
+@@ -1485,6 +1487,24 @@ dns_db_getnsec3parameters(dns_db_t *db, dns_dbversion_t *version,
+  */
+ 
+ isc_result_t
++dns_db_getsize(dns_db_t *db, dns_dbversion_t *version, isc_uint64_t *records,
++               isc_uint64_t *bytes);
++/*%<
++ * Get the number of records in the given version of the database as well
++ * as the number bytes used to store those records.
++ *
++ * Requires:
++ * \li	'db' is a valid zone database.
++ * \li	'version' is NULL or a valid version.
++ * \li	'records' is NULL or a pointer to return the record count in.
++ * \li	'bytes' is NULL or a pointer to return the byte count in.
++ *
++ * Returns:
++ * \li	#ISC_R_SUCCESS
++ * \li	#ISC_R_NOTIMPLEMENTED
++ */
++
++isc_result_t
+ dns_db_findnsec3node(dns_db_t *db, dns_name_t *name,
+ 		     isc_boolean_t create, dns_dbnode_t **nodep);
+ /*%<
+diff --git a/lib/dns/include/dns/rdataslab.h b/lib/dns/include/dns/rdataslab.h
+index 3ac44b8..2e1e759 100644
+--- a/lib/dns/include/dns/rdataslab.h
++++ b/lib/dns/include/dns/rdataslab.h
+@@ -104,6 +104,7 @@ dns_rdataslab_tordataset(unsigned char *slab, unsigned int reservelen,
+  * Ensures:
+  *\li	'rdataset' is associated and points to a valid rdataest.
+  */
++
+ unsigned int
+ dns_rdataslab_size(unsigned char *slab, unsigned int reservelen);
+ /*%<
+@@ -116,6 +117,18 @@ dns_rdataslab_size(unsigned char *slab, unsigned int reservelen);
+  *\li	The number of bytes in the slab, including the reservelen.
+  */
+ 
++unsigned int
++dns_rdataslab_count(unsigned char *slab, unsigned int reservelen);
++/*%<
++ * Return the number of records in the rdataslab
++ *
++ * Requires:
++ *\li	'slab' points to a slab.
++ *
++ * Returns:
++ *\li	The number of records in the slab.
++ */
++
+ isc_result_t
+ dns_rdataslab_merge(unsigned char *oslab, unsigned char *nslab,
+ 		    unsigned int reservelen, isc_mem_t *mctx,
+diff --git a/lib/dns/include/dns/result.h b/lib/dns/include/dns/result.h
+index 7d11c2b..93d1fd5 100644
+--- a/lib/dns/include/dns/result.h
++++ b/lib/dns/include/dns/result.h
+@@ -157,8 +157,12 @@
+ #define DNS_R_BADCDS			(ISC_RESULTCLASS_DNS + 111)
+ #define DNS_R_BADCDNSKEY		(ISC_RESULTCLASS_DNS + 112)
+ #define DNS_R_OPTERR			(ISC_RESULTCLASS_DNS + 113)
++#define DNS_R_BADDNSTAP			(ISC_RESULTCLASS_DNS + 114)
++#define DNS_R_BADTSIG			(ISC_RESULTCLASS_DNS + 115)
++#define DNS_R_BADSIG0			(ISC_RESULTCLASS_DNS + 116)
++#define DNS_R_TOOMANYRECORDS		(ISC_RESULTCLASS_DNS + 117)
+ 
+-#define DNS_R_NRESULTS			114	/*%< Number of results */
++#define DNS_R_NRESULTS			118	/*%< Number of results */
+ 
+ /*
+  * DNS wire format rcodes.
+diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h
+index a9367f1..227540b 100644
+--- a/lib/dns/include/dns/zone.h
++++ b/lib/dns/include/dns/zone.h
+@@ -296,6 +296,32 @@ dns_zone_getfile(dns_zone_t *zone);
+  */
+ 
+ void
++dns_zone_setmaxrecords(dns_zone_t *zone, isc_uint32_t records);
++/*%<
++ * 	Sets the maximim number of records permitted in a zone.
++ *	0 implies unlimited.
++ *
++ * Requires:
++ *\li	'zone' to be valid initialised zone.
++ *
++ * Returns:
++ *\li	void
++ */
++
++isc_uint32_t
++dns_zone_getmaxrecords(dns_zone_t *zone);
++/*%<
++ * 	Gets the maximim number of records permitted in a zone.
++ *	0 implies unlimited.
++ *
++ * Requires:
++ *\li	'zone' to be valid initialised zone.
++ *
++ * Returns:
++ *\li	isc_uint32_t maxrecords.
++ */
++
++void
+ dns_zone_setmaxttl(dns_zone_t *zone, isc_uint32_t maxttl);
+ /*%<
+  * 	Sets the max ttl of the zone.
+@@ -316,7 +342,7 @@ dns_zone_getmaxttl(dns_zone_t *zone);
+  *\li	'zone' to be valid initialised zone.
+  *
+  * Returns:
+- *\li	isc_uint32_t maxttl.
++ *\li	dns_ttl_t maxttl.
+  */
+ 
+ isc_result_t
+diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c
+index 62becfc..72d722f 100644
+--- a/lib/dns/rbtdb.c
++++ b/lib/dns/rbtdb.c
+@@ -209,6 +209,7 @@ typedef isc_uint64_t                    rbtdb_serial_t;
+ #define free_rbtdb_callback free_rbtdb_callback64
+ #define free_rdataset free_rdataset64
+ #define getnsec3parameters getnsec3parameters64
++#define getsize getsize64
+ #define getoriginnode getoriginnode64
+ #define getrrsetstats getrrsetstats64
+ #define getsigningtime getsigningtime64
+@@ -589,6 +590,13 @@ typedef struct rbtdb_version {
+ 	isc_uint16_t			iterations;
+ 	isc_uint8_t			salt_length;
+ 	unsigned char			salt[DNS_NSEC3_SALTSIZE];
++
++	/*
++	 * records and bytes are covered by rwlock.
++	 */
++	isc_rwlock_t                    rwlock;
++	isc_uint64_t			records;
++	isc_uint64_t			bytes;
+ } rbtdb_version_t;
+ 
+ typedef ISC_LIST(rbtdb_version_t)       rbtdb_versionlist_t;
+@@ -1130,6 +1138,7 @@ free_rbtdb(dns_rbtdb_t *rbtdb, isc_boolean_t log, isc_event_t *event) {
+ 		INSIST(refs == 0);
+ 		UNLINK(rbtdb->open_versions, rbtdb->current_version, link);
+ 		isc_refcount_destroy(&rbtdb->current_version->references);
++		isc_rwlock_destroy(&rbtdb->current_version->rwlock);
+ 		isc_mem_put(rbtdb->common.mctx, rbtdb->current_version,
+ 			    sizeof(rbtdb_version_t));
+ 	}
+@@ -1383,6 +1392,7 @@ allocate_version(isc_mem_t *mctx, rbtdb_serial_t serial,
+ 
+ static isc_result_t
+ newversion(dns_db_t *db, dns_dbversion_t **versionp) {
++	isc_result_t result;
+ 	dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+ 	rbtdb_version_t *version;
+ 
+@@ -1415,13 +1425,28 @@ newversion(dns_db_t *db, dns_dbversion_t **versionp) {
+ 			version->salt_length = 0;
+ 			memset(version->salt, 0, sizeof(version->salt));
+ 		}
+-		rbtdb->next_serial++;
+-		rbtdb->future_version = version;
+-	}
++		result = isc_rwlock_init(&version->rwlock, 0, 0);
++		if (result != ISC_R_SUCCESS) {
++			isc_refcount_destroy(&version->references);
++			isc_mem_put(rbtdb->common.mctx, version,
++				    sizeof(*version));
++			version = NULL;
++		} else {
++			RWLOCK(&rbtdb->current_version->rwlock,
++			       isc_rwlocktype_read);
++			version->records = rbtdb->current_version->records;
++			version->bytes = rbtdb->current_version->bytes;
++			RWUNLOCK(&rbtdb->current_version->rwlock,
++				 isc_rwlocktype_read);
++			rbtdb->next_serial++;
++			rbtdb->future_version = version;
++		}
++	} else
++		result = ISC_R_NOMEMORY;
+ 	RBTDB_UNLOCK(&rbtdb->lock, isc_rwlocktype_write);
+ 
+ 	if (version == NULL)
+-		return (ISC_R_NOMEMORY);
++		return (result);
+ 
+ 	*versionp = version;
+ 
+@@ -2681,6 +2706,7 @@ closeversion(dns_db_t *db, dns_dbversion_t **versionp, isc_boolean_t commit) {
+ 
+ 	if (cleanup_version != NULL) {
+ 		INSIST(EMPTY(cleanup_version->changed_list));
++		isc_rwlock_destroy(&cleanup_version->rwlock);
+ 		isc_mem_put(rbtdb->common.mctx, cleanup_version,
+ 			    sizeof(*cleanup_version));
+ 	}
+@@ -6254,6 +6280,26 @@ add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion,
+ 		else
+ 			rbtnode->data = newheader;
+ 		newheader->next = topheader->next;
++		if (rbtversion != NULL)
++			RWLOCK(&rbtversion->rwlock, isc_rwlocktype_write);
++		if (rbtversion != NULL && !header_nx) {
++			rbtversion->records -=
++				dns_rdataslab_count((unsigned char *)header,
++						    sizeof(*header));
++			rbtversion->bytes -=
++				dns_rdataslab_size((unsigned char *)header,
++						   sizeof(*header));
++		}
++		if (rbtversion != NULL && !newheader_nx) {
++			rbtversion->records +=
++				dns_rdataslab_count((unsigned char *)newheader,
++						    sizeof(*newheader));
++			rbtversion->bytes +=
++				dns_rdataslab_size((unsigned char *)newheader,
++						   sizeof(*newheader));
++		}
++		if (rbtversion != NULL)
++			RWUNLOCK(&rbtversion->rwlock, isc_rwlocktype_write);
+ 		if (loading) {
+ 			/*
+ 			 * There are no other references to 'header' when
+@@ -6355,6 +6401,16 @@ add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion,
+ 			newheader->down = NULL;
+ 			rbtnode->data = newheader;
+ 		}
++		if (rbtversion != NULL && !newheader_nx) {
++			RWLOCK(&rbtversion->rwlock, isc_rwlocktype_write);
++			rbtversion->records +=
++				dns_rdataslab_count((unsigned char *)newheader,
++						    sizeof(*newheader));
++			rbtversion->bytes +=
++				dns_rdataslab_size((unsigned char *)newheader,
++						   sizeof(*newheader));
++			RWUNLOCK(&rbtversion->rwlock, isc_rwlocktype_write);
++		}
+ 		idx = newheader->node->locknum;
+ 		if (IS_CACHE(rbtdb)) {
+ 			ISC_LIST_PREPEND(rbtdb->rdatasets[idx],
+@@ -6811,6 +6867,12 @@ subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ 			 */
+ 			newheader->additional_auth = NULL;
+ 			newheader->additional_glue = NULL;
++			rbtversion->records +=
++				dns_rdataslab_count((unsigned char *)newheader,
++						    sizeof(*newheader));
++			rbtversion->bytes +=
++				dns_rdataslab_size((unsigned char *)newheader,
++						   sizeof(*newheader));
+ 		} else if (result == DNS_R_NXRRSET) {
+ 			/*
+ 			 * This subtraction would remove all of the rdata;
+@@ -6846,6 +6908,12 @@ subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ 		 * topheader.
+ 		 */
+ 		INSIST(rbtversion->serial >= topheader->serial);
++		rbtversion->records -=
++				dns_rdataslab_count((unsigned char *)header,
++						    sizeof(*header));
++		rbtversion->bytes -=
++				dns_rdataslab_size((unsigned char *)header,
++						   sizeof(*header));
+ 		if (topheader_prev != NULL)
+ 			topheader_prev->next = newheader;
+ 		else
+@@ -7172,6 +7240,7 @@ rbt_datafixer(dns_rbtnode_t *rbtnode, void *base, size_t filesize,
+ 	unsigned char *limit = ((unsigned char *) base) + filesize;
+ 	unsigned char *p;
+ 	size_t size;
++	unsigned int count;
+ 
+ 	REQUIRE(rbtnode != NULL);
+ 
+@@ -7179,6 +7248,9 @@ rbt_datafixer(dns_rbtnode_t *rbtnode, void *base, size_t filesize,
+ 		p = (unsigned char *) header;
+ 
+ 		size = dns_rdataslab_size(p, sizeof(*header));
++		count = dns_rdataslab_count(p, sizeof(*header));;
++		rbtdb->current_version->records += count;
++		rbtdb->current_version->bytes += size;
+ 		isc_crc64_update(crc, p, size);
+ #ifdef DEBUG
+ 		hexdump("hashing header", p, sizeof(rdatasetheader_t));
+@@ -7777,6 +7849,33 @@ getnsec3parameters(dns_db_t *db, dns_dbversion_t *version, dns_hash_t *hash,
+ }
+ 
+ static isc_result_t
++getsize(dns_db_t *db, dns_dbversion_t *version, isc_uint64_t *records,
++        isc_uint64_t *bytes)
++{
++	dns_rbtdb_t *rbtdb;
++	isc_result_t result = ISC_R_SUCCESS;
++	rbtdb_version_t *rbtversion = version;
++
++	rbtdb = (dns_rbtdb_t *)db;
++
++	REQUIRE(VALID_RBTDB(rbtdb));
++	INSIST(rbtversion == NULL || rbtversion->rbtdb == rbtdb);
++
++	if (rbtversion == NULL)
++		rbtversion = rbtdb->current_version;
++
++	RWLOCK(&rbtversion->rwlock, isc_rwlocktype_read);
++	if (records != NULL)
++		*records = rbtversion->records;
++
++	if (bytes != NULL)
++		*bytes = rbtversion->bytes;
++	RWUNLOCK(&rbtversion->rwlock, isc_rwlocktype_read);
++
++	return (result);
++}
++
++static isc_result_t
+ setsigningtime(dns_db_t *db, dns_rdataset_t *rdataset, isc_stdtime_t resign) {
+ 	dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+ 	isc_stdtime_t oldresign;
+@@ -7972,7 +8071,8 @@ static dns_dbmethods_t zone_methods = {
+ 	NULL,
+ 	NULL,
+ 	NULL,
+-	hashsize
++	hashsize,
++	getsize
+ };
+ 
+ static dns_dbmethods_t cache_methods = {
+@@ -8018,7 +8118,8 @@ static dns_dbmethods_t cache_methods = {
+ 	NULL,
+ 	NULL,
+ 	setcachestats,
+-	hashsize
++	hashsize,
++	NULL
+ };
+ 
+ isc_result_t
+@@ -8310,6 +8411,20 @@ dns_rbtdb_create
+ 	rbtdb->current_version->salt_length = 0;
+ 	memset(rbtdb->current_version->salt, 0,
+ 	       sizeof(rbtdb->current_version->salt));
++	result = isc_rwlock_init(&rbtdb->current_version->rwlock, 0, 0);
++	if (result != ISC_R_SUCCESS) {
++		isc_refcount_destroy(&rbtdb->current_version->references);
++		isc_mem_put(mctx, rbtdb->current_version,
++			    sizeof(*rbtdb->current_version));
++		rbtdb->current_version = NULL;
++		isc_refcount_decrement(&rbtdb->references, NULL);
++		isc_refcount_destroy(&rbtdb->references);
++		free_rbtdb(rbtdb, ISC_FALSE, NULL);
++		return (result);
++	}
++
++	rbtdb->current_version->records = 0;
++	rbtdb->current_version->bytes = 0;
+ 	rbtdb->future_version = NULL;
+ 	ISC_LIST_INIT(rbtdb->open_versions);
+ 	/*
+diff --git a/lib/dns/rdataslab.c b/lib/dns/rdataslab.c
+index e29dc84..63e3728 100644
+--- a/lib/dns/rdataslab.c
++++ b/lib/dns/rdataslab.c
+@@ -523,6 +523,19 @@ dns_rdataslab_size(unsigned char *slab, unsigned int reservelen) {
+ 	return ((unsigned int)(current - slab));
+ }
+ 
++unsigned int
++dns_rdataslab_count(unsigned char *slab, unsigned int reservelen) {
++	unsigned int count;
++	unsigned char *current;
++
++	REQUIRE(slab != NULL);
++
++	current = slab + reservelen;
++	count = *current++ * 256;
++	count += *current++;
++	return (count);
++}
++
+ /*
+  * Make the dns_rdata_t 'rdata' refer to the slab item
+  * beginning at '*current', which is part of a slab of type
+diff --git a/lib/dns/result.c b/lib/dns/result.c
+index 7be4f57..a621909 100644
+--- a/lib/dns/result.c
++++ b/lib/dns/result.c
+@@ -167,11 +167,16 @@ static const char *text[DNS_R_NRESULTS] = {
+ 	"covered by negative trust anchor",    /*%< 110 DNS_R_NTACOVERED */
+ 	"bad CDS",			       /*%< 111 DNS_R_BADCSD */
+ 	"bad CDNSKEY",			       /*%< 112 DNS_R_BADCDNSKEY */
+-	"malformed OPT option"		       /*%< 113 DNS_R_OPTERR */
++	"malformed OPT option",		       /*%< 113 DNS_R_OPTERR */
++	"malformed DNSTAP data",	       /*%< 114 DNS_R_BADDNSTAP */
++
++	"TSIG in wrong location",	       /*%< 115 DNS_R_BADTSIG */
++	"SIG(0) in wrong location",	       /*%< 116 DNS_R_BADSIG0 */
++	"too many records",	               /*%< 117 DNS_R_TOOMANYRECORDS */
+ };
+ 
+ static const char *rcode_text[DNS_R_NRCODERESULTS] = {
+-	"NOERROR",				/*%< 0 DNS_R_NOEROR */
++	"NOERROR",				/*%< 0 DNS_R_NOERROR */
+ 	"FORMERR",				/*%< 1 DNS_R_FORMERR */
+ 	"SERVFAIL",				/*%< 2 DNS_R_SERVFAIL */
+ 	"NXDOMAIN",				/*%< 3 DNS_R_NXDOMAIN */
+diff --git a/lib/dns/sdb.c b/lib/dns/sdb.c
+index abfeeb0..19397e0 100644
+--- a/lib/dns/sdb.c
++++ b/lib/dns/sdb.c
+@@ -1298,7 +1298,8 @@ static dns_dbmethods_t sdb_methods = {
+ 	findnodeext,
+ 	findext,
+ 	NULL,			/* setcachestats */
+-	NULL			/* hashsize */
++	NULL,			/* hashsize */
++	NULL			/* getsize */
+ };
+ 
+ static isc_result_t
+diff --git a/lib/dns/sdlz.c b/lib/dns/sdlz.c
+index b1198a4..0e3163d 100644
+--- a/lib/dns/sdlz.c
++++ b/lib/dns/sdlz.c
+@@ -1269,7 +1269,8 @@ static dns_dbmethods_t sdlzdb_methods = {
+ 	findnodeext,
+ 	findext,
+ 	NULL,			/* setcachestats */
+-	NULL			/* hashsize */
++	NULL,			/* hashsize */
++	NULL			/* getsize */
+ };
+ 
+ /*
+diff --git a/lib/dns/xfrin.c b/lib/dns/xfrin.c
+index 2a6c1b4..ac566e1 100644
+--- a/lib/dns/xfrin.c
++++ b/lib/dns/xfrin.c
+@@ -149,6 +149,9 @@ struct dns_xfrin_ctx {
+ 	unsigned int		nrecs;		/*%< Number of records recvd */
+ 	isc_uint64_t		nbytes;		/*%< Number of bytes received */
+ 
++	unsigned int		maxrecords;	/*%< The maximum number of
++						     records set for the zone */
++
+ 	isc_time_t		start;		/*%< Start time of the transfer */
+ 	isc_time_t		end;		/*%< End time of the transfer */
+ 
+@@ -309,10 +312,18 @@ axfr_putdata(dns_xfrin_ctx_t *xfr, dns_diffop_t op,
+ static isc_result_t
+ axfr_apply(dns_xfrin_ctx_t *xfr) {
+ 	isc_result_t result;
++	isc_uint64_t records;
+ 
+ 	CHECK(dns_diff_load(&xfr->diff, xfr->axfr.add, xfr->axfr.add_private));
+ 	xfr->difflen = 0;
+ 	dns_diff_clear(&xfr->diff);
++	if (xfr->maxrecords != 0U) {
++		result = dns_db_getsize(xfr->db, xfr->ver, &records, NULL);
++		if (result == ISC_R_SUCCESS && records > xfr->maxrecords) {
++			result = DNS_R_TOOMANYRECORDS;
++			goto failure;
++		}
++	}
+ 	result = ISC_R_SUCCESS;
+  failure:
+ 	return (result);
+@@ -396,6 +407,7 @@ ixfr_putdata(dns_xfrin_ctx_t *xfr, dns_diffop_t op,
+ static isc_result_t
+ ixfr_apply(dns_xfrin_ctx_t *xfr) {
+ 	isc_result_t result;
++	isc_uint64_t records;
+ 
+ 	if (xfr->ver == NULL) {
+ 		CHECK(dns_db_newversion(xfr->db, &xfr->ver));
+@@ -403,6 +415,13 @@ ixfr_apply(dns_xfrin_ctx_t *xfr) {
+ 			CHECK(dns_journal_begin_transaction(xfr->ixfr.journal));
+ 	}
+ 	CHECK(dns_diff_apply(&xfr->diff, xfr->db, xfr->ver));
++	if (xfr->maxrecords != 0U) {
++		result = dns_db_getsize(xfr->db, xfr->ver, &records, NULL);
++		if (result == ISC_R_SUCCESS && records > xfr->maxrecords) {
++			result = DNS_R_TOOMANYRECORDS;
++			goto failure;
++		}
++	}
+ 	if (xfr->ixfr.journal != NULL) {
+ 		result = dns_journal_writediff(xfr->ixfr.journal, &xfr->diff);
+ 		if (result != ISC_R_SUCCESS)
+@@ -759,7 +778,7 @@ xfrin_reset(dns_xfrin_ctx_t *xfr) {
+ 
+ static void
+ xfrin_fail(dns_xfrin_ctx_t *xfr, isc_result_t result, const char *msg) {
+-	if (result != DNS_R_UPTODATE) {
++	if (result != DNS_R_UPTODATE && result != DNS_R_TOOMANYRECORDS) {
+ 		xfrin_log(xfr, ISC_LOG_ERROR, "%s: %s",
+ 			  msg, isc_result_totext(result));
+ 		if (xfr->is_ixfr)
+@@ -852,6 +871,7 @@ xfrin_create(isc_mem_t *mctx,
+ 	xfr->nmsg = 0;
+ 	xfr->nrecs = 0;
+ 	xfr->nbytes = 0;
++	xfr->maxrecords = dns_zone_getmaxrecords(zone);
+ 	isc_time_now(&xfr->start);
+ 
+ 	xfr->tsigkey = NULL;
+diff --git a/lib/dns/zone.c b/lib/dns/zone.c
+index 90e558d..2b0d8e4 100644
+--- a/lib/dns/zone.c
++++ b/lib/dns/zone.c
+@@ -253,6 +253,8 @@ struct dns_zone {
+ 	isc_uint32_t		maxretry;
+ 	isc_uint32_t		minretry;
+ 
++	isc_uint32_t		maxrecords;
++
+ 	isc_sockaddr_t		*masters;
+ 	isc_dscp_t		*masterdscps;
+ 	dns_name_t		**masterkeynames;
+@@ -10088,6 +10090,20 @@ dns_zone_setmaxretrytime(dns_zone_t *zone, isc_uint32_t val) {
+ 	zone->maxretry = val;
+ }
+ 
++isc_uint32_t
++dns_zone_getmaxrecords(dns_zone_t *zone) {
++        REQUIRE(DNS_ZONE_VALID(zone));
++
++	return (zone->maxrecords);
++}
++
++void
++dns_zone_setmaxrecords(dns_zone_t *zone, isc_uint32_t val) {
++        REQUIRE(DNS_ZONE_VALID(zone));
++
++	zone->maxrecords = val;
++}
++
+ static isc_boolean_t
+ notify_isqueued(dns_zone_t *zone, unsigned int flags, dns_name_t *name,
+ 		isc_sockaddr_t *addr, dns_tsigkey_t *key)
+@@ -14431,7 +14447,7 @@ zone_xfrdone(dns_zone_t *zone, isc_result_t result) {
+ 	DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_SOABEFOREAXFR);
+ 
+ 	TIME_NOW(&now);
+-	switch (result) {
++	switch (xfrresult) {
+ 	case ISC_R_SUCCESS:
+ 		DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NEEDNOTIFY);
+ 		/*FALLTHROUGH*/
+@@ -14558,6 +14574,11 @@ zone_xfrdone(dns_zone_t *zone, isc_result_t result) {
+ 		DNS_ZONE_SETFLAG(zone, DNS_ZONEFLAG_NOIXFR);
+ 		goto same_master;
+ 
++	case DNS_R_TOOMANYRECORDS:
++		DNS_ZONE_JITTER_ADD(&now, zone->refresh, &zone->refreshtime);
++		inc_stats(zone, dns_zonestatscounter_xfrfail);
++		break;
++
+ 	default:
+ 	next_master:
+ 		/*
+diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c
+index 780ab46..e7ff1cc 100644
+--- a/lib/isccfg/namedconf.c
++++ b/lib/isccfg/namedconf.c
+@@ -1679,6 +1679,7 @@ zone_clauses[] = {
+ 	{ "masterfile-format", &cfg_type_masterformat, 0 },
+ 	{ "max-ixfr-log-size", &cfg_type_size, CFG_CLAUSEFLAG_OBSOLETE },
+ 	{ "max-journal-size", &cfg_type_sizenodefault, 0 },
++	{ "max-records", &cfg_type_uint32, 0 },
+ 	{ "max-refresh-time", &cfg_type_uint32, 0 },
+ 	{ "max-retry-time", &cfg_type_uint32, 0 },
+ 	{ "max-transfer-idle-in", &cfg_type_uint32, 0 },
+-- 
+2.7.4
+
diff --git a/meta/recipes-connectivity/bind/bind_9.10.3-P3.bb b/meta/recipes-connectivity/bind/bind_9.10.3-P3.bb
index fa45809..8160625 100644
--- a/meta/recipes-connectivity/bind/bind_9.10.3-P3.bb
+++ b/meta/recipes-connectivity/bind/bind_9.10.3-P3.bb
@@ -28,6 +28,7 @@ SRC_URI = "ftp://ftp.isc.org/isc/bind9/${PV}/${BPN}-${PV}.tar.gz \
            file://CVE-2016-2775.patch \
            file://CVE-2016-2776.patch \
            file://CVE-2016-8864.patch \
+           file://CVE-2016-6170.patch \
            "
 
 SRC_URI[md5sum] = "bcf7e772b616f7259420a3edc5df350a"
-- 
2.7.4




More information about the Openembedded-core mailing list