From 714693742e9361c4ad9397debae530070a4b1cd2 Mon Sep 17 00:00:00 2001 From: Colin Vidal Date: Tue, 16 Sep 2025 17:14:46 +0200 Subject: [PATCH 1/2] test that cache is preserved on reconfing failure A named bug scrap the cache on a second reload after an initial reload failure. Adds a test checking that the cache is preserved between server reconfiguration/reloads even if it fails at some point (after attempting to re-use the cache) and the server is re-loaded later. --- bin/tests/system/resolver/ns1/named.conf.j2 | 49 +++++++++++++++++++ bin/tests/system/resolver/tests_resolver.py | 39 +++++++++++++++ .../system/resolver/tests_sh_resolver.py | 1 + 3 files changed, 89 insertions(+) create mode 100644 bin/tests/system/resolver/ns1/named.conf.j2 create mode 100644 bin/tests/system/resolver/tests_resolver.py diff --git a/bin/tests/system/resolver/ns1/named.conf.j2 b/bin/tests/system/resolver/ns1/named.conf.j2 new file mode 100644 index 0000000000..ff3caa8216 --- /dev/null +++ b/bin/tests/system/resolver/ns1/named.conf.j2 @@ -0,0 +1,49 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ +{% set wrongoption = wrongoption | default(False) %} + +options { + query-source address 10.53.0.1; + notify-source 10.53.0.1; + transfer-source 10.53.0.1; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.1; }; + listen-on-v6 { none; }; + recursion yes; + dnssec-validation no; + attach-cache "globalcache"; + max-zone-ttl unlimited; + resolver-query-timeout 5000; # 5 seconds + max-recursion-queries 100; + request-zoneversion yes; +}; + +view "default" { + zone "." { + type hint; + file "root.hint"; + }; +{% if wrongoption %} + forwarders port 9999999 { 127.0.0.1; }; +{% endif %} +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm @DEFAULT_HMAC@; +}; + +controls { + inet 10.53.0.1 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; diff --git a/bin/tests/system/resolver/tests_resolver.py b/bin/tests/system/resolver/tests_resolver.py new file mode 100644 index 0000000000..286a33e4e0 --- /dev/null +++ b/bin/tests/system/resolver/tests_resolver.py @@ -0,0 +1,39 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +import time + + +import isctest + + +def test_resolver_cache_reloadfails(ns1, templates): + ns1.rndc("flush") + msg = isctest.query.create("www.example.org.", "A") + res = isctest.query.udp(msg, "10.53.0.1") + isctest.check.noerror(res) + assert res.answer[0].ttl == 300 + templates.render("ns1/named.conf", {"wrongoption": True}) + try: + # The first reload fails, and the old cache list will be preserved + ns1.rndc("reload") + except isctest.rndc.RNDCException: + templates.render("ns1/named.conf", {"wrongoption": False}) + # The second reload succeed, and the cache is still there, as preserved + # from the old cache list + ns1.rndc("reload") + time.sleep(3) + msg = isctest.query.create("www.example.org.", "A") + res = isctest.query.udp(msg, "10.53.0.1") + isctest.check.noerror(res) + # The ttl being lower than 300 (provided by fake authoritative) proves + # the cache is still in use + assert res.answer[0].ttl < 300 diff --git a/bin/tests/system/resolver/tests_sh_resolver.py b/bin/tests/system/resolver/tests_sh_resolver.py index 10a36ce4e4..0a90735408 100644 --- a/bin/tests/system/resolver/tests_sh_resolver.py +++ b/bin/tests/system/resolver/tests_sh_resolver.py @@ -11,6 +11,7 @@ import pytest + pytestmark = pytest.mark.extra_artifacts( [ ".digrc", From a1703fa35b1ad3e2be1ff961122b5fdc39654b5e Mon Sep 17 00:00:00 2001 From: Colin Vidal Date: Tue, 16 Sep 2025 17:14:33 +0200 Subject: [PATCH 2/2] preserve cache when reload fails If the server is reloaded, new views are created and preexisting cache is attached to those _but_ something goes wrong later, the previous views are restored but the previous cache list is destroyed. This makes the subsequent reload to drop the existing cache. This fixes it by avoiding a mutation of the old cache list. --- bin/named/server.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/bin/named/server.c b/bin/named/server.c index 3ae0d826ed..ca39a4be5a 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -4414,13 +4414,15 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config, dns_cache_getname(nsc->cache)); nsc = NULL; } else { - if (oldcache) { - ISC_LIST_UNLINK(*oldcachelist, nsc, link); - ISC_LIST_APPEND(*cachelist, nsc, link); - nsc->primaryview = view; - } - dns_cache_attach(nsc->cache, &cache); shared_cache = true; + dns_cache_attach(nsc->cache, &cache); + if (oldcache) { + /* + * We need to re-use the cache, but we don't + * want to mutate the old production list. + */ + nsc = NULL; + } } } else if (strcmp(cachename, view->name) == 0) { result = dns_viewlist_find(&named_g_server->viewlist, cachename,