]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Fix a serve-stale issue with a delegated zone
authorAram Sargsyan <aram@isc.org>
Thu, 10 Apr 2025 18:15:49 +0000 (18:15 +0000)
committerArаm Sаrgsyаn <aram@isc.org>
Wed, 23 Apr 2025 11:46:16 +0000 (11:46 +0000)
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.

lib/ns/query.c

index 430d41e6418910dfbe4f7334670b1657408a954b..dddaed62248a7e15e985c8b7308ba11a43755407 100644 (file)
@@ -5946,11 +5946,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,
@@ -8604,7 +8611,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);