From 046c6819b24ebd7d087ed33518b71b0ff23d9918 Mon Sep 17 00:00:00 2001 From: Colin Vidal Date: Wed, 19 Nov 2025 17:34:16 +0100 Subject: [PATCH 1/8] allow named remote-servers list with key or tls The remote-servers clause enables the following pattern: remote-servers a { 1.2.3.4; ... }; remote-servers b { a key foo; }; However, `check.c` was explicitly throwing an error if a `key` or `tls` was provided after a named server-list. Remove this check, as this is a valid use case. --- lib/isccfg/check.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/lib/isccfg/check.c b/lib/isccfg/check.c index f8c1697457..022e29d0da 100644 --- a/lib/isccfg/check.c +++ b/lib/isccfg/check.c @@ -2494,20 +2494,6 @@ resume: } continue; } - if (!cfg_obj_isvoid(key)) { - cfg_obj_log(key, ISC_LOG_ERROR, "unexpected token '%s'", - cfg_obj_asstring(key)); - if (result == ISC_R_SUCCESS) { - result = ISC_R_FAILURE; - } - } - if (!cfg_obj_isvoid(tls)) { - cfg_obj_log(key, ISC_LOG_ERROR, "unexpected token '%s'", - cfg_obj_asstring(tls)); - if (result == ISC_R_SUCCESS) { - result = ISC_R_FAILURE; - } - } listname = cfg_obj_asstring(addr); symvalue.as_cpointer = addr; tresult = isc_symtab_define(symtab, listname, 1, symvalue, From 32a4aa95aed294a28da8abfabfb17f53699e0b56 Mon Sep 17 00:00:00 2001 From: Colin Vidal Date: Fri, 21 Nov 2025 17:05:15 +0100 Subject: [PATCH 2/8] test named remote-servers `key` usage Even though `remote-servers` now allows using named server-list with `key` (or `tls`), the `key` or `tls` is not used, in the context of a named server-list, when configuring the server. For instance, remote-servers foo { 10.53.0.5; }; also-notify { foo key fookey; }; won't use `fookey`. Add a system test highlighting the problem. --- .../xfer-servers-list/ns1/named.conf.j2 | 77 +++++++++++++++++++ .../system/xfer-servers-list/ns1/test.db.j2 | 23 ++++++ .../xfer-servers-list/ns2/named.conf.j2 | 48 ++++++++++++ .../xfer-servers-list/ns3/named.conf.j2 | 48 ++++++++++++ .../xfer-servers-list/ns4/named.conf.j2 | 48 ++++++++++++ .../tests_xfer_servers_list.py | 68 ++++++++++++++++ 6 files changed, 312 insertions(+) create mode 100644 bin/tests/system/xfer-servers-list/ns1/named.conf.j2 create mode 100644 bin/tests/system/xfer-servers-list/ns1/test.db.j2 create mode 100644 bin/tests/system/xfer-servers-list/ns2/named.conf.j2 create mode 100644 bin/tests/system/xfer-servers-list/ns3/named.conf.j2 create mode 100644 bin/tests/system/xfer-servers-list/ns4/named.conf.j2 create mode 100644 bin/tests/system/xfer-servers-list/tests_xfer_servers_list.py diff --git a/bin/tests/system/xfer-servers-list/ns1/named.conf.j2 b/bin/tests/system/xfer-servers-list/ns1/named.conf.j2 new file mode 100644 index 0000000000..8400ca2458 --- /dev/null +++ b/bin/tests/system/xfer-servers-list/ns1/named.conf.j2 @@ -0,0 +1,77 @@ +/* + * 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. + */ + +options { + listen-on port @PORT@ { 10.53.0.1; }; + transfer-source 10.53.0.1; + pid-file "named.pid"; + recursion no; + /* + * Notifications are sent from 10.53.1.1. This, the `notify + * explicit` and `also-notify` below, enables us to use specific TSIG + * keys for notifications for each secondary server instead of using + * the xfer TSIG key. + */ + notify-source 10.53.1.1; + /* + * The test. zone doesn't specify other NS, however this makes it clear + * this NS must only notify from also-notify list. + */ + notify explicit; +}; + +key xfrkey { + algorithm @DEFAULT_HMAC@; + secret "9999abcd8765"; +}; + +key notifykey2 { + algorithm @DEFAULT_HMAC@; + secret "2222abcd8765"; +}; + +key notifykey3 { + algorithm @DEFAULT_HMAC@; + secret "3333abcd8765"; +}; + +key notifykey4 { + algorithm @DEFAULT_HMAC@; + secret "4444abcd8765"; +}; + +remote-servers secondariesbis { + 10.53.0.4 port @PORT@; /* gets notifykey4 */ +}; + +remote-servers secondaries { + 10.53.0.3 port @PORT@ key notifykey3; + secondariesbis key notifykey4; + 10.53.0.2 port @PORT@; /* gets notifykey2 */ +}; + +zone "test" { + type primary; + allow-transfer { key xfrkey; }; + also-notify { secondaries key notifykey2; }; + file "test.db"; +}; + +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/xfer-servers-list/ns1/test.db.j2 b/bin/tests/system/xfer-servers-list/ns1/test.db.j2 new file mode 100644 index 0000000000..569a54f164 --- /dev/null +++ b/bin/tests/system/xfer-servers-list/ns1/test.db.j2 @@ -0,0 +1,23 @@ +; 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 serial = serial | default(1) %} + +$TTL 60 +test. IN SOA ns.test. op.test. ( + @serial@ ; serial + 100 ; refresh + 100 ; retry + 300 ; expire + 60 ; minimum + ) +test. NS ns.test. +ns.test. A 10.53.0.1 diff --git a/bin/tests/system/xfer-servers-list/ns2/named.conf.j2 b/bin/tests/system/xfer-servers-list/ns2/named.conf.j2 new file mode 100644 index 0000000000..22b9595932 --- /dev/null +++ b/bin/tests/system/xfer-servers-list/ns2/named.conf.j2 @@ -0,0 +1,48 @@ +/* + * 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. + */ + +options { + listen-on port @PORT@ { 10.53.0.2; }; + transfer-source 10.53.0.2; + pid-file "named.pid"; + recursion no; +}; + +key xfrkey { + algorithm @DEFAULT_HMAC@; + secret "9999abcd8765"; +}; + +key notifykey2 { + algorithm @DEFAULT_HMAC@; + secret "2222abcd8765"; +}; + +zone "test" { + /* + * Notify comes from a different IP address than the primary listening + * address, and with a different key. + */ + allow-notify { key notifykey2; }; + type secondary; + primaries { 10.53.0.1 port @PORT@ key xfrkey; }; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm @DEFAULT_HMAC@; +}; + +controls { + inet 10.53.0.2 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; diff --git a/bin/tests/system/xfer-servers-list/ns3/named.conf.j2 b/bin/tests/system/xfer-servers-list/ns3/named.conf.j2 new file mode 100644 index 0000000000..b17757e135 --- /dev/null +++ b/bin/tests/system/xfer-servers-list/ns3/named.conf.j2 @@ -0,0 +1,48 @@ +/* + * 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. + */ + +options { + listen-on port @PORT@ { 10.53.0.3; }; + transfer-source 10.53.0.3; + pid-file "named.pid"; + recursion no; +}; + +key xfrkey { + algorithm @DEFAULT_HMAC@; + secret "9999abcd8765"; +}; + +key notifykey3 { + algorithm @DEFAULT_HMAC@; + secret "3333abcd8765"; +}; + +zone "test" { + /* + * Notify comes from a different IP address than the primary listening + * address, and with a different key. + */ + allow-notify { key notifykey3; }; + type secondary; + primaries { 10.53.0.1 port @PORT@ key xfrkey; }; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm @DEFAULT_HMAC@; +}; + +controls { + inet 10.53.0.3 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; diff --git a/bin/tests/system/xfer-servers-list/ns4/named.conf.j2 b/bin/tests/system/xfer-servers-list/ns4/named.conf.j2 new file mode 100644 index 0000000000..fce6e301ba --- /dev/null +++ b/bin/tests/system/xfer-servers-list/ns4/named.conf.j2 @@ -0,0 +1,48 @@ +/* + * 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. + */ + +options { + listen-on port @PORT@ { 10.53.0.4; }; + transfer-source 10.53.0.4; + pid-file "named.pid"; + recursion no; +}; + +key xfrkey { + algorithm @DEFAULT_HMAC@; + secret "9999abcd8765"; +}; + +key notifykey4 { + algorithm @DEFAULT_HMAC@; + secret "4444abcd8765"; +}; + +zone "test" { + /* + * Notify comes from a different IP address than the primary listening + * address, and with a different key. + */ + allow-notify { key notifykey4; }; + type secondary; + primaries { 10.53.0.1 port @PORT@ key xfrkey; }; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm @DEFAULT_HMAC@; +}; + +controls { + inet 10.53.0.4 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; diff --git a/bin/tests/system/xfer-servers-list/tests_xfer_servers_list.py b/bin/tests/system/xfer-servers-list/tests_xfer_servers_list.py new file mode 100644 index 0000000000..612be36b1d --- /dev/null +++ b/bin/tests/system/xfer-servers-list/tests_xfer_servers_list.py @@ -0,0 +1,68 @@ +# 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 re + +import isctest + + +def check_soa(ns, serial): + msg = isctest.query.create("test.", "SOA") + res = isctest.query.udp(msg, ns.ip) + isctest.check.noerror(res) + assert len(res.answer) == 1 + assert ( + res.answer[0].to_text() + == f"test. 60 IN SOA ns.test. op.test. {serial} 100 100 300 60" + ) + + +def wait_for_initial_xfrin(ns): + with ns.watch_log_from_start() as watcher: + watcher.wait_for_line("Transfer status: success") + check_soa(ns, 1) + + +def wait_for_sending_notify(ns1, ns, key_name): + pattern = re.compile( + f"zone test/IN: sending notify to {ns.ip}#[0-9]+ : TSIG \\({key_name}\\)" + ) + with ns1.watch_log_from_start() as watcher: + watcher.wait_for_line(pattern) + + +def test_xfer_servers_list(ns1, ns2, ns3, ns4, templates): + # First, wait for ns2, ns3 and ns4 to xfrin foo.fr and answer it + wait_for_initial_xfrin(ns2) + wait_for_initial_xfrin(ns3) + wait_for_initial_xfrin(ns4) + + # ns1 initially notifies the secondaries using the respectively configured keys + # - 10.53.0.2 has the key defined where `secondaries` is used + # - 10.53.0.3 has the key directly after its IP address + # - 10.53.0.4 has the key defined where `secondariesbis` is used + # (inside `secondaries`), so it uses this one instead of the one + # defined where `secondaries` is used. + # Because the order notification are sent doesn't matter here, we can't use wait_for_sequence + seq = [(ns2, "notifykey2"), (ns3, "notifykey3"), (ns4, "notifykey4")] + for ns, key_name in seq: + wait_for_sending_notify(ns1, ns, key_name) + + # Then, ns1 update foo.fr. It notifies ns2, ns3 and ns4 about it + templates.render("ns1/test.db", {"serial": 2}) + with ns2.watch_log_from_here() as ns2_watcher, ns3.watch_log_from_here() as ns3_watcher, ns4.watch_log_from_here() as ns4_watcher: + ns1.rndc("reload") + ns2_watcher.wait_for_line("Transfer status: success") + ns3_watcher.wait_for_line("Transfer status: success") + ns4_watcher.wait_for_line("Transfer status: success") + check_soa(ns2, 2) + check_soa(ns3, 2) + check_soa(ns4, 2) From 9c96c382680d7c7051b83c96ff8d008c247ea8c7 Mon Sep 17 00:00:00 2001 From: Colin Vidal Date: Tue, 25 Nov 2025 10:15:51 +0100 Subject: [PATCH 3/8] remove `dns_notify_dequeue` Function `dns_notify_dequeue` is dead code, removing it. --- lib/dns/include/dns/notify.h | 9 --------- lib/dns/notify.c | 5 ----- 2 files changed, 14 deletions(-) diff --git a/lib/dns/include/dns/notify.h b/lib/dns/include/dns/notify.h index 2db6028fad..11aaffc66a 100644 --- a/lib/dns/include/dns/notify.h +++ b/lib/dns/include/dns/notify.h @@ -110,15 +110,6 @@ dns_notify_queue(dns_notify_t *notify, bool startup); * 'notify' is a valid notify. */ -isc_result_t -dns_notify_dequeue(dns_notify_t *notify, bool startup); -/*%< - * Dequeue notify. - * - * Requires: - * 'notify' is a valid notify. - */ - void dns_notify_find_address(dns_notify_t *notify); /*%< diff --git a/lib/dns/notify.c b/lib/dns/notify.c index 371b33482e..ed47a92447 100644 --- a/lib/dns/notify.c +++ b/lib/dns/notify.c @@ -509,11 +509,6 @@ notify_queue(dns_notify_t *notify, bool startup, bool dequeue) { ¬ify->rlevent); } -isc_result_t -dns_notify_dequeue(dns_notify_t *notify, bool startup) { - return notify_queue(notify, startup, true); -} - isc_result_t dns_notify_queue(dns_notify_t *notify, bool startup) { return notify_queue(notify, startup, false); From e732a8d25a8cbb78eed50b33d2569e8e9ea6e038 Mon Sep 17 00:00:00 2001 From: Colin Vidal Date: Tue, 25 Nov 2025 10:16:13 +0100 Subject: [PATCH 4/8] minimal fix for missing `key`/`tls` in named `remote-servers` The following case remote-servers foo { 10.53.0.5; }; remote-servers bar { foo key fookey; }; did not work: the `fookey` was silently ignored. No matter how `bar` was used, the server `10.53.0.5` wouldn't be contacted using the TSIG key `fookey`. The problem is the same the for `tls` property. The reason of the problem was that when `named_config_getipandkeylist()` reached a named server-list (here, `foo`), it modified the current context in order to immediately process what is inside `foo`, but forgot to look at the fields `key` and `tls`, to associate those with `foo` addresses. Fix the problem by wrapping the `key` and `tls` from the "caller" list inside the existing `lists` struct which is used to figure out if a list is already processed or not. That way, the `key` and `tls` values can be read when adding the addresses of the nested list. --- bin/named/config.c | 67 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 4 deletions(-) diff --git a/bin/named/config.c b/bin/named/config.c index 05c832798a..6e0c835bc3 100644 --- a/bin/named/config.c +++ b/bin/named/config.c @@ -353,9 +353,8 @@ named_config_getipandkeylist(const cfg_obj_t *config, const cfg_obj_t *list, dns_name_t **tlss = NULL; struct { const char *name; - in_port_t port; - isc_sockaddr_t *src4s; - isc_sockaddr_t *src6s; + dns_name_t *key; + dns_name_t *tls; } *lists = NULL; struct { const cfg_listelt_t *element; @@ -462,7 +461,20 @@ resume: result = tresult; goto cleanup; } - lists[l++].name = listname; + lists[l].name = listname; + + result = named_config_getname(mctx, key, &lists[l].key); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + result = named_config_getname(mctx, tls, &lists[l].tls); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + l++; + /* Grow stack? */ grow_array(mctx, stack, pushed, stackcount); /* @@ -491,6 +503,12 @@ resume: goto cleanup; } + if (keys[i] == NULL && lists != NULL && lists[l-1].key != NULL) { + keys[i] = isc_mem_get(mctx, sizeof(*keys[i])); + dns_name_init(keys[i]); + dns_name_dup(lists[l-1].key, mctx, keys[i]); + } + result = named_config_getname(mctx, tls, &tlss[i]); if (result != ISC_R_SUCCESS) { i++; /* Increment here so that cleanup on error works. @@ -498,6 +516,12 @@ resume: goto cleanup; } + if (tlss[i] == NULL && lists != NULL && lists[l-1].tls != NULL) { + tlss[i] = isc_mem_get(mctx, sizeof(*tlss[i])); + dns_name_init(tlss[i]); + dns_name_dup(lists[l-1].tls, mctx, tlss[i]); + } + /* If the port is unset, take it from one of the upper levels */ if (isc_sockaddr_getport(&addrs[i]) == 0) { in_port_t addr_port = port; @@ -536,6 +560,7 @@ resume: port = stack[pushed].port; src4 = stack[pushed].src4; src6 = stack[pushed].src6; + l--; goto resume; } @@ -545,6 +570,23 @@ resume: shrink_array(mctx, sources, i, srccount); if (lists != NULL) { + for (size_t j = 0; j < listcount; j++) { + if (lists[j].key != NULL) { + if (dns_name_dynamic(lists[j].key)) { + dns_name_free(lists[j].key, mctx); + } + isc_mem_put(mctx, lists[j].key, + sizeof(*lists[j].key)); + } + + if (lists[j].tls != NULL) { + if (dns_name_dynamic(lists[j].tls)) { + dns_name_free(lists[j].tls, mctx); + } + isc_mem_put(mctx, lists[j].tls, + sizeof(*lists[j].tls)); + } + } isc_mem_cput(mctx, lists, listcount, sizeof(lists[0])); } if (stack != NULL) { @@ -596,6 +638,23 @@ cleanup: isc_mem_cput(mctx, sources, srccount, sizeof(sources[0])); } if (lists != NULL) { + for (size_t j = 0; j < listcount; j++) { + if (lists[j].key != NULL) { + if (dns_name_dynamic(lists[j].key)) { + dns_name_free(lists[j].key, mctx); + } + isc_mem_put(mctx, lists[j].key, + sizeof(*lists[j].key)); + } + + if (lists[j].tls != NULL) { + if (dns_name_dynamic(lists[j].tls)) { + dns_name_free(lists[j].tls, mctx); + } + isc_mem_put(mctx, lists[j].tls, + sizeof(*lists[j].tls)); + } + } isc_mem_cput(mctx, lists, listcount, sizeof(lists[0])); } if (stack != NULL) { From ccb82ea85d34f8f3802e5f5c93c8bc7c825757bd Mon Sep 17 00:00:00 2001 From: Colin Vidal Date: Tue, 25 Nov 2025 13:56:37 +0100 Subject: [PATCH 5/8] refactoring of `named_config_getipandkeylist` Function `named_config_getipandkeylist()` processes the nested lists by overriding the current local variable of the function, jumping back to the beginning of the list processing. Of course, in order to go back to the previous state and process the remaining items of the current list, a "stack" array is used in order to put and get back the next list element and associated values. This makes the logic quite complex and error prone. Instead, this commit changes the logic by recursing into the nested list (while sharing a state between all the invocations). The processing is fundamentally identical, but instead of "manually" handling the stack to go back to the previous state (and process remaining elements of the current list), takes advantage of recursion. --- bin/named/config.c | 560 ++++++++++++++++++++++----------------------- 1 file changed, 277 insertions(+), 283 deletions(-) diff --git a/bin/named/config.c b/bin/named/config.c index 6e0c835bc3..a3b75ef593 100644 --- a/bin/named/config.c +++ b/bin/named/config.c @@ -329,39 +329,232 @@ named_config_getname(isc_mem_t *mctx, const cfg_obj_t *obj, static const char *remotesnames[4] = { "remote-servers", "parental-agents", "primaries", "masters" }; +typedef struct { + isc_sockaddr_t *addrs; + size_t addrsallocated; + + isc_sockaddr_t *sources; + size_t sourcesallocated; + + dns_name_t **keys; + size_t keysallocated; + + dns_name_t **tlss; + size_t tlssallocated; + + size_t count; /* common to addrs, sources, keys and tlss */ + + const char **seen; + size_t seencount; + size_t seenallocated; +} getipandkeylist_state_t; + +static isc_result_t +getipandkeylist(in_port_t defport, in_port_t deftlsport, + const cfg_obj_t *config, const cfg_obj_t *list, + in_port_t listport, const cfg_obj_t *listkey, + const cfg_obj_t *listtls, isc_mem_t *mctx, + getipandkeylist_state_t *s) { + const cfg_obj_t *addrlist = cfg_tuple_get(list, "addresses"); + const cfg_obj_t *portobj = cfg_tuple_get(list, "port"); + const cfg_obj_t *src4obj = cfg_tuple_get(list, "source"); + const cfg_obj_t *src6obj = cfg_tuple_get(list, "source-v6"); + in_port_t port; + isc_sockaddr_t src4; + isc_sockaddr_t src6; + isc_result_t result = ISC_R_SUCCESS; + + if (cfg_obj_isuint32(portobj)) { + uint32_t val = cfg_obj_asuint32(portobj); + if (val > UINT16_MAX) { + cfg_obj_log(portobj, ISC_LOG_ERROR, + "port '%u' out of range", val); + return ISC_R_RANGE; + } + port = (in_port_t)val; + } else if (listport > 0) { + /* + * No port in the current list, but it is a list named elsewhere + * where the port is defined, i.e: + * + * remote-servers bar { 10.53.0.4; }; + * remote-servers foo port 5555 { bar; 10.54.0.3; }; + * ^^^ + * + * The current list is the list `bar`, and the server + * `10.53.0.4` has the port `5555` defined. + */ + port = listport; + } + + if (src4obj != NULL && cfg_obj_issockaddr(src4obj)) { + src4 = *cfg_obj_assockaddr(src4obj); + } else { + isc_sockaddr_any(&src4); + } + + if (src6obj != NULL && cfg_obj_issockaddr(src6obj)) { + src6 = *cfg_obj_assockaddr(src6obj); + } else { + isc_sockaddr_any6(&src6); + } + + for (const cfg_listelt_t *element = cfg_list_first(addrlist); + element != NULL; element = cfg_list_next(element)) + { + const cfg_obj_t *addr; + const cfg_obj_t *key; + const cfg_obj_t *tls; + + skiplist: + addr = cfg_tuple_get(cfg_listelt_value(element), + "remoteselement"); + key = cfg_tuple_get(cfg_listelt_value(element), "key"); + tls = cfg_tuple_get(cfg_listelt_value(element), "tls"); + + /* + * If this is not an address, this is the name of a nested list, + * i.e. + * + * remote-servers nestedlist { 10.53.0.4; }; + * remote-servers list { nestedlist key foo; 10.54.0.6; }; + * ^^^^^^^^^^^^^^^^^^ + * + * We are currently in the list `list`, and `addr` is the name + * `nestedlist`, so we'll immediately recurse to process + * `nestedlist` before processing the next element of `list`. + */ + if (!cfg_obj_issockaddr(addr)) { + const char *listname = cfg_obj_asstring(addr); + const cfg_obj_t *nestedlist = NULL; + isc_result_t tresult; + + for (size_t i = 0; i < s->seencount; i++) { + if (strcasecmp(s->seen[i], listname) == 0) { + element = cfg_list_next(element); + goto skiplist; + } + } + + grow_array(mctx, s->seen, s->seencount, + s->seenallocated); + s->seen[s->seencount] = listname; + + for (size_t i = 0; i < ARRAY_SIZE(remotesnames); i++) { + tresult = named_config_getremotesdef( + config, remotesnames[i], listname, + &nestedlist); + if (tresult == ISC_R_SUCCESS) { + break; + } + } + + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(addr, ISC_LOG_ERROR, + "remote-servers \"%s\" not found", + listname); + return tresult; + } + + result = getipandkeylist(defport, deftlsport, config, + nestedlist, port, key, tls, + mctx, s); + if (result != ISC_R_SUCCESS) { + goto out; + } + continue; + } + + grow_array(mctx, s->addrs, s->count, s->addrsallocated); + grow_array(mctx, s->keys, s->count, s->keysallocated); + grow_array(mctx, s->tlss, s->count, s->tlssallocated); + grow_array(mctx, s->sources, s->count, s->sourcesallocated); + + s->addrs[s->count] = *cfg_obj_assockaddr(addr); + + result = named_config_getname(mctx, key, &s->keys[s->count]); + if (result != ISC_R_SUCCESS) { + goto out; + } + + /* + * The `key` is not provided for this address, so, if we're + * inside a named list, get the `key` provided at the point the + * list is used. + */ + if (s->keys[s->count] == NULL && listkey != NULL) { + result = named_config_getname(mctx, listkey, + &s->keys[s->count]); + if (result != ISC_R_SUCCESS) { + goto out; + } + } + + result = named_config_getname(mctx, tls, &s->tlss[s->count]); + if (result != ISC_R_SUCCESS) { + goto out; + } + + /* + * The `tls` is not provided for this address, so, if we're + * inside a named list, get the `tls` provided at the point the + * named list is used. + */ + if (s->tlss[s->count] == NULL && listtls != NULL) { + result = named_config_getname(mctx, listtls, + &s->tlss[s->count]); + } + + /* If the port is unset, take it from one of the upper levels */ + if (isc_sockaddr_getport(&s->addrs[s->count]) == 0) { + in_port_t addr_port = port; + + /* If unset, use the default port or tls-port */ + if (addr_port == 0) { + if (s->tlss[s->count] != NULL) { + addr_port = deftlsport; + } else { + addr_port = defport; + } + } + + isc_sockaddr_setport(&s->addrs[s->count], addr_port); + } + + switch (isc_sockaddr_pf(&s->addrs[s->count])) { + case PF_INET: + s->sources[s->count] = src4; + break; + case PF_INET6: + s->sources[s->count] = src6; + break; + default: + result = ISC_R_NOTIMPLEMENTED; + goto out; + } + + s->count++; + } + +out: + if (result != ISC_R_SUCCESS) { + /* + * Reaching this point without success means we were in the + * middle of adding a new entry, so it needs to be counted for + * correctly free `s.keys` and `s.tlss` (as they potentially + * added a new element right before something fails) + */ + s->count++; + } + return result; +} + isc_result_t named_config_getipandkeylist(const cfg_obj_t *config, const cfg_obj_t *list, isc_mem_t *mctx, dns_ipkeylist_t *ipkl) { - uint32_t addrcount = 0, srccount = 0; - uint32_t keycount = 0, tlscount = 0; - uint32_t listcount = 0, l = 0, i = 0; - uint32_t stackcount = 0, pushed = 0; isc_result_t result; - const cfg_listelt_t *element; - const cfg_obj_t *addrlist; - const cfg_obj_t *portobj; - const cfg_obj_t *src4obj; - const cfg_obj_t *src6obj; - in_port_t port = (in_port_t)0; in_port_t def_port; in_port_t def_tlsport; - isc_sockaddr_t src4; - isc_sockaddr_t src6; - isc_sockaddr_t *addrs = NULL; - isc_sockaddr_t *sources = NULL; - dns_name_t **keys = NULL; - dns_name_t **tlss = NULL; - struct { - const char *name; - dns_name_t *key; - dns_name_t *tls; - } *lists = NULL; - struct { - const cfg_listelt_t *element; - in_port_t port; - isc_sockaddr_t src4; - isc_sockaddr_t src6; - } *stack = NULL; REQUIRE(ipkl != NULL); REQUIRE(ipkl->count == 0); @@ -384,282 +577,83 @@ named_config_getipandkeylist(const cfg_obj_t *config, const cfg_obj_t *list, goto cleanup; } -newlist: - addrlist = cfg_tuple_get(list, "addresses"); - portobj = cfg_tuple_get(list, "port"); - src4obj = cfg_tuple_get(list, "source"); - src6obj = cfg_tuple_get(list, "source-v6"); - - if (cfg_obj_isuint32(portobj)) { - uint32_t val = cfg_obj_asuint32(portobj); - if (val > UINT16_MAX) { - cfg_obj_log(portobj, ISC_LOG_ERROR, - "port '%u' out of range", val); - result = ISC_R_RANGE; - goto cleanup; - } - port = (in_port_t)val; + /* + * Process the (nested) list(s). + */ + getipandkeylist_state_t s = {}; + result = getipandkeylist(def_port, def_tlsport, config, list, + (in_port_t)0, NULL, NULL, mctx, &s); + if (result != ISC_R_SUCCESS) { + goto cleanup; } - if (src4obj != NULL && cfg_obj_issockaddr(src4obj)) { - src4 = *cfg_obj_assockaddr(src4obj); - } else { - isc_sockaddr_any(&src4); + shrink_array(mctx, s.addrs, s.count, s.addrsallocated); + shrink_array(mctx, s.keys, s.count, s.keysallocated); + shrink_array(mctx, s.tlss, s.count, s.tlssallocated); + shrink_array(mctx, s.sources, s.count, s.sourcesallocated); + + ipkl->addrs = s.addrs; + ipkl->keys = s.keys; + ipkl->tlss = s.tlss; + ipkl->sources = s.sources; + ipkl->count = s.count; + + INSIST(s.addrsallocated == s.keysallocated); + INSIST(s.addrsallocated == s.tlssallocated); + INSIST(s.addrsallocated == s.sourcesallocated); + ipkl->allocated = s.addrsallocated; + + if (s.seen != NULL) { + /* + * `s.seen` is not shrinked (no point, as it's deleted right + * away anyway), so we need to use `s.seenallocated` to + * correctly free the array. + */ + isc_mem_cput(mctx, s.seen, s.seenallocated, sizeof(s.seen[0])); } - if (src6obj != NULL && cfg_obj_issockaddr(src6obj)) { - src6 = *cfg_obj_assockaddr(src6obj); - } else { - isc_sockaddr_any6(&src6); - } - - element = cfg_list_first(addrlist); -resume: - for (; element != NULL; element = cfg_list_next(element)) { - const cfg_obj_t *addr; - const cfg_obj_t *key; - const cfg_obj_t *tls; - - addr = cfg_tuple_get(cfg_listelt_value(element), - "remoteselement"); - key = cfg_tuple_get(cfg_listelt_value(element), "key"); - tls = cfg_tuple_get(cfg_listelt_value(element), "tls"); - - if (!cfg_obj_issockaddr(addr)) { - const char *listname = cfg_obj_asstring(addr); - isc_result_t tresult; - uint32_t j; - - /* Grow lists? */ - grow_array(mctx, lists, l, listcount); - - /* Seen? */ - for (j = 0; j < l; j++) { - if (strcasecmp(lists[j].name, listname) == 0) { - break; - } - } - if (j < l) { - continue; - } - list = NULL; - tresult = ISC_R_NOTFOUND; - for (size_t n = 0; n < ARRAY_SIZE(remotesnames); n++) { - tresult = named_config_getremotesdef( - config, remotesnames[n], listname, - &list); - if (tresult == ISC_R_SUCCESS) { - break; - } - } - if (tresult == ISC_R_NOTFOUND) { - cfg_obj_log(addr, ISC_LOG_ERROR, - "remote-servers \"%s\" not found", - listname); - } - if (tresult != ISC_R_SUCCESS) { - result = tresult; - goto cleanup; - } - lists[l].name = listname; - - result = named_config_getname(mctx, key, &lists[l].key); - if (result != ISC_R_SUCCESS) { - goto cleanup; - } - - result = named_config_getname(mctx, tls, &lists[l].tls); - if (result != ISC_R_SUCCESS) { - goto cleanup; - } - - l++; - - /* Grow stack? */ - grow_array(mctx, stack, pushed, stackcount); - /* - * We want to resume processing this list on the - * next element. - */ - stack[pushed].element = cfg_list_next(element); - stack[pushed].port = port; - stack[pushed].src4 = src4; - stack[pushed].src6 = src6; - pushed++; - goto newlist; - } - - grow_array(mctx, addrs, i, addrcount); - grow_array(mctx, keys, i, keycount); - grow_array(mctx, tlss, i, tlscount); - grow_array(mctx, sources, i, srccount); - - addrs[i] = *cfg_obj_assockaddr(addr); - - result = named_config_getname(mctx, key, &keys[i]); - if (result != ISC_R_SUCCESS) { - i++; /* Increment here so that cleanup on error works. - */ - goto cleanup; - } - - if (keys[i] == NULL && lists != NULL && lists[l-1].key != NULL) { - keys[i] = isc_mem_get(mctx, sizeof(*keys[i])); - dns_name_init(keys[i]); - dns_name_dup(lists[l-1].key, mctx, keys[i]); - } - - result = named_config_getname(mctx, tls, &tlss[i]); - if (result != ISC_R_SUCCESS) { - i++; /* Increment here so that cleanup on error works. - */ - goto cleanup; - } - - if (tlss[i] == NULL && lists != NULL && lists[l-1].tls != NULL) { - tlss[i] = isc_mem_get(mctx, sizeof(*tlss[i])); - dns_name_init(tlss[i]); - dns_name_dup(lists[l-1].tls, mctx, tlss[i]); - } - - /* If the port is unset, take it from one of the upper levels */ - if (isc_sockaddr_getport(&addrs[i]) == 0) { - in_port_t addr_port = port; - - /* If unset, use the default port or tls-port */ - if (addr_port == 0) { - if (tlss[i] != NULL) { - addr_port = def_tlsport; - } else { - addr_port = def_port; - } - } - - isc_sockaddr_setport(&addrs[i], addr_port); - } - - switch (isc_sockaddr_pf(&addrs[i])) { - case PF_INET: - sources[i] = src4; - break; - case PF_INET6: - sources[i] = src6; - break; - default: - i++; /* Increment here so that cleanup on error works. - */ - result = ISC_R_NOTIMPLEMENTED; - goto cleanup; - } - - i++; - } - if (pushed != 0) { - pushed--; - element = stack[pushed].element; - port = stack[pushed].port; - src4 = stack[pushed].src4; - src6 = stack[pushed].src6; - l--; - goto resume; - } - - shrink_array(mctx, addrs, i, addrcount); - shrink_array(mctx, keys, i, keycount); - shrink_array(mctx, tlss, i, tlscount); - shrink_array(mctx, sources, i, srccount); - - if (lists != NULL) { - for (size_t j = 0; j < listcount; j++) { - if (lists[j].key != NULL) { - if (dns_name_dynamic(lists[j].key)) { - dns_name_free(lists[j].key, mctx); - } - isc_mem_put(mctx, lists[j].key, - sizeof(*lists[j].key)); - } - - if (lists[j].tls != NULL) { - if (dns_name_dynamic(lists[j].tls)) { - dns_name_free(lists[j].tls, mctx); - } - isc_mem_put(mctx, lists[j].tls, - sizeof(*lists[j].tls)); - } - } - isc_mem_cput(mctx, lists, listcount, sizeof(lists[0])); - } - if (stack != NULL) { - isc_mem_cput(mctx, stack, stackcount, sizeof(stack[0])); - } - - INSIST(keycount == addrcount); - INSIST(tlscount == addrcount); - INSIST(srccount == addrcount); - - ipkl->addrs = addrs; - ipkl->keys = keys; - ipkl->tlss = tlss; - ipkl->sources = sources; - ipkl->count = addrcount; - ipkl->allocated = addrcount; - return ISC_R_SUCCESS; cleanup: - if (addrs != NULL) { - isc_mem_cput(mctx, addrs, addrcount, sizeof(addrs[0])); + /* + * Because we didn't shrinked the array back in this path, we need to + * use `s.*allocated` to correctly free the allocated arrays. + */ + if (s.addrs != NULL) { + isc_mem_cput(mctx, s.addrs, s.count, sizeof(s.addrs[0])); } - if (keys != NULL) { - for (size_t j = 0; j < i; j++) { - if (keys[j] == NULL) { + if (s.keys != NULL) { + for (size_t i = 0; i < s.count; i++) { + if (s.keys[i] == NULL) { continue; } - if (dns_name_dynamic(keys[j])) { - dns_name_free(keys[j], mctx); + if (dns_name_dynamic(s.keys[i])) { + dns_name_free(s.keys[i], mctx); } - isc_mem_put(mctx, keys[j], sizeof(*keys[j])); + isc_mem_put(mctx, s.keys[i], sizeof(*s.keys[i])); } - isc_mem_cput(mctx, keys, keycount, sizeof(keys[0])); + isc_mem_cput(mctx, s.keys, s.keysallocated, sizeof(s.keys[0])); } - if (tlss != NULL) { - for (size_t j = 0; j < i; j++) { - if (tlss[j] == NULL) { + if (s.tlss != NULL) { + for (size_t i = 0; i < s.count; i++) { + if (s.tlss[i] == NULL) { continue; } - if (dns_name_dynamic(tlss[j])) { - dns_name_free(tlss[j], mctx); + if (dns_name_dynamic(s.tlss[i])) { + dns_name_free(s.tlss[i], mctx); } - isc_mem_put(mctx, tlss[j], sizeof(*tlss[j])); + isc_mem_put(mctx, s.tlss[i], sizeof(*s.tlss[i])); } - isc_mem_cput(mctx, tlss, tlscount, sizeof(tlss[0])); + isc_mem_cput(mctx, s.tlss, s.tlssallocated, sizeof(s.tlss[0])); } - if (sources != NULL) { - isc_mem_cput(mctx, sources, srccount, sizeof(sources[0])); + if (s.sources != NULL) { + isc_mem_cput(mctx, s.sources, s.sourcesallocated, + sizeof(s.sources[0])); + } + if (s.seen != NULL) { + isc_mem_cput(mctx, s.seen, s.seenallocated, sizeof(s.seen[0])); } - if (lists != NULL) { - for (size_t j = 0; j < listcount; j++) { - if (lists[j].key != NULL) { - if (dns_name_dynamic(lists[j].key)) { - dns_name_free(lists[j].key, mctx); - } - isc_mem_put(mctx, lists[j].key, - sizeof(*lists[j].key)); - } - if (lists[j].tls != NULL) { - if (dns_name_dynamic(lists[j].tls)) { - dns_name_free(lists[j].tls, mctx); - } - isc_mem_put(mctx, lists[j].tls, - sizeof(*lists[j].tls)); - } - } - isc_mem_cput(mctx, lists, listcount, sizeof(lists[0])); - } - if (stack != NULL) { - isc_mem_cput(mctx, stack, stackcount, sizeof(stack[0])); - } return result; } From 1a732b6b8ecc89232724d80adba84350976ae2ca Mon Sep 17 00:00:00 2001 From: Colin Vidal Date: Tue, 25 Nov 2025 15:34:26 +0100 Subject: [PATCH 6/8] check remote-servers list correctness `check.c` only checks if `remote-servers`, `primaries`, etc. are not duplicated inside the configuration file, but does not check the correctness of its definition. This commit fixes this by calling `validate_remotes()` for each `remote-servers` (and other aliases), which validates the correctness of the definition itself (this is the same call done to validate other cases like `also-notify`, etc.). --- lib/isccfg/check.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/isccfg/check.c b/lib/isccfg/check.c index 022e29d0da..6ff5b12a0b 100644 --- a/lib/isccfg/check.c +++ b/lib/isccfg/check.c @@ -84,6 +84,9 @@ keydirexist(const cfg_obj_t *zcgf, const char *optname, dns_name_t *zname, static const cfg_obj_t * find_maplist(const cfg_obj_t *config, const char *listname, const char *name); +static isc_result_t +validate_remotes(const cfg_obj_t *obj, const cfg_obj_t *config, + uint32_t *countp, isc_mem_t *mctx); static void freekey(char *key, unsigned int type, isc_symvalue_t value, void *userarg) { UNUSED(type); @@ -2084,6 +2087,12 @@ check_remoteserverlist(const cfg_obj_t *cctx, const char *list, result = tresult; break; } + + uint32_t dummy = 0; + result = validate_remotes(obj, cctx, &dummy, mctx); + if (result != ISC_R_SUCCESS) { + break; + } } return result; } From 2956e4fc45b3c2142a3351682d4200647448f193 Mon Sep 17 00:00:00 2001 From: Colin Vidal Date: Tue, 25 Nov 2025 15:45:22 +0100 Subject: [PATCH 7/8] check validity of key and tls in a server-list If a `key` or `tls` is associated to an IP address inside a server-list, only the `tls` existence in the configuration was checked. Also, if `key` or `tls` is associated to a named server-list inside a server-list, there was no check at all. Add the check for making sure a `key` is defined in the configuration, as well as the check for `key` and `tls` when used on a named server-list. --- bin/named/config.c | 2 +- lib/isccfg/check.c | 149 +++++++++++++++++++++++++++++++-------------- 2 files changed, 105 insertions(+), 46 deletions(-) diff --git a/bin/named/config.c b/bin/named/config.c index a3b75ef593..32918feba6 100644 --- a/bin/named/config.c +++ b/bin/named/config.c @@ -359,7 +359,7 @@ getipandkeylist(in_port_t defport, in_port_t deftlsport, const cfg_obj_t *portobj = cfg_tuple_get(list, "port"); const cfg_obj_t *src4obj = cfg_tuple_get(list, "source"); const cfg_obj_t *src6obj = cfg_tuple_get(list, "source-v6"); - in_port_t port; + in_port_t port = (in_port_t)0; isc_sockaddr_t src4; isc_sockaddr_t src6; isc_result_t result = ISC_R_SUCCESS; diff --git a/lib/isccfg/check.c b/lib/isccfg/check.c index 6ff5b12a0b..59d73b3030 100644 --- a/lib/isccfg/check.c +++ b/lib/isccfg/check.c @@ -2423,6 +2423,96 @@ get_remoteservers_def(const char *name, const cfg_obj_t *cctx, return get_remotes(cctx, "masters", name, ret); } +static isc_result_t +validate_remotes_key(const cfg_obj_t *config, const cfg_obj_t *key) { + isc_result_t result = ISC_R_SUCCESS; + + if (cfg_obj_isstring(key)) { + const cfg_obj_t *keys = NULL; + const char *str = cfg_obj_asstring(key); + dns_fixedname_t fname; + dns_name_t *nm = dns_fixedname_initname(&fname); + bool found = false; + + result = dns_name_fromstring(nm, str, dns_rootname, 0, NULL); + if (result != ISC_R_SUCCESS) { + cfg_obj_log(key, ISC_LOG_ERROR, + "'%s' is not a valid name", str); + } + + result = cfg_map_get(config, "key", &keys); + CFG_LIST_FOREACH(keys, elt) { + /* + * `key` are normalized TSIG which must be identified by + * a domain name, so this is needed. Otherwise, with a + * raw string comparison we could have: + * + * remote-servers { x.y.z.s key foo }; + * key foo. { + * ... + * }; + * + * This would otherwise fail, even though the key + * exists. + */ + const cfg_obj_t *foundkey = cfg_listelt_value(elt); + const char *foundkeystr = + cfg_obj_asstring(cfg_map_getname(foundkey)); + dns_fixedname_t foundfname; + dns_name_t *foundkeyname = + dns_fixedname_initname(&foundfname); + + result = dns_name_fromstring(foundkeyname, foundkeystr, + dns_rootname, 0, NULL); + + if (dns_name_equal(nm, foundkeyname)) { + found = true; + break; + } + } + + if (!found) { + cfg_obj_log(key, ISC_LOG_ERROR, + "key '%s' is not defined", + cfg_obj_asstring(key)); + result = ISC_R_FAILURE; + } + } + + return result; +} + +static isc_result_t +validate_remotes_tls(const cfg_obj_t *config, const cfg_obj_t *tls) { + isc_result_t result = ISC_R_SUCCESS; + + if (cfg_obj_isstring(tls)) { + const char *str = cfg_obj_asstring(tls); + dns_fixedname_t fname; + dns_name_t *nm = dns_fixedname_initname(&fname); + + result = dns_name_fromstring(nm, str, dns_rootname, 0, NULL); + if (result != ISC_R_SUCCESS) { + cfg_obj_log(tls, ISC_LOG_ERROR, + "'%s' is not a valid name", str); + } + + if (strcasecmp(str, "ephemeral") != 0) { + const cfg_obj_t *tlsmap = NULL; + + tlsmap = find_maplist(config, "tls", str); + if (tlsmap == NULL) { + cfg_obj_log(tls, ISC_LOG_ERROR, + "tls '%s' is not defined", + cfg_obj_asstring(tls)); + result = ISC_R_FAILURE; + } + } + } + + return result; +} + static isc_result_t validate_remotes(const cfg_obj_t *obj, const cfg_obj_t *config, uint32_t *countp, isc_mem_t *mctx) { @@ -2454,53 +2544,19 @@ resume: key = cfg_tuple_get(cfg_listelt_value(element), "key"); tls = cfg_tuple_get(cfg_listelt_value(element), "tls"); + result = validate_remotes_key(config, key); + if (result != ISC_R_SUCCESS) { + goto out; + } + + result = validate_remotes_tls(config, tls); + if (result != ISC_R_SUCCESS) { + goto out; + } + if (cfg_obj_issockaddr(addr)) { count++; - if (cfg_obj_isstring(key)) { - const char *str = cfg_obj_asstring(key); - dns_fixedname_t fname; - dns_name_t *nm = dns_fixedname_initname(&fname); - tresult = dns_name_fromstring( - nm, str, dns_rootname, 0, NULL); - if (tresult != ISC_R_SUCCESS) { - cfg_obj_log(key, ISC_LOG_ERROR, - "'%s' is not a valid name", - str); - if (result == ISC_R_SUCCESS) { - result = tresult; - } - } - } - if (cfg_obj_isstring(tls)) { - const char *str = cfg_obj_asstring(tls); - dns_fixedname_t fname; - dns_name_t *nm = dns_fixedname_initname(&fname); - tresult = dns_name_fromstring( - nm, str, dns_rootname, 0, NULL); - if (tresult != ISC_R_SUCCESS) { - cfg_obj_log(tls, ISC_LOG_ERROR, - "'%s' is not a valid name", - str); - if (result == ISC_R_SUCCESS) { - result = tresult; - } - } - if (strcasecmp(str, "ephemeral") != 0) { - const cfg_obj_t *tlsmap = NULL; - - tlsmap = find_maplist(config, "tls", - str); - if (tlsmap == NULL) { - cfg_obj_log( - tls, ISC_LOG_ERROR, - "tls '%s' is not " - "defined", - cfg_obj_asstring(tls)); - result = ISC_R_FAILURE; - } - } - } continue; } listname = cfg_obj_asstring(addr); @@ -2534,11 +2590,14 @@ resume: element = stack[--pushed]; goto resume; } + + *countp = count; + +out: if (stack != NULL) { isc_mem_cput(mctx, stack, stackcount, sizeof(*stack)); } isc_symtab_destroy(&symtab); - *countp = count; return result; } From 4bc435ab103002fedc4ce79a37c6a0fdab8c14bb Mon Sep 17 00:00:00 2001 From: Colin Vidal Date: Wed, 19 Nov 2025 17:36:17 +0100 Subject: [PATCH 8/8] add checkconf test for named remote-servers Add checkconf system test to cover usage of `remote-servers` with a named server-list and a `key` and/or a `tls` option. --- .../checkconf/bad-remote-servers-key.conf | 15 +++++++++++ .../checkconf/bad-remote-servers-tls.conf | 15 +++++++++++ .../checkconf/good-remote-servers-named.conf | 27 +++++++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 bin/tests/system/checkconf/bad-remote-servers-key.conf create mode 100644 bin/tests/system/checkconf/bad-remote-servers-tls.conf create mode 100644 bin/tests/system/checkconf/good-remote-servers-named.conf diff --git a/bin/tests/system/checkconf/bad-remote-servers-key.conf b/bin/tests/system/checkconf/bad-remote-servers-key.conf new file mode 100644 index 0000000000..d4963a87f8 --- /dev/null +++ b/bin/tests/system/checkconf/bad-remote-servers-key.conf @@ -0,0 +1,15 @@ +/* + * 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. + */ + +remote-servers a { 1.2.3.4; }; +remote-servers d { a key foo; }; diff --git a/bin/tests/system/checkconf/bad-remote-servers-tls.conf b/bin/tests/system/checkconf/bad-remote-servers-tls.conf new file mode 100644 index 0000000000..461975da59 --- /dev/null +++ b/bin/tests/system/checkconf/bad-remote-servers-tls.conf @@ -0,0 +1,15 @@ +/* + * 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. + */ + +remote-servers a { 1.2.3.4; }; +remote-servers d { a tls foo; }; diff --git a/bin/tests/system/checkconf/good-remote-servers-named.conf b/bin/tests/system/checkconf/good-remote-servers-named.conf new file mode 100644 index 0000000000..f7eadc587f --- /dev/null +++ b/bin/tests/system/checkconf/good-remote-servers-named.conf @@ -0,0 +1,27 @@ +/* + * 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. + */ + +key foo { + algorithm hmac-sha256; + secret "9999abcd8765"; +}; + +tls bar { +}; + +remote-servers a { 1.2.3.4; }; +remote-servers b { 1.2.3.4; }; +remote-servers c { 1.2.3.4; 5.6.7.8; ::1; }; +remote-servers d { a key foo; b tls bar; c key foo tls bar; }; +remote-servers e { a key foo.; }; +remote-servers f { b tls bar; };