" max-cache-size default;\n\
max-cache-ttl 604800; /* 1 week */\n\
max-clients-per-query 100;\n\
+ max-delegation-servers 13;\n\
max-ncache-ttl 10800; /* 3 hours */\n\
max-recursion-depth 7;\n\
max-recursion-queries 50;\n\
INSIST(result == ISC_R_SUCCESS);
dns_view_setmaxqueries(view, cfg_obj_asuint32(obj));
+ obj = NULL;
+ result = named_config_get(maps, "max-delegation-servers", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ CHECK(dns_view_setmaxdelegationservers(view, cfg_obj_asuint32(obj)));
+
obj = NULL;
result = named_config_get(maps, "max-validations-per-fetch", &obj);
if (result == ISC_R_SUCCESS) {
* information regarding copyright ownership.
*/
+{% set maxdelegationservers = maxdelegationservers | default(None) %}
+
options {
query-source address 10.53.0.4;
notify-source 10.53.0.4;
dnssec-validation no;
dnstap { resolver query; };
dnstap-output file "dnstap.out";
+ {% if maxdelegationservers %}
+ @maxdelegationservers@
+ {% endif %}
};
zone "." {
assert query == expected_query
-def test_selfpointedglue_nslimit(ns4):
+def check_nsprocessinglimit(ns, queries_count):
msg = isctest.query.create("a.sub.example.tld.", "A")
- res = isctest.query.tcp(msg, ns4.ip)
+ res = isctest.query.tcp(msg, ns.ip)
isctest.check.servfail(res)
- # The 4 formers lines are request to find sub.example2.tld NSs.
- # The latest 20 are queries to sub.example2.tld NSs.
- ips_and_queries = extract_dnstap(ns4, 24)
+ # The 4 formers lines are request to find sub.example.tld NSs.
+ # The latest are queries to sub.example.tld NSs.
+ ips_and_queries = extract_dnstap(ns, queries_count)
# Checking the begining of the resulution
expect_next_ip_and_query(
],
ips_and_queries,
)
- expect_query("a.sub.example.tld/IN/A", 20, ips_and_queries)
+ expect_query("a.sub.example.tld/IN/A", queries_count - 4, ips_and_queries)
+
+
+def test_nsprocessinglimit_default(ns4):
+ check_nsprocessinglimit(ns4, 17)
+
+
+def reconfig_maxdelegationservers(ns, templates, count):
+ templates.render(
+ "ns4/named.conf", {"maxdelegationservers": f"max-delegation-servers {count};"}
+ )
+ with ns.watch_log_from_here() as watcher:
+ ns.rndc("flush")
+ ns.rndc("reload")
+ watcher.wait_for_line("running")
+
+
+def reconfig_maxdelegationservers_failure(ns, templates, count):
+ templates.render(
+ "ns4/named.conf", {"maxdelegationservers": f"max-delegation-servers {count};"}
+ )
+ with ns.watch_log_from_here() as watcher:
+ # Reload will fail, so do not raise the exception so the config line
+ # can be checked.
+ ns.rndc("reload", raise_on_exception=False)
+ watcher.wait_for_line("reloading configuration failed: out of range")
+
+
+def test_nsprocessinglimit_13ns(ns4, templates):
+ reconfig_maxdelegationservers(ns4, templates, 13)
+ check_nsprocessinglimit(ns4, 17)
+
+
+def test_nsprocessinglimit_5ns(ns4, templates):
+ reconfig_maxdelegationservers(ns4, templates, 5)
+ check_nsprocessinglimit(ns4, 9)
+
+
+def test_nsprocessinglimit_20ns(ns4, templates):
+ reconfig_maxdelegationservers(ns4, templates, 20)
+ check_nsprocessinglimit(ns4, 24)
+
+
+def test_nsprocessinglimit_lower(ns4, templates):
+ reconfig_maxdelegationservers_failure(ns4, templates, 0)
+
+
+def test_nsprocessinglimit_upper(ns4, templates):
+ reconfig_maxdelegationservers_failure(ns4, templates, 101)
exceed 90 seconds and is truncated to 90 seconds if set to a greater
value.
+.. namedconf:statement:: max-delegation-servers
+ :tags: server
+ :short: Configure the maximum number of nameserver names considered for a delegation
+
+ When looking up remote nameservers for a delegation, the list of nameserver
+ names is sorted according to Canonical RR Ordering within an RRset (see
+ :rfc:`4034` Section 6.3), and the number of names for which :iscman:`named`
+ looks up IP addresses is capped at :any:`max-delegation-servers`.
+
+ This capped list of nameserver names is then randomly shuffled every time
+ :iscman:`named` needs additional remote addresses for those nameservers.
+ This randomized selection works around situations where the first few
+ nameserver names in the zone are unresponsive.
+
+ A limited number of outgoing DNS queries (fetches) are then sent to resolve
+ those shuffled nameserver names. The number of concurrent fetches starts at
+ 3 for fetch depth 0 (the initial level where remote DNS queries begin) and
+ decreases with each deeper level. At depth 4 and below, :iscman:`named` only
+ initiates a remote fetch on an as-needed basis. This means that a new
+ outgoing DNS query is initiated only if the DNS resolver does not already have
+ existing IP addresses for any of the nameserver names in the cache.
+
+ The default and recommended value is ``13``. This limit prevents excessive
+ resource use while processing large or misconfigured delegations. The default
+ value should only be increased in controlled environments where a remote
+ attacker cannot force the resolver to perform excessive, wasteful queries.
+
+ :any:`max-delegation-servers` cannot be set to less than ``1``, as this would
+ break DNS resolution. The maximum permitted value is ``100``. Any configured
+ value outside of those bounds is rejected.
+
.. namedconf:statement:: max-ncache-ttl
:tags: server
:short: Specifies the maximum retention time (in seconds) for storage of negative answers in the server's cache.
max-cache-size ( default | unlimited | <sizeval> | <percentage> );
max-cache-ttl <duration>;
max-clients-per-query <integer>;
+ max-delegation-servers <integer>; // experimental
max-ixfr-ratio ( unlimited | <percentage> );
max-journal-size ( default | unlimited | <sizeval> );
max-ncache-ttl <duration>;
max-cache-size ( default | unlimited | <sizeval> | <percentage> );
max-cache-ttl <duration>;
max-clients-per-query <integer>;
+ max-delegation-servers <integer>; // experimental
max-ixfr-ratio ( unlimited | <percentage> );
max-journal-size ( default | unlimited | <sizeval> );
max-ncache-ttl <duration>;
uint32_t sig0message_checks_limit;
uint32_t maxrrperset;
uint32_t maxtypepername;
+ uint32_t max_delegation_servers;
uint16_t max_queries;
uint8_t max_restarts;
#define DNS_VIEWATTR_ADBSHUTDOWN 0x02
#define DNS_VIEWATTR_REQSHUTDOWN 0x04
+/*
+ * Maximum number of the max-delegation-servers, this defines the size of the
+ * thread-local storage.
+ */
+#define MAX_DELEGATION_SERVERS 100
+
#ifdef HAVE_LMDB
#define DNS_LMDB_COMMON_FLAGS (MDB_CREATE | MDB_NOSUBDIR | MDB_NOLOCK)
#ifndef __OpenBSD__
*\li 'view' is valid;
*\li 'max_queries' is greater than 0.
*/
+
+isc_result_t
+dns_view_setmaxdelegationservers(dns_view_t *view, uint32_t max_servers);
+/*%
+ * Set the maximum number of delegation nameservers processed when looking up
+ * their IP addresses.
+ */
#include <dns/validator.h>
#include <dns/zone.h>
+#include "dns/view.h"
+
#ifdef WANT_QUERYTRACE
#define RTRACE(m) \
isc_log_write(DNS_LOGCATEGORY_RESOLVER, DNS_LOGMODULE_RESOLVER, \
#define DEFAULT_MAX_QUERIES 50
#endif /* ifndef DEFAULT_MAX_QUERIES */
-/*
- * IP address lookups are performed for at most NS_PROCESSING_LIMIT NS RRs in
- * any NS RRset encountered, to avoid excessive resource use while processing
- * large delegations.
- */
-#define NS_PROCESSING_LIMIT 20
-
/* Hash table for zone counters */
#ifndef RES_DOMAIN_HASH_BITS
#define RES_DOMAIN_HASH_BITS 12
dns_rdata_ns_t ns;
bool have_address = false;
unsigned int ns_processed = 0;
- dns_rdata_t nameservers_s[NS_PROCESSING_LIMIT];
- dns_rdata_t *nameservers[NS_PROCESSING_LIMIT];
+ uint32_t ns_processing_limit = fctx->res->view->max_delegation_servers;
+ static thread_local dns_rdata_t nameservers_s[MAX_DELEGATION_SERVERS];
+ static thread_local dns_rdata_t *nameservers[MAX_DELEGATION_SERVERS];
DNS_RDATASET_FOREACH(&fctx->nameservers) {
dns_rdata_t *rdata = nameservers[ns_processed] =
dns_rdataset_current(&fctx->nameservers, rdata);
- if (++ns_processed >= NS_PROCESSING_LIMIT) {
+ if (++ns_processed >= ns_processing_limit) {
break;
}
}
view->max_queries = max_queries;
}
+
+isc_result_t
+dns_view_setmaxdelegationservers(dns_view_t *view, uint32_t max_servers) {
+ REQUIRE(DNS_VIEW_VALID(view));
+
+ if (max_servers < 1 || max_servers > MAX_DELEGATION_SERVERS) {
+ return ISC_R_RANGE;
+ }
+
+ view->max_delegation_servers = max_servers;
+
+ return ISC_R_SUCCESS;
+}
{ "max-cache-size", &cfg_type_maxcachesize, 0, NULL },
{ "max-cache-ttl", &cfg_type_duration, 0, NULL },
{ "max-clients-per-query", &cfg_type_uint32, 0, NULL },
+ { "max-delegation-servers", &cfg_type_uint32,
+ CFG_CLAUSEFLAG_EXPERIMENTAL, NULL },
{ "max-ncache-ttl", &cfg_type_duration, 0, NULL },
{ "max-recursion-depth", &cfg_type_uint32, 0, NULL },
{ "max-recursion-queries", &cfg_type_uint32, 0, NULL },