From: Ondřej Surý Date: Fri, 1 Mar 2024 07:26:07 +0000 (+0100) Subject: Add a limit to the number of RRs in RRSets X-Git-Tag: v9.18.28~9^2~18 X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=e699ef939e68c51eb3c50ecaaf0eb122216fc488;p=thirdparty%2Fbind9.git Add a limit to the number of RRs in RRSets Previously, the number of RRs in the RRSets were internally unlimited. As the data structure that holds the RRs is just a linked list, and there are places where we just walk through all of the RRs, adding an RRSet with huge number of RRs inside would slow down processing of said RRSets. Add a configurable limit to cap the number of the RRs in a single RRSet. This is enforced at the database (rbtdb, qpzone, qpcache) level and configured with new max-records-per-type configuration option that can be configured globally, per-view and per-zone. (cherry picked from commit 3fbd21f69a1bcbd26c4c00920e7b0a419e8762fc) --- diff --git a/bin/named/config.c b/bin/named/config.c index 6bf0b3ca81c..25f3715fc6b 100644 --- a/bin/named/config.c +++ b/bin/named/config.c @@ -233,6 +233,7 @@ options {\n\ ixfr-from-differences false;\n\ max-journal-size default;\n\ max-records 0;\n\ + max-records-per-type 100;\n\ max-refresh-time 2419200; /* 4 weeks */\n\ max-retry-time 1209600; /* 2 weeks */\n\ max-transfer-idle-in 60;\n\ diff --git a/bin/named/server.c b/bin/named/server.c index fe65ddbae92..843f5566d05 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -5556,6 +5556,15 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config, dns_resolver_setclientsperquery(view->resolver, cfg_obj_asuint32(obj), max_clients_per_query); + /* + * This is used for the cache and also as a default value + * for zone databases. + */ + obj = NULL; + result = named_config_get(maps, "max-records-per-type", &obj); + INSIST(result == ISC_R_SUCCESS); + dns_view_setmaxrrperset(view, cfg_obj_asuint32(obj)); + obj = NULL; result = named_config_get(maps, "max-recursion-depth", &obj); INSIST(result == ISC_R_SUCCESS); diff --git a/bin/named/zoneconf.c b/bin/named/zoneconf.c index 44c2242bdf4..e6bf9377d64 100644 --- a/bin/named/zoneconf.c +++ b/bin/named/zoneconf.c @@ -1083,6 +1083,14 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, dns_zone_setmaxrecords(zone, 0); } + obj = NULL; + result = named_config_get(maps, "max-records-per-type", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + dns_zone_setmaxrrperset(mayberaw, cfg_obj_asuint32(obj)); + if (zone != mayberaw) { + dns_zone_setmaxrrperset(zone, 0); + } + if (raw != NULL && filename != NULL) { #define SIGNED ".signed" size_t signedlen = strlen(filename) + sizeof(SIGNED); diff --git a/bin/tests/system/doth/ns2/named.conf.in b/bin/tests/system/doth/ns2/named.conf.in index e533f47e4a2..f10dac5d0fc 100644 --- a/bin/tests/system/doth/ns2/named.conf.in +++ b/bin/tests/system/doth/ns2/named.conf.in @@ -49,6 +49,7 @@ options { ixfr-from-differences yes; check-integrity no; dnssec-validation yes; + max-records-per-type 0; transfers-in 100; transfers-out 100; }; diff --git a/bin/tests/system/doth/ns3/named.conf.in b/bin/tests/system/doth/ns3/named.conf.in index cd1ab9cfa6e..cd9fc63562a 100644 --- a/bin/tests/system/doth/ns3/named.conf.in +++ b/bin/tests/system/doth/ns3/named.conf.in @@ -44,6 +44,7 @@ options { ixfr-from-differences yes; check-integrity no; dnssec-validation yes; + max-records-per-type 0; }; zone "." { diff --git a/bin/tests/system/doth/ns4/named.conf.in b/bin/tests/system/doth/ns4/named.conf.in index c7c6c91a58a..43b7c78c7a1 100644 --- a/bin/tests/system/doth/ns4/named.conf.in +++ b/bin/tests/system/doth/ns4/named.conf.in @@ -52,6 +52,7 @@ options { ixfr-from-differences yes; check-integrity no; dnssec-validation yes; + max-records-per-type 0; }; zone "." { diff --git a/bin/tests/system/doth/ns5/named.conf.in b/bin/tests/system/doth/ns5/named.conf.in index 6808618882d..93236371550 100644 --- a/bin/tests/system/doth/ns5/named.conf.in +++ b/bin/tests/system/doth/ns5/named.conf.in @@ -40,6 +40,7 @@ options { ixfr-from-differences yes; check-integrity no; dnssec-validation yes; + max-records-per-type 0; }; zone "." { diff --git a/bin/tests/system/dyndb/driver/db.c b/bin/tests/system/dyndb/driver/db.c index 96857224c2c..4e08edca5e9 100644 --- a/bin/tests/system/dyndb/driver/db.c +++ b/bin/tests/system/dyndb/driver/db.c @@ -563,28 +563,56 @@ hashsize(dns_db_t *db) { * determine which implementation of dns_db_*() function to call. */ static dns_dbmethods_t sampledb_methods = { - attach, detach, beginload, - endload, dump, currentversion, - newversion, attachversion, closeversion, - findnode, find, findzonecut, - attachnode, detachnode, expirenode, - printnode, createiterator, findrdataset, - allrdatasets, addrdataset, subtractrdataset, - deleterdataset, issecure, nodecount, - ispersistent, overmem, settask, - getoriginnode, transfernode, getnsec3parameters, - findnsec3node, setsigningtime, getsigningtime, - resigned, isdnssec, getrrsetstats, + attach, + detach, + beginload, + endload, + dump, + currentversion, + newversion, + attachversion, + closeversion, + findnode, + find, + findzonecut, + attachnode, + detachnode, + expirenode, + printnode, + createiterator, + findrdataset, + allrdatasets, + addrdataset, + subtractrdataset, + deleterdataset, + issecure, + nodecount, + ispersistent, + overmem, + settask, + getoriginnode, + transfernode, + getnsec3parameters, + findnsec3node, + setsigningtime, + getsigningtime, + resigned, + isdnssec, + getrrsetstats, NULL, /* rpz_attach */ NULL, /* rpz_ready */ - findnodeext, findext, setcachestats, - hashsize, NULL, /* nodefullname */ - NULL, /* getsize */ - NULL, /* setservestalettl */ - NULL, /* getservestalettl */ - NULL, /* setservestalerefresh */ - NULL, /* getservestalerefresh */ - NULL, /* setgluecachestats */ + findnodeext, + findext, + setcachestats, + hashsize, + NULL, /* nodefullname */ + NULL, /* getsize */ + NULL, /* setservestalettl */ + NULL, /* getservestalettl */ + NULL, /* setservestalerefresh */ + NULL, /* getservestalerefresh */ + NULL, /* setgluecachestats */ + NULL /* setmaxrrperset */ }; /* Auxiliary driver functions. */ diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst index 88faad4023c..f7cde02804d 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -3766,6 +3766,21 @@ system. This sets the maximum number of records permitted in a zone. The default is zero, which means the maximum is unlimited. +.. namedconf:statement:: max-records-per-type + :tags: server + :short: Sets the maximum number of records that can be stored in an RRset + + This sets the maximum number of resource records that can be stored + in an RRset in a database. When configured in :namedconf:ref:`options` + or :namedconf:ref:`view`, it controls the cache database; it also sets + the default value for zone databases, which can be overridden by setting + it at the :namedconf:ref:`zone` level. + + If set to a positive value, any attempt to cache or to add to a zone + an RRset with more than the specified number of records will result in + a failure. If set to 0, there is no cap on RRset size. The default is + 100. + .. namedconf:statement:: recursive-clients :tags: query :short: Specifies the maximum number of concurrent recursive queries the server can perform. diff --git a/doc/misc/mirror.zoneopt b/doc/misc/mirror.zoneopt index ac371cd6e1b..8d4a687ee8f 100644 --- a/doc/misc/mirror.zoneopt +++ b/doc/misc/mirror.zoneopt @@ -18,6 +18,7 @@ zone [ ] { max-ixfr-ratio ( unlimited | ); max-journal-size ( default | unlimited | ); max-records ; + max-records-per-type ; max-refresh-time ; max-retry-time ; max-transfer-idle-in ; diff --git a/doc/misc/options b/doc/misc/options index 56cbf323b65..4fd272224c4 100644 --- a/doc/misc/options +++ b/doc/misc/options @@ -181,6 +181,7 @@ options { max-journal-size ( default | unlimited | ); max-ncache-ttl ; max-records ; + max-records-per-type ; max-recursion-depth ; max-recursion-queries ; max-refresh-time ; @@ -471,6 +472,7 @@ view [ ] { max-journal-size ( default | unlimited | ); max-ncache-ttl ; max-records ; + max-records-per-type ; max-recursion-depth ; max-recursion-queries ; max-refresh-time ; diff --git a/doc/misc/primary.zoneopt b/doc/misc/primary.zoneopt index 8f646e3560f..e8ef80cf6ea 100644 --- a/doc/misc/primary.zoneopt +++ b/doc/misc/primary.zoneopt @@ -38,6 +38,7 @@ zone [ ] { max-ixfr-ratio ( unlimited | ); max-journal-size ( default | unlimited | ); max-records ; + max-records-per-type ; max-transfer-idle-out ; max-transfer-time-out ; max-zone-ttl ( unlimited | ); diff --git a/doc/misc/redirect.zoneopt b/doc/misc/redirect.zoneopt index bcd9a571ea4..613dbdb890e 100644 --- a/doc/misc/redirect.zoneopt +++ b/doc/misc/redirect.zoneopt @@ -7,6 +7,7 @@ zone [ ] { masterfile-format ( raw | text ); masterfile-style ( full | relative ); max-records ; + max-records-per-type ; max-zone-ttl ( unlimited | ); primaries [ port ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; zone-statistics ( full | terse | none | ); diff --git a/doc/misc/secondary.zoneopt b/doc/misc/secondary.zoneopt index 3237aab04d2..fc1c8526db5 100644 --- a/doc/misc/secondary.zoneopt +++ b/doc/misc/secondary.zoneopt @@ -30,6 +30,7 @@ zone [ ] { max-ixfr-ratio ( unlimited | ); max-journal-size ( default | unlimited | ); max-records ; + max-records-per-type ; max-refresh-time ; max-retry-time ; max-transfer-idle-in ; diff --git a/doc/misc/static-stub.zoneopt b/doc/misc/static-stub.zoneopt index 5357528e6fb..706fa3cc88e 100644 --- a/doc/misc/static-stub.zoneopt +++ b/doc/misc/static-stub.zoneopt @@ -5,6 +5,7 @@ zone [ ] { forward ( first | only ); forwarders [ port ] { ( | ) [ port ]; ... }; max-records ; + max-records-per-type ; server-addresses { ( | ); ... }; server-names { ; ... }; zone-statistics ( full | terse | none | ); diff --git a/doc/misc/stub.zoneopt b/doc/misc/stub.zoneopt index 29c1d56e3f3..c1c5d68566a 100644 --- a/doc/misc/stub.zoneopt +++ b/doc/misc/stub.zoneopt @@ -12,6 +12,7 @@ zone [ ] { masterfile-format ( raw | text ); masterfile-style ( full | relative ); max-records ; + max-records-per-type ; max-refresh-time ; max-retry-time ; max-transfer-idle-in ; diff --git a/lib/dns/cache.c b/lib/dns/cache.c index cb7f35a95fd..0669f4a9f63 100644 --- a/lib/dns/cache.c +++ b/lib/dns/cache.c @@ -146,6 +146,7 @@ struct dns_cache { dns_ttl_t serve_stale_ttl; dns_ttl_t serve_stale_refresh; isc_stats_t *stats; + uint32_t maxrrperset; }; /*** @@ -210,6 +211,7 @@ cache_create_db(dns_cache_t *cache, dns_db_t **dbp, isc_mem_t **tmctxp, dns_db_setservestalettl(db, cache->serve_stale_ttl); dns_db_setservestalerefresh(db, cache->serve_stale_refresh); + dns_db_setmaxrrperset(db, cache->maxrrperset); if (cache->taskmgr == NULL) { *dbp = db; @@ -1239,6 +1241,16 @@ dns_cache_updatestats(dns_cache_t *cache, isc_result_t result) { } } +void +dns_cache_setmaxrrperset(dns_cache_t *cache, uint32_t value) { + REQUIRE(VALID_CACHE(cache)); + + cache->maxrrperset = value; + if (cache->db != NULL) { + dns_db_setmaxrrperset(cache->db, value); + } +} + /* * XXX: Much of the following code has been copied in from statschannel.c. * We should refactor this into a generic function in stats.c that can be diff --git a/lib/dns/db.c b/lib/dns/db.c index 653b29ddec6..09bf9394b5b 100644 --- a/lib/dns/db.c +++ b/lib/dns/db.c @@ -1121,3 +1121,12 @@ dns_db_setgluecachestats(dns_db_t *db, isc_stats_t *stats) { return (ISC_R_NOTIMPLEMENTED); } + +void +dns_db_setmaxrrperset(dns_db_t *db, uint32_t value) { + REQUIRE(DNS_DB_VALID(db)); + + if (db->methods->setmaxrrperset != NULL) { + (db->methods->setmaxrrperset)(db, value); + } +} diff --git a/lib/dns/dnsrps.c b/lib/dns/dnsrps.c index d4a1c65e7f8..a8a6f9c6a3d 100644 --- a/lib/dns/dnsrps.c +++ b/lib/dns/dnsrps.c @@ -975,6 +975,7 @@ static dns_dbmethods_t rpsdb_db_methods = { NULL, /* setservestalerefresh */ NULL, /* getservestalerefresh */ NULL, /* setgluecachestats */ + NULL /* setmaxrrperset */ }; static dns_rdatasetmethods_t rpsdb_rdataset_methods = { diff --git a/lib/dns/include/dns/cache.h b/lib/dns/include/dns/cache.h index e5c6e49be93..10911109265 100644 --- a/lib/dns/include/dns/cache.h +++ b/lib/dns/include/dns/cache.h @@ -280,6 +280,12 @@ dns_cache_updatestats(dns_cache_t *cache, isc_result_t result); * Update cache statistics based on result code in 'result' */ +void +dns_cache_setmaxrrperset(dns_cache_t *cache, uint32_t value); +/*%< + * Set the maximum resource records per RRSet that can be cached. + */ + #ifdef HAVE_LIBXML2 int dns_cache_renderxml(dns_cache_t *cache, void *writer0); diff --git a/lib/dns/include/dns/db.h b/lib/dns/include/dns/db.h index 584186872db..4157ae4ed98 100644 --- a/lib/dns/include/dns/db.h +++ b/lib/dns/include/dns/db.h @@ -185,6 +185,7 @@ typedef struct dns_dbmethods { isc_result_t (*setservestalerefresh)(dns_db_t *db, uint32_t interval); isc_result_t (*getservestalerefresh)(dns_db_t *db, uint32_t *interval); isc_result_t (*setgluecachestats)(dns_db_t *db, isc_stats_t *stats); + void (*setmaxrrperset)(dns_db_t *db, uint32_t value); } dns_dbmethods_t; typedef isc_result_t (*dns_dbcreatefunc_t)(isc_mem_t *mctx, @@ -1759,4 +1760,11 @@ dns_db_setgluecachestats(dns_db_t *db, isc_stats_t *stats); * dns_rdatasetstats_create(); otherwise NULL. */ +void +dns_db_setmaxrrperset(dns_db_t *db, uint32_t value); +/*%< + * Set the maximum permissible number of RRs per RRset. If 'value' + * is nonzero, then any subsequent attempt to add an rdataset with + * more than 'value' RRs will return ISC_R_NOSPACE. + */ ISC_LANG_ENDDECLS diff --git a/lib/dns/include/dns/rdataslab.h b/lib/dns/include/dns/rdataslab.h index 7364b8d6448..5729c004caa 100644 --- a/lib/dns/include/dns/rdataslab.h +++ b/lib/dns/include/dns/rdataslab.h @@ -66,7 +66,8 @@ ISC_LANG_BEGINDECLS isc_result_t dns_rdataslab_fromrdataset(dns_rdataset_t *rdataset, isc_mem_t *mctx, - isc_region_t *region, unsigned int reservelen); + isc_region_t *region, unsigned int reservelen, + uint32_t limit); /*%< * Slabify a rdataset. The slab area will be allocated and returned * in 'region'. @@ -122,7 +123,8 @@ isc_result_t dns_rdataslab_merge(unsigned char *oslab, unsigned char *nslab, unsigned int reservelen, isc_mem_t *mctx, dns_rdataclass_t rdclass, dns_rdatatype_t type, - unsigned int flags, unsigned char **tslabp); + unsigned int flags, uint32_t maxrrperset, + unsigned char **tslabp); /*%< * Merge 'oslab' and 'nslab'. */ diff --git a/lib/dns/include/dns/view.h b/lib/dns/include/dns/view.h index 18b0b3394a0..38ebb922dc8 100644 --- a/lib/dns/include/dns/view.h +++ b/lib/dns/include/dns/view.h @@ -191,6 +191,7 @@ struct dns_view { dns_dlzdblist_t dlz_unsearched; uint32_t fail_ttl; dns_badcache_t *failcache; + uint32_t maxrrperset; /* * Configurable data for server use only, @@ -1413,4 +1414,10 @@ dns_view_sfd_find(dns_view_t *view, const dns_name_t *name, *\li 'foundname' to be valid with a buffer sufficient to hold the name. */ +void +dns_view_setmaxrrperset(dns_view_t *view, uint32_t value); +/*%< + * Set the maximum resource records per RRSet that can be cached. + */ + ISC_LANG_ENDDECLS diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index 7699a577b9b..25761194154 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -363,6 +363,19 @@ dns_zone_getmaxrecords(dns_zone_t *zone); *\li uint32_t maxrecords. */ +void +dns_zone_setmaxrrperset(dns_zone_t *zone, uint32_t maxrrperset); +/*%< + * Sets the maximum number of records per rrset permitted in a zone. + * 0 implies unlimited. + * + * Requires: + *\li 'zone' to be valid initialised zone. + * + * Returns: + *\li void + */ + void dns_zone_setmaxttl(dns_zone_t *zone, uint32_t maxttl); /*%< diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c index 43f05742f72..7b2e62d7c82 100644 --- a/lib/dns/rbtdb.c +++ b/lib/dns/rbtdb.c @@ -462,6 +462,7 @@ struct dns_rbtdb { rbtdb_serial_t current_serial; rbtdb_serial_t least_serial; rbtdb_serial_t next_serial; + uint32_t maxrrperset; rbtdb_version_t *current_version; rbtdb_version_t *future_version; rbtdb_versionlist_t open_versions; @@ -6486,7 +6487,7 @@ find_header: rbtdb->common.mctx, rbtdb->common.rdclass, (dns_rdatatype_t)header->type, flags, - &merged); + rbtdb->maxrrperset, &merged); } if (result == ISC_R_SUCCESS) { /* @@ -6825,7 +6826,7 @@ delegating_type(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, static isc_result_t addnoqname(dns_rbtdb_t *rbtdb, rdatasetheader_t *newheader, - dns_rdataset_t *rdataset) { + uint32_t maxrrperset, dns_rdataset_t *rdataset) { struct noqname *noqname; isc_mem_t *mctx = rbtdb->common.mctx; dns_name_t name; @@ -6846,12 +6847,12 @@ addnoqname(dns_rbtdb_t *rbtdb, rdatasetheader_t *newheader, noqname->negsig = NULL; noqname->type = neg.type; dns_name_dup(&name, mctx, &noqname->name); - result = dns_rdataslab_fromrdataset(&neg, mctx, &r, 0); + result = dns_rdataslab_fromrdataset(&neg, mctx, &r, 0, maxrrperset); if (result != ISC_R_SUCCESS) { goto cleanup; } noqname->neg = r.base; - result = dns_rdataslab_fromrdataset(&negsig, mctx, &r, 0); + result = dns_rdataslab_fromrdataset(&negsig, mctx, &r, 0, maxrrperset); if (result != ISC_R_SUCCESS) { goto cleanup; } @@ -6870,7 +6871,7 @@ cleanup: static isc_result_t addclosest(dns_rbtdb_t *rbtdb, rdatasetheader_t *newheader, - dns_rdataset_t *rdataset) { + uint32_t maxrrperset, dns_rdataset_t *rdataset) { struct noqname *closest; isc_mem_t *mctx = rbtdb->common.mctx; dns_name_t name; @@ -6891,12 +6892,12 @@ addclosest(dns_rbtdb_t *rbtdb, rdatasetheader_t *newheader, closest->negsig = NULL; closest->type = neg.type; dns_name_dup(&name, mctx, &closest->name); - result = dns_rdataslab_fromrdataset(&neg, mctx, &r, 0); + result = dns_rdataslab_fromrdataset(&neg, mctx, &r, 0, maxrrperset); if (result != ISC_R_SUCCESS) { goto cleanup; } closest->neg = r.base; - result = dns_rdataslab_fromrdataset(&negsig, mctx, &r, 0); + result = dns_rdataslab_fromrdataset(&negsig, mctx, &r, 0, maxrrperset); if (result != ISC_R_SUCCESS) { goto cleanup; } @@ -6977,7 +6978,8 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, } result = dns_rdataslab_fromrdataset(rdataset, rbtdb->common.mctx, - ®ion, sizeof(rdatasetheader_t)); + ®ion, sizeof(rdatasetheader_t), + rbtdb->maxrrperset); if (result != ISC_R_SUCCESS) { return (result); } @@ -7035,7 +7037,8 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, RDATASET_ATTR_SET(newheader, RDATASET_ATTR_OPTOUT); } if ((rdataset->attributes & DNS_RDATASETATTR_NOQNAME) != 0) { - result = addnoqname(rbtdb, newheader, rdataset); + result = addnoqname(rbtdb, newheader, + rbtdb->maxrrperset, rdataset); if (result != ISC_R_SUCCESS) { free_rdataset(rbtdb, rbtdb->common.mctx, newheader); @@ -7043,7 +7046,8 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, } } if ((rdataset->attributes & DNS_RDATASETATTR_CLOSEST) != 0) { - result = addclosest(rbtdb, newheader, rdataset); + result = addclosest(rbtdb, newheader, + rbtdb->maxrrperset, rdataset); if (result != ISC_R_SUCCESS) { free_rdataset(rbtdb, rbtdb->common.mctx, newheader); @@ -7188,7 +7192,8 @@ subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, nodefullname(db, node, nodename); result = dns_rdataslab_fromrdataset(rdataset, rbtdb->common.mctx, - ®ion, sizeof(rdatasetheader_t)); + ®ion, sizeof(rdatasetheader_t), + 0); if (result != ISC_R_SUCCESS) { return (result); } @@ -7570,7 +7575,8 @@ loading_addrdataset(void *arg, const dns_name_t *name, } result = dns_rdataslab_fromrdataset(rdataset, rbtdb->common.mctx, - ®ion, sizeof(rdatasetheader_t)); + ®ion, sizeof(rdatasetheader_t), + rbtdb->maxrrperset); if (result != ISC_R_SUCCESS) { return (result); } @@ -8112,6 +8118,15 @@ setgluecachestats(dns_db_t *db, isc_stats_t *stats) { return (ISC_R_SUCCESS); } +static void +setmaxrrperset(dns_db_t *db, uint32_t maxrrperset) { + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; + + REQUIRE(VALID_RBTDB(rbtdb)); + + rbtdb->maxrrperset = maxrrperset; +} + static dns_stats_t * getrrsetstats(dns_db_t *db) { dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; @@ -8233,7 +8248,8 @@ static dns_dbmethods_t zone_methods = { attach, NULL, /* getservestalettl */ NULL, /* setservestalerefresh */ NULL, /* getservestalerefresh */ - setgluecachestats }; + setgluecachestats, + setmaxrrperset }; static dns_dbmethods_t cache_methods = { attach, detach, @@ -8283,7 +8299,8 @@ static dns_dbmethods_t cache_methods = { attach, getservestalettl, setservestalerefresh, getservestalerefresh, - NULL }; + NULL, + setmaxrrperset }; isc_result_t dns_rbtdb_create(isc_mem_t *mctx, const dns_name_t *origin, dns_dbtype_t type, diff --git a/lib/dns/rdataslab.c b/lib/dns/rdataslab.c index 2b4cc4bed3f..1b228f6ac39 100644 --- a/lib/dns/rdataslab.c +++ b/lib/dns/rdataslab.c @@ -114,7 +114,8 @@ fillin_offsets(unsigned char *offsetbase, unsigned int *offsettable, isc_result_t dns_rdataslab_fromrdataset(dns_rdataset_t *rdataset, isc_mem_t *mctx, - isc_region_t *region, unsigned int reservelen) { + isc_region_t *region, unsigned int reservelen, + uint32_t maxrrperset) { /* * Use &removed as a sentinel pointer for duplicate * rdata as rdata.data == NULL is valid. @@ -156,6 +157,10 @@ dns_rdataslab_fromrdataset(dns_rdataset_t *rdataset, isc_mem_t *mctx, return (ISC_R_SUCCESS); } + if (maxrrperset > 0 && nitems > maxrrperset) { + return (DNS_R_TOOMANYRECORDS); + } + if (nitems > 0xffff) { return (ISC_R_NOSPACE); } @@ -484,7 +489,8 @@ isc_result_t dns_rdataslab_merge(unsigned char *oslab, unsigned char *nslab, unsigned int reservelen, isc_mem_t *mctx, dns_rdataclass_t rdclass, dns_rdatatype_t type, - unsigned int flags, unsigned char **tslabp) { + unsigned int flags, uint32_t maxrrperset, + unsigned char **tslabp) { unsigned char *ocurrent, *ostart, *ncurrent, *tstart, *tcurrent, *data; unsigned int ocount, ncount, count, olength, tlength, tcount, length; dns_rdata_t ordata = DNS_RDATA_INIT; @@ -524,6 +530,10 @@ dns_rdataslab_merge(unsigned char *oslab, unsigned char *nslab, #endif /* if DNS_RDATASET_FIXED */ INSIST(ocount > 0 && ncount > 0); + if (maxrrperset > 0 && ocount + ncount > maxrrperset) { + return (DNS_R_TOOMANYRECORDS); + } + #if DNS_RDATASET_FIXED oncount = ncount; #endif /* if DNS_RDATASET_FIXED */ diff --git a/lib/dns/sdb.c b/lib/dns/sdb.c index b58080a2fae..3d841e35114 100644 --- a/lib/dns/sdb.c +++ b/lib/dns/sdb.c @@ -1270,20 +1270,33 @@ settask(dns_db_t *db, isc_task_t *task, isc_task_t *prunetask) { } static dns_dbmethods_t sdb_methods = { - attach, detach, - beginload, endload, - dump, currentversion, - newversion, attachversion, - closeversion, NULL, /* findnode */ - NULL, /* find */ - findzonecut, attachnode, - detachnode, expirenode, - printnode, createiterator, - findrdataset, allrdatasets, - addrdataset, subtractrdataset, - deleterdataset, issecure, - nodecount, ispersistent, - overmem, settask, + attach, + detach, + beginload, + endload, + dump, + currentversion, + newversion, + attachversion, + closeversion, + NULL, /* findnode */ + NULL, /* find */ + findzonecut, + attachnode, + detachnode, + expirenode, + printnode, + createiterator, + findrdataset, + allrdatasets, + addrdataset, + subtractrdataset, + deleterdataset, + issecure, + nodecount, + ispersistent, + overmem, + settask, getoriginnode, /* getoriginnode */ NULL, /* transfernode */ NULL, /* getnsec3parameters */ @@ -1295,7 +1308,8 @@ static dns_dbmethods_t sdb_methods = { NULL, /* getrrsetstats */ NULL, /* rpz_attach */ NULL, /* rpz_ready */ - findnodeext, findext, + findnodeext, + findext, NULL, /* setcachestats */ NULL, /* hashsize */ NULL, /* nodefullname */ @@ -1305,6 +1319,7 @@ static dns_dbmethods_t sdb_methods = { NULL, /* setservestalerefresh */ NULL, /* getservestalerefresh */ NULL, /* setgluecachestats */ + NULL /* setmaxrrperset */ }; static isc_result_t diff --git a/lib/dns/sdlz.c b/lib/dns/sdlz.c index ff674488658..2568485d3e3 100644 --- a/lib/dns/sdlz.c +++ b/lib/dns/sdlz.c @@ -1243,34 +1243,56 @@ getoriginnode(dns_db_t *db, dns_dbnode_t **nodep) { } static dns_dbmethods_t sdlzdb_methods = { - attach, detach, beginload, - endload, dump, currentversion, - newversion, attachversion, closeversion, - findnode, find, findzonecut, - attachnode, detachnode, expirenode, - printnode, createiterator, findrdataset, - allrdatasets, addrdataset, subtractrdataset, - deleterdataset, issecure, nodecount, - ispersistent, overmem, settask, - getoriginnode, NULL, /* transfernode */ - NULL, /* getnsec3parameters */ - NULL, /* findnsec3node */ - NULL, /* setsigningtime */ - NULL, /* getsigningtime */ - NULL, /* resigned */ - NULL, /* isdnssec */ - NULL, /* getrrsetstats */ - NULL, /* rpz_attach */ - NULL, /* rpz_ready */ - findnodeext, findext, NULL, /* setcachestats */ - NULL, /* hashsize */ - NULL, /* nodefullname */ - NULL, /* getsize */ - NULL, /* setservestalettl */ - NULL, /* getservestalettl */ - NULL, /* setservestalerefresh */ - NULL, /* getservestalerefresh */ - NULL, /* setgluecachestats */ + attach, + detach, + beginload, + endload, + dump, + currentversion, + newversion, + attachversion, + closeversion, + findnode, + find, + findzonecut, + attachnode, + detachnode, + expirenode, + printnode, + createiterator, + findrdataset, + allrdatasets, + addrdataset, + subtractrdataset, + deleterdataset, + issecure, + nodecount, + ispersistent, + overmem, + settask, + getoriginnode, + NULL, /* transfernode */ + NULL, /* getnsec3parameters */ + NULL, /* findnsec3node */ + NULL, /* setsigningtime */ + NULL, /* getsigningtime */ + NULL, /* resigned */ + NULL, /* isdnssec */ + NULL, /* getrrsetstats */ + NULL, /* rpz_attach */ + NULL, /* rpz_ready */ + findnodeext, + findext, + NULL, /* setcachestats */ + NULL, /* hashsize */ + NULL, /* nodefullname */ + NULL, /* getsize */ + NULL, /* setservestalettl */ + NULL, /* getservestalettl */ + NULL, /* setservestalerefresh */ + NULL, /* getservestalerefresh */ + NULL, /* setgluecachestats */ + NULL /* setmaxrrperset */ }; /* diff --git a/lib/dns/view.c b/lib/dns/view.c index 49c9aee9416..66c7d85cf73 100644 --- a/lib/dns/view.c +++ b/lib/dns/view.c @@ -892,6 +892,8 @@ dns_view_setcache(dns_view_t *view, dns_cache_t *cache, bool shared) { dns_cache_attach(cache, &view->cache); dns_cache_attachdb(cache, &view->cachedb); INSIST(DNS_DB_VALID(view->cachedb)); + + dns_cache_setmaxrrperset(view->cache, view->maxrrperset); } bool @@ -2759,3 +2761,12 @@ dns_view_sfd_find(dns_view_t *view, const dns_name_t *name, dns_name_copy(dns_rootname, foundname); } } + +void +dns_view_setmaxrrperset(dns_view_t *view, uint32_t value) { + REQUIRE(DNS_VIEW_VALID(view)); + view->maxrrperset = value; + if (view->cache != NULL) { + dns_cache_setmaxrrperset(view->cache, value); + } +} diff --git a/lib/dns/zone.c b/lib/dns/zone.c index cf714779643..7f81c9b4399 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -309,6 +309,7 @@ struct dns_zone { uint32_t minretry; uint32_t maxrecords; + uint32_t maxrrperset; isc_sockaddr_t *primaries; dns_name_t **primarykeynames; @@ -12298,6 +12299,16 @@ dns_zone_setmaxrecords(dns_zone_t *zone, uint32_t val) { zone->maxrecords = val; } +void +dns_zone_setmaxrrperset(dns_zone_t *zone, uint32_t val) { + REQUIRE(DNS_ZONE_VALID(zone)); + + zone->maxrrperset = val; + if (zone->db != NULL) { + dns_db_setmaxrrperset(zone->db, val); + } +} + static bool notify_isqueued(dns_zone_t *zone, unsigned int flags, dns_name_t *name, isc_sockaddr_t *addr, dns_tsigkey_t *key, @@ -14786,6 +14797,7 @@ ns_query(dns_zone_t *zone, dns_rdataset_t *soardataset, dns_stub_t *stub) { goto cleanup; } dns_db_settask(stub->db, zone->task, zone->task); + dns_db_setmaxrrperset(stub->db, zone->maxrrperset); } result = dns_db_newversion(stub->db, &stub->version); @@ -17890,6 +17902,7 @@ zone_replacedb(dns_zone_t *zone, dns_db_t *db, bool dump) { } zone_attachdb(zone, db); dns_db_settask(zone->db, zone->task, zone->task); + dns_db_setmaxrrperset(zone->db, zone->maxrrperset); DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED | DNS_ZONEFLG_NEEDNOTIFY); return (ISC_R_SUCCESS); @@ -24315,6 +24328,7 @@ dns_zone_makedb(dns_zone_t *zone, dns_db_t **dbp) { } dns_db_settask(db, zone->task, zone->task); + dns_db_setmaxrrperset(db, zone->maxrrperset); *dbp = db; diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index 7bfd8f9bde8..7cbdab60d0a 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -2300,6 +2300,9 @@ static cfg_clausedef_t zone_clauses[] = { { "max-records", &cfg_type_uint32, CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR | CFG_ZONE_STUB | CFG_ZONE_STATICSTUB | CFG_ZONE_REDIRECT }, + { "max-records-per-type", &cfg_type_uint32, + CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR | + CFG_ZONE_STUB | CFG_ZONE_STATICSTUB | CFG_ZONE_REDIRECT }, { "max-refresh-time", &cfg_type_uint32, CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR | CFG_ZONE_STUB }, { "max-retry-time", &cfg_type_uint32,