| 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 |
| |