diff --git a/bin/named/config.c b/bin/named/config.c index 1943eb1879..732e28e606 100644 --- a/bin/named/config.c +++ b/bin/named/config.c @@ -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\ diff --git a/bin/named/server.c b/bin/named/server.c index 6bcd0b5d56..c41f5d9b24 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -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); diff --git a/bin/named/zoneconf.c b/bin/named/zoneconf.c index f6646e3819..d9b0b90eb3 100644 --- a/bin/named/zoneconf.c +++ b/bin/named/zoneconf.c @@ -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); diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst index 0decf3a6e0..1129dce66c 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -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. diff --git a/doc/misc/mirror.zoneopt b/doc/misc/mirror.zoneopt index 4238e689f5..e7cb0b9ccb 100644 --- a/doc/misc/mirror.zoneopt +++ b/doc/misc/mirror.zoneopt @@ -23,6 +23,7 @@ zone [ ] { 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 261d46d093..de24eef2c2 100644 --- a/doc/misc/options +++ b/doc/misc/options @@ -194,6 +194,7 @@ options { max-transfer-idle-out ; max-transfer-time-in ; max-transfer-time-out ; + max-types-per-name ; max-udp-size ; max-validation-failures-per-fetch ; // experimental max-validations-per-fetch ; // experimental @@ -479,6 +480,7 @@ view [ ] { max-transfer-idle-out ; max-transfer-time-in ; max-transfer-time-out ; + max-types-per-name ; max-udp-size ; max-validation-failures-per-fetch ; // experimental max-validations-per-fetch ; // experimental diff --git a/doc/misc/primary.zoneopt b/doc/misc/primary.zoneopt index 6586686300..7b351064fe 100644 --- a/doc/misc/primary.zoneopt +++ b/doc/misc/primary.zoneopt @@ -40,6 +40,7 @@ zone [ ] { max-records-per-type ; max-transfer-idle-out ; max-transfer-time-out ; + max-types-per-name ; max-zone-ttl ( unlimited | ); // deprecated notify ( explicit | master-only | primary-only | ); notify-delay ; diff --git a/doc/misc/redirect.zoneopt b/doc/misc/redirect.zoneopt index b389f6eede..5faa1e6ddd 100644 --- a/doc/misc/redirect.zoneopt +++ b/doc/misc/redirect.zoneopt @@ -8,6 +8,7 @@ zone [ ] { masterfile-style ( full | relative ); max-records ; max-records-per-type ; + max-types-per-name ; max-zone-ttl ( unlimited | ); // deprecated primaries [ port ] [ source ( | * ) ] [ source-v6 ( | * ) ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; zone-statistics ( full | terse | none | ); diff --git a/doc/misc/secondary.zoneopt b/doc/misc/secondary.zoneopt index 4ded7c8e19..610d32f262 100644 --- a/doc/misc/secondary.zoneopt +++ b/doc/misc/secondary.zoneopt @@ -35,6 +35,7 @@ zone [ ] { 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 5f68d83c52..40a340f629 100644 --- a/doc/misc/static-stub.zoneopt +++ b/doc/misc/static-stub.zoneopt @@ -6,6 +6,7 @@ zone [ ] { forwarders [ port ] [ tls ] { ( | ) [ port ] [ tls ]; ... }; 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 8d0537b136..992aa51e96 100644 --- a/doc/misc/stub.zoneopt +++ b/doc/misc/stub.zoneopt @@ -16,6 +16,7 @@ zone [ ] { 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 52d92037d3..24e2d4f205 100644 --- a/lib/dns/cache.c +++ b/lib/dns/cache.c @@ -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 diff --git a/lib/dns/db.c b/lib/dns/db.c index 3f3ca0ede1..ce27fce8c1 100644 --- a/lib/dns/db.c +++ b/lib/dns/db.c @@ -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); + } +} diff --git a/lib/dns/include/dns/cache.h b/lib/dns/include/dns/cache.h index 738ab4cfe0..c629ab26c4 100644 --- a/lib/dns/include/dns/cache.h +++ b/lib/dns/include/dns/cache.h @@ -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); diff --git a/lib/dns/include/dns/db.h b/lib/dns/include/dns/db.h index 96f9d58a12..254b7d38e5 100644 --- a/lib/dns/include/dns/db.h +++ b/lib/dns/include/dns/db.h @@ -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 diff --git a/lib/dns/include/dns/view.h b/lib/dns/include/dns/view.h index e97835f8c6..ddba1e7401 100644 --- a/lib/dns/include/dns/view.h +++ b/lib/dns/include/dns/view.h @@ -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); /*%< diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index bdcff3061c..623edf162b 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -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); /*%< diff --git a/lib/dns/qpcache.c b/lib/dns/qpcache.c index 329decbb6f..c56bc2abe7 100644 --- a/lib/dns/qpcache.c +++ b/lib/dns/qpcache.c @@ -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 diff --git a/lib/dns/qpzone.c b/lib/dns/qpzone.c index da692d2538..4bcd5e2890 100644 --- a/lib/dns/qpzone.c +++ b/lib/dns/qpzone.c @@ -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 diff --git a/lib/dns/rbt-cachedb.c b/lib/dns/rbt-cachedb.c index 779eb143d6..1f252eaef4 100644 --- a/lib/dns/rbt-cachedb.c +++ b/lib/dns/rbt-cachedb.c @@ -1583,6 +1583,7 @@ dns_dbmethods_t dns__rbtdb_cachemethods = { .expiredata = expiredata, .deletedata = dns__rbtdb_deletedata, .setmaxrrperset = dns__rbtdb_setmaxrrperset, + .setmaxtypepername = dns__rbtdb_setmaxtypepername, }; /* diff --git a/lib/dns/rbt-zonedb.c b/lib/dns/rbt-zonedb.c index 93b71b9a98..5f29bdafe5 100644 --- a/lib/dns/rbt-zonedb.c +++ b/lib/dns/rbt-zonedb.c @@ -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 diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c index 71ac5c1951..deadde73c7 100644 --- a/lib/dns/rbtdb.c +++ b/lib/dns/rbtdb.c @@ -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; +} diff --git a/lib/dns/rbtdb_p.h b/lib/dns/rbtdb_p.h index fe06b30b13..cee0499a51 100644 --- a/lib/dns/rbtdb_p.h +++ b/lib/dns/rbtdb_p.h @@ -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. */ diff --git a/lib/dns/view.c b/lib/dns/view.c index 15e2e303db..99d8b61b59 100644 --- a/lib/dns/view.c +++ b/lib/dns/view.c @@ -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)); diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 6c27dfe3ec..9f152a7c02 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -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; diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index 528a52de05..18b40fab7f 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -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,