From 39d3e2a8ecc1cb4dccefa3ddea477a2887989485 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Sat, 25 May 2024 11:46:56 +0200 Subject: [PATCH] Add a limit to the number of RR types for single name 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. (cherry picked from commit 00d16211d6368b99f070c1182d8c76b3798ca1db) --- 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 | 15 +++++++++++++++ doc/misc/mirror.zoneopt | 1 + doc/misc/options | 2 ++ doc/misc/primary.zoneopt | 1 + doc/misc/redirect.zoneopt | 1 + doc/misc/secondary.zoneopt | 1 + doc/misc/static-stub.zoneopt | 1 + doc/misc/stub.zoneopt | 1 + lib/dns/cache.c | 12 ++++++++++++ lib/dns/db.c | 9 +++++++++ lib/dns/dnsrps.c | 3 ++- lib/dns/include/dns/cache.h | 6 ++++++ lib/dns/include/dns/db.h | 11 +++++++++++ lib/dns/include/dns/view.h | 7 +++++++ lib/dns/include/dns/zone.h | 13 +++++++++++++ lib/dns/rbtdb.c | 30 ++++++++++++++++++++++++++++-- lib/dns/sdb.c | 3 ++- lib/dns/sdlz.c | 3 ++- lib/dns/view.c | 10 ++++++++++ lib/dns/zone.c | 15 +++++++++++++++ lib/isccfg/namedconf.c | 3 +++ 25 files changed, 163 insertions(+), 6 deletions(-) diff --git a/bin/named/config.c b/bin/named/config.c index 25f3715fc6..47549c54f8 100644 --- a/bin/named/config.c +++ b/bin/named/config.c @@ -236,6 +236,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 843f5566d0..6ad090fd5f 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -5565,6 +5565,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 e6bf9377d6..384a81e185 100644 --- a/bin/named/zoneconf.c +++ b/bin/named/zoneconf.c @@ -1091,6 +1091,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/bin/tests/system/dyndb/driver/db.c b/bin/tests/system/dyndb/driver/db.c index 4e08edca5e..d9cdfa016a 100644 --- a/bin/tests/system/dyndb/driver/db.c +++ b/bin/tests/system/dyndb/driver/db.c @@ -612,7 +612,8 @@ static dns_dbmethods_t sampledb_methods = { NULL, /* setservestalerefresh */ NULL, /* getservestalerefresh */ NULL, /* setgluecachestats */ - NULL /* setmaxrrperset */ + NULL, /* setmaxrrperset */ + NULL /* setmaxtypepername */ }; /* Auxiliary driver functions. */ diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst index f7cde02804..2ddc368514 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -3781,6 +3781,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 8d4a687ee8..5f688ca354 100644 --- a/doc/misc/mirror.zoneopt +++ b/doc/misc/mirror.zoneopt @@ -25,6 +25,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 4fd272224c..a8cd16425c 100644 --- a/doc/misc/options +++ b/doc/misc/options @@ -192,6 +192,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 ; @@ -482,6 +483,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 ; diff --git a/doc/misc/primary.zoneopt b/doc/misc/primary.zoneopt index e8ef80cf6e..1de2f217d4 100644 --- a/doc/misc/primary.zoneopt +++ b/doc/misc/primary.zoneopt @@ -41,6 +41,7 @@ zone [ ] { 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/redirect.zoneopt b/doc/misc/redirect.zoneopt index 613dbdb890..9d238c1a3e 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 | ); primaries [ port ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; zone-statistics ( full | terse | none | ); diff --git a/doc/misc/secondary.zoneopt b/doc/misc/secondary.zoneopt index fc1c8526db..169fa9bbb0 100644 --- a/doc/misc/secondary.zoneopt +++ b/doc/misc/secondary.zoneopt @@ -37,6 +37,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 706fa3cc88..93a3220017 100644 --- a/doc/misc/static-stub.zoneopt +++ b/doc/misc/static-stub.zoneopt @@ -6,6 +6,7 @@ zone [ ] { forwarders [ port ] { ( | ) [ port ]; ... }; 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 c1c5d68566..28346826e4 100644 --- a/doc/misc/stub.zoneopt +++ b/doc/misc/stub.zoneopt @@ -17,6 +17,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 0669f4a9f6..8fdc14290c 100644 --- a/lib/dns/cache.c +++ b/lib/dns/cache.c @@ -147,6 +147,7 @@ struct dns_cache { dns_ttl_t serve_stale_refresh; isc_stats_t *stats; uint32_t maxrrperset; + uint32_t maxtypepername; }; /*** @@ -212,6 +213,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); if (cache->taskmgr == NULL) { *dbp = db; @@ -1251,6 +1253,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 09bf9394b5..315b97dbd3 100644 --- a/lib/dns/db.c +++ b/lib/dns/db.c @@ -1130,3 +1130,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/dnsrps.c b/lib/dns/dnsrps.c index a8a6f9c6a3..73f11daacb 100644 --- a/lib/dns/dnsrps.c +++ b/lib/dns/dnsrps.c @@ -975,7 +975,8 @@ static dns_dbmethods_t rpsdb_db_methods = { NULL, /* setservestalerefresh */ NULL, /* getservestalerefresh */ NULL, /* setgluecachestats */ - NULL /* setmaxrrperset */ + NULL, /* setmaxrrperset */ + NULL /* setmaxtypepername */ }; static dns_rdatasetmethods_t rpsdb_rdataset_methods = { diff --git a/lib/dns/include/dns/cache.h b/lib/dns/include/dns/cache.h index 1091110926..9f4a57fadb 100644 --- a/lib/dns/include/dns/cache.h +++ b/lib/dns/include/dns/cache.h @@ -286,6 +286,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 4157ae4ed9..07ff9a43d5 100644 --- a/lib/dns/include/dns/db.h +++ b/lib/dns/include/dns/db.h @@ -186,6 +186,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); 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, @@ -1767,4 +1768,14 @@ dns_db_setmaxrrperset(dns_db_t *db, uint32_t value); * is nonzero, then any subsequent attempt to add an rdataset with * more than 'value' RRs will return ISC_R_NOSPACE. */ + +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, then any subsequent attempt to add an rdataset with a + * RR type that would exceed the number of already stored RR types will return + * ISC_R_NOSPACE. + */ ISC_LANG_ENDDECLS diff --git a/lib/dns/include/dns/view.h b/lib/dns/include/dns/view.h index 38ebb922dc..516c209b92 100644 --- a/lib/dns/include/dns/view.h +++ b/lib/dns/include/dns/view.h @@ -192,6 +192,7 @@ struct dns_view { uint32_t fail_ttl; dns_badcache_t *failcache; uint32_t maxrrperset; + uint32_t maxtypepername; /* * Configurable data for server use only, @@ -1420,4 +1421,10 @@ 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. + */ + ISC_LANG_ENDDECLS diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index 2576119415..9c63e8848c 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -376,6 +376,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/rbtdb.c b/lib/dns/rbtdb.c index 7b2e62d7c8..50df5508e6 100644 --- a/lib/dns/rbtdb.c +++ b/lib/dns/rbtdb.c @@ -463,6 +463,7 @@ struct dns_rbtdb { rbtdb_serial_t least_serial; rbtdb_serial_t next_serial; uint32_t maxrrperset; + uint32_t maxtypepername; rbtdb_version_t *current_version; rbtdb_version_t *future_version; rbtdb_versionlist_t open_versions; @@ -6260,6 +6261,7 @@ add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, const dns_name_t *nodename, rbtdb_rdatatype_t negtype, sigtype; dns_trust_t trust; int idx; + uint32_t ntypes = 0; /* * Add an rdatasetheader_t to a node. @@ -6324,6 +6326,7 @@ add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, const dns_name_t *nodename, set_ttl(rbtdb, topheader, 0); mark_header_ancient(rbtdb, topheader); } + ntypes = 0; /* Always add the negative entry */ goto find_header; } /* @@ -6347,9 +6350,11 @@ add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, const dns_name_t *nodename, * 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 == RBTDB_RDATATYPE_NCACHEANY) || (newheader->type == sigtype && @@ -6394,9 +6399,11 @@ add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, const dns_name_t *nodename, } } + ntypes = 0; for (topheader = rbtnode->data; topheader != NULL; topheader = topheader->next) { + ++ntypes; if (prio_type(topheader->type)) { prioheader = topheader; } @@ -6766,6 +6773,14 @@ find_header: /* * No rdatasets of the given type exist at the node. */ + if (rbtdb->maxtypepername > 0 && + ntypes >= rbtdb->maxtypepername) + { + free_rdataset(rbtdb, rbtdb->common.mctx, + newheader); + return (DNS_R_TOOMANYRECORDS); + } + newheader->down = NULL; if (prio_type(newheader->type)) { @@ -8127,6 +8142,15 @@ setmaxrrperset(dns_db_t *db, uint32_t maxrrperset) { rbtdb->maxrrperset = maxrrperset; } +static void +setmaxtypepername(dns_db_t *db, uint32_t maxtypepername) { + dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; + + REQUIRE(VALID_RBTDB(rbtdb)); + + rbtdb->maxtypepername = maxtypepername; +} + static dns_stats_t * getrrsetstats(dns_db_t *db) { dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db; @@ -8249,7 +8273,8 @@ static dns_dbmethods_t zone_methods = { attach, NULL, /* setservestalerefresh */ NULL, /* getservestalerefresh */ setgluecachestats, - setmaxrrperset }; + setmaxrrperset, + setmaxtypepername }; static dns_dbmethods_t cache_methods = { attach, detach, @@ -8300,7 +8325,8 @@ static dns_dbmethods_t cache_methods = { attach, setservestalerefresh, getservestalerefresh, NULL, - setmaxrrperset }; + setmaxrrperset, + setmaxtypepername }; isc_result_t dns_rbtdb_create(isc_mem_t *mctx, const dns_name_t *origin, dns_dbtype_t type, diff --git a/lib/dns/sdb.c b/lib/dns/sdb.c index 3d841e3511..f4481d5ca0 100644 --- a/lib/dns/sdb.c +++ b/lib/dns/sdb.c @@ -1319,7 +1319,8 @@ static dns_dbmethods_t sdb_methods = { NULL, /* setservestalerefresh */ NULL, /* getservestalerefresh */ NULL, /* setgluecachestats */ - NULL /* setmaxrrperset */ + NULL, /* setmaxrrperset */ + NULL /* setmaxtypepername */ }; static isc_result_t diff --git a/lib/dns/sdlz.c b/lib/dns/sdlz.c index 2568485d3e..cb8fde5260 100644 --- a/lib/dns/sdlz.c +++ b/lib/dns/sdlz.c @@ -1292,7 +1292,8 @@ static dns_dbmethods_t sdlzdb_methods = { NULL, /* setservestalerefresh */ NULL, /* getservestalerefresh */ NULL, /* setgluecachestats */ - NULL /* setmaxrrperset */ + NULL, /* setmaxrrperset */ + NULL /* setmaxtypepername */ }; /* diff --git a/lib/dns/view.c b/lib/dns/view.c index 66c7d85cf7..231041efab 100644 --- a/lib/dns/view.c +++ b/lib/dns/view.c @@ -894,6 +894,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 @@ -2770,3 +2771,12 @@ dns_view_setmaxrrperset(dns_view_t *view, uint32_t value) { dns_cache_setmaxrrperset(view->cache, 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); + } +} diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 7f81c9b439..9b27c4a90a 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -310,6 +310,7 @@ struct dns_zone { uint32_t maxrecords; uint32_t maxrrperset; + uint32_t maxtypepername; isc_sockaddr_t *primaries; dns_name_t **primarykeynames; @@ -12309,6 +12310,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, @@ -14798,6 +14809,8 @@ ns_query(dns_zone_t *zone, dns_rdataset_t *soardataset, dns_stub_t *stub) { } dns_db_settask(stub->db, zone->task, zone->task); dns_db_setmaxrrperset(stub->db, zone->maxrrperset); + dns_db_setmaxtypepername(stub->db, + zone->maxtypepername); } result = dns_db_newversion(stub->db, &stub->version); @@ -17903,6 +17916,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_db_setmaxtypepername(zone->db, zone->maxtypepername); DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED | DNS_ZONEFLG_NEEDNOTIFY); return (ISC_R_SUCCESS); @@ -24329,6 +24343,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); + dns_db_setmaxtypepername(db, zone->maxtypepername); *dbp = db; diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index 7cbdab60d0..0b78e3c5d6 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -2303,6 +2303,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,