From 7d652d9994b3c87e3a1b97c93e22cdc63a95b6f1 Mon Sep 17 00:00:00 2001 From: Aram Sargsyan Date: Thu, 10 Apr 2025 18:15:49 +0000 Subject: [PATCH] Fix a serve-stale issue with a delegated zone When 'stale-answer-client-timeout' is 0, named is allowed to return a stale answer immediately, while also initiating a new query to get the real answer. This mode is activated in ns__query_start() by setting the 'qctx->options.stalefirst' optoin to 'true' before calling the query_lookup() function, but not when the zone is known to be authoritative to the server. When the zone is authoritative, and query_looup() finds out that the requested name is a delegation, then before proceeding with the query, named tries to look it up in the cache first. Here comes the issue that it doesn't consider enabling 'qctx->options.stalefirst' in this case, and so the 'stale-answer-client-timeout 0' setting doesn't work for those delegated zones - instead of immediately returning the stale answer (if it exists), named tries to resolve it. Fix this issue by enabling 'qctx->options.stalefirst' in the query_zone_delegation() function just before named looks up the name in the cache using a new query_lookup() call. Also, if nothing was found in the cache, don't initiate another query_lookup() from inside query_notfound(), and let query_notfound() do its work, i.e. it will call query_delegation() for further processing. (cherry picked from commit 412aa881f2ce0e9d07b9ab46d2d4863ba388b898) --- lib/ns/query.c | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/lib/ns/query.c b/lib/ns/query.c index 3759ee371b..a61fd92bce 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -6206,11 +6206,18 @@ query_lookup(query_ctx_t *qctx) { } } else if (stale_timeout) { if (qctx->options.stalefirst) { - if (!stale_found && !answer_found) { - /* - * We have nothing useful in cache to return - * immediately. - */ + /* + * If 'qctx->zdb' is set, this was a cache lookup after + * an authoritative lookup returned a delegation (in + * order to find a better answer). But we still can + * return without getting any usable answer here, as + * query_notfound() should handle it from here. + * Otherwise, if nothing useful was found in cache then + * recursively call query_lookup() again without the + * 'stalefirst' option set. + */ + if (!stale_found && !answer_found && qctx->zdb == NULL) + { qctx_clean(qctx); qctx_freedata(qctx); dns_db_attach(qctx->client->view->cachedb, @@ -8936,7 +8943,25 @@ query_zone_delegation(query_ctx_t *qctx) { dns_db_attach(qctx->view->cachedb, &qctx->db); qctx->is_zone = false; - return query_lookup(qctx); + /* + * Since 'qctx->is_zone' is now false, we should reconsider + * setting the 'stalefirst' option, which is usually set in + * the beginning in ns__query_start(). + */ + if (qctx->view->staleanswerclienttimeout == 0 && + dns_view_staleanswerenabled(qctx->view)) + { + qctx->options.stalefirst = true; + } + + result = query_lookup(qctx); + + /* + * After fetch completes, this option is not expected to be set. + */ + qctx->options.stalefirst = false; + + return result; } return query_prepare_delegation_response(qctx);