summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYi Zhao <yi.zhao@windriver.com>2017-04-13 13:48:13 +0800
committerRichard Purdie <richard.purdie@linuxfoundation.org>2017-04-28 11:26:07 +0100
commit14abd767349bc868ca59838f1af3aaf17dfe4350 (patch)
treee942cc9010a4a1777ff6aa7d0cb354c1220ec66e
parentc06f3a5993c7d63d91840c2a4d5b621e946ef78f (diff)
downloadopenembedded-core-contrib-14abd767349bc868ca59838f1af3aaf17dfe4350.tar.gz
openembedded-core-contrib-14abd767349bc868ca59838f1af3aaf17dfe4350.tar.bz2
openembedded-core-contrib-14abd767349bc868ca59838f1af3aaf17dfe4350.zip
bind: Security fix CVE-2016-6170
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@windriver.com> Signed-off-by: Ross Burton <ross.burton@intel.com>
-rw-r--r--meta/recipes-connectivity/bind/bind/CVE-2016-6170.patch1090
-rw-r--r--meta/recipes-connectivity/bind/bind_9.10.3-P3.bb1
2 files changed, 1091 insertions, 0 deletions
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 0000000000..75bc211cb6
--- /dev/null
+++ b/meta/recipes-connectivity/bind/bind/CVE-2016-6170.patch
@@ -0,0 +1,1090 @@
+From 1bbcfe2fc84f57b1e4e075fb3bc2a1dd0a3a851f Mon Sep 17 00:00:00 2001
+From: Mark Andrews <marka@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]
+
+CVE: CVE-2016-6170
+
+Signed-off-by: Yi Zhao <yi.zhao@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 fa45809980..8160625282 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"