[9.20] chg: usr: emit more helpful log for exceeding max-records-per-type

The new log message is emitted when adding or updating an RRset
fails due to exceeding the max-records-per-type limit. The log includes
the owner name and type, corresponding zone name, and the limit value.
It will be emitted on loading a zone file, inbound zone transfer
(both AXFR and IXFR), handling a DDNS update, or updating a cache DB.
It's especially helpful in the case of zone transfer, since the
secondary side doesn't have direct access to the offending zone data.

It could also be used for max-types-per-name, but this change
doesn't implement it yet as it's much less likely to happen
in practice.

Backport of MR !9509

Merge branch 'backport-helpful-log-on-toomanyrecords-9.20' into 'bind-9.20'

See merge request isc-projects/bind9!9771
This commit is contained in:
Mark Andrews 2024-11-27 00:51:59 +00:00
commit 74e7e229f2
11 changed files with 113 additions and 0 deletions

View file

@ -183,6 +183,11 @@ $DIG $DIGOPTS @10.53.0.1 nil. TXT | grep 'AXFR on too many records' >/dev/null |
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
msg="error adding 'nil/TXT' in 'nil/IN' (zone): too many records (must not exceed 5)"
wait_for_log 10 "$msg" ns1/named.run || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
n=$((n + 1))
echo_i "testing AXFR fallback after IXFR failure (bad SOA owner) ($n)"
ret=0

View file

@ -315,6 +315,14 @@ n=$((n + 1))
[ $ret -eq 0 ] || echo_i "failed"
status=$((status + ret))
# Check that the corresponding log message about exceeding the limit is present.
msg="error adding '2100-txt.above-limit/TXT' in 'above-limit/IN' (zone): too many records (must not exceed 2050)"
wait_for_log 10 "$msg" ns1/named.run || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
# Prepare for any further checking of the logs later on.
nextpart ns1/named.run >/dev/null
echo_i "checking that kasp-max-records-per-type rdatasets loaded ($n)"
for _attempt in 0 1 2 3 4 5 6 7 8 9; do
ret=0

View file

@ -80,6 +80,7 @@ zone "max-ttl.nil" {
check-integrity no;
allow-update { named-acl; };
allow-transfer { any; };
max-records-per-type 3;
};
zone "other.nil" {

View file

@ -1359,6 +1359,27 @@ if [ $ret -ne 0 ]; then
status=1
fi
n=$((n + 1))
echo_i "check adding more records than max-records-per-type fails ($n)"
ret=0
$NSUPDATE <<END >nsupdate.out.test$n 2>&1 && ret=1
server 10.53.0.1 ${PORT}
zone max-ttl.nil.
update add a.max-ttl.nil. 60 IN A 192.0.2.1
update add a.max-ttl.nil. 60 IN A 192.0.2.2
update add a.max-ttl.nil. 60 IN A 192.0.2.3
update add a.max-ttl.nil. 60 IN A 192.0.2.4
send
END
grep "update failed: SERVFAIL" nsupdate.out.test$n >/dev/null || ret=1
msg="error updating 'a.max-ttl.nil/A' in 'max-ttl.nil/IN' (zone): too many records (must not exceed 3)"
wait_for_log 10 "$msg" ns1/named.run || ret=1
[ $ret = 0 ] || {
echo_i "failed"
status=1
}
nextpart ns1/named.run >/dev/null
n=$((n + 1))
ret=0
echo_i "add a record which is truncated when logged. ($n)"

View file

@ -233,6 +233,12 @@ echo_i "checking RRset that exceeds max-records-per-type ($n)"
ret=0
dig_with_opts @10.53.0.3 biganswer.big >dig.out.1.test$n || ret=1
grep 'status: SERVFAIL' dig.out.1.test$n >/dev/null || ret=1
msg="error adding 'biganswer.big/A' in './IN' (cache): too many records (must not exceed 100)"
wait_for_log 10 "$msg" ns3/named.run || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
ns3_reset ns3/named5.conf.in
dig_with_opts @10.53.0.3 biganswer.big >dig.out.2.test$n || ret=1
grep 'status: NOERROR' dig.out.2.test$n >/dev/null || ret=1

View file

@ -38,6 +38,7 @@
#include <dns/log.h>
#include <dns/master.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
#include <dns/rdataset.h>
#include <dns/rdatasetiter.h>
@ -1190,3 +1191,26 @@ dns_db_setmaxtypepername(dns_db_t *db, uint32_t value) {
(db->methods->setmaxtypepername)(db, value);
}
}
void
dns__db_logtoomanyrecords(dns_db_t *db, const dns_name_t *name,
dns_rdatatype_t type, const char *op,
uint32_t limit) {
char namebuf[DNS_NAME_FORMATSIZE];
char originbuf[DNS_NAME_FORMATSIZE];
char typebuf[DNS_RDATATYPE_FORMATSIZE];
char clsbuf[DNS_RDATACLASS_FORMATSIZE];
dns_name_format(name, namebuf, sizeof(namebuf));
dns_name_format(&db->origin, originbuf, sizeof(originbuf));
dns_rdatatype_format(type, typebuf, sizeof(typebuf));
dns_rdataclass_format(db->rdclass, clsbuf, sizeof(clsbuf));
isc_log_write(
dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DB,
ISC_LOG_ERROR,
"error %s '%s/%s' in '%s/%s' (%s): %s (must not exceed %u)", op,
namebuf, typebuf, originbuf, clsbuf,
(db->attributes & DNS_DBATTR_CACHE) != 0 ? "cache" : "zone",
isc_result_totext(DNS_R_TOOMANYRECORDS), limit);
}

View file

@ -187,4 +187,13 @@ prio_type(dns_typepair_t type) {
return false;
}
void
dns__db_logtoomanyrecords(dns_db_t *db, const dns_name_t *name,
dns_rdatatype_t type, const char *op, uint32_t limit);
/*
* Emit a log message when adding an rdataset of name/type would exceed the
* 'maxrrperset' limit. 'op' is 'adding' or 'updating' depending on whether
* the addition is to create a new rdataset or to merge to an existing one.
*/
ISC_LANG_ENDDECLS

View file

@ -3414,6 +3414,11 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
&region, sizeof(dns_slabheader_t),
qpdb->maxrrperset);
if (result != ISC_R_SUCCESS) {
if (result == DNS_R_TOOMANYRECORDS) {
dns__db_logtoomanyrecords((dns_db_t *)qpdb,
&qpnode->name, rdataset->type,
"adding", qpdb->maxrrperset);
}
return result;
}

View file

@ -1875,6 +1875,12 @@ add(qpzonedb_t *qpdb, qpznode_t *node, const dns_name_t *nodename,
header->resign_lsb;
}
} else {
if (result == DNS_R_TOOMANYRECORDS) {
dns__db_logtoomanyrecords(
(dns_db_t *)qpdb, nodename,
(dns_rdatatype_t)header->type,
"updating", qpdb->maxrrperset);
}
dns_slabheader_destroy(&newheader);
return result;
}
@ -2109,6 +2115,11 @@ loading_addrdataset(void *arg, const dns_name_t *name,
&region, sizeof(dns_slabheader_t),
qpdb->maxrrperset);
if (result != ISC_R_SUCCESS) {
if (result == DNS_R_TOOMANYRECORDS) {
dns__db_logtoomanyrecords((dns_db_t *)qpdb, name,
rdataset->type, "adding",
qpdb->maxrrperset);
}
return result;
}
@ -4607,6 +4618,11 @@ addrdataset(dns_db_t *db, dns_dbnode_t *dbnode, dns_dbversion_t *dbversion,
&region, sizeof(dns_slabheader_t),
qpdb->maxrrperset);
if (result != ISC_R_SUCCESS) {
if (result == DNS_R_TOOMANYRECORDS) {
dns__db_logtoomanyrecords((dns_db_t *)qpdb, &node->name,
rdataset->type, "adding",
qpdb->maxrrperset);
}
return result;
}

View file

@ -1751,6 +1751,11 @@ loading_addrdataset(void *arg, const dns_name_t *name,
&region, sizeof(dns_slabheader_t),
rbtdb->maxrrperset);
if (result != ISC_R_SUCCESS) {
if (result == DNS_R_TOOMANYRECORDS) {
dns__db_logtoomanyrecords((dns_db_t *)rbtdb, name,
rdataset->type, "adding",
rbtdb->maxrrperset);
}
return result;
}
newheader = (dns_slabheader_t *)region.base;

View file

@ -2807,6 +2807,12 @@ find_header:
header->resign_lsb;
}
} else {
if (result == DNS_R_TOOMANYRECORDS) {
dns__db_logtoomanyrecords(
(dns_db_t *)rbtdb, nodename,
(dns_rdatatype_t)(header->type),
"updating", rbtdb->maxrrperset);
}
dns_slabheader_destroy(&newheader);
return result;
}
@ -3305,6 +3311,13 @@ dns__rbtdb_addrdataset(dns_db_t *db, dns_dbnode_t *node,
&region, sizeof(dns_slabheader_t),
rbtdb->maxrrperset);
if (result != ISC_R_SUCCESS) {
if (result == DNS_R_TOOMANYRECORDS) {
name = dns_fixedname_initname(&fixed);
dns__rbtdb_nodefullname(db, node, name);
dns__db_logtoomanyrecords((dns_db_t *)rbtdb, name,
rdataset->type, "adding",
rbtdb->maxrrperset);
}
return result;
}