From 2df11e5a549a71a8f792ac5207254b6a9e09859c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Fri, 1 Mar 2024 08:26:07 +0100 Subject: [PATCH] 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) (cherry picked from commit f63d72fb7e5813585c92d7f92bdcc5885cd04edc) --- bin/named/config.c | 1 + bin/named/server.c | 9 ++++ bin/named/zoneconf.c | 8 ++++ bin/tests/system/dyndb/driver/db.c | 3 +- doc/arm/reference.rst | 12 +++++ doc/misc/master.zoneopt | 2 + doc/misc/mirror.zoneopt | 2 + doc/misc/options | 44 +++++++++++------- doc/misc/options.active | 44 +++++++++++------- doc/misc/redirect.zoneopt | 2 + doc/misc/slave.zoneopt | 2 + doc/misc/static-stub.zoneopt | 2 + doc/misc/stub.zoneopt | 2 + lib/dns/cache.c | 13 ++++++ lib/dns/db.c | 9 ++++ lib/dns/dnsrps.c | 3 +- lib/dns/ecdb.c | 8 +++- lib/dns/include/dns/cache.h | 6 +++ lib/dns/include/dns/db.h | 8 ++++ lib/dns/include/dns/rdataslab.h | 6 ++- lib/dns/include/dns/view.h | 7 +++ lib/dns/include/dns/zone.h | 26 +++++++++++ lib/dns/rbtdb.c | 45 ++++++++++++------ lib/dns/rdataslab.c | 10 ++-- lib/dns/sdb.c | 3 +- lib/dns/sdlz.c | 3 +- lib/dns/view.c | 11 +++++ lib/dns/xfrin.c | 24 ++-------- lib/dns/zone.c | 74 ++++++++++++++++++++++-------- lib/isccfg/namedconf.c | 3 ++ 30 files changed, 296 insertions(+), 96 deletions(-) diff --git a/bin/named/config.c b/bin/named/config.c index c37015f3b2..9cba6f588b 100644 --- a/bin/named/config.c +++ b/bin/named/config.c @@ -215,6 +215,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 8fd9ee7e5a..7bf5f2664d 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -5418,6 +5418,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 97c2094ca2..ae5cc656ee 100644 --- a/bin/named/zoneconf.c +++ b/bin/named/zoneconf.c @@ -1092,6 +1092,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/dyndb/driver/db.c b/bin/tests/system/dyndb/driver/db.c index 77d335e2ea..6725a3bacd 100644 --- a/bin/tests/system/dyndb/driver/db.c +++ b/bin/tests/system/dyndb/driver/db.c @@ -592,7 +592,8 @@ static dns_dbmethods_t sampledb_methods = { NULL, /* setservestalerefresh */ NULL, /* getservestalerefresh */ NULL, /* setgluecachestats */ - NULL /* adjusthashsize */ + NULL, /* adjusthashsize */ + NULL /* setmaxrrperset */ }; /* Auxiliary driver functions. */ diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst index ad7ff2761a..b1983ef30d 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -2890,6 +2890,18 @@ system. This sets the maximum number of records permitted in a zone. The default is zero, which means the maximum is unlimited. +``max-records-per-type`` + This sets the maximum number of resource records that can be stored + in an RRset in a database. When configured in ``options`` + or ``view``, it controls the cache database; it also sets + the default value for zone databases, which can be overridden by setting + it at the ``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. + ``recursive-clients`` This sets the maximum number (a "hard quota") of simultaneous recursive lookups the server performs on behalf of clients. The default is diff --git a/doc/misc/master.zoneopt b/doc/misc/master.zoneopt index cc8a33b56f..864a852ef4 100644 --- a/doc/misc/master.zoneopt +++ b/doc/misc/master.zoneopt @@ -38,8 +38,10 @@ 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-types-per-name ; max-zone-ttl ( unlimited | ); notify ( explicit | master-only | primary-only | ); notify-delay ; diff --git a/doc/misc/mirror.zoneopt b/doc/misc/mirror.zoneopt index 3d45a3d1e5..701edce540 100644 --- a/doc/misc/mirror.zoneopt +++ b/doc/misc/mirror.zoneopt @@ -19,12 +19,14 @@ 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 ; max-transfer-idle-out ; max-transfer-time-in ; max-transfer-time-out ; + max-types-per-name ; min-refresh-time ; min-retry-time ; multi-master ; diff --git a/doc/misc/options b/doc/misc/options index 90d07f5a56..ee152b8179 100644 --- a/doc/misc/options +++ b/doc/misc/options @@ -170,13 +170,16 @@ options { dnssec-secure-to-insecure ; dnssec-update-mode ( maintain | no-resign ); dnssec-validation ( yes | no | auto ); - dnstap { ( all | auth | client | forwarder | resolver | update ) [ - ( query | response ) ]; ... }; - dnstap-identity ( | none | hostname ); - dnstap-output ( file | unix ) [ size ( unlimited | - ) ] [ versions ( unlimited | ) ] [ suffix ( - increment | timestamp ) ]; - dnstap-version ( | none ); + dnstap { ( all | auth | client | forwarder | + resolver | update ) [ ( query | response ) ]; + ... }; // not configured + dnstap-identity ( | none | + hostname ); // not configured + dnstap-output ( file | unix ) [ + size ( unlimited | ) ] [ versions ( + unlimited | ) ] [ suffix ( increment + | timestamp ) ]; // not configured + dnstap-version ( | none ); // not configured dscp ; dual-stack-servers [ port ] { ( [ port ] [ dscp ] | [ port @@ -200,13 +203,13 @@ options { forward ( first | only ); forwarders [ port ] [ dscp ] { ( | ) [ port ] [ dscp ]; ... }; - fstrm-set-buffer-hint ; - fstrm-set-flush-timeout ; - fstrm-set-input-queue-size ; - fstrm-set-output-notify-threshold ; - fstrm-set-output-queue-model ( mpsc | spsc ); - fstrm-set-output-queue-size ; - fstrm-set-reopen-interval ; + fstrm-set-buffer-hint ; // not configured + fstrm-set-flush-timeout ; // not configured + fstrm-set-input-queue-size ; // not configured + fstrm-set-output-notify-threshold ; // not configured + fstrm-set-output-queue-model ( mpsc | spsc ); // not configured + fstrm-set-output-queue-size ; // not configured + fstrm-set-reopen-interval ; // not configured geoip-directory ( | none ); geoip-use-ecs ; // obsolete glue-cache ; @@ -243,6 +246,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 ; @@ -253,6 +257,7 @@ options { max-transfer-idle-out ; max-transfer-time-in ; max-transfer-time-out ; + max-types-per-name ; max-udp-size ; max-zone-ttl ( unlimited | ); memstatistics ; @@ -569,8 +574,9 @@ view [ ] { dnssec-secure-to-insecure ; dnssec-update-mode ( maintain | no-resign ); dnssec-validation ( yes | no | auto ); - dnstap { ( all | auth | client | forwarder | resolver | update ) [ - ( query | response ) ]; ... }; + dnstap { ( all | auth | client | forwarder | + resolver | update ) [ ( query | response ) ]; + ... }; // not configured dual-stack-servers [ port ] { ( [ port ] [ dscp ] | [ port ] [ dscp ] | [ port @@ -622,6 +628,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 ; @@ -631,6 +638,7 @@ view [ ] { max-transfer-idle-out ; max-transfer-time-in ; max-transfer-time-out ; + max-types-per-name ; max-udp-size ; max-zone-ttl ( unlimited | ); message-compression ; @@ -854,12 +862,14 @@ view [ ] { 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 ; max-transfer-idle-out ; max-transfer-time-in ; max-transfer-time-out ; + max-types-per-name ; max-zone-ttl ( unlimited | ); min-refresh-time ; min-retry-time ; @@ -971,12 +981,14 @@ 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 ; max-transfer-idle-out ; max-transfer-time-in ; max-transfer-time-out ; + max-types-per-name ; max-zone-ttl ( unlimited | ); min-refresh-time ; min-retry-time ; diff --git a/doc/misc/options.active b/doc/misc/options.active index 0229d8da6d..ad4e29a9ba 100644 --- a/doc/misc/options.active +++ b/doc/misc/options.active @@ -156,13 +156,16 @@ options { dnssec-secure-to-insecure ; dnssec-update-mode ( maintain | no-resign ); dnssec-validation ( yes | no | auto ); - dnstap { ( all | auth | client | forwarder | resolver | update ) [ - ( query | response ) ]; ... }; - dnstap-identity ( | none | hostname ); - dnstap-output ( file | unix ) [ size ( unlimited | - ) ] [ versions ( unlimited | ) ] [ suffix ( - increment | timestamp ) ]; - dnstap-version ( | none ); + dnstap { ( all | auth | client | forwarder | + resolver | update ) [ ( query | response ) ]; + ... }; // not configured + dnstap-identity ( | none | + hostname ); // not configured + dnstap-output ( file | unix ) [ + size ( unlimited | ) ] [ versions ( + unlimited | ) ] [ suffix ( increment + | timestamp ) ]; // not configured + dnstap-version ( | none ); // not configured dscp ; dual-stack-servers [ port ] { ( [ port ] [ dscp ] | [ port @@ -181,13 +184,13 @@ options { forward ( first | only ); forwarders [ port ] [ dscp ] { ( | ) [ port ] [ dscp ]; ... }; - fstrm-set-buffer-hint ; - fstrm-set-flush-timeout ; - fstrm-set-input-queue-size ; - fstrm-set-output-notify-threshold ; - fstrm-set-output-queue-model ( mpsc | spsc ); - fstrm-set-output-queue-size ; - fstrm-set-reopen-interval ; + fstrm-set-buffer-hint ; // not configured + fstrm-set-flush-timeout ; // not configured + fstrm-set-input-queue-size ; // not configured + fstrm-set-output-notify-threshold ; // not configured + fstrm-set-output-queue-model ( mpsc | spsc ); // not configured + fstrm-set-output-queue-size ; // not configured + fstrm-set-reopen-interval ; // not configured geoip-directory ( | none ); glue-cache ; heartbeat-interval ; @@ -217,6 +220,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 ; @@ -227,6 +231,7 @@ options { max-transfer-idle-out ; max-transfer-time-in ; max-transfer-time-out ; + max-types-per-name ; max-udp-size ; max-zone-ttl ( unlimited | ); memstatistics ; @@ -513,8 +518,9 @@ view [ ] { dnssec-secure-to-insecure ; dnssec-update-mode ( maintain | no-resign ); dnssec-validation ( yes | no | auto ); - dnstap { ( all | auth | client | forwarder | resolver | update ) [ - ( query | response ) ]; ... }; + dnstap { ( all | auth | client | forwarder | + resolver | update ) [ ( query | response ) ]; + ... }; // not configured dual-stack-servers [ port ] { ( [ port ] [ dscp ] | [ port ] [ dscp ] | [ port @@ -559,6 +565,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 ; @@ -568,6 +575,7 @@ view [ ] { max-transfer-idle-out ; max-transfer-time-in ; max-transfer-time-out ; + max-types-per-name ; max-udp-size ; max-zone-ttl ( unlimited | ); message-compression ; @@ -774,12 +782,14 @@ view [ ] { 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 ; max-transfer-idle-out ; max-transfer-time-in ; max-transfer-time-out ; + max-types-per-name ; max-zone-ttl ( unlimited | ); min-refresh-time ; min-retry-time ; @@ -884,12 +894,14 @@ 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 ; max-transfer-idle-out ; max-transfer-time-in ; max-transfer-time-out ; + max-types-per-name ; max-zone-ttl ( unlimited | ); min-refresh-time ; min-retry-time ; diff --git a/doc/misc/redirect.zoneopt b/doc/misc/redirect.zoneopt index 6a5ef660a2..c4ab59ddad 100644 --- a/doc/misc/redirect.zoneopt +++ b/doc/misc/redirect.zoneopt @@ -8,6 +8,8 @@ zone [ ] { masterfile-style ( full | relative ); masters [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ]; ... }; max-records ; + max-records-per-type ; + max-types-per-name ; max-zone-ttl ( unlimited | ); primaries [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ]; ... }; zone-statistics ( full | terse | none | ); diff --git a/doc/misc/slave.zoneopt b/doc/misc/slave.zoneopt index 8cb7c3143f..ccd88e5c15 100644 --- a/doc/misc/slave.zoneopt +++ b/doc/misc/slave.zoneopt @@ -31,12 +31,14 @@ 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 ; max-transfer-idle-out ; max-transfer-time-in ; max-transfer-time-out ; + max-types-per-name ; min-refresh-time ; min-retry-time ; multi-master ; diff --git a/doc/misc/static-stub.zoneopt b/doc/misc/static-stub.zoneopt index f89d46248a..102b980c7a 100644 --- a/doc/misc/static-stub.zoneopt +++ b/doc/misc/static-stub.zoneopt @@ -5,6 +5,8 @@ zone [ ] { forward ( first | only ); forwarders [ port ] [ dscp ] { ( | ) [ port ] [ dscp ]; ... }; max-records ; + max-records-per-type ; + max-types-per-name ; server-addresses { ( | ); ... }; server-names { ; ... }; zone-statistics ( full | terse | none | ); diff --git a/doc/misc/stub.zoneopt b/doc/misc/stub.zoneopt index 2db604dae6..ca4bf1ce39 100644 --- a/doc/misc/stub.zoneopt +++ b/doc/misc/stub.zoneopt @@ -13,10 +13,12 @@ zone [ ] { masterfile-style ( full | relative ); masters [ port ] [ dscp ] { ( | [ port ] | [ port ] ) [ key ]; ... }; max-records ; + max-records-per-type ; max-refresh-time ; max-retry-time ; max-transfer-idle-in ; max-transfer-time-in ; + max-types-per-name ; min-refresh-time ; min-retry-time ; multi-master ; diff --git a/lib/dns/cache.c b/lib/dns/cache.c index ae173b86ab..9f0412dbe7 100644 --- a/lib/dns/cache.c +++ b/lib/dns/cache.c @@ -148,6 +148,8 @@ struct dns_cache { /* Locked by 'filelock'. */ char *filename; /* Access to the on-disk cache file is also locked by 'filelock'. */ + + uint32_t maxrrperset; }; /*** @@ -175,6 +177,7 @@ cache_create_db(dns_cache_t *cache, dns_db_t **db) { cache->db_argv, db); if (result == ISC_R_SUCCESS) { dns_db_setservestalettl(*db, cache->serve_stale_ttl); + dns_db_setmaxrrperset(*db, cache->maxrrperset); } return (result); } @@ -1277,6 +1280,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 ec537486cb..8439265a7f 100644 --- a/lib/dns/db.c +++ b/lib/dns/db.c @@ -1122,3 +1122,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 0f2ffb5f35..539090d1bd 100644 --- a/lib/dns/dnsrps.c +++ b/lib/dns/dnsrps.c @@ -970,7 +970,8 @@ static dns_dbmethods_t rpsdb_db_methods = { NULL, /* setservestalerefresh */ NULL, /* getservestalerefresh */ NULL, /* setgluecachestats */ - NULL /* adjusthashsize */ + NULL, /* adjusthashsize */ + NULL /* setmaxrrperset */ }; static dns_rdatasetmethods_t rpsdb_rdataset_methods = { diff --git a/lib/dns/ecdb.c b/lib/dns/ecdb.c index 1d9343361d..bab5da5503 100644 --- a/lib/dns/ecdb.c +++ b/lib/dns/ecdb.c @@ -426,7 +426,7 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, } result = dns_rdataslab_fromrdataset(rdataset, mctx, &r, - sizeof(rdatasetheader_t)); + sizeof(rdatasetheader_t), 0); if (result != ISC_R_SUCCESS) { goto unlock; } @@ -556,7 +556,11 @@ static dns_dbmethods_t ecdb_methods = { NULL, /* getsize */ NULL, /* setservestalettl */ NULL, /* getservestalettl */ - NULL /* setgluecachestats */ + NULL, /* setservestalerefresh */ + NULL, /* getservestalerefresh */ + NULL, /* setgluecachestats */ + NULL, /* adjusthashsize */ + NULL /* setmaxrrperset */ }; static isc_result_t diff --git a/lib/dns/include/dns/cache.h b/lib/dns/include/dns/cache.h index 22e94da9d5..3fa2a891e0 100644 --- a/lib/dns/include/dns/cache.h +++ b/lib/dns/include/dns/cache.h @@ -337,6 +337,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 90bd7fb112..732bfe473d 100644 --- a/lib/dns/include/dns/db.h +++ b/lib/dns/include/dns/db.h @@ -182,6 +182,7 @@ typedef struct dns_dbmethods { isc_result_t (*getservestalerefresh)(dns_db_t *db, uint32_t *interval); isc_result_t (*setgluecachestats)(dns_db_t *db, isc_stats_t *stats); isc_result_t (*adjusthashsize)(dns_db_t *db, size_t size); + void (*setmaxrrperset)(dns_db_t *db, uint32_t value); } dns_dbmethods_t; typedef isc_result_t (*dns_dbcreatefunc_t)(isc_mem_t *mctx, @@ -1783,6 +1784,13 @@ 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 #endif /* DNS_DB_H */ diff --git a/lib/dns/include/dns/rdataslab.h b/lib/dns/include/dns/rdataslab.h index 5a22f099c3..f2f3513a19 100644 --- a/lib/dns/include/dns/rdataslab.h +++ b/lib/dns/include/dns/rdataslab.h @@ -65,7 +65,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'. @@ -121,7 +122,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 2cf7eced16..0d502f4dd2 100644 --- a/lib/dns/include/dns/view.h +++ b/lib/dns/include/dns/view.h @@ -186,6 +186,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, @@ -1339,6 +1340,12 @@ dns_view_staleanswerenabled(dns_view_t *view); *\li 'view' to be valid. */ +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 #endif /* DNS_VIEW_H */ diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index 08e2263c5b..e902043357 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -162,6 +162,19 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx); *\li #ISC_R_UNEXPECTED */ +isc_result_t +dns_zone_makedb(dns_zone_t *zone, dns_db_t **dbp); +/*%< + * Creates a new empty database for the 'zone'. + * + * Requires: + *\li 'zone' to be a valid zone. + *\li 'dbp' to point to NULL pointer. + * + * Returns: + *\li dns_db_create() error codes. + */ + void dns_zone_setclass(dns_zone_t *zone, dns_rdataclass_t rdclass); /*%< @@ -330,6 +343,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 e840c0665d..ca71bb9c03 100644 --- a/lib/dns/rbtdb.c +++ b/lib/dns/rbtdb.c @@ -482,6 +482,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; @@ -6495,7 +6496,7 @@ find_header: rbtdb->common.mctx, rbtdb->common.rdclass, (dns_rdatatype_t)header->type, flags, - &merged); + rbtdb->maxrrperset, &merged); } if (result == ISC_R_SUCCESS) { /* @@ -6880,7 +6881,7 @@ delegating_type(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, static inline 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; @@ -6901,12 +6902,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; } @@ -6925,7 +6926,7 @@ cleanup: static inline 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; @@ -6946,12 +6947,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; } @@ -7028,7 +7029,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); } @@ -7086,7 +7088,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); @@ -7094,7 +7097,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); @@ -7261,7 +7265,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); } @@ -7669,7 +7674,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); } @@ -8608,6 +8614,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; @@ -8731,7 +8746,8 @@ static dns_dbmethods_t zone_methods = { attach, NULL, /* setservestalerefresh */ NULL, /* getservestalerefresh */ setgluecachestats, - adjusthashsize }; + adjusthashsize, + setmaxrrperset }; static dns_dbmethods_t cache_methods = { attach, detach, @@ -8783,7 +8799,8 @@ static dns_dbmethods_t cache_methods = { attach, setservestalerefresh, getservestalerefresh, NULL, - adjusthashsize }; + adjusthashsize, + 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 dda903819a..b2d1e1cddb 100644 --- a/lib/dns/rdataslab.c +++ b/lib/dns/rdataslab.c @@ -116,7 +116,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. @@ -158,7 +159,7 @@ dns_rdataslab_fromrdataset(dns_rdataset_t *rdataset, isc_mem_t *mctx, return (ISC_R_SUCCESS); } - if (nitems > DNS_RDATASET_MAX_RECORDS) { + if (maxrrperset > 0 && nitems > maxrrperset) { return (DNS_R_TOOMANYRECORDS); } @@ -488,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; @@ -528,7 +530,7 @@ dns_rdataslab_merge(unsigned char *oslab, unsigned char *nslab, #endif /* if DNS_RDATASET_FIXED */ INSIST(ocount > 0 && ncount > 0); - if (ocount + ncount > DNS_RDATASET_MAX_RECORDS) { + if (maxrrperset > 0 && ocount + ncount > maxrrperset) { return (DNS_R_TOOMANYRECORDS); } diff --git a/lib/dns/sdb.c b/lib/dns/sdb.c index d9de422409..84cd324fb4 100644 --- a/lib/dns/sdb.c +++ b/lib/dns/sdb.c @@ -1312,7 +1312,8 @@ static dns_dbmethods_t sdb_methods = { NULL, /* setservestalerefresh */ NULL, /* getservestalerefresh */ NULL, /* setgluecachestats */ - NULL /* adjusthashsize */ + NULL, /* adjusthashsize */ + NULL /* setmaxrrperset */ }; static isc_result_t diff --git a/lib/dns/sdlz.c b/lib/dns/sdlz.c index c8a615a0f3..60a1d23b3b 100644 --- a/lib/dns/sdlz.c +++ b/lib/dns/sdlz.c @@ -1284,7 +1284,8 @@ static dns_dbmethods_t sdlzdb_methods = { NULL, /* setservestalerefresh */ NULL, /* getservestalerefresh */ NULL, /* setgluecachestats */ - NULL /* adjusthashsize */ + NULL, /* adjusthashsize */ + NULL /* setmaxrrperset */ }; /* diff --git a/lib/dns/view.c b/lib/dns/view.c index dcb0f1804e..a672aa8bc8 100644 --- a/lib/dns/view.c +++ b/lib/dns/view.c @@ -869,6 +869,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 @@ -2544,3 +2546,12 @@ dns_view_staleanswerenabled(dns_view_t *view) { return (result); } + +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/xfrin.c b/lib/dns/xfrin.c index a4569f545c..c61e8f5f6b 100644 --- a/lib/dns/xfrin.c +++ b/lib/dns/xfrin.c @@ -203,8 +203,6 @@ xfrin_create(isc_mem_t *mctx, dns_zone_t *zone, dns_db_t *db, isc_task_t *task, static isc_result_t axfr_init(dns_xfrin_ctx_t *xfr); static isc_result_t -axfr_makedb(dns_xfrin_ctx_t *xfr, dns_db_t **dbp); -static isc_result_t axfr_putdata(dns_xfrin_ctx_t *xfr, dns_diffop_t op, dns_name_t *name, dns_ttl_t ttl, dns_rdata_t *rdata); static isc_result_t @@ -277,7 +275,11 @@ axfr_init(dns_xfrin_ctx_t *xfr) { dns_db_detach(&xfr->db); } - CHECK(axfr_makedb(xfr, &xfr->db)); + CHECK(dns_zone_makedb(xfr->zone, &xfr->db)); + + dns_zone_rpz_enable_db(xfr->zone, xfr->db); + dns_zone_catz_enable_db(xfr->zone, xfr->db); + dns_rdatacallbacks_init(&xfr->axfr); CHECK(dns_db_beginload(xfr->db, &xfr->axfr)); result = ISC_R_SUCCESS; @@ -285,22 +287,6 @@ failure: return (result); } -static isc_result_t -axfr_makedb(dns_xfrin_ctx_t *xfr, dns_db_t **dbp) { - isc_result_t result; - - result = dns_db_create(xfr->mctx, /* XXX */ - "rbt", /* XXX guess */ - &xfr->name, dns_dbtype_zone, xfr->rdclass, 0, - NULL, /* XXX guess */ - dbp); - if (result == ISC_R_SUCCESS) { - dns_zone_rpz_enable_db(xfr->zone, *dbp); - dns_zone_catz_enable_db(xfr->zone, *dbp); - } - return (result); -} - static isc_result_t axfr_putdata(dns_xfrin_ctx_t *xfr, dns_diffop_t op, dns_name_t *name, dns_ttl_t ttl, dns_rdata_t *rdata) { diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 65a3aacab7..5c8d97ed18 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -276,6 +276,7 @@ struct dns_zone { uint32_t minretry; uint32_t maxrecords; + uint32_t maxrrperset; isc_sockaddr_t *masters; isc_dscp_t *masterdscps; @@ -2254,31 +2255,13 @@ zone_load(dns_zone_t *zone, unsigned int flags, bool locked) { dns_zone_logc(zone, DNS_LOGCATEGORY_ZONELOAD, ISC_LOG_DEBUG(1), "starting load"); - result = dns_db_create(zone->mctx, zone->db_argv[0], &zone->origin, - (zone->type == dns_zone_stub) ? dns_dbtype_stub - : dns_dbtype_zone, - zone->rdclass, zone->db_argc - 1, - zone->db_argv + 1, &db); - + result = dns_zone_makedb(zone, &db); if (result != ISC_R_SUCCESS) { dns_zone_logc(zone, DNS_LOGCATEGORY_ZONELOAD, ISC_LOG_ERROR, "loading zone: creating database: %s", isc_result_totext(result)); goto cleanup; } - dns_db_settask(db, zone->task); - - if (zone->type == dns_zone_primary || - zone->type == dns_zone_secondary || zone->type == dns_zone_mirror) - { - result = dns_db_setgluecachestats(db, zone->gluecachestats); - if (result == ISC_R_NOTIMPLEMENTED) { - result = ISC_R_SUCCESS; - } - if (result != ISC_R_SUCCESS) { - goto cleanup; - } - } if (!dns_db_ispersistent(db)) { if (zone->masterfile != NULL) { @@ -12175,6 +12158,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) { @@ -14579,6 +14572,7 @@ ns_query(dns_zone_t *zone, dns_rdataset_t *soardataset, dns_stub_t *stub) { goto cleanup; } dns_db_settask(stub->db, zone->task); + dns_db_setmaxrrperset(stub->db, zone->maxrrperset); } result = dns_db_newversion(stub->db, &stub->version); @@ -17300,6 +17294,7 @@ zone_replacedb(dns_zone_t *zone, dns_db_t *db, bool dump) { } zone_attachdb(zone, db); dns_db_settask(zone->db, zone->task); + dns_db_setmaxrrperset(zone->db, zone->maxrrperset); DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED | DNS_ZONEFLG_NEEDNOTIFY); return (ISC_R_SUCCESS); @@ -23413,3 +23408,44 @@ zone_nsecttl(dns_zone_t *zone) { return (ISC_MIN(zone->minimum, zone->soattl)); } + +isc_result_t +dns_zone_makedb(dns_zone_t *zone, dns_db_t **dbp) { + REQUIRE(DNS_ZONE_VALID(zone)); + REQUIRE(dbp != NULL && *dbp == NULL); + + dns_db_t *db = NULL; + + isc_result_t result = dns_db_create( + zone->mctx, zone->db_argv[0], &zone->origin, + (zone->type == dns_zone_stub) ? dns_dbtype_stub + : dns_dbtype_zone, + zone->rdclass, zone->db_argc - 1, zone->db_argv + 1, &db); + if (result != ISC_R_SUCCESS) { + return (result); + } + + switch (zone->type) { + case dns_zone_primary: + case dns_zone_secondary: + case dns_zone_mirror: + result = dns_db_setgluecachestats(db, zone->gluecachestats); + if (result == ISC_R_NOTIMPLEMENTED) { + result = ISC_R_SUCCESS; + } + if (result != ISC_R_SUCCESS) { + dns_db_detach(&db); + return (result); + } + break; + default: + break; + } + + dns_db_settask(db, zone->task); + dns_db_setmaxrrperset(db, zone->maxrrperset); + + *dbp = db; + + return (ISC_R_SUCCESS); +} diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index aab0462936..dce30537dd 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -2236,6 +2236,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, -- 2.45.2