]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Add a limit to the number of RR types for single name
authorOndřej Surý <ondrej@isc.org>
Sat, 25 May 2024 09:46:56 +0000 (11:46 +0200)
committerNicki Křížek <nicki@isc.org>
Mon, 10 Jun 2024 14:55:09 +0000 (16:55 +0200)
Previously, the number of RR types for a single owner name was limited
only by the maximum number of the types (64k).  As the data structure
that holds the RR types for the database node is just a linked list, and
there are places where we just walk through the whole list (again and
again), adding a large number of RR types for a single owner named with
would slow down processing of such name (database node).

Add a configurable limit to cap the number of the RR types for a single
owner.  This is enforced at the database (rbtdb, qpzone, qpcache) level
and configured with new max-types-per-name configuration option that
can be configured globally, per-view and per-zone.

26 files changed:
bin/named/config.c
bin/named/server.c
bin/named/zoneconf.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/include/dns/cache.h
lib/dns/include/dns/db.h
lib/dns/include/dns/view.h
lib/dns/include/dns/zone.h
lib/dns/qpcache.c
lib/dns/qpzone.c
lib/dns/rbt-cachedb.c
lib/dns/rbt-zonedb.c
lib/dns/rbtdb.c
lib/dns/rbtdb_p.h
lib/dns/view.c
lib/dns/zone.c
lib/isccfg/namedconf.c

index 1943eb187900ef9e9604c414523adafae5e6855e..732e28e606a9e3541eea3677e458aea585e12a42 100644 (file)
@@ -225,6 +225,7 @@ options {\n\
        max-records-per-type 100;\n\
        max-refresh-time 2419200; /* 4 weeks */\n\
        max-retry-time 1209600; /* 2 weeks */\n\
+       max-types-per-name 100;\n\
        max-transfer-idle-in 60;\n\
        max-transfer-idle-out 60;\n\
        max-transfer-time-in 120;\n\
index 6bcd0b5d561780e387180c3f93fef60719424774..c41f5d9b249c21c997558316766c3b5148c50479 100644 (file)
@@ -5463,6 +5463,15 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config,
        INSIST(result == ISC_R_SUCCESS);
        dns_view_setmaxrrperset(view, cfg_obj_asuint32(obj));
 
+       /*
+        * This is used for the cache and also as a default value
+        * for zone databases.
+        */
+       obj = NULL;
+       result = named_config_get(maps, "max-types-per-name", &obj);
+       INSIST(result == ISC_R_SUCCESS);
+       dns_view_setmaxtypepername(view, cfg_obj_asuint32(obj));
+
        obj = NULL;
        result = named_config_get(maps, "max-recursion-depth", &obj);
        INSIST(result == ISC_R_SUCCESS);
index f6646e38198f36745f70685c9a2dc730bc6fb99c..d9b0b90eb3895891cf336c7a9b9a8c4c6c896126 100644 (file)
@@ -1082,6 +1082,14 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig,
                dns_zone_setmaxrrperset(zone, 0);
        }
 
+       obj = NULL;
+       result = named_config_get(maps, "max-types-per-name", &obj);
+       INSIST(result == ISC_R_SUCCESS && obj != NULL);
+       dns_zone_setmaxtypepername(mayberaw, cfg_obj_asuint32(obj));
+       if (zone != mayberaw) {
+               dns_zone_setmaxtypepername(zone, 0);
+       }
+
        if (raw != NULL && filename != NULL) {
 #define SIGNED ".signed"
                size_t signedlen = strlen(filename) + sizeof(SIGNED);
index 0decf3a6e0341c6797d7569eec9a94e4e2e423ff..1129dce66c76cd922831a73e5a411316df7140f5 100644 (file)
@@ -3696,6 +3696,21 @@ system.
    a failure.  If set to 0, there is no cap on RRset size.  The default is
    100.
 
+.. namedconf:statement:: max-types-per-name
+   :tags: server
+   :short: Sets the maximum number of RR types that can be stored for an owner name
+
+   This sets the maximum number of resource record types that can be stored
+   for a single owner name in a database. When configured in :namedconf:ref:`options`
+   or :namedconf:ref:`view`, it controls the cache database, and 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 owner
+   name with more than the specified number of resource record types will result
+   in a failure.  If set to 0, there is no cap on RR types number.  The default is
+   100.
+
 .. namedconf:statement:: recursive-clients
    :tags: query
    :short: Specifies the maximum number of concurrent recursive queries the server can perform.
index 4238e689f59cbb449d6efe886d85e588fc961620..e7cb0b9ccbf40682585b289d1b9723b0d58a5591 100644 (file)
@@ -23,6 +23,7 @@ zone <string> [ <class> ] {
        max-transfer-idle-out <integer>;
        max-transfer-time-in <integer>;
        max-transfer-time-out <integer>;
+       max-types-per-name <integer>;
        min-refresh-time <integer>;
        min-retry-time <integer>;
        multi-master <boolean>;
index 261d46d0935dd2425085449145818737c21b44d4..de24eef2c216d7cd792b6b4d31d73d8ad2e3ef27 100644 (file)
@@ -194,6 +194,7 @@ options {
        max-transfer-idle-out <integer>;
        max-transfer-time-in <integer>;
        max-transfer-time-out <integer>;
+       max-types-per-name <integer>;
        max-udp-size <integer>;
        max-validation-failures-per-fetch <integer>; // experimental
        max-validations-per-fetch <integer>; // experimental
@@ -479,6 +480,7 @@ view <string> [ <class> ] {
        max-transfer-idle-out <integer>;
        max-transfer-time-in <integer>;
        max-transfer-time-out <integer>;
+       max-types-per-name <integer>;
        max-udp-size <integer>;
        max-validation-failures-per-fetch <integer>; // experimental
        max-validations-per-fetch <integer>; // experimental
index 6586686300f3413d6a29f2cf1c888cc38d86df27..7b351064fef80f6c1d3fa96c0dbf545b26509dfe 100644 (file)
@@ -40,6 +40,7 @@ zone <string> [ <class> ] {
        max-records-per-type <integer>;
        max-transfer-idle-out <integer>;
        max-transfer-time-out <integer>;
+       max-types-per-name <integer>;
        max-zone-ttl ( unlimited | <duration> ); // deprecated
        notify ( explicit | master-only | primary-only | <boolean> );
        notify-delay <integer>;
index b389f6eede942d59b16d1416a38d70155e2a82db..5faa1e6ddd21c729cc463ba0a91f00a41a1b6d96 100644 (file)
@@ -8,6 +8,7 @@ zone <string> [ <class> ] {
        masterfile-style ( full | relative );
        max-records <integer>;
        max-records-per-type <integer>;
+       max-types-per-name <integer>;
        max-zone-ttl ( unlimited | <duration> ); // deprecated
        primaries [ port <integer> ] [ source ( <ipv4_address> | * ) ] [ source-v6 ( <ipv6_address> | * ) ] { ( <remote-servers> | <ipv4_address> [ port <integer> ] | <ipv6_address> [ port <integer> ] ) [ key <string> ] [ tls <string> ]; ... };
        zone-statistics ( full | terse | none | <boolean> );
index 4ded7c8e1926363b77d779b9212ff4635793a3cc..610d32f26273c58bc1ae413b9ff15f65837d6182 100644 (file)
@@ -35,6 +35,7 @@ zone <string> [ <class> ] {
        max-transfer-idle-out <integer>;
        max-transfer-time-in <integer>;
        max-transfer-time-out <integer>;
+       max-types-per-name <integer>;
        min-refresh-time <integer>;
        min-retry-time <integer>;
        multi-master <boolean>;
index 5f68d83c52f90c220771e086167489cbfe389393..40a340f6298123557f130576faa33d563e7ce7bd 100644 (file)
@@ -6,6 +6,7 @@ zone <string> [ <class> ] {
        forwarders [ port <integer> ] [ tls <string> ] { ( <ipv4_address> | <ipv6_address> ) [ port <integer> ] [ tls <string> ]; ... };
        max-records <integer>;
        max-records-per-type <integer>;
+       max-types-per-name <integer>;
        server-addresses { ( <ipv4_address> | <ipv6_address> ); ... };
        server-names { <string>; ... };
        zone-statistics ( full | terse | none | <boolean> );
index 8d0537b136424c3362e5a883ff7a43cbab99c428..992aa51e96320a89f6e14b9a338c4e14e3ebff20 100644 (file)
@@ -16,6 +16,7 @@ zone <string> [ <class> ] {
        max-retry-time <integer>;
        max-transfer-idle-in <integer>;
        max-transfer-time-in <integer>;
+       max-types-per-name <integer>;
        min-refresh-time <integer>;
        min-retry-time <integer>;
        multi-master <boolean>;
index 52d92037d3141752103a9101251ca3729d4dbb8a..24e2d4f20532838c1ed350ce3d0dbfadfa87dbdb 100644 (file)
@@ -81,6 +81,7 @@ struct dns_cache {
        dns_ttl_t serve_stale_refresh;
        isc_stats_t *stats;
        uint32_t maxrrperset;
+       uint32_t maxtypepername;
 };
 
 /***
@@ -130,6 +131,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);
+       dns_db_setmaxtypepername(db, cache->maxtypepername);
 
        /*
         * XXX this is only used by the RBT cache, and can
@@ -558,6 +560,16 @@ dns_cache_setmaxrrperset(dns_cache_t *cache, uint32_t value) {
        }
 }
 
+void
+dns_cache_setmaxtypepername(dns_cache_t *cache, uint32_t value) {
+       REQUIRE(VALID_CACHE(cache));
+
+       cache->maxtypepername = value;
+       if (cache->db != NULL) {
+               dns_db_setmaxtypepername(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 3f3ca0ede1dc0f7f05ff743c1c77cb0d9fdc98bf..ce27fce8c1a81f99d7f5fc12a381495abe5669a1 100644 (file)
@@ -1179,3 +1179,12 @@ dns_db_setmaxrrperset(dns_db_t *db, uint32_t value) {
                (db->methods->setmaxrrperset)(db, value);
        }
 }
+
+void
+dns_db_setmaxtypepername(dns_db_t *db, uint32_t value) {
+       REQUIRE(DNS_DB_VALID(db));
+
+       if (db->methods->setmaxtypepername != NULL) {
+               (db->methods->setmaxtypepername)(db, value);
+       }
+}
index 738ab4cfe08f86da4b62bf5400f21c4da816d92a..c629ab26c467b7333f22d2d868431a93ed5a26f0 100644 (file)
@@ -252,6 +252,12 @@ dns_cache_setmaxrrperset(dns_cache_t *cache, uint32_t value);
  * Set the maximum resource records per RRSet that can be cached.
  */
 
+void
+dns_cache_setmaxtypepername(dns_cache_t *cache, uint32_t value);
+/*%<
+ * Set the maximum resource record types per owner name that can be cached.
+ */
+
 #ifdef HAVE_LIBXML2
 int
 dns_cache_renderxml(dns_cache_t *cache, void *writer0);
index 96f9d58a12ad152021dcf3d49890b61441830c50..254b7d38e507ca6e62cd7d8d3bef8ab687ce9011 100644 (file)
@@ -184,6 +184,7 @@ typedef struct dns_dbmethods {
        isc_result_t (*nodefullname)(dns_db_t *db, dns_dbnode_t *node,
                                     dns_name_t *name);
        void (*setmaxrrperset)(dns_db_t *db, uint32_t value);
+       void (*setmaxtypepername)(dns_db_t *db, uint32_t value);
 } dns_dbmethods_t;
 
 typedef isc_result_t (*dns_dbcreatefunc_t)(isc_mem_t       *mctx,
@@ -1805,8 +1806,19 @@ dns_db_nodefullname(dns_db_t *db, dns_dbnode_t *node, dns_name_t *name);
 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_TOOMANYRECORDS.
+ * 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_TOOMANYRECORDS.
+ */
+
+void
+dns_db_setmaxtypepername(dns_db_t *db, uint32_t value);
+/*%<
+ * Set the maximum permissible number of RR types per owner name.
+ *
+ * If 'value' is nonzero, and if there are already 'value' RR types
+ * stored at a given node, then any subsequent attempt to add an rdataset
+ * with a new RR type will return ISC_R_TOOMANYRECORDS.
  */
 ISC_LANG_ENDDECLS
index e97835f8c636234ec65bc159ca66177d20dee5d7..ddba1e7401e80904c15470136039a1494269ef31 100644 (file)
@@ -184,6 +184,7 @@ struct dns_view {
        dns_badcache_t       *failcache;
        unsigned int          udpsize;
        uint32_t              maxrrperset;
+       uint32_t              maxtypepername;
 
        /*
         * Configurable data for server use only,
@@ -1249,6 +1250,12 @@ dns_view_setmaxrrperset(dns_view_t *view, uint32_t value);
  * Set the maximum resource records per RRSet that can be cached.
  */
 
+void
+dns_view_setmaxtypepername(dns_view_t *view, uint32_t value);
+/*%<
+ * Set the maximum resource record types per owner name that can be cached.
+ */
+
 void
 dns_view_setudpsize(dns_view_t *view, uint16_t udpsize);
 /*%<
index bdcff3061c2eed14bf21e0c2c320737587ca111e..623edf162b79065700cecd9064b9405d1faae2ff 100644 (file)
@@ -379,6 +379,19 @@ dns_zone_setmaxrrperset(dns_zone_t *zone, uint32_t maxrrperset);
  *\li  void
  */
 
+void
+dns_zone_setmaxtypepername(dns_zone_t *zone, uint32_t maxtypepername);
+/*%<
+ *     Sets the maximum number of resource record types per owner name
+ *     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 329decbb6f097357184e016f7b79852a0abe31b6..c56bc2abe7fddcfaacb7fd9e8274a694f8a007fe 100644 (file)
@@ -217,7 +217,8 @@ struct qpcache {
        /* Locked by lock. */
        unsigned int active;
 
-       uint32_t maxrrperset; /* Maximum RRs per RRset */
+       uint32_t maxrrperset;    /* Maximum RRs per RRset */
+       uint32_t maxtypepername; /* Maximum number of RR types per owner */
 
        /*
         * The time after a failed lookup, where stale answers from cache
@@ -2885,6 +2886,7 @@ add(qpcache_t *qpdb, qpcnode_t *qpnode,
        dns_typepair_t negtype = 0, sigtype;
        dns_trust_t trust;
        int idx;
+       uint32_t ntypes;
 
        if ((options & DNS_DBADD_FORCE) != 0) {
                trust = dns_trust_ultimate;
@@ -2917,6 +2919,7 @@ add(qpcache_t *qpdb, qpcnode_t *qpnode,
                                {
                                        mark_ancient(topheader);
                                }
+                               ntypes = 0; /* Always add the negative entry */
                                goto find_header;
                        }
                        /*
@@ -2940,9 +2943,11 @@ add(qpcache_t *qpdb, qpcnode_t *qpnode,
                         * check for an extant non-ancient NODATA ncache
                         * entry which covers the same type as the RRSIG.
                         */
+                       ntypes = 0;
                        for (topheader = qpnode->data; topheader != NULL;
                             topheader = topheader->next)
                        {
+                               ++ntypes;
                                if ((topheader->type == RDATATYPE_NCACHEANY) ||
                                    (newheader->type == sigtype &&
                                     topheader->type ==
@@ -2985,9 +2990,12 @@ add(qpcache_t *qpdb, qpcnode_t *qpnode,
                }
        }
 
+       ntypes = 0;
        for (topheader = qpnode->data; topheader != NULL;
             topheader = topheader->next)
        {
+               ++ntypes;
+
                if (prio_type(topheader->type)) {
                        prioheader = topheader;
                }
@@ -3255,6 +3263,14 @@ find_header:
                        /*
                         * No rdatasets of the given type exist at the node.
                         */
+                       if (trust != dns_trust_ultimate &&
+                           qpdb->maxtypepername > 0 &&
+                           ntypes >= qpdb->maxtypepername)
+                       {
+                               dns_slabheader_destroy(&newheader);
+                               return (DNS_R_TOOMANYRECORDS);
+                       }
+
                        INSIST(newheader->down == NULL);
 
                        if (prio_type(newheader->type)) {
@@ -4344,6 +4360,15 @@ setmaxrrperset(dns_db_t *db, uint32_t value) {
        qpdb->maxrrperset = value;
 }
 
+static void
+setmaxtypepername(dns_db_t *db, uint32_t value) {
+       qpcache_t *qpdb = (qpcache_t *)db;
+
+       REQUIRE(VALID_QPDB(qpdb));
+
+       qpdb->maxtypepername = value;
+}
+
 static dns_dbmethods_t qpdb_cachemethods = {
        .destroy = qpdb_destroy,
        .findnode = findnode,
@@ -4369,6 +4394,7 @@ static dns_dbmethods_t qpdb_cachemethods = {
        .expiredata = expiredata,
        .deletedata = deletedata,
        .setmaxrrperset = setmaxrrperset,
+       .setmaxtypepername = setmaxtypepername,
 };
 
 static void
index da692d2538d1a77b1e36fe7ab8107607c907d6ba..4bcd5e289091a50885c5a35c9e803c4369ba17e7 100644 (file)
@@ -178,7 +178,8 @@ struct qpzonedb {
        uint32_t current_serial;
        uint32_t least_serial;
        uint32_t next_serial;
-       uint32_t maxrrperset;
+       uint32_t maxrrperset;    /* Maximum RRs per RRset */
+       uint32_t maxtypepername; /* Maximum number of RR types per owner */
        qpz_version_t *current_version;
        qpz_version_t *future_version;
        qpz_versionlist_t open_versions;
@@ -1834,6 +1835,7 @@ add(qpzonedb_t *qpdb, qpznode_t *node, const dns_name_t *nodename,
        unsigned char *merged = NULL;
        isc_result_t result;
        bool merge = false;
+       uint32_t ntypes;
 
        if ((options & DNS_DBADD_MERGE) != 0) {
                REQUIRE(version != NULL);
@@ -1849,9 +1851,11 @@ add(qpzonedb_t *qpdb, qpznode_t *node, const dns_name_t *nodename,
                changed = add_changed(newheader, version DNS__DB_FLARG_PASS);
        }
 
+       ntypes = 0;
        for (topheader = node->data; topheader != NULL;
             topheader = topheader->next)
        {
+               ++ntypes;
                if (prio_type(topheader->type)) {
                        prioheader = topheader;
                }
@@ -2018,6 +2022,14 @@ add(qpzonedb_t *qpdb, qpznode_t *node, const dns_name_t *nodename,
                        /*
                         * No rdatasets of the given type exist at the node.
                         */
+
+                       if (qpdb->maxtypepername > 0 &&
+                           ntypes >= qpdb->maxtypepername)
+                       {
+                               dns_slabheader_destroy(&newheader);
+                               return (DNS_R_TOOMANYRECORDS);
+                       }
+
                        INSIST(newheader->down == NULL);
 
                        if (prio_type(newheader->type)) {
@@ -5290,6 +5302,15 @@ setmaxrrperset(dns_db_t *db, uint32_t value) {
        qpdb->maxrrperset = value;
 }
 
+static void
+setmaxtypepername(dns_db_t *db, uint32_t value) {
+       qpzonedb_t *qpdb = (qpzonedb_t *)db;
+
+       REQUIRE(VALID_QPZONE(qpdb));
+
+       qpdb->maxtypepername = value;
+}
+
 static dns_dbmethods_t qpdb_zonemethods = {
        .destroy = qpdb_destroy,
        .beginload = beginload,
@@ -5324,6 +5345,7 @@ static dns_dbmethods_t qpdb_zonemethods = {
        .deletedata = deletedata,
        .nodefullname = nodefullname,
        .setmaxrrperset = setmaxrrperset,
+       .setmaxtypepername = setmaxtypepername,
 };
 
 static void
index 779eb143d6499d4029eec9039569070016cf085c..1f252eaef484f251130bb4dcb535d94f66b0a88f 100644 (file)
@@ -1583,6 +1583,7 @@ dns_dbmethods_t dns__rbtdb_cachemethods = {
        .expiredata = expiredata,
        .deletedata = dns__rbtdb_deletedata,
        .setmaxrrperset = dns__rbtdb_setmaxrrperset,
+       .setmaxtypepername = dns__rbtdb_setmaxtypepername,
 };
 
 /*
index 93b71b9a98d497ec061edf45ad34a09887789730..5f29bdafe58f534021d05a4063e6cf9a8a2a2511 100644 (file)
@@ -2420,6 +2420,7 @@ dns_dbmethods_t dns__rbtdb_zonemethods = {
        .deletedata = dns__rbtdb_deletedata,
        .nodefullname = dns__rbtdb_nodefullname,
        .setmaxrrperset = dns__rbtdb_setmaxrrperset,
+       .setmaxtypepername = dns__rbtdb_setmaxtypepername,
 };
 
 void
index 71ac5c1951ba4b93b07f3c099140e67cc495e03f..deadde73c7ccec1281043e789d7bc61d8083ceb0 100644 (file)
@@ -2566,6 +2566,7 @@ dns__rbtdb_add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode,
        dns_typepair_t negtype = 0, sigtype;
        dns_trust_t trust;
        int idx;
+       uint32_t ntypes = 0;
 
        if ((options & DNS_DBADD_MERGE) != 0) {
                REQUIRE(rbtversion != NULL);
@@ -2618,6 +2619,7 @@ dns__rbtdb_add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode,
                                {
                                        mark_ancient(topheader);
                                }
+                               ntypes = 0; /* Always add the negative entry */
                                goto find_header;
                        }
                        /*
@@ -2641,9 +2643,11 @@ dns__rbtdb_add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode,
                         * check for an extant non-ancient NODATA ncache
                         * entry which covers the same type as the RRSIG.
                         */
+                       ntypes = 0;
                        for (topheader = rbtnode->data; topheader != NULL;
                             topheader = topheader->next)
                        {
+                               ++ntypes;
                                if ((topheader->type == RDATATYPE_NCACHEANY) ||
                                    (newheader->type == sigtype &&
                                     topheader->type ==
@@ -2686,9 +2690,11 @@ dns__rbtdb_add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode,
                }
        }
 
+       ntypes = 0;
        for (topheader = rbtnode->data; topheader != NULL;
             topheader = topheader->next)
        {
+               ++ntypes;
                if (prio_type(topheader->type)) {
                        prioheader = topheader;
                }
@@ -3082,6 +3088,14 @@ find_header:
                        /*
                         * No rdatasets of the given type exist at the node.
                         */
+
+                       if (rbtdb->maxtypepername > 0 &&
+                           ntypes >= rbtdb->maxtypepername)
+                       {
+                               dns_slabheader_destroy(&newheader);
+                               return (DNS_R_TOOMANYRECORDS);
+                       }
+
                        INSIST(newheader->down == NULL);
 
                        if (prio_type(newheader->type)) {
@@ -4968,3 +4982,12 @@ dns__rbtdb_setmaxrrperset(dns_db_t *db, uint32_t value) {
 
        rbtdb->maxrrperset = value;
 }
+
+void
+dns__rbtdb_setmaxtypepername(dns_db_t *db, uint32_t maxtypepername) {
+       dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+
+       REQUIRE(VALID_RBTDB(rbtdb));
+
+       rbtdb->maxtypepername = maxtypepername;
+}
index fe06b30b1397107349cc8860b3f7798378b51ec6..cee0499a51b281b742e05c59c2ff7d42a7846eb0 100644 (file)
@@ -115,6 +115,7 @@ struct dns_rbtdb {
        uint32_t least_serial;
        uint32_t next_serial;
        uint32_t maxrrperset;
+       uint32_t maxtypepername;
        dns_rbtdb_version_t *current_version;
        dns_rbtdb_version_t *future_version;
        rbtdb_versionlist_t open_versions;
@@ -429,7 +430,13 @@ dns__rbtdb_setttl(dns_slabheader_t *header, dns_ttl_t newttl);
  */
 
 void
-dns__rbtdb_setmaxrrperset(dns_db_t *db, uint32_t value);
+dns__rbtdb_setmaxrrperset(dns_db_t *db, uint32_t maxrrperset);
+/*%<
+ * Set the max RRs per RRset limit.
+ */
+
+void
+dns__rbtdb_setmaxtypepername(dns_db_t *db, uint32_t maxtypepername);
 /*%<
  * Set the max RRs per RRset limit.
  */
index 15e2e303dbfd14269e01a7b6074f7ca7c7ff3ef9..99d8b61b592cf542d232d67669e144c261819b12 100644 (file)
@@ -645,6 +645,7 @@ dns_view_setcache(dns_view_t *view, dns_cache_t *cache, bool shared) {
        INSIST(DNS_DB_VALID(view->cachedb));
 
        dns_cache_setmaxrrperset(view->cache, view->maxrrperset);
+       dns_cache_setmaxtypepername(view->cache, view->maxtypepername);
 }
 
 bool
@@ -2347,6 +2348,15 @@ dns_view_setmaxrrperset(dns_view_t *view, uint32_t value) {
        }
 }
 
+void
+dns_view_setmaxtypepername(dns_view_t *view, uint32_t value) {
+       REQUIRE(DNS_VIEW_VALID(view));
+       view->maxtypepername = value;
+       if (view->cache != NULL) {
+               dns_cache_setmaxtypepername(view->cache, value);
+       }
+}
+
 void
 dns_view_setudpsize(dns_view_t *view, uint16_t udpsize) {
        REQUIRE(DNS_VIEW_VALID(view));
index 6c27dfe3ec0543efcf4d2120fd5f238d1c7e26df..9f152a7c0255f151582f85326f325c306082e1cd 100644 (file)
@@ -319,6 +319,7 @@ struct dns_zone {
 
        uint32_t maxrecords;
        uint32_t maxrrperset;
+       uint32_t maxtypepername;
 
        dns_remote_t primaries;
 
@@ -12068,6 +12069,16 @@ dns_zone_setmaxrrperset(dns_zone_t *zone, uint32_t val) {
        }
 }
 
+void
+dns_zone_setmaxtypepername(dns_zone_t *zone, uint32_t val) {
+       REQUIRE(DNS_ZONE_VALID(zone));
+
+       zone->maxtypepername = val;
+       if (zone->db != NULL) {
+               dns_db_setmaxtypepername(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,
@@ -14470,6 +14481,8 @@ ns_query(dns_zone_t *zone, dns_rdataset_t *soardataset, dns_stub_t *stub) {
                        }
                        dns_db_setloop(stub->db, zone->loop);
                        dns_db_setmaxrrperset(stub->db, zone->maxrrperset);
+                       dns_db_setmaxtypepername(stub->db,
+                                                zone->maxtypepername);
                }
 
                result = dns_db_newversion(stub->db, &stub->version);
@@ -17527,6 +17540,7 @@ zone_replacedb(dns_zone_t *zone, dns_db_t *db, bool dump) {
        zone_attachdb(zone, db);
        dns_db_setloop(zone->db, zone->loop);
        dns_db_setmaxrrperset(zone->db, zone->maxrrperset);
+       dns_db_setmaxtypepername(zone->db, zone->maxtypepername);
        DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED | DNS_ZONEFLG_NEEDNOTIFY);
        return (ISC_R_SUCCESS);
 
@@ -24167,6 +24181,7 @@ dns_zone_makedb(dns_zone_t *zone, dns_db_t **dbp) {
 
        dns_db_setloop(db, zone->loop);
        dns_db_setmaxrrperset(db, zone->maxrrperset);
+       dns_db_setmaxtypepername(db, zone->maxtypepername);
 
        *dbp = db;
 
index 528a52de05fb0a02ec896c85284f74fe2781a26f..18b40fab7ffb131f408ac1c1cef9589f294155d4 100644 (file)
@@ -2375,6 +2375,9 @@ static cfg_clausedef_t zone_clauses[] = {
        { "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-types-per-name", &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,