[9.20] new: usr: Tighten 'max-recursion-queries' and add 'max-query-restarts' option

There were cases in resolver.c when the `max-recursion-queries` quota was ineffective. It was possible to craft zones that would cause a resolver to waste resources by sending excessive queries while attempting to resolve a name. This has been addressed by correcting errors in the implementation of `max-recursion-queries`, and by reducing the default value from 100 to 32.

In addition, a new `max-query-restarts` option has been added which limits the number of times a recursive server will follow CNAME or DNAME records before terminating resolution. This was previously a hard-coded limit of 16, and now defaults to 11.
 
Closes #4741

Backport of MR !9281

Merge branch 'backport-4741-reclimit-restarts-9.20' into 'bind-9.20'

See merge request isc-projects/bind9!9282
This commit is contained in:
Evan Hunt 2024-08-07 22:52:52 +00:00
commit 42e70b0f0e
24 changed files with 232 additions and 57 deletions

View file

@ -88,6 +88,9 @@
#define MAXNAME (DNS_NAME_MAXTEXT + 1)
#define MAX_QUERIES 32
#define MAX_RESTARTS 11
/* Variables used internally by delv. */
char *progname = NULL;
static isc_mem_t *mctx = NULL;
@ -130,6 +133,9 @@ static bool showcomments = true, showdnssec = true, showtrust = true,
multiline = false, short_form = false, print_unknown_format = false,
yaml = false, fulltrace = false;
static uint32_t maxqueries = MAX_QUERIES;
static uint32_t restarts = MAX_RESTARTS;
static bool resolve_trace = false, validator_trace = false,
message_trace = false, send_trace = false;
@ -1191,6 +1197,23 @@ plus_option(char *option) {
break;
case 'm':
switch (cmd[1]) {
case 'a': /* maxqueries */
FULLCHECK("maxqueries");
if (value == NULL) {
goto need_value;
}
if (!state) {
goto invalid_option;
}
result = parse_uint(&maxqueries, value, UINT_MAX,
"maxqueries");
if (result != ISC_R_SUCCESS) {
fatal("Couldn't parse maxqueries");
}
if (maxqueries == 0) {
fatal("maxqueries must be nonzero");
}
break;
case 't': /* mtrace */
FULLCHECK("mtrace");
message_trace = state;
@ -1243,6 +1266,22 @@ plus_option(char *option) {
break;
case 'r':
switch (cmd[1]) {
case 'e': /* restarts */
FULLCHECK("restarts");
if (value == NULL) {
goto need_value;
}
if (!state) {
goto invalid_option;
}
result = parse_uint(&restarts, value, 255, "restarts");
if (result != ISC_R_SUCCESS) {
fatal("Couldn't parse restarts");
}
if (restarts == 0) {
fatal("restarts must be between 1..255");
}
break;
case 'o': /* root */
FULLCHECK("root");
if (state && no_sigs) {
@ -1370,10 +1409,7 @@ plus_option(char *option) {
break;
default:
invalid_option:
/*
* We can also add a "need_value:" case here if we ever
* add a plus-option that requires a specified value
*/
need_value:
fprintf(stderr, "Invalid option: +%s\n", option);
usage();
}
@ -1898,6 +1934,7 @@ run_resolve(void *arg) {
/* Create client */
CHECK(dns_client_create(mctx, loopmgr, netmgr, 0, tlsctx_client_cache,
&client, srcaddr4, srcaddr6));
dns_client_setmaxrestarts(client, restarts);
/* Set the nameserver */
if (server != NULL) {
@ -2162,6 +2199,7 @@ run_server(void *arg) {
dns_view_setcache(view, cache, false);
dns_cache_detach(&cache);
dns_view_setdstport(view, destport);
dns_view_setmaxrestarts(view, restarts);
CHECK(dns_rootns_create(mctx, dns_rdataclass_in, hintfile, &roothints));
dns_view_sethints(view, roothints);
@ -2175,6 +2213,7 @@ run_server(void *arg) {
CHECK(dns_view_createresolver(view, netmgr, 0, tlsctx_client_cache,
dispatch, NULL));
dns_resolver_setmaxqueries(view->resolver, maxqueries);
isc_stats_create(mctx, &resstats, dns_resstatscounter_max);
dns_resolver_setstats(view->resolver, resstats);

View file

@ -337,6 +337,18 @@ assign values to options like the timeout interval. They have the form
they are replaced by the string ``[omitted]`` or, in the DNSKEY case, the
key ID is displayed as the replacement, e.g. ``[ key id = value ]``.
.. option:: +restarts
When name server mode (``delv +ns``) is in use, this option sets the
maximum number of CNAME queries to follow before terminating resolution.
This prevents ``delv`` from hanging in the event of a CNAME loop.
The default is 11.
.. option:: +maxqueries
This option specifies the maximum number of queries to send to resolve
a name before giving up. The default is 32.
.. option:: +trust, +notrust
This option controls whether to display the trust level when printing a record.

View file

@ -170,7 +170,8 @@ options {\n\
max-clients-per-query 100;\n\
max-ncache-ttl 10800; /* 3 hours */\n\
max-recursion-depth 7;\n\
max-recursion-queries 100;\n\
max-recursion-queries 32;\n\
max-query-restarts 11;\n\
max-stale-ttl 86400; /* 1 day */\n\
message-compression yes;\n\
min-ncache-ttl 0; /* 0 hours */\n\

View file

@ -5502,6 +5502,11 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config,
INSIST(result == ISC_R_SUCCESS);
dns_resolver_setmaxqueries(view->resolver, cfg_obj_asuint32(obj));
obj = NULL;
result = named_config_get(maps, "max-query-restarts", &obj);
INSIST(result == ISC_R_SUCCESS);
dns_view_setmaxrestarts(view, cfg_obj_asuint32(obj));
obj = NULL;
result = named_config_get(maps, "max-validations-per-fetch", &obj);
if (result == ISC_R_SUCCESS) {

View file

@ -37,11 +37,28 @@ key rndc_key {
algorithm @DEFAULT_HMAC@;
};
key restart16 {
secret "1234abcd8765";
algorithm @DEFAULT_HMAC@;
};
controls {
inet 10.53.0.7 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
};
zone "." {
type hint;
file "root.hint";
view restart16 {
match-clients { key restart16; none; };
max-query-restarts 16;
zone "." {
type hint;
file "root.hint";
};
};
view default {
zone "." {
type hint;
file "root.hint";
};
};

View file

@ -442,11 +442,13 @@ n=$((n + 1))
echo_i "checking CNAME loops are detected (resolver) ($n)"
ret=0
$RNDCCMD 10.53.0.7 null --- start test$n --- 2>&1 | sed 's/^/ns7 /' | cat_i
$DIG $DIGOPTS @10.53.0.7 loop.example >dig.out.test$n
grep "status: SERVFAIL" dig.out.test$n >/dev/null || ret=1
grep "ANSWER: 0" dig.out.test$n >/dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
$DIG $DIGOPTS @10.53.0.7 loop.example >dig.out.1.test$n
grep "status: NOERROR" dig.out.1.test$n >/dev/null || ret=1
grep "ANSWER: 12" dig.out.1.test$n >/dev/null || ret=1
# also check with max-query-restarts 16:
$DIG $DIGOPTS @10.53.0.7 -y "${DEFAULT_HMAC}:restart16:1234abcd8765" loop.example >dig.out.2.test$n
grep "status: NOERROR" dig.out.2.test$n >/dev/null || ret=1
grep "ANSWER: 17" dig.out.2.test$n >/dev/null || ret=1
n=$((n + 1))
echo_i "checking CNAME loops are detected (auth) ($n)"
@ -454,7 +456,7 @@ ret=0
$DIG $DIGOPTS @10.53.0.2 loop.example >dig.out.test$n
grep "status: SERVFAIL" dig.out.test$n >/dev/null || ret=1
grep "max. restarts reached" dig.out.test$n >/dev/null || ret=1
grep "ANSWER: 17" dig.out.test$n >/dev/null || ret=1
grep "ANSWER: 12" dig.out.test$n >/dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))

View file

@ -81,6 +81,7 @@ options {
check-names primary warn;
check-names secondary ignore;
max-cache-size 20000000000000;
max-query-restarts 10;
nta-lifetime 604800;
nta-recheck 604800;
validate-except {
@ -112,6 +113,7 @@ view "first" {
max-ixfr-ratio unlimited;
};
dnssec-validation auto;
max-query-restarts 15;
zone-statistics terse;
};
view "second" {

View file

@ -22,6 +22,7 @@ options {
listen-on-v6 { none; };
servfail-ttl 0;
qname-minimization disabled;
max-recursion-queries 50;
max-recursion-depth 12;
recursion yes;
dnssec-validation yes;

View file

@ -30,6 +30,7 @@ options {
max-zone-ttl unlimited;
resolver-query-timeout 5000; # 5 seconds
attach-cache "globalcache";
max-recursion-queries 50;
};
trust-anchors { };

View file

@ -149,7 +149,7 @@ dig_with_opts +tcp longcname1.example.net @10.53.0.1 a >dig.out.ns1.test${n} ||
grep -F "status: SERVFAIL" dig.out.ns1.test${n} >/dev/null || ret=1
grep -F "max. restarts reached" dig.out.ns1.test${n} >/dev/null || ret=1
lines=$(grep -F "CNAME" dig.out.ns1.test${n} | wc -l)
test ${lines:-1} -eq 17 || ret=1
test ${lines:-1} -eq 12 || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))

View file

@ -4594,9 +4594,20 @@ Tuning
:tags: server, query
:short: Sets the maximum number of iterative queries while servicing a recursive query.
This sets the maximum number of iterative queries that may be sent while
servicing a recursive query. If more queries are sent, the recursive
query is terminated and returns SERVFAIL. The default is 100.
This sets the maximum number of iterative queries that may be sent
by a resolver while looking up a single name. If more queries than this
need to be sent before an answer is reached, then recursion is terminated
and a SERVFAIL response is returned to the client. (Note: if the answer
is a CNAME, then the subsequent lookup for the target of the CNAME is
counted separately.) The default is 32.
.. namedconf:statement:: max-query-restarts
:tags: server, query
:short: Sets the maximum number of chained CNAMEs to follow
This sets the maximum number of successive CNAME targets to follow
when resolving a client query, before terminating the query to avoid a
CNAME loop. Valid values are 1 to 255. The default is 11.
.. namedconf:statement:: notify-delay
:tags: transfer, zone

View file

@ -182,6 +182,7 @@ options {
max-ixfr-ratio ( unlimited | <percentage> );
max-journal-size ( default | unlimited | <sizeval> );
max-ncache-ttl <duration>;
max-query-restarts <integer>;
max-records <integer>;
max-records-per-type <integer>;
max-recursion-depth <integer>;
@ -471,6 +472,7 @@ view <string> [ <class> ] {
max-ixfr-ratio ( unlimited | <percentage> );
max-journal-size ( default | unlimited | <sizeval> );
max-ncache-ttl <duration>;
max-query-restarts <integer>;
max-records <integer>;
max-records-per-type <integer>;
max-recursion-depth <integer>;

View file

@ -57,8 +57,6 @@
#define UCTX_MAGIC ISC_MAGIC('U', 'c', 't', 'x')
#define UCTX_VALID(c) ISC_MAGIC_VALID(c, UCTX_MAGIC)
#define MAX_RESTARTS 16
#define CHECK(r) \
do { \
result = (r); \
@ -81,6 +79,7 @@ struct dns_client {
unsigned int find_timeout;
unsigned int find_udpretries;
uint8_t max_restarts;
isc_refcount_t references;
@ -90,6 +89,7 @@ struct dns_client {
#define DEF_FIND_TIMEOUT 5
#define DEF_FIND_UDPRETRIES 3
#define DEF_MAX_RESTARTS 11
/*%
* Internal state for a single name resolution procedure
@ -250,6 +250,7 @@ dns_client_create(isc_mem_t *mctx, isc_loopmgr_t *loopmgr, isc_nm_t *nm,
*client = (dns_client_t){
.loop = isc_loop_get(loopmgr, 0),
.nm = nm,
.max_restarts = DEF_MAX_RESTARTS,
};
result = dns_dispatchmgr_create(mctx, loopmgr, nm,
@ -384,6 +385,14 @@ dns_client_setservers(dns_client_t *client, dns_rdataclass_t rdclass,
return (result);
}
void
dns_client_setmaxrestarts(dns_client_t *client, uint8_t max_restarts) {
REQUIRE(DNS_CLIENT_VALID(client));
REQUIRE(max_restarts > 0);
client->max_restarts = max_restarts;
}
static isc_result_t
getrdataset(isc_mem_t *mctx, dns_rdataset_t **rdatasetp) {
dns_rdataset_t *rdataset;
@ -778,7 +787,9 @@ client_resfind(resctx_t *rctx, dns_fetchresponse_t *resp) {
/*
* Limit the number of restarts.
*/
if (want_restart && rctx->restarts == MAX_RESTARTS) {
if (want_restart &&
rctx->restarts == rctx->client->max_restarts)
{
want_restart = false;
result = ISC_R_QUOTA;
send_event = true;

View file

@ -174,6 +174,19 @@ dns_client_setservers(dns_client_t *client, dns_rdataclass_t rdclass,
*\li Anything else Failure.
*/
void
dns_client_setmaxrestarts(dns_client_t *client, uint8_t max_restarts);
/*%<
* Set the number of permissible chained queries before we give up,
* to prevent CNAME loops. This defaults to 11.
*
* Requires:
*
*\li 'client' is a valid client.
*\li 'max_restarts' is greater than 0.
*/
typedef void (*dns_client_resolve_cb)(dns_client_t *client,
const dns_name_t *name,
dns_namelist_t *namelist,

View file

@ -146,12 +146,13 @@ struct dns_validator {
unsigned int authfail;
isc_stdtime_t start;
bool digest_sha1;
bool supported_algorithm;
dns_rdata_t rdata;
bool resume;
uint32_t *nvalidations;
uint32_t *nfails;
bool digest_sha1;
bool supported_algorithm;
dns_rdata_t rdata;
bool resume;
uint32_t *nvalidations;
uint32_t *nfails;
isc_counter_t *qc;
};
/*%
@ -170,7 +171,7 @@ dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
dns_message_t *message, unsigned int options,
isc_loop_t *loop, isc_job_cb cb, void *arg,
uint32_t *nvalidations, uint32_t *nfails,
dns_validator_t **validatorp);
isc_counter_t *qc, dns_validator_t **validatorp);
/*%<
* Start a DNSSEC validation.
*

View file

@ -185,6 +185,7 @@ struct dns_view {
unsigned int udpsize;
uint32_t maxrrperset;
uint32_t maxtypepername;
uint8_t max_restarts;
/*
* Configurable data for server use only,
@ -1327,4 +1328,16 @@ dns_view_getadb(dns_view_t *view, dns_adb_t **adbp);
*\li 'adbp' is non-NULL and '*adbp' is NULL.
*/
void
dns_view_setmaxrestarts(dns_view_t *view, uint8_t max_restarts);
/*%<
* Set the number of permissible chained queries before we give up,
* to prevent CNAME loops. This defaults to 11.
*
* Requires:
*
*\li 'view' is valid;
*\li 'max_restarts' is greater than 0.
*/
ISC_LANG_ENDDECLS

View file

@ -215,7 +215,7 @@
/* The default maximum number of iterative queries to allow before giving up. */
#ifndef DEFAULT_MAX_QUERIES
#define DEFAULT_MAX_QUERIES 100
#define DEFAULT_MAX_QUERIES 50
#endif /* ifndef DEFAULT_MAX_QUERIES */
/*
@ -991,7 +991,7 @@ valcreate(fetchctx_t *fctx, dns_message_t *message, dns_adbaddrinfo_t *addrinfo,
result = dns_validator_create(
fctx->res->view, name, type, rdataset, sigrdataset, message,
valoptions, fctx->loop, validated, valarg, &fctx->nvalidations,
&fctx->nfails, &validator);
&fctx->nfails, fctx->qc, &validator);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
inc_stats(fctx->res, dns_resstatscounter_val);
if ((valoptions & DNS_VALIDATOR_DEFER) == 0) {
@ -4520,16 +4520,6 @@ fctx_create(dns_resolver_t *res, isc_loop_t *loop, const dns_name_t *name,
isc_mutex_init(&fctx->lock);
if (qc != NULL) {
isc_counter_attach(qc, &fctx->qc);
} else {
result = isc_counter_create(fctx->mctx, res->maxqueries,
&fctx->qc);
if (result != ISC_R_SUCCESS) {
goto cleanup_fetch;
}
}
/*
* Make fctx->info point to a copy of a formatted string
* "name/type". FCTXTRACE won't work until this is done.
@ -4542,6 +4532,24 @@ fctx_create(dns_resolver_t *res, isc_loop_t *loop, const dns_name_t *name,
FCTXTRACE("create");
if (qc != NULL) {
isc_counter_attach(qc, &fctx->qc);
isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(9),
"fctx %p(%s): attached to counter %p (%d)", fctx,
fctx->info, fctx->qc, isc_counter_used(fctx->qc));
} else {
result = isc_counter_create(fctx->mctx, res->maxqueries,
&fctx->qc);
if (result != ISC_R_SUCCESS) {
goto cleanup_fetch;
}
isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(9),
"fctx %p(%s): created counter %p", fctx,
fctx->info, fctx->qc);
}
#if DNS_RESOLVER_TRACE
fprintf(stderr, "fetchctx__init:%s:%s:%d:%p:%p->references = 1\n",
__func__, __FILE__, __LINE__, fctx, fctx);
@ -7128,7 +7136,7 @@ resume_dslookup(void *arg) {
fetchctx_ref(fctx);
result = dns_resolver_createfetch(
res, fctx->nsname, dns_rdatatype_ns, domain, nsrdataset,
NULL, NULL, 0, fctx->options, 0, NULL, loop,
NULL, NULL, 0, fctx->options, 0, fctx->qc, loop,
resume_dslookup, fctx, &fctx->nsrrset, NULL,
&fctx->nsfetch);
if (result != ISC_R_SUCCESS) {
@ -9556,8 +9564,8 @@ rctx_chaseds(respctx_t *rctx, dns_message_t *message,
fetchctx_ref(fctx);
result = dns_resolver_createfetch(
fctx->res, fctx->nsname, dns_rdatatype_ns, NULL, NULL, NULL,
NULL, 0, fctx->options, 0, NULL, fctx->loop, resume_dslookup,
fctx, &fctx->nsrrset, NULL, &fctx->nsfetch);
NULL, 0, fctx->options, 0, fctx->qc, fctx->loop,
resume_dslookup, fctx, &fctx->nsrrset, NULL, &fctx->nsfetch);
if (result != ISC_R_SUCCESS) {
if (result == DNS_R_DUPLICATE) {
result = DNS_R_SERVFAIL;

View file

@ -16,6 +16,7 @@
#include <isc/async.h>
#include <isc/base32.h>
#include <isc/counter.h>
#include <isc/job.h>
#include <isc/md.h>
#include <isc/mem.h>
@ -974,9 +975,10 @@ create_validator(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
(DNS_VALIDATOR_NOCDFLAG | DNS_VALIDATOR_NONTA));
validator_logcreate(val, name, type, caller, "validator");
result = dns_validator_create(
val->view, name, type, rdataset, sig, NULL, vopts, val->loop,
cb, val, val->nvalidations, val->nfails, &val->subvalidator);
result = dns_validator_create(val->view, name, type, rdataset, sig,
NULL, vopts, val->loop, cb, val,
val->nvalidations, val->nfails, val->qc,
&val->subvalidator);
if (result == ISC_R_SUCCESS) {
dns_validator_attach(val, &val->subvalidator->parent);
val->subvalidator->depth = val->depth + 1;
@ -3355,7 +3357,7 @@ dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
dns_message_t *message, unsigned int options,
isc_loop_t *loop, isc_job_cb cb, void *arg,
uint32_t *nvalidations, uint32_t *nfails,
dns_validator_t **validatorp) {
isc_counter_t *qc, dns_validator_t **validatorp) {
isc_result_t result = ISC_R_FAILURE;
dns_validator_t *val = NULL;
dns_keytable_t *kt = NULL;
@ -3395,6 +3397,10 @@ dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
dns_message_attach(message, &val->message);
}
if (qc != NULL) {
isc_counter_attach(qc, &val->qc);
}
val->mustbesecure = dns_resolver_getmustbesecure(view->resolver, name);
dns_rdataset_init(&val->fdsset);
dns_rdataset_init(&val->frdataset);
@ -3470,6 +3476,9 @@ destroy_validator(dns_validator_t *val) {
if (val->message != NULL) {
dns_message_detach(&val->message);
}
if (val->qc != NULL) {
isc_counter_detach(&val->qc);
}
dns_view_detach(&val->view);
isc_mem_put(mctx, val, sizeof(*val));
}

View file

@ -74,6 +74,12 @@
#define DNS_VIEW_DELONLYHASH 111
/*%
* Default maximum number of chained queries before we give up
* to prevent CNAME loops.
*/
#define DEFAULT_MAX_RESTARTS 11
/*%
* Default EDNS0 buffer size
*/
@ -116,6 +122,7 @@ dns_view_create(isc_mem_t *mctx, dns_dispatchmgr_t *dispatchmgr,
.trust_anchor_telemetry = true,
.root_key_sentinel = true,
.udpsize = DEFAULT_EDNS_BUFSIZE,
.max_restarts = DEFAULT_MAX_RESTARTS,
};
isc_refcount_init(&view->references, 1);
@ -2455,3 +2462,11 @@ dns_view_getadb(dns_view_t *view, dns_adb_t **adbp) {
}
rcu_read_unlock();
}
void
dns_view_setmaxrestarts(dns_view_t *view, uint8_t max_restarts) {
REQUIRE(DNS_VIEW_VALID(view));
REQUIRE(max_restarts > 0);
view->max_restarts = max_restarts;
}

View file

@ -2108,6 +2108,20 @@ check_options(const cfg_obj_t *options, const cfg_obj_t *config,
}
}
obj = NULL;
(void)cfg_map_get(options, "max-query-restarts", &obj);
if (obj != NULL) {
uint32_t restarts = cfg_obj_asuint32(obj);
if (restarts == 0 || restarts > 255) {
cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
"'max-query-restarts' is out of "
"range 1..255)");
if (result == ISC_R_SUCCESS) {
result = ISC_R_RANGE;
}
}
}
if (actx != NULL) {
cfg_aclconfctx_detach(&actx);
}

View file

@ -2153,6 +2153,7 @@ static cfg_clausedef_t view_clauses[] = {
{ "max-ncache-ttl", &cfg_type_duration, 0 },
{ "max-recursion-depth", &cfg_type_uint32, 0 },
{ "max-recursion-queries", &cfg_type_uint32, 0 },
{ "max-query-restarts", &cfg_type_uint32, 0 },
{ "max-stale-ttl", &cfg_type_duration, 0 },
{ "max-udp-size", &cfg_type_uint32, 0 },
{ "max-validations-per-fetch", &cfg_type_uint32,

View file

@ -103,6 +103,7 @@ struct ns_server {
uint16_t transfer_tcp_message_size;
bool interface_auto;
dns_tkeyctx_t *tkeyctx;
uint8_t max_restarts;
/*% Server id for NSID */
char *server_id;

View file

@ -86,12 +86,6 @@
#define dns64_bis_return_excluded_addresses 1
#endif /* if 0 */
/*%
* Maximum number of chained queries before we give up
* to prevent CNAME loops.
*/
#define MAX_RESTARTS 16
#define QUERY_ERROR(qctx, r) \
do { \
(qctx)->result = r; \
@ -2046,10 +2040,10 @@ addname:
/*
* In some cases, a record that has been added as additional
* data may *also* trigger the addition of additional data.
* This cannot go more than MAX_RESTARTS levels deep.
* This cannot go more than 'max-restarts' levels deep.
*/
if (trdataset != NULL && dns_rdatatype_followadditional(type)) {
if (client->additionaldepth++ < MAX_RESTARTS) {
if (client->additionaldepth++ < client->view->max_restarts) {
eresult = dns_rdataset_additionaldata(
trdataset, fname, query_additional_cb, qctx);
}
@ -11580,7 +11574,9 @@ ns_query_done(query_ctx_t *qctx) {
* Do we need to restart the query (e.g. for CNAME chaining)?
*/
if (qctx->want_restart) {
if (qctx->client->query.restarts < MAX_RESTARTS) {
if (qctx->client->query.restarts <
qctx->client->view->max_restarts)
{
query_ctx_t *saved_qctx = NULL;
qctx->client->query.restarts++;
saved_qctx = isc_mem_get(qctx->client->manager->mctx,

View file

@ -38,7 +38,7 @@
void
ns_server_create(isc_mem_t *mctx, ns_matchview_t matchingview,
ns_server_t **sctxp) {
ns_server_t *sctx;
ns_server_t *sctx = NULL;
REQUIRE(sctxp != NULL && *sctxp == NULL);