]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Add a limit to the number of RRs in RRSets
authorOndřej Surý <ondrej@isc.org>
Fri, 1 Mar 2024 07:26:07 +0000 (08:26 +0100)
committerNicki Křížek <nicki@isc.org>
Mon, 10 Jun 2024 16:50:03 +0000 (18:50 +0200)
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)

31 files changed:
bin/named/config.c
bin/named/server.c
bin/named/zoneconf.c
bin/tests/system/doth/ns2/named.conf.in
bin/tests/system/doth/ns3/named.conf.in
bin/tests/system/doth/ns4/named.conf.in
bin/tests/system/doth/ns5/named.conf.in
bin/tests/system/dyndb/driver/db.c
doc/arm/reference.rst
doc/misc/mirror.zoneopt
doc/misc/options
doc/misc/primary.zoneopt
doc/misc/redirect.zoneopt
doc/misc/secondary.zoneopt
doc/misc/static-stub.zoneopt
doc/misc/stub.zoneopt
lib/dns/cache.c
lib/dns/db.c
lib/dns/dnsrps.c
lib/dns/include/dns/cache.h
lib/dns/include/dns/db.h
lib/dns/include/dns/rdataslab.h
lib/dns/include/dns/view.h
lib/dns/include/dns/zone.h
lib/dns/rbtdb.c
lib/dns/rdataslab.c
lib/dns/sdb.c
lib/dns/sdlz.c
lib/dns/view.c
lib/dns/zone.c
lib/isccfg/namedconf.c

index 6bf0b3ca81cc61c5f91b56c004115c3113eac5d8..25f3715fc6b3ecb962d619f6aa6590cddbe50ad9 100644 (file)
@@ -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\
index fe65ddbae927c3bf00c1b205cf810e21d1b0a3f0..843f5566d05e6be383dddd85d00317ef3aa617c8 100644 (file)
@@ -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);
index 44c2242bdf451ce5d1211c4b91dc06b27ee75a57..e6bf9377d649f8449c0363f35ba6723ec47a15e9 100644 (file)
@@ -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);
index e533f47e4a2c1fafc00dd92eb0f111be894c8e34..f10dac5d0fc817cce30f9e4bbbc968e3f2cd28a5 100644 (file)
@@ -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;
 };
index cd1ab9cfa6e9a02a4d93a21d0ac9d34479116093..cd9fc63562a56689c86a3e3b5bccf51ca741003a 100644 (file)
@@ -44,6 +44,7 @@ options {
        ixfr-from-differences yes;
        check-integrity no;
        dnssec-validation yes;
+       max-records-per-type 0;
 };
 
 zone "." {
index c7c6c91a58a847f5b9cbe033f2ad29b7a668fa71..43b7c78c7a1c0b293c5f0d65d143e40e24473fc2 100644 (file)
@@ -52,6 +52,7 @@ options {
        ixfr-from-differences yes;
        check-integrity no;
        dnssec-validation yes;
+       max-records-per-type 0;
 };
 
 zone "." {
index 6808618882de260e3f2c93796afe328a44e14ac6..93236371550b6cc83c292a8210fc0adb780962ca 100644 (file)
@@ -40,6 +40,7 @@ options {
        ixfr-from-differences yes;
        check-integrity no;
        dnssec-validation yes;
+       max-records-per-type 0;
 };
 
 zone "." {
index 96857224c2cfdbe30ddcbe04a444ab3551f94ee5..4e08edca5e948340ab3324db87411461907cf68e 100644 (file)
@@ -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. */
index 88faad4023c868c68d054f6d2014f26568f3aa49..f7cde02804db1453cefc53ce46a5bedcf4b90987 100644 (file)
@@ -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.
index ac371cd6e1bd5a68ac8f5785aba80009dcbcb267..8d4a687ee8f223b464678479d01822c5b874f6ad 100644 (file)
@@ -18,6 +18,7 @@ zone <string> [ <class> ] {
        max-ixfr-ratio ( unlimited | <percentage> );
        max-journal-size ( default | unlimited | <sizeval> );
        max-records <integer>;
+       max-records-per-type <integer>;
        max-refresh-time <integer>;
        max-retry-time <integer>;
        max-transfer-idle-in <integer>;
index 56cbf323b6552d8542facce821ca8819b15d26b8..4fd272224c40ac98b6aaf10d7443e1f7c48126c8 100644 (file)
@@ -181,6 +181,7 @@ options {
        max-journal-size ( default | unlimited | <sizeval> );
        max-ncache-ttl <duration>;
        max-records <integer>;
+       max-records-per-type <integer>;
        max-recursion-depth <integer>;
        max-recursion-queries <integer>;
        max-refresh-time <integer>;
@@ -471,6 +472,7 @@ view <string> [ <class> ] {
        max-journal-size ( default | unlimited | <sizeval> );
        max-ncache-ttl <duration>;
        max-records <integer>;
+       max-records-per-type <integer>;
        max-recursion-depth <integer>;
        max-recursion-queries <integer>;
        max-refresh-time <integer>;
index 8f646e3560f4157563ddd59c5503d8ddfbb4344c..e8ef80cf6eabbd46cbcddd3cfca95598dea4b9b5 100644 (file)
@@ -38,6 +38,7 @@ zone <string> [ <class> ] {
        max-ixfr-ratio ( unlimited | <percentage> );
        max-journal-size ( default | unlimited | <sizeval> );
        max-records <integer>;
+       max-records-per-type <integer>;
        max-transfer-idle-out <integer>;
        max-transfer-time-out <integer>;
        max-zone-ttl ( unlimited | <duration> );
index bcd9a571ea4849453a91eb57f6a256127fd7b0d3..613dbdb890ea13e6ca1f397dc458a551672decb7 100644 (file)
@@ -7,6 +7,7 @@ zone <string> [ <class> ] {
        masterfile-format ( raw | text );
        masterfile-style ( full | relative );
        max-records <integer>;
+       max-records-per-type <integer>;
        max-zone-ttl ( unlimited | <duration> );
        primaries [ port <integer> ]  { ( <remote-servers> | <ipv4_address> [ port <integer> ] | <ipv6_address> [ port <integer> ] ) [ key <string> ] [ tls <string> ]; ... };
        zone-statistics ( full | terse | none | <boolean> );
index 3237aab04d2bd256c578d455b7a47ab91de7129b..fc1c8526db51b53a90ae82cd51c6e5fc87bde4b8 100644 (file)
@@ -30,6 +30,7 @@ zone <string> [ <class> ] {
        max-ixfr-ratio ( unlimited | <percentage> );
        max-journal-size ( default | unlimited | <sizeval> );
        max-records <integer>;
+       max-records-per-type <integer>;
        max-refresh-time <integer>;
        max-retry-time <integer>;
        max-transfer-idle-in <integer>;
index 5357528e6fbe2bfed7da856a79391085a9cfb75f..706fa3cc88e7342b86d6e72337a73113ab40bcf0 100644 (file)
@@ -5,6 +5,7 @@ zone <string> [ <class> ] {
        forward ( first | only );
        forwarders [ port <integer> ]  { ( <ipv4_address> | <ipv6_address> ) [ port <integer> ]; ... };
        max-records <integer>;
+       max-records-per-type <integer>;
        server-addresses { ( <ipv4_address> | <ipv6_address> ); ... };
        server-names { <string>; ... };
        zone-statistics ( full | terse | none | <boolean> );
index 29c1d56e3f3cb3beb1fab68f23307ca6bcfba911..c1c5d68566a3ea730204fa6102047933c6833de9 100644 (file)
@@ -12,6 +12,7 @@ zone <string> [ <class> ] {
        masterfile-format ( raw | text );
        masterfile-style ( full | relative );
        max-records <integer>;
+       max-records-per-type <integer>;
        max-refresh-time <integer>;
        max-retry-time <integer>;
        max-transfer-idle-in <integer>;
index cb7f35a95fde27820be18d211e6bc5976f81d83a..0669f4a9f6359559f10943e0eac5b07019107fcf 100644 (file)
@@ -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
index 653b29ddec635769d2fdd109d83427de4ad5704e..09bf9394b5b3faf61712f1aa86d3958b263d6efd 100644 (file)
@@ -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);
+       }
+}
index d4a1c65e7f8146527c8efea5a4a2b0ba2bcd76c7..a8a6f9c6a3dad550c43f58f188e3bf69fc5f2f9a 100644 (file)
@@ -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 = {
index e5c6e49be93262b633a2b19335b02f9b382fb981..10911109265f41b00f65a2f79a50d67a1c4774ce 100644 (file)
@@ -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);
index 584186872db7b0e33837bd981bc467da33f5554d..4157ae4ed989b17a4532529a46e798369985eb80 100644 (file)
@@ -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
index 7364b8d6448b16a4abba32f72fd4add314c585a1..5729c004caa1e47c625d4f6272e4f4e4b0c07936 100644 (file)
@@ -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'.
  */
index 18b0b3394a0bfe1318a5b3c575105e9689dadede..38ebb922dc86cee97faaf00f81b2331ac6f9071f 100644 (file)
@@ -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
index 7699a577b9b662fe19fa2aba8d904b17ebb2dce8..2576119415475dfc6fb19f15c4746d6dad9bdcf5 100644 (file)
@@ -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);
 /*%<
index 43f05742f7287593ab06f957a041b7c0853d7b3a..7b2e62d7c8240a7ce79f6f17598132b9105b2eb6 100644 (file)
@@ -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,
-                                           &region, sizeof(rdatasetheader_t));
+                                           &region, 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,
-                                           &region, sizeof(rdatasetheader_t));
+                                           &region, 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,
-                                           &region, sizeof(rdatasetheader_t));
+                                           &region, 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,
index 2b4cc4bed3f113429f0b49d6b7409d759bae0f22..1b228f6ac396bcaa890378355406f70dfa69be73 100644 (file)
@@ -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 */
index b58080a2faed30ad6b47c9b9a32a649b2f352b46..3d841e351149b5f55013632927eee2f536273424 100644 (file)
@@ -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
index ff674488658b403308747f7898ffab262fc2e679..2568485d3e3887f904a0a390b97091f71f7f5fa3 100644 (file)
@@ -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 */
 };
 
 /*
index 49c9aee9416ab6e682ea9f082eb3b15b5f0c181e..66c7d85cf735374ae4e49a1787b7ff583e18910f 100644 (file)
@@ -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);
+       }
+}
index cf714779643f973ff31fe76c25cf06cc36bca6f9..7f81c9b4399addfacfa7932cc4d7ec0a72660fd2 100644 (file)
@@ -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;
 
index 7bfd8f9bde82d61fca64509df36b6e16d4003cec..7cbdab60d0a37b2e932a6d4b498c630ed47b7bc2 100644 (file)
@@ -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,