mirror of
https://github.com/isc-projects/bind9.git
synced 2026-06-03 22:08:25 -04:00
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]
This commit is contained in:
parent
1b2e798976
commit
5f8412a4cb
35 changed files with 437 additions and 14 deletions
4
CHANGES
4
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]
|
||||
|
||||
4503. [cleanup] "make uninstall" now removes files installed by
|
||||
BIND. (This currently excludes Python files
|
||||
due to lack of support in setup.py.) [RT #42912]
|
||||
|
|
|
|||
|
|
@ -221,6 +221,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\
|
||||
|
|
|
|||
|
|
@ -360,6 +360,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>;
|
||||
|
|
@ -564,6 +565,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>;
|
||||
|
|
@ -665,6 +667,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>;
|
||||
|
|
|
|||
|
|
@ -2496,6 +2496,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);
|
||||
|
||||
|
|
@ -3180,6 +3182,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,
|
||||
|
|
|
|||
|
|
@ -991,6 +991,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);
|
||||
|
|
|
|||
|
|
@ -623,7 +623,8 @@ static dns_dbmethods_t sampledb_methods = {
|
|||
findext,
|
||||
setcachestats,
|
||||
hashsize,
|
||||
NULL
|
||||
NULL,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* Auxiliary driver functions. */
|
||||
|
|
|
|||
|
|
@ -31,5 +31,6 @@ rm -f ns3/dsset-*
|
|||
rm -f ns3/example.db
|
||||
rm -f ns3/many.test.bk
|
||||
rm -f ns3/nsec3param.test.db
|
||||
rm -f ns3/too-big.test.db
|
||||
rm -f nsupdate.out*
|
||||
rm -f typelist.out.*
|
||||
|
|
|
|||
|
|
@ -65,3 +65,10 @@ zone "delegation.test" {
|
|||
allow-update { any; };
|
||||
file "delegation.test.db.signed";
|
||||
};
|
||||
|
||||
zone "too-big.test" {
|
||||
type master;
|
||||
allow-update { any; };
|
||||
max-records 3;
|
||||
file "too-big.test.db";
|
||||
};
|
||||
|
|
|
|||
10
bin/tests/system/nsupdate/ns3/too-big.test.db.in
Normal file
10
bin/tests/system/nsupdate/ns3/too-big.test.db.in
Normal file
|
|
@ -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
|
||||
|
|
@ -18,6 +18,7 @@ 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
|
||||
|
|
@ -25,6 +26,7 @@ sed 's/example.nil/unixtime.nil/g' ns1/example1.db > ns1/unixtime.db
|
|||
sed 's/example.nil/yyyymmddvv.nil/g' ns1/example1.db > ns1/yyyymmddvv.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.
|
||||
|
|
|
|||
|
|
@ -686,5 +686,20 @@ $DIG +tcp @10.53.0.3 -p 5300 ns child.delegation.test > dig.out.ns1.test$n
|
|||
grep "status: NXDOMAIN" dig.out.ns1.test$n > /dev/null 2>&1 || ret=1
|
||||
[ $ret = 0 ] || { echo I:failed; status=1; }
|
||||
|
||||
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"
|
||||
[ $status -eq 0 ] || exit 1
|
||||
|
|
|
|||
|
|
@ -34,3 +34,4 @@ rm -f ns*/named.lock
|
|||
rm -f ns2/mapped.db
|
||||
rm -f ns3/mapped.bk
|
||||
rm -f dig.out.?.*
|
||||
rm -f ns1/ixfr-too-big.db ns1/ixfr-too-big.db.jnl
|
||||
|
|
|
|||
10
bin/tests/system/xfer/ns1/axfr-too-big.db
Normal file
10
bin/tests/system/xfer/ns1/axfr-too-big.db
Normal file
|
|
@ -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.$
|
||||
13
bin/tests/system/xfer/ns1/ixfr-too-big.db.in
Normal file
13
bin/tests/system/xfer/ns1/ixfr-too-big.db.in
Normal file
|
|
@ -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.$
|
||||
|
|
@ -40,3 +40,14 @@ zone "edns-expire" {
|
|||
type master;
|
||||
file "edns-expire.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";
|
||||
};
|
||||
|
|
|
|||
|
|
@ -50,3 +50,17 @@ zone "edns-expire" {
|
|||
masters { 10.53.0.1; };
|
||||
file "edns-expire.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";
|
||||
};
|
||||
|
|
|
|||
|
|
@ -30,3 +30,5 @@ cp ns2/mapped.db.in ns2/mapped.db
|
|||
|
||||
$PERL -e 'for ($i=0;$i<4096;$i++){ printf("name%u 259200 A 1.2.3.4\nname%u 259200 TXT \"Hello World %u\"\n", $i, $i, $i);}' > ns8/small.db
|
||||
$PERL -e 'printf("large IN TYPE45234 \\# 48000 "); for ($i=0;$i<16*3000;$i++) { printf("%02x", $i % 256); } printf("\n");' > ns8/large.db
|
||||
|
||||
cp -f ns1/ixfr-too-big.db.in ns1/ixfr-too-big.db
|
||||
|
|
|
|||
|
|
@ -414,5 +414,31 @@ $PERL ../digcomp.pl knowngood.mapped dig.out.3.$n || 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 (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"
|
||||
[ $status -eq 0 ] || exit 1
|
||||
|
|
|
|||
|
|
@ -4521,6 +4521,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>
|
||||
|
|
@ -8453,6 +8454,16 @@ avoid-v6-udp-ports { 40000; range 50000 60000; };
|
|||
</listitem>
|
||||
</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>
|
||||
|
|
@ -12503,6 +12514,16 @@ zone <replaceable>zone_name</replaceable> <optional><replaceable>class</replacea
|
|||
</listitem>
|
||||
</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>
|
||||
|
|
|
|||
|
|
@ -66,6 +66,15 @@
|
|||
|
||||
<section xml:id="relnotes_security"><info><title>Security Fixes</title></info>
|
||||
<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>
|
||||
It was possible to trigger a assertion when rendering a
|
||||
|
|
|
|||
|
|
@ -1756,6 +1756,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 },
|
||||
|
|
|
|||
13
lib/dns/db.c
13
lib/dns/db.c
|
|
@ -1003,6 +1003,19 @@ dns_db_getnsec3parameters(dns_db_t *db, dns_dbversion_t *version,
|
|||
return (ISC_R_NOTFOUND);
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -582,7 +582,8 @@ static dns_dbmethods_t ecdb_methods = {
|
|||
NULL, /* findext */
|
||||
NULL, /* setcachestats */
|
||||
NULL, /* hashsize */
|
||||
NULL /* nodefullname */
|
||||
NULL, /* nodefullname */
|
||||
NULL /* getsize */
|
||||
};
|
||||
|
||||
static isc_result_t
|
||||
|
|
|
|||
|
|
@ -188,6 +188,8 @@ typedef struct dns_dbmethods {
|
|||
size_t (*hashsize)(dns_db_t *db);
|
||||
isc_result_t (*nodefullname)(dns_db_t *db, dns_dbnode_t *node,
|
||||
dns_name_t *name);
|
||||
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
|
||||
|
|
@ -1501,6 +1503,24 @@ dns_db_getnsec3parameters(dns_db_t *db, dns_dbversion_t *version,
|
|||
* or this zone does not have NSEC3 records.
|
||||
*/
|
||||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -95,6 +95,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);
|
||||
/*%<
|
||||
|
|
@ -107,6 +108,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,
|
||||
|
|
|
|||
|
|
@ -149,8 +149,9 @@
|
|||
#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 117 /*%< Number of results */
|
||||
#define DNS_R_NRESULTS 118 /*%< Number of results */
|
||||
|
||||
/*
|
||||
* DNS wire format rcodes.
|
||||
|
|
|
|||
|
|
@ -295,6 +295,32 @@ dns_zone_getfile(dns_zone_t *zone);
|
|||
*\li Pointer to null-terminated file name, or NULL.
|
||||
*/
|
||||
|
||||
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);
|
||||
/*%<
|
||||
|
|
@ -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
|
||||
|
|
|
|||
127
lib/dns/rbtdb.c
127
lib/dns/rbtdb.c
|
|
@ -202,6 +202,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
|
||||
|
|
@ -603,6 +604,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;
|
||||
|
|
@ -1174,6 +1182,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));
|
||||
}
|
||||
|
|
@ -1437,6 +1446,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;
|
||||
|
||||
|
|
@ -1469,13 +1479,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;
|
||||
|
||||
|
|
@ -2760,6 +2785,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));
|
||||
}
|
||||
|
|
@ -6341,6 +6367,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
|
||||
|
|
@ -6442,6 +6488,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],
|
||||
|
|
@ -6914,6 +6970,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;
|
||||
|
|
@ -6950,6 +7012,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
|
||||
|
|
@ -7288,6 +7356,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);
|
||||
|
||||
|
|
@ -7295,6 +7364,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));
|
||||
|
|
@ -7910,6 +7982,33 @@ getnsec3parameters(dns_db_t *db, dns_dbversion_t *version, dns_hash_t *hash,
|
|||
return (result);
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
@ -8125,7 +8224,8 @@ static dns_dbmethods_t zone_methods = {
|
|||
NULL,
|
||||
NULL,
|
||||
hashsize,
|
||||
nodefullname
|
||||
nodefullname,
|
||||
getsize
|
||||
};
|
||||
|
||||
static dns_dbmethods_t cache_methods = {
|
||||
|
|
@ -8172,7 +8272,8 @@ static dns_dbmethods_t cache_methods = {
|
|||
NULL,
|
||||
setcachestats,
|
||||
hashsize,
|
||||
nodefullname
|
||||
nodefullname,
|
||||
NULL
|
||||
};
|
||||
|
||||
isc_result_t
|
||||
|
|
@ -8466,6 +8567,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);
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -516,6 +516,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
|
||||
|
|
|
|||
|
|
@ -161,6 +161,7 @@ static const char *text[DNS_R_NRESULTS] = {
|
|||
|
||||
"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 *ids[DNS_R_NRESULTS] = {
|
||||
|
|
|
|||
|
|
@ -1290,7 +1290,8 @@ static dns_dbmethods_t sdb_methods = {
|
|||
findext,
|
||||
NULL, /* setcachestats */
|
||||
NULL, /* hashsize */
|
||||
NULL /* nodefullname */
|
||||
NULL, /* nodefullname */
|
||||
NULL /* getsize */
|
||||
};
|
||||
|
||||
static isc_result_t
|
||||
|
|
|
|||
|
|
@ -1324,7 +1324,8 @@ static dns_dbmethods_t sdlzdb_methods = {
|
|||
findext,
|
||||
NULL, /* setcachestats */
|
||||
NULL, /* hashsize */
|
||||
NULL /* nodefullname */
|
||||
NULL, /* nodefullname */
|
||||
NULL /* getsize */
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -141,6 +141,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 */
|
||||
|
||||
|
|
@ -306,10 +309,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;
|
||||
|
|
|
|||
|
|
@ -245,6 +245,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;
|
||||
|
|
@ -10214,6 +10216,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)
|
||||
|
|
@ -14674,7 +14690,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*/
|
||||
|
|
@ -14801,6 +14817,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:
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -1847,6 +1847,7 @@ zone_clauses[] = {
|
|||
{ "masterfile-style", &cfg_type_masterstyle, 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 },
|
||||
|
|
|
|||
Loading…
Reference in a new issue