diff --git a/doc/unbound.conf.5.in b/doc/unbound.conf.5.in index 08c13e898..2dd855c51 100644 --- a/doc/unbound.conf.5.in +++ b/doc/unbound.conf.5.in @@ -1834,6 +1834,13 @@ a name, making sure large NS sets do not loop. It resets on query restarts (e.g., CNAME) and referrals. Default is 32. .TP 5 +.B max\-query\-restarts: \fI +Set the maximum number of times a query is allowed to restart upon encountering +a CNAME record. +If a query encounters more than the specified number of CNAME +records before resolving, Unbound will reply with SERVFAIL. +Default is 11. +.TP 5 .B fast\-server\-permil: \fI Specify how many times out of 1000 to pick from the set of fastest servers. 0 turns the feature off. A value of 900 would pick from the fastest @@ -1867,7 +1874,7 @@ errors. Default is "no". When the \fBval-log-level\fR option is also set to \fB2\fR, responses with Extended DNS Errors concerning DNSSEC failures that are not served from cache, will also contain a descriptive text message about the reason for the failure. -.TP +.TP 5 .B ede\-serve\-expired: \fI If enabled, Unbound will attach an Extended DNS Error (RFC8914) Code 3 - Stale Answer as EDNS0 option to the expired response. Note that this will not attach diff --git a/iterator/iter_utils.c b/iterator/iter_utils.c index b17b6ef86..b2a2309ab 100644 --- a/iterator/iter_utils.c +++ b/iterator/iter_utils.c @@ -176,6 +176,7 @@ iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg) iter_env->supports_ipv4 = cfg->do_ip4; iter_env->outbound_msg_retry = cfg->outbound_msg_retry; iter_env->max_sent_count = cfg->max_sent_count; + iter_env->max_query_restarts = cfg->max_query_restarts; return 1; } diff --git a/iterator/iterator.c b/iterator/iterator.c index dba9b58bb..33095b2b5 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -1314,7 +1314,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, /* We enforce a maximum number of query restarts. This is primarily a * cheap way to prevent CNAME loops. */ - if(iq->query_restart_count > MAX_RESTART_COUNT) { + if(iq->query_restart_count > ie->max_query_restarts) { verbose(VERB_QUERY, "request has exceeded the maximum number" " of query restarts with %d", iq->query_restart_count); errinf(qstate, "request has exceeded the maximum number " diff --git a/iterator/iterator.h b/iterator/iterator.h index 8175ec12e..3118e65b4 100644 --- a/iterator/iterator.h +++ b/iterator/iterator.h @@ -141,6 +141,8 @@ struct iter_env { /** number of queries that have been ratelimited */ size_t num_queries_ratelimited; + /** max number of query restarts to limit length of CNAME chain */ + size_t max_query_restarts; /** number of retries on outgoing queries */ int outbound_msg_retry; diff --git a/util/config_file.c b/util/config_file.c index 7f97a4f21..b6f16a587 100644 --- a/util/config_file.c +++ b/util/config_file.c @@ -358,6 +358,7 @@ config_create(void) cfg->pad_responses_block_size = 468; /* from RFC8467 */ cfg->pad_queries = 1; cfg->pad_queries_block_size = 128; /* from RFC8467 */ + cfg->max_query_restarts = MAX_RESTART_COUNT; #ifdef USE_IPSECMOD cfg->ipsecmod_enabled = 1; cfg->ipsecmod_ignore_bogus = 0; @@ -782,6 +783,7 @@ int config_set_option(struct config_file* cfg, const char* opt, else S_YNO("ratelimit-backoff:", ratelimit_backoff) else S_NUMBER_NONZERO("outbound-msg-retry:", outbound_msg_retry) else S_NUMBER_NONZERO("max-sent-count", max_sent_count) + else S_SIZET_NONZERO("max-query-restarts:", max_query_restarts) else S_SIZET_NONZERO("fast-server-num:", fast_server_num) else S_NUMBER_OR_ZERO("fast-server-permil:", fast_server_permil) else S_YNO("qname-minimisation:", qname_minimisation) @@ -1244,6 +1246,7 @@ config_get_option(struct config_file* cfg, const char* opt, else O_YNO(opt, "ratelimit-backoff", ratelimit_backoff) else O_UNS(opt, "outbound-msg-retry", outbound_msg_retry) else O_UNS(opt, "max-sent-count", max_sent_count) + else O_DEC(opt, "max-query-restarts", max_query_restarts) else O_DEC(opt, "fast-server-num", fast_server_num) else O_DEC(opt, "fast-server-permil", fast_server_permil) else O_DEC(opt, "val-sig-skew-min", val_sig_skew_min) diff --git a/util/config_file.h b/util/config_file.h index 0c3cc6947..15592ef7d 100644 --- a/util/config_file.h +++ b/util/config_file.h @@ -662,6 +662,9 @@ struct config_file { /** block size with which to pad encrypted queries (default: 128) */ size_t pad_queries_block_size; + /** max number of query restarts. Determines max number of CNAME chain (default: 8) */ + size_t max_query_restarts; + /** IPsec module */ #ifdef USE_IPSECMOD /** false to bypass the IPsec module */ diff --git a/util/configlexer.lex b/util/configlexer.lex index 8bd565933..cd07dbc7b 100644 --- a/util/configlexer.lex +++ b/util/configlexer.lex @@ -541,6 +541,7 @@ pad-responses{COLON} { YDVAR(1, VAR_PAD_RESPONSES) } pad-responses-block-size{COLON} { YDVAR(1, VAR_PAD_RESPONSES_BLOCK_SIZE) } pad-queries{COLON} { YDVAR(1, VAR_PAD_QUERIES) } pad-queries-block-size{COLON} { YDVAR(1, VAR_PAD_QUERIES_BLOCK_SIZE) } +max-query-restarts{COLON} { YDVAR(1, VAR_MAX_QUERY_RESTARTS) } ipsecmod-enabled{COLON} { YDVAR(1, VAR_IPSECMOD_ENABLED) } ipsecmod-ignore-bogus{COLON} { YDVAR(1, VAR_IPSECMOD_IGNORE_BOGUS) } ipsecmod-hook{COLON} { YDVAR(1, VAR_IPSECMOD_HOOK) } diff --git a/util/configparser.y b/util/configparser.y index a982bdd40..c14f7bd33 100644 --- a/util/configparser.y +++ b/util/configparser.y @@ -171,6 +171,7 @@ extern struct config_parser_state* cfg_parser; %token VAR_DNSCRYPT_NONCE_CACHE_SLABS %token VAR_PAD_RESPONSES VAR_PAD_RESPONSES_BLOCK_SIZE %token VAR_PAD_QUERIES VAR_PAD_QUERIES_BLOCK_SIZE +%token VAR_MAX_QUERY_RESTARTS %token VAR_IPSECMOD_ENABLED VAR_IPSECMOD_HOOK VAR_IPSECMOD_IGNORE_BOGUS %token VAR_IPSECMOD_MAX_TTL VAR_IPSECMOD_WHITELIST VAR_IPSECMOD_STRICT %token VAR_CACHEDB VAR_CACHEDB_BACKEND VAR_CACHEDB_SECRETSEED @@ -298,6 +299,7 @@ content_server: server_num_threads | server_verbosity | server_port | server_qname_minimisation_strict | server_pad_responses | server_pad_responses_block_size | server_pad_queries | server_pad_queries_block_size | + server_max_query_restarts | server_serve_expired | server_serve_expired_ttl | server_serve_expired_ttl_reset | server_serve_expired_reply_ttl | server_serve_expired_client_timeout | @@ -2736,6 +2738,15 @@ server_pad_queries_block_size: VAR_PAD_QUERIES_BLOCK_SIZE STRING_ARG free($2); } ; +server_max_query_restarts: VAR_MAX_QUERY_RESTARTS STRING_ARG + { + OUTYY(("P(server_max_query_restarts:%s)\n", $2)); + if(atoi($2) == 0) + yyerror("number expected"); + else cfg_parser->cfg->max_query_restarts = atoi($2); + free($2); + } + ; server_ipsecmod_enabled: VAR_IPSECMOD_ENABLED STRING_ARG { #ifdef USE_IPSECMOD