Version in base suite: 9.18.41-1~deb12u1 Base version: bind9_9.18.41-1~deb12u1 Target version: bind9_9.18.44-1~deb12u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/b/bind9/bind9_9.18.41-1~deb12u1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/b/bind9/bind9_9.18.44-1~deb12u1.dsc ChangeLog | 3 NEWS | 3 bin/check/check-tool.c | 7 bin/check/named-checkconf.c | 7 bin/delv/delv.c | 7 bin/dnssec/dnssec-importkey.rst | 5 bin/named/controlconf.c | 8 bin/named/logconf.c | 7 bin/named/server.c | 7 bin/named/statschannel.c | 8 bin/named/tkeyconf.c | 23 bin/named/transportconf.c | 5 bin/named/zoneconf.c | 14 bin/plugins/filter-a.c | 8 bin/plugins/filter-aaaa.c | 8 bin/rndc/rndc.rst | 22 bin/tests/convert-trs-to-junit.py | 10 bin/tests/system/Makefile.am | 1 bin/tests/system/Makefile.in | 8 bin/tests/system/addzone/tests_rndc_deadlock.py | 15 bin/tests/system/ans.py | 48 bin/tests/system/auth/ns1/example.net.db | 2 bin/tests/system/auth/tests.sh | 10 bin/tests/system/bailiwick/ans1/ans.py | 68 bin/tests/system/bailiwick/ans1/root.db | 24 bin/tests/system/bailiwick/ans2/ans.py | 110 bin/tests/system/bailiwick/ans2/victim.db | 17 bin/tests/system/bailiwick/ans3/ans.py | 48 bin/tests/system/bailiwick/ans3/attacker.db | 17 bin/tests/system/bailiwick/ans3/victim.db | 16 bin/tests/system/bailiwick/bailiwick_ans.py | 102 bin/tests/system/bailiwick/ns4/named.conf.j2 | 36 bin/tests/system/bailiwick/tests_bailiwick.py | 119 bin/tests/system/chain/ans4/ans.py | 6 bin/tests/system/checkds/tests_checkds.py | 4 bin/tests/system/checkzone/zones/bad-cname-and-amtrelay.db | 17 bin/tests/system/checkzone/zones/bad-cname-and-atmrelay.db | 17 bin/tests/system/conftest.py | 41 bin/tests/system/dlzexternal/driver/driver.c | 15 bin/tests/system/dnssec-malformed-dnskey/ns2/example.db.in | 128 bin/tests/system/dnssec-malformed-dnskey/ns2/named.conf.j2 | 42 bin/tests/system/dnssec-malformed-dnskey/ns2/truncated.selfsigned.db.signed | 40 bin/tests/system/dnssec-malformed-dnskey/ns2/trusted.conf.j2 | 27 bin/tests/system/dnssec-malformed-dnskey/ns3/named.conf.j2 | 36 bin/tests/system/dnssec-malformed-dnskey/ns3/trusted.conf.j2 | 27 bin/tests/system/dnssec-malformed-dnskey/tests_malformed_dnskey.py | 194 + bin/tests/system/dnssec/ns2/example.db.in | 3 bin/tests/system/dnssec/ns2/sign.sh | 2 bin/tests/system/dnssec/ns3/named.conf.in | 6 bin/tests/system/dnssec/ns3/sign.sh | 31 bin/tests/system/dnssec/ns3/template.db.in | 27 bin/tests/system/dnssec/tests.sh | 11 bin/tests/system/dnssec/tests_sh_dnssec.py | 1 bin/tests/system/dnstap/tests_dnstap.py | 24 bin/tests/system/doth/conftest.py | 6 bin/tests/system/doth/example.axfr.good | 4 bin/tests/system/doth/example8.axfr.good | 4 bin/tests/system/dyndb/driver/util.h | 13 bin/tests/system/dyndb/driver/zone.c | 11 bin/tests/system/enginepkcs11/setup.sh | 2 bin/tests/system/genzone.sh | 4 bin/tests/system/hooks/driver/test-async.c | 8 bin/tests/system/isctest/__init__.py | 1 bin/tests/system/isctest/asyncserver.py | 1530 ++++++++++ bin/tests/system/isctest/check.py | 53 bin/tests/system/isctest/compat.py | 34 bin/tests/system/isctest/instance.py | 101 bin/tests/system/isctest/log/__init__.py | 2 bin/tests/system/isctest/log/watchlog.py | 147 bin/tests/system/isctest/rndc.py | 69 bin/tests/system/isctest/run.py | 39 bin/tests/system/isctest/template.py | 8 bin/tests/system/isctest/text.py | 178 + bin/tests/system/ixfr/tests.sh | 2 bin/tests/system/keepalive/tests_keepalive.py | 35 bin/tests/system/keyfromlabel/tests_keyfromlabel.py | 31 bin/tests/system/masterfile/tests_masterfile.py | 6 bin/tests/system/optout/ns2/named.conf.j2 | 57 bin/tests/system/optout/ns2/small.test.db | 25 bin/tests/system/optout/ns2/test.db | 25 bin/tests/system/optout/setup.sh | 16 bin/tests/system/optout/tests_optout.py | 145 bin/tests/system/pipelined/pipequeries.c | 18 bin/tests/system/re_compile_checker.py | 46 bin/tests/system/rpz/tests.sh | 4 bin/tests/system/rrchecker/tests_rrchecker.py | 18 bin/tests/system/rsabigexponent/bigkey.c | 26 bin/tests/system/serve-stale/ans2/ans.pl | 19 bin/tests/system/serve-stale/ns3/named10.conf.in | 48 bin/tests/system/serve-stale/tests.sh | 32 bin/tests/system/shutdown/tests_shutdown.py | 7 bin/tests/system/spf/tests_spf_zones.py | 6 bin/tests/system/start.pl | 1 bin/tests/system/stress/tests_stress_update.py | 17 bin/tests/system/tkey/keycreate.c | 26 bin/tests/system/tkey/keydelete.c | 16 bin/tests/system/tools/tests_tools_nsec3hash.py | 32 bin/tests/system/verify/tests_verify.py | 67 bin/tests/system/xfer/dig1.good | 4 bin/tests/system/xfer/dig2.good | 4 bin/tests/system/xferquota/tests_xferquota.py | 3 bin/tools/mdig.c | 88 configure | 24 configure.ac | 2 debian/changelog | 8 doc/arm/changelog.rst | 3 doc/arm/notes.rst | 3 doc/arm/platforms.inc.rst | 6 doc/arm/reference.rst | 2 doc/arm/troubleshooting.inc.rst | 2 doc/changelog/changelog-9.18.42.rst | 26 doc/changelog/changelog-9.18.43.rst | 61 doc/changelog/changelog-9.18.44.rst | 53 doc/man/arpaname.1in | 2 doc/man/ddns-confgen.8in | 2 doc/man/delv.1in | 2 doc/man/dig.1in | 2 doc/man/dnssec-cds.1in | 2 doc/man/dnssec-dsfromkey.1in | 2 doc/man/dnssec-importkey.1in | 7 doc/man/dnssec-keyfromlabel.1in | 2 doc/man/dnssec-keygen.1in | 2 doc/man/dnssec-revoke.1in | 2 doc/man/dnssec-settime.1in | 2 doc/man/dnssec-signzone.1in | 2 doc/man/dnssec-verify.1in | 2 doc/man/dnstap-read.1in | 2 doc/man/filter-a.8in | 2 doc/man/filter-aaaa.8in | 2 doc/man/host.1in | 2 doc/man/mdig.1in | 2 doc/man/named-checkconf.1in | 2 doc/man/named-checkzone.1in | 2 doc/man/named-compilezone.1in | 2 doc/man/named-journalprint.1in | 2 doc/man/named-nzd2nzf.1in | 2 doc/man/named-rrchecker.1in | 2 doc/man/named.8in | 2 doc/man/named.conf.5in | 2 doc/man/nsec3hash.1in | 2 doc/man/nslookup.1in | 2 doc/man/nsupdate.1in | 2 doc/man/rndc-confgen.8in | 2 doc/man/rndc.8in | 24 doc/man/rndc.conf.5in | 2 doc/man/tsig-keygen.8in | 2 doc/notes/notes-9.18.42.rst | 24 doc/notes/notes-9.18.43.rst | 33 doc/notes/notes-9.18.44.rst | 43 fuzz/dns_rdata_fromwire_text.c | 3 fuzz/fuzz.h | 5 fuzz/isc_lex_getmastertoken.c | 1 fuzz/isc_lex_gettoken.c | 1 lib/dns/client.c | 7 lib/dns/diff.c | 11 lib/dns/dnssec.c | 253 - lib/dns/dnstap.c | 7 lib/dns/dst_api.c | 278 - lib/dns/dst_parse.c | 99 lib/dns/dyndb.c | 7 lib/dns/gssapictx.c | 27 lib/dns/include/dns/librpz.h | 4 lib/dns/include/dns/rrl.h | 4 lib/dns/journal.c | 88 lib/dns/keymgr.c | 33 lib/dns/masterdump.c | 13 lib/dns/nsec.c | 13 lib/dns/nsec3.c | 151 lib/dns/opensslecdsa_link.c | 162 - lib/dns/openssleddsa_link.c | 94 lib/dns/opensslrsa_link.c | 79 lib/dns/private.c | 23 lib/dns/rbt.c | 7 lib/dns/rbtdb.c | 12 lib/dns/rcode.c | 7 lib/dns/rdata.c | 14 lib/dns/rdata/generic/amtrelay_260.c | 26 lib/dns/rdata/generic/brid_68.c | 2 lib/dns/rdata/generic/hhit_67.c | 2 lib/dns/tkey.c | 239 - lib/dns/ttl.c | 7 lib/dns/update.c | 147 lib/dns/validator.c | 8 lib/dns/view.c | 7 lib/dns/xfrin.c | 151 lib/dns/zone.c | 614 +--- lib/irs/resconf.c | 7 lib/isc/base32.c | 7 lib/isc/base64.c | 7 lib/isc/hex.c | 7 lib/isc/httpd.c | 8 lib/isc/include/isc/log.h | 1 lib/isc/include/isc/util.h | 23 lib/isc/random.c | 3 lib/isccfg/namedconf.c | 8 lib/isccfg/parser.c | 8 lib/ns/hooks.c | 8 lib/ns/query.c | 5 lib/ns/update.c | 153 - lib/ns/xfrout.c | 60 srcid | 2 tests/dns/db_test.c | 7 tests/dns/dispatch_test.c | 2 tests/dns/rbtdb_test.c | 2 tests/dns/rdata_test.c | 10 tests/dns/resolver_test.c | 2 tests/dns/update_test.c | 2 tests/include/tests/isc.h | 14 tests/isc/Makefile.am | 5 tests/isc/Makefile.in | 55 tests/isc/mem_test.c | 6 tests/isc/random_test.c | 789 ----- 212 files changed, 5255 insertions(+), 3465 deletions(-) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmp0db3xcrf/bind9_9.18.41-1~deb12u1.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmp0db3xcrf/bind9_9.18.44-1~deb12u1.dsc: no acceptable signature found diff -Nru bind9-9.18.41/ChangeLog bind9-9.18.44/ChangeLog --- bind9-9.18.41/ChangeLog 2025-10-18 10:21:03.052258742 +0000 +++ bind9-9.18.44/ChangeLog 2026-01-09 13:44:04.731037455 +0000 @@ -18,6 +18,9 @@ development. Regular users should refer to :ref:`Release Notes ` for changes relevant to them. +.. include:: ../changelog/changelog-9.18.44.rst +.. include:: ../changelog/changelog-9.18.43.rst +.. include:: ../changelog/changelog-9.18.42.rst .. include:: ../changelog/changelog-9.18.41.rst .. include:: ../changelog/changelog-9.18.40.rst .. include:: ../changelog/changelog-9.18.39.rst diff -Nru bind9-9.18.41/NEWS bind9-9.18.44/NEWS --- bind9-9.18.41/NEWS 2025-10-18 10:21:03.052258742 +0000 +++ bind9-9.18.44/NEWS 2026-01-09 13:44:04.731037455 +0000 @@ -18,6 +18,9 @@ development. Regular users should refer to :ref:`Release Notes ` for changes relevant to them. +.. include:: ../changelog/changelog-9.18.44.rst +.. include:: ../changelog/changelog-9.18.43.rst +.. include:: ../changelog/changelog-9.18.42.rst .. include:: ../changelog/changelog-9.18.41.rst .. include:: ../changelog/changelog-9.18.40.rst .. include:: ../changelog/changelog-9.18.39.rst diff -Nru bind9-9.18.41/bin/check/check-tool.c bind9-9.18.44/bin/check/check-tool.c --- bind9-9.18.41/bin/check/check-tool.c 2025-10-18 10:21:02.790251738 +0000 +++ bind9-9.18.44/bin/check/check-tool.c 2026-01-09 13:44:04.462033017 +0000 @@ -58,13 +58,6 @@ #define CHECK_LOCAL 1 #endif /* ifndef CHECK_LOCAL */ -#define CHECK(r) \ - do { \ - result = (r); \ - if (result != ISC_R_SUCCESS) \ - goto cleanup; \ - } while (0) - #define ERR_IS_CNAME 1 #define ERR_NO_ADDRESSES 2 #define ERR_LOOKUP_FAILURE 3 diff -Nru bind9-9.18.41/bin/check/named-checkconf.c bind9-9.18.44/bin/check/named-checkconf.c --- bind9-9.18.41/bin/check/named-checkconf.c 2025-10-18 10:21:02.790251738 +0000 +++ bind9-9.18.44/bin/check/named-checkconf.c 2026-01-09 13:44:04.462033017 +0000 @@ -50,13 +50,6 @@ isc_log_t *logc = NULL; -#define CHECK(r) \ - do { \ - result = (r); \ - if (result != ISC_R_SUCCESS) \ - goto cleanup; \ - } while (0) - /*% usage */ noreturn static void usage(void); diff -Nru bind9-9.18.41/bin/delv/delv.c bind9-9.18.44/bin/delv/delv.c --- bind9-9.18.41/bin/delv/delv.c 2025-10-18 10:21:02.792251792 +0000 +++ bind9-9.18.44/bin/delv/delv.c 2026-01-09 13:44:04.464033050 +0000 @@ -67,13 +67,6 @@ #include -#define CHECK(r) \ - do { \ - result = (r); \ - if (result != ISC_R_SUCCESS) \ - goto cleanup; \ - } while (0) - #define MAXNAME (DNS_NAME_MAXTEXT + 1) /* diff -Nru bind9-9.18.41/bin/dnssec/dnssec-importkey.rst bind9-9.18.44/bin/dnssec/dnssec-importkey.rst --- bind9-9.18.41/bin/dnssec/dnssec-importkey.rst 2025-10-18 10:21:02.796251899 +0000 +++ bind9-9.18.44/bin/dnssec/dnssec-importkey.rst 2026-01-09 13:44:04.467033100 +0000 @@ -40,6 +40,11 @@ key, which means the public key can be added to and removed from the DNSKEY RRset on schedule even if the true private key is stored offline. +When using ``dnssec-policy``, do not use :program:`dnssec-importkey` to +import key files that cannot be used for signing. In this case, simply publish the +imported DNSKEY record in the zone, and make sure that the files are outside +the configured ``key-directory``. + Options ~~~~~~~ diff -Nru bind9-9.18.41/bin/named/controlconf.c bind9-9.18.44/bin/named/controlconf.c --- bind9-9.18.41/bin/named/controlconf.c 2025-10-18 10:21:02.799251979 +0000 +++ bind9-9.18.44/bin/named/controlconf.c 2026-01-09 13:44:04.470033149 +0000 @@ -801,14 +801,6 @@ } } -#define CHECK(x) \ - do { \ - result = (x); \ - if (result != ISC_R_SUCCESS) { \ - goto cleanup; \ - } \ - } while (0) - static isc_result_t get_rndckey(isc_mem_t *mctx, controlkeylist_t *keyids) { isc_result_t result; diff -Nru bind9-9.18.41/bin/named/logconf.c bind9-9.18.44/bin/named/logconf.c --- bind9-9.18.41/bin/named/logconf.c 2025-10-18 10:21:02.801252032 +0000 +++ bind9-9.18.44/bin/named/logconf.c 2026-01-09 13:44:04.473033199 +0000 @@ -31,13 +31,6 @@ #include #include -#define CHECK(op) \ - do { \ - result = (op); \ - if (result != ISC_R_SUCCESS) \ - goto cleanup; \ - } while (0) - /*% * Set up a logging category according to the named.conf data * in 'ccat' and add it to 'logconfig'. diff -Nru bind9-9.18.41/bin/named/server.c bind9-9.18.44/bin/named/server.c --- bind9-9.18.41/bin/named/server.c 2025-10-18 10:21:02.803252086 +0000 +++ bind9-9.18.44/bin/named/server.c 2026-01-09 13:44:04.475033232 +0000 @@ -172,13 +172,6 @@ * Check an operation for failure. Assumes that the function * using it has a 'result' variable and a 'cleanup' label. */ -#define CHECK(op) \ - do { \ - result = (op); \ - if (result != ISC_R_SUCCESS) \ - goto cleanup; \ - } while (0) - #define TCHECK(op) \ do { \ tresult = (op); \ diff -Nru bind9-9.18.41/bin/named/statschannel.c bind9-9.18.44/bin/named/statschannel.c --- bind9-9.18.41/bin/named/statschannel.c 2025-10-18 10:21:02.804252113 +0000 +++ bind9-9.18.44/bin/named/statschannel.c 2026-01-09 13:44:04.476033248 +0000 @@ -62,14 +62,6 @@ #define STATS_JSON_VERSION_MINOR "7" #define STATS_JSON_VERSION STATS_JSON_VERSION_MAJOR "." STATS_JSON_VERSION_MINOR -#define CHECK(m) \ - do { \ - result = (m); \ - if (result != ISC_R_SUCCESS) { \ - goto cleanup; \ - } \ - } while (0) - struct named_statschannel { /* Unlocked */ isc_httpdmgr_t *httpdmgr; diff -Nru bind9-9.18.41/bin/named/tkeyconf.c bind9-9.18.44/bin/named/tkeyconf.c --- bind9-9.18.41/bin/named/tkeyconf.c 2025-10-18 10:21:02.804252113 +0000 +++ bind9-9.18.44/bin/named/tkeyconf.c 2026-01-09 13:44:04.476033248 +0000 @@ -28,16 +28,8 @@ #include -#include - -#define RETERR(x) \ - do { \ - result = (x); \ - if (result != ISC_R_SUCCESS) \ - goto failure; \ - } while (0) - #include +#include #define LOG(msg) \ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, \ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, "%s", msg) @@ -47,12 +39,12 @@ dns_tkeyctx_t **tctxp) { isc_result_t result; dns_tkeyctx_t *tctx = NULL; - const char *s; + const char *s = NULL; uint32_t n; dns_fixedname_t fname; - dns_name_t *name; + dns_name_t *name = NULL; isc_buffer_t b; - const cfg_obj_t *obj; + const cfg_obj_t *obj = NULL; int type; result = dns_tkeyctx_create(mctx, &tctx); @@ -60,7 +52,6 @@ return result; } - obj = NULL; result = cfg_map_get(options, "tkey-dhkey", &obj); if (result == ISC_R_SUCCESS) { s = cfg_obj_asstring(cfg_tuple_get(obj, "name")); @@ -95,8 +86,8 @@ isc_buffer_constinit(&b, s, strlen(s)); isc_buffer_add(&b, strlen(s)); name = dns_fixedname_initname(&fname); - RETERR(dns_name_fromtext(name, &b, dns_rootname, 0, NULL)); - RETERR(dst_gssapi_acquirecred(name, false, &tctx->gsscred)); + CHECK(dns_name_fromtext(name, &b, dns_rootname, 0, NULL)); + CHECK(dst_gssapi_acquirecred(name, false, &tctx->gsscred)); } obj = NULL; @@ -109,7 +100,7 @@ *tctxp = tctx; return ISC_R_SUCCESS; -failure: +cleanup: dns_tkeyctx_destroy(&tctx); return result; } diff -Nru bind9-9.18.41/bin/named/transportconf.c bind9-9.18.44/bin/named/transportconf.c --- bind9-9.18.41/bin/named/transportconf.c 2025-10-18 10:21:02.804252113 +0000 +++ bind9-9.18.44/bin/named/transportconf.c 2026-01-09 13:44:04.476033248 +0000 @@ -190,11 +190,6 @@ return result; } -#define CHECK(f) \ - if ((result = f) != ISC_R_SUCCESS) { \ - goto failure; \ - } - static isc_result_t transport_list_fromconfig(const cfg_obj_t *config, dns_transport_list_t *list) { const cfg_obj_t *obj = NULL; diff -Nru bind9-9.18.41/bin/named/zoneconf.c bind9-9.18.44/bin/named/zoneconf.c --- bind9-9.18.41/bin/named/zoneconf.c 2025-10-18 10:21:02.805252139 +0000 +++ bind9-9.18.44/bin/named/zoneconf.c 2026-01-09 13:44:04.476033248 +0000 @@ -62,20 +62,6 @@ allow_update_forwarding } acl_type_t; -#define RETERR(x) \ - do { \ - isc_result_t _r = (x); \ - if (_r != ISC_R_SUCCESS) \ - return ((_r)); \ - } while (0) - -#define CHECK(x) \ - do { \ - result = (x); \ - if (result != ISC_R_SUCCESS) \ - goto cleanup; \ - } while (0) - /*% * Convenience function for configuring a single zone ACL. */ diff -Nru bind9-9.18.41/bin/plugins/filter-a.c bind9-9.18.44/bin/plugins/filter-a.c --- bind9-9.18.41/bin/plugins/filter-a.c 2025-10-18 10:21:02.806252166 +0000 +++ bind9-9.18.44/bin/plugins/filter-a.c 2026-01-09 13:44:04.477033265 +0000 @@ -48,14 +48,6 @@ #include #include -#define CHECK(op) \ - do { \ - result = (op); \ - if (result != ISC_R_SUCCESS) { \ - goto cleanup; \ - } \ - } while (0) - /* * Possible values for the settings of filter-a-on-v6 and * filter-a-on-v4: "no" is NONE, "yes" is FILTER, "break-dnssec" diff -Nru bind9-9.18.41/bin/plugins/filter-aaaa.c bind9-9.18.44/bin/plugins/filter-aaaa.c --- bind9-9.18.41/bin/plugins/filter-aaaa.c 2025-10-18 10:21:02.806252166 +0000 +++ bind9-9.18.44/bin/plugins/filter-aaaa.c 2026-01-09 13:44:04.478033281 +0000 @@ -48,14 +48,6 @@ #include #include -#define CHECK(op) \ - do { \ - result = (op); \ - if (result != ISC_R_SUCCESS) { \ - goto cleanup; \ - } \ - } while (0) - /* * Possible values for the settings of filter-aaaa-on-v4 and * filter-aaaa-on-v6: "no" is NONE, "yes" is FILTER, "break-dnssec" diff -Nru bind9-9.18.41/bin/rndc/rndc.rst bind9-9.18.44/bin/rndc/rndc.rst --- bind9-9.18.41/bin/rndc/rndc.rst 2025-10-18 10:21:02.807252193 +0000 +++ bind9-9.18.44/bin/rndc/rndc.rst 2026-01-09 13:44:04.478033281 +0000 @@ -122,10 +122,10 @@ .. option:: addzone zone [class [view]] configuration - This command adds a zone while the server is running. This command requires the - ``allow-new-zones`` option to be set to ``yes``. The configuration - string specified on the command line is the zone configuration text - that would ordinarily be placed in :iscman:`named.conf`. + This command adds a zone while the server is running. This command + requires the ``allow-new-zones`` option to be set to ``yes``. The + configuration string specified on the command line is the zone + configuration text that would ordinarily be placed in :iscman:`named.conf`. The configuration is saved in a file called ``viewname.nzf`` (or, if :iscman:`named` is compiled with liblmdb, an LMDB database file called @@ -298,10 +298,10 @@ .. option:: modzone zone [class [view]] configuration - This command modifies the configuration of a zone while the server is running. This - command requires the ``allow-new-zones`` option to be set to ``yes``. - As with ``addzone``, the configuration string specified on the - command line is the zone configuration text that would ordinarily be + This command modifies the configuration of a zone while the server is + running. This command requires the ``allow-new-zones`` option to be set + to ``yes``. As with ``addzone``, the configuration string specified on + the command line is the zone configuration text that would ordinarily be placed in :iscman:`named.conf`. If the zone was originally added via :option:`rndc addzone`, the @@ -480,9 +480,11 @@ .. option:: showzone zone [class [view]] - This command prints the configuration of a running zone. + If the server is configured with ``allow-new-zones`` set to ``yes``, + then this command prints the configuration of a running zone. - See also :option:`rndc zonestatus`. + See also :option:`rndc addzone`, :option:`rndc modzone`. + and :option:`rndc delzone`. .. option:: sign zone [class [view]] diff -Nru bind9-9.18.41/bin/tests/convert-trs-to-junit.py bind9-9.18.44/bin/tests/convert-trs-to-junit.py --- bind9-9.18.41/bin/tests/convert-trs-to-junit.py 2025-10-18 10:21:02.807252193 +0000 +++ bind9-9.18.44/bin/tests/convert-trs-to-junit.py 2026-01-09 13:44:04.479033298 +0000 @@ -65,7 +65,13 @@ full_trs_path = os.path.join(cur_dir, filename) full_log_path = os.path.join(cur_dir, log_name) sub_dir = os.path.relpath(cur_dir, source_dir) - test_name = os.path.join(sub_dir, filename_prefix) + test_dir_path = os.path.join(sub_dir, filename_prefix) + + if sub_dir.startswith("bin/tests/system"): + # Match the `pytest` style test names for system tests + test_name = f"test_{filename_prefix}" + else: + test_name = test_dir_path t = { "name": test_name, @@ -76,7 +82,7 @@ # try to find dir/file path for a clickable link try: - t["rel_file_path"] = find_test_relative_path(source_dir, test_name) + t["rel_file_path"] = find_test_relative_path(source_dir, test_dir_path) except KeyError: pass # no existing path found diff -Nru bind9-9.18.41/bin/tests/system/Makefile.am bind9-9.18.44/bin/tests/system/Makefile.am --- bind9-9.18.41/bin/tests/system/Makefile.am 2025-10-18 10:21:02.808252220 +0000 +++ bind9-9.18.44/bin/tests/system/Makefile.am 2026-01-09 13:44:04.480033314 +0000 @@ -117,6 +117,7 @@ dlzexternal \ dns64 \ dnssec \ + dnssec-malformed-dnskey \ dnstap \ doth \ dsdigest \ diff -Nru bind9-9.18.41/bin/tests/system/Makefile.in bind9-9.18.44/bin/tests/system/Makefile.in --- bind9-9.18.41/bin/tests/system/Makefile.in 2025-10-18 10:21:43.128304098 +0000 +++ bind9-9.18.44/bin/tests/system/Makefile.in 2026-01-09 13:45:07.668172687 +0000 @@ -850,6 +850,7 @@ @HAVE_PERL_TRUE@@HAVE_PYTEST_TRUE@@HAVE_PYTHON_TRUE@ dlzexternal \ @HAVE_PERL_TRUE@@HAVE_PYTEST_TRUE@@HAVE_PYTHON_TRUE@ dns64 \ @HAVE_PERL_TRUE@@HAVE_PYTEST_TRUE@@HAVE_PYTHON_TRUE@ dnssec \ +@HAVE_PERL_TRUE@@HAVE_PYTEST_TRUE@@HAVE_PYTHON_TRUE@ dnssec-malformed-dnskey \ @HAVE_PERL_TRUE@@HAVE_PYTEST_TRUE@@HAVE_PYTHON_TRUE@ dnstap \ @HAVE_PERL_TRUE@@HAVE_PYTEST_TRUE@@HAVE_PYTHON_TRUE@ doth \ @HAVE_PERL_TRUE@@HAVE_PYTEST_TRUE@@HAVE_PYTHON_TRUE@ dsdigest \ @@ -1697,6 +1698,13 @@ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +dnssec-malformed-dnskey.log: dnssec-malformed-dnskey + @p='dnssec-malformed-dnskey'; \ + b='dnssec-malformed-dnskey'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) dnstap.log: dnstap @p='dnstap'; \ diff -Nru bind9-9.18.41/bin/tests/system/addzone/tests_rndc_deadlock.py bind9-9.18.44/bin/tests/system/addzone/tests_rndc_deadlock.py --- bind9-9.18.41/bin/tests/system/addzone/tests_rndc_deadlock.py 2025-10-18 10:21:02.814252380 +0000 +++ bind9-9.18.44/bin/tests/system/addzone/tests_rndc_deadlock.py 2026-01-09 13:44:04.486033413 +0000 @@ -10,12 +10,12 @@ # information regarding copyright ownership. import concurrent.futures +import os +import subprocess import time import pytest -import isctest - pytestmark = pytest.mark.extra_artifacts( [ "ns*/*.nzf*", @@ -43,20 +43,19 @@ ["delzone", domain], ] + args = [os.environ["RNDC"]] + ns3.rndc_args.split() while not test_state["finished"]: for command in rndc_commands: - ns3.rndc(" ".join(command), ignore_errors=True, log=False) + # avoid using ns3.rndc() directly to avoid log spam + subprocess.run(args + " ".join(command), timeout=10, check=False) def check_if_server_is_responsive(ns3): """ Check if server status can be successfully retrieved using "rndc status" """ - try: - ns3.rndc("status", log=False) - return True - except isctest.rndc.RNDCException: - return False + cmd = ns3.rndc("status", raise_on_exception=False) + return cmd.rc == 0 def test_rndc_deadlock(servers): diff -Nru bind9-9.18.41/bin/tests/system/ans.py bind9-9.18.44/bin/tests/system/ans.py --- bind9-9.18.41/bin/tests/system/ans.py 1970-01-01 00:00:00.000000000 +0000 +++ bind9-9.18.44/bin/tests/system/ans.py 2026-01-09 13:44:04.490033479 +0000 @@ -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. +""" + +""" +This is a bare-bones DNS server that only serves data from zone files. It is +meant to be used as a replacement for full-blown named instances in system +tests when a given server is only required to return zone-based data. + +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + BEWARE! THIS SERVER DOES NOT NECESSARILY RETURN PROTOCOL-COMPLIANT ANSWERS! +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +See AsyncDnsServer._abort_if_*() methods in isctests/asyncserver.py for more +details. Use a regular named instance for anything non-trivial. + +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + DO NOT ADD CUSTOM LOGIC TO THIS FILE. IT IS ONLY MEANT TO BE SYMLINKED INTO +ansX/ SUBDIRECTORIES IN SYSTEM TESTS TO REDUCE THE AMOUNT OF BOILERPLATE CODE. +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +If you need to customize server behavior, implement it in a dedicated ans.py +server in the system test at hand. If an extension you are working on can be +useful in other system tests, please consider opening a merge request extending +isctest/asyncserver.py. +""" + +from isctest.asyncserver import ( + AsyncDnsServer, +) + + +def main() -> None: + server = AsyncDnsServer() + server.run() + + +if __name__ == "__main__": + main() diff -Nru bind9-9.18.41/bin/tests/system/auth/ns1/example.net.db bind9-9.18.44/bin/tests/system/auth/ns1/example.net.db --- bind9-9.18.41/bin/tests/system/auth/ns1/example.net.db 2025-10-18 10:21:02.818252487 +0000 +++ bind9-9.18.44/bin/tests/system/auth/ns1/example.net.db 2026-01-09 13:44:04.490033479 +0000 @@ -20,3 +20,5 @@ NS ns ns A 10.53.0.1 server A 10.53.0.100 +child NS ns.child +ns.child A 10.53.0.1 diff -Nru bind9-9.18.41/bin/tests/system/auth/tests.sh bind9-9.18.44/bin/tests/system/auth/tests.sh --- bind9-9.18.41/bin/tests/system/auth/tests.sh 2025-10-18 10:21:02.818252487 +0000 +++ bind9-9.18.44/bin/tests/system/auth/tests.sh 2026-01-09 13:44:04.490033479 +0000 @@ -186,5 +186,15 @@ [ $ret -eq 0 ] || echo_i "failed" status=$((status + ret)) +n=$((n + 1)) +echo_i "check delegation response to ANY query ($n)" +ret=0 +$DIG $DIGOPTS @10.53.0.1 foo.child.example.net any >dig.out.test$n || ret=1 +grep "ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 2" dig.out.test$n >/dev/null || ret=1 +grep 'child\.example\.net\..300.IN.NS.ns\.child\.example\.net\.$' dig.out.test$n >/dev/null || ret=1 +grep 'ns\.child\.example\.net\..300.IN.A.10\.53\.0\.1$' dig.out.test$n >/dev/null || ret=1 +[ $ret -eq 0 ] || echo_i "failed" +status=$((status + ret)) + echo_i "exit status: $status" [ $status -eq 0 ] || exit 1 diff -Nru bind9-9.18.41/bin/tests/system/bailiwick/ans1/ans.py bind9-9.18.44/bin/tests/system/bailiwick/ans1/ans.py --- bind9-9.18.41/bin/tests/system/bailiwick/ans1/ans.py 1970-01-01 00:00:00.000000000 +0000 +++ bind9-9.18.44/bin/tests/system/bailiwick/ans1/ans.py 2026-01-09 13:44:04.496033578 +0000 @@ -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. +""" + +from typing import AsyncGenerator + +import dns.rdatatype +import dns.rrset + +from isctest.asyncserver import ( + DnsResponseSend, + QueryContext, + ResponseAction, +) + +from bailiwick_ans import ResponseSpoofer, spoofing_server + + +ATTACKER_IP = "10.53.0.3" +TTL = 3600 + + +class SiblingNsSpoofer(ResponseSpoofer, mode="sibling-ns"): + + qname = "trigger." + + async def get_responses( + self, qctx: QueryContext + ) -> AsyncGenerator[ResponseAction, None]: + response = qctx.prepare_new_response(with_zone_data=False) + + txt_rrset = dns.rrset.from_text( + qctx.qname, + TTL, + qctx.qclass, + dns.rdatatype.TXT, + '"spoofed answer with extra NS"', + ) + response.answer.append(txt_rrset) + + ns_rrset = dns.rrset.from_text( + "victim.", TTL, qctx.qclass, dns.rdatatype.NS, "ns.attacker." + ) + response.authority.append(ns_rrset) + + a_rrset = dns.rrset.from_text( + "ns.attacker.", TTL, qctx.qclass, dns.rdatatype.A, ATTACKER_IP + ) + response.additional.append(a_rrset) + + yield DnsResponseSend(response, authoritative=True) + + +def main() -> None: + spoofing_server().run() + + +if __name__ == "__main__": + main() diff -Nru bind9-9.18.41/bin/tests/system/bailiwick/ans1/root.db bind9-9.18.44/bin/tests/system/bailiwick/ans1/root.db --- bind9-9.18.41/bin/tests/system/bailiwick/ans1/root.db 1970-01-01 00:00:00.000000000 +0000 +++ bind9-9.18.44/bin/tests/system/bailiwick/ans1/root.db 2026-01-09 13:44:04.496033578 +0000 @@ -0,0 +1,24 @@ +; 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. + +$ORIGIN . +$TTL 3600 +. SOA . . 0 0 0 0 3600 +. NS a.root-servers.nil. +a.root-servers.nil. A 10.53.0.1 + +; queries should go here ... unless the attack succeeded +victim. NS ns.victim. +ns.victim. A 10.53.0.2 + +; no query should go here +attacker. NS ns.attacker. +ns.attacker. A 10.53.0.3 diff -Nru bind9-9.18.41/bin/tests/system/bailiwick/ans2/ans.py bind9-9.18.44/bin/tests/system/bailiwick/ans2/ans.py --- bind9-9.18.41/bin/tests/system/bailiwick/ans2/ans.py 1970-01-01 00:00:00.000000000 +0000 +++ bind9-9.18.44/bin/tests/system/bailiwick/ans2/ans.py 2026-01-09 13:44:04.496033578 +0000 @@ -0,0 +1,110 @@ +""" +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. +""" + +from typing import AsyncGenerator + +import dns.rdatatype +import dns.rrset + +from isctest.asyncserver import ( + DnsResponseSend, + QueryContext, + ResponseAction, +) + +from bailiwick_ans import ResponseSpoofer, spoofing_server + + +ATTACKER_IP = "10.53.0.3" +TTL = 3600 + + +class UnsolicitedNsSpoofer(ResponseSpoofer, mode="unsolicited-ns"): + + qname = "trigger.victim." + + async def get_responses( + self, qctx: QueryContext + ) -> AsyncGenerator[ResponseAction, None]: + response = qctx.prepare_new_response(with_zone_data=False) + + txt_rrset = dns.rrset.from_text( + qctx.qname, + TTL, + qctx.qclass, + dns.rdatatype.TXT, + '"spoofed answer with extra NS"', + ) + response.answer.append(txt_rrset) + + ns_rrset = dns.rrset.from_text( + "victim.", TTL, qctx.qclass, dns.rdatatype.NS, "ns.attacker." + ) + response.authority.append(ns_rrset) + + yield DnsResponseSend(response, authoritative=True) + + +class ParentGlueSpoofer(ResponseSpoofer, mode="parent-glue"): + + qname = "trigger.victim." + + async def get_responses( + self, qctx: QueryContext + ) -> AsyncGenerator[ResponseAction, None]: + response = qctx.prepare_new_response(with_zone_data=False) + + ns_rrset = dns.rrset.from_text( + "trigger.victim.", TTL, qctx.qclass, dns.rdatatype.NS, "ns.victim." + ) + response.authority.append(ns_rrset) + + glue_rrset = dns.rrset.from_text( + "ns.victim.", TTL, qctx.qclass, dns.rdatatype.A, ATTACKER_IP + ) + response.additional.append(glue_rrset) + + yield DnsResponseSend(response, authoritative=False) + + +class DnameSpoofer(ResponseSpoofer, mode="dname"): + + qname = "trigger.victim." + + async def get_responses( + self, qctx: QueryContext + ) -> AsyncGenerator[ResponseAction, None]: + response = qctx.prepare_new_response(with_zone_data=False) + + cname_rrset = dns.rrset.from_text( + qctx.qname, + TTL, + qctx.qclass, + dns.rdatatype.CNAME, + "trigger.attacker.", + ) + dname_rrset = dns.rrset.from_text( + "victim.", TTL, qctx.qclass, dns.rdatatype.DNAME, "attacker." + ) + response.answer.append(cname_rrset) + response.answer.append(dname_rrset) + + yield DnsResponseSend(response, authoritative=True) + + +def main() -> None: + spoofing_server().run() + + +if __name__ == "__main__": + main() diff -Nru bind9-9.18.41/bin/tests/system/bailiwick/ans2/victim.db bind9-9.18.44/bin/tests/system/bailiwick/ans2/victim.db --- bind9-9.18.41/bin/tests/system/bailiwick/ans2/victim.db 1970-01-01 00:00:00.000000000 +0000 +++ bind9-9.18.44/bin/tests/system/bailiwick/ans2/victim.db 2026-01-09 13:44:04.496033578 +0000 @@ -0,0 +1,17 @@ +; 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. + +$TTL 3600 +@ SOA ns.victim. . 0 0 0 0 3600 +@ NS ns +ns A 10.53.0.2 +prime TXT "this record is used for priming the cache of the targeted resolver" +canary TXT "correct answer from the domain under attack" diff -Nru bind9-9.18.41/bin/tests/system/bailiwick/ans3/ans.py bind9-9.18.44/bin/tests/system/bailiwick/ans3/ans.py --- bind9-9.18.41/bin/tests/system/bailiwick/ans3/ans.py 1970-01-01 00:00:00.000000000 +0000 +++ bind9-9.18.44/bin/tests/system/bailiwick/ans3/ans.py 2026-01-09 13:44:04.490033479 +0000 @@ -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. +""" + +""" +This is a bare-bones DNS server that only serves data from zone files. It is +meant to be used as a replacement for full-blown named instances in system +tests when a given server is only required to return zone-based data. + +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + BEWARE! THIS SERVER DOES NOT NECESSARILY RETURN PROTOCOL-COMPLIANT ANSWERS! +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +See AsyncDnsServer._abort_if_*() methods in isctests/asyncserver.py for more +details. Use a regular named instance for anything non-trivial. + +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + DO NOT ADD CUSTOM LOGIC TO THIS FILE. IT IS ONLY MEANT TO BE SYMLINKED INTO +ansX/ SUBDIRECTORIES IN SYSTEM TESTS TO REDUCE THE AMOUNT OF BOILERPLATE CODE. +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +If you need to customize server behavior, implement it in a dedicated ans.py +server in the system test at hand. If an extension you are working on can be +useful in other system tests, please consider opening a merge request extending +isctest/asyncserver.py. +""" + +from isctest.asyncserver import ( + AsyncDnsServer, +) + + +def main() -> None: + server = AsyncDnsServer() + server.run() + + +if __name__ == "__main__": + main() diff -Nru bind9-9.18.41/bin/tests/system/bailiwick/ans3/attacker.db bind9-9.18.44/bin/tests/system/bailiwick/ans3/attacker.db --- bind9-9.18.41/bin/tests/system/bailiwick/ans3/attacker.db 1970-01-01 00:00:00.000000000 +0000 +++ bind9-9.18.44/bin/tests/system/bailiwick/ans3/attacker.db 2026-01-09 13:44:04.496033578 +0000 @@ -0,0 +1,17 @@ +; 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. + +$TTL 3600 +@ SOA ns.attacker. . 0 0 0 0 3600 +@ NS ns +ns A 10.53.0.3 +only-if-hijacked TXT "this record only exists in the hijacked version of the zone" +canary TXT "fake answer from attacker" diff -Nru bind9-9.18.41/bin/tests/system/bailiwick/ans3/victim.db bind9-9.18.44/bin/tests/system/bailiwick/ans3/victim.db --- bind9-9.18.41/bin/tests/system/bailiwick/ans3/victim.db 1970-01-01 00:00:00.000000000 +0000 +++ bind9-9.18.44/bin/tests/system/bailiwick/ans3/victim.db 2026-01-09 13:44:04.496033578 +0000 @@ -0,0 +1,16 @@ +; 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. + +$TTL 3600 +@ SOA ns.attacker. . 0 0 0 0 3600 +@ NS ns.attacker. +only-if-hijacked TXT "this record only exists in the hijacked version of the zone" +canary TXT "fake answer from attacker's auth" diff -Nru bind9-9.18.41/bin/tests/system/bailiwick/bailiwick_ans.py bind9-9.18.44/bin/tests/system/bailiwick/bailiwick_ans.py --- bind9-9.18.41/bin/tests/system/bailiwick/bailiwick_ans.py 1970-01-01 00:00:00.000000000 +0000 +++ bind9-9.18.44/bin/tests/system/bailiwick/bailiwick_ans.py 2026-01-09 13:44:04.496033578 +0000 @@ -0,0 +1,102 @@ +""" +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. +""" + +from typing import Dict, List, Optional, Type + +import abc + +import dns.name +import dns.rcode +import dns.rdatatype + +from isctest.asyncserver import ( + ControlCommand, + ControllableAsyncDnsServer, + DnsProtocol, + QueryContext, + ResponseHandler, +) + + +class ResponseSpoofer(ResponseHandler, abc.ABC): + + spoofers: Dict[str, Type["ResponseSpoofer"]] = {} + + def __init_subclass__(cls, mode: str) -> None: + assert mode not in cls.spoofers + cls.spoofers[mode] = cls + + @classmethod + def get_spoofer(cls, mode: str) -> Optional["ResponseSpoofer"]: + try: + return cls.spoofers[mode]() + except KeyError: + return None + + @property + @abc.abstractmethod + def qname(self) -> str: + raise NotImplementedError + + def match(self, qctx: QueryContext) -> bool: + return ( + qctx.qname == dns.name.from_text(self.qname) + and qctx.qtype == dns.rdatatype.TXT + and qctx.protocol == DnsProtocol.UDP + ) + + +class SetSpoofingModeCommand(ControlCommand): + """ + Select the ResponseSpoofer to use while handling queries from the resolver + under test (ns4). This control command is used at the start of each test + function in tests_bailiwick.py. + """ + + control_subdomain = "set-spoofing-mode" + + def __init__(self) -> None: + self._current_handler: Optional[ResponseSpoofer] = None + + def handle( + self, args: List[str], server: ControllableAsyncDnsServer, qctx: QueryContext + ) -> Optional[str]: + if len(args) != 1: + qctx.response.set_rcode(dns.rcode.SERVFAIL) + return "invalid control command" + + mode = args[0] + + if mode == "none": + if self._current_handler: + server.uninstall_response_handler(self._current_handler) + self._current_handler = None + return "response spoofing disabled" + + spoofer = ResponseSpoofer.get_spoofer(mode) + if not spoofer: + qctx.response.set_rcode(dns.rcode.SERVFAIL) + return f"unknown spoofing mode {mode}" + + if self._current_handler: + server.uninstall_response_handler(self._current_handler) + server.install_response_handler(spoofer) + self._current_handler = spoofer + + return f"response spoofing enabled (mode: {mode})" + + +def spoofing_server() -> ControllableAsyncDnsServer: + server = ControllableAsyncDnsServer(default_rcode=dns.rcode.NOERROR) + server.install_control_command(SetSpoofingModeCommand()) + return server diff -Nru bind9-9.18.41/bin/tests/system/bailiwick/ns4/named.conf.j2 bind9-9.18.44/bin/tests/system/bailiwick/ns4/named.conf.j2 --- bind9-9.18.41/bin/tests/system/bailiwick/ns4/named.conf.j2 1970-01-01 00:00:00.000000000 +0000 +++ bind9-9.18.44/bin/tests/system/bailiwick/ns4/named.conf.j2 2026-01-09 13:44:04.497033595 +0000 @@ -0,0 +1,36 @@ +/* + * 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 { + query-source address 10.53.0.4; + notify-source 10.53.0.4; + transfer-source 10.53.0.4; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.4; }; + listen-on-v6 { none; }; + recursion yes; + dnssec-validation no; + qname-minimization off; +}; + +controls { + inet 10.53.0.4 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +include "../../_common/rndc.key"; + +zone "." { + type hint; + file "../../_common/root.hint"; +}; diff -Nru bind9-9.18.41/bin/tests/system/bailiwick/tests_bailiwick.py bind9-9.18.44/bin/tests/system/bailiwick/tests_bailiwick.py --- bind9-9.18.41/bin/tests/system/bailiwick/tests_bailiwick.py 1970-01-01 00:00:00.000000000 +0000 +++ bind9-9.18.44/bin/tests/system/bailiwick/tests_bailiwick.py 2026-01-09 13:44:04.497033595 +0000 @@ -0,0 +1,119 @@ +# 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. + +from typing import Dict + +import time + +import dns.message + +import pytest + +# isctest.asyncserver requires dnspython >= 2.0.0 +pytest.importorskip("dns", minversion="2.0.0") + +import isctest +from isctest.instance import NamedInstance + + +@pytest.fixture(autouse=True) +def autouse_flush_resolver_cache(servers: Dict[str, NamedInstance]) -> None: + servers["ns4"].rndc("flush") + + +def set_spoofing_mode(ans1: str, ans2: str) -> None: + for ip, mode in (("10.53.0.1", ans1), ("10.53.0.2", ans2)): + msg = dns.message.make_query(f"{mode}.set-spoofing-mode._control.", "TXT") + res = isctest.query.tcp(msg, ip) + isctest.check.noerror(res) + + +def prime_cache(ns4: NamedInstance) -> None: + msg = dns.message.make_query("prime.victim.", "TXT") + res = isctest.query.tcp(msg, ns4.ip) + isctest.check.noerror(res) + + assert res.answer[0] == dns.rrset.from_text( + "prime.victim.", + 0, + "IN", + "TXT", + '"this record is used for priming the cache of the targeted resolver"', + ) + + +def send_trigger_query(ns4: NamedInstance, qname: str) -> None: + msg = dns.message.make_query(qname, "TXT") + isctest.query.tcp(msg, ns4.ip) + # The contents of the resolver's response to the trigger query do not + # matter, so they are not checked in any way; what matters is whether the + # spoofed response succeeded in hijacking the "victim." domain, which is + # checked below. + + +def check_domain_hijack(ns4: NamedInstance) -> None: + # Not necessary for triggering bugs, but useful for troubleshooting test + # behavior. + ns4.rndc("dumpdb -cache") + + msg = dns.message.make_query("only-if-hijacked.victim.", "TXT") + res = isctest.query.tcp(msg, ns4.ip) + isctest.check.nxdomain(res) + + msg = dns.message.make_query("canary.victim.", "TXT") + res = isctest.query.tcp(msg, ns4.ip) + isctest.check.noerror(res) + + assert res.answer[0] == dns.rrset.from_text( + "canary.victim.", + 0, + "IN", + "TXT", + '"correct answer from the domain under attack"', + ) + + +def test_bailiwick_sibling_ns_referral(servers: Dict[str, NamedInstance]) -> None: + set_spoofing_mode(ans1="sibling-ns", ans2="none") + + ns4 = servers["ns4"] + send_trigger_query(ns4, "trigger.") + check_domain_hijack(ns4) + + +def test_bailiwick_unsolicited_authority(servers: Dict[str, NamedInstance]) -> None: + set_spoofing_mode(ans1="none", ans2="unsolicited-ns") + + ns4 = servers["ns4"] + prime_cache(ns4) + send_trigger_query(ns4, "trigger.victim.") + check_domain_hijack(ns4) + + +def test_bailiwick_parent_glue(servers: Dict[str, NamedInstance]) -> None: + set_spoofing_mode(ans1="none", ans2="parent-glue") + + ns4 = servers["ns4"] + prime_cache(ns4) + send_trigger_query(ns4, "trigger.victim.") + + isctest.log.info("Waiting 11 seconds for the ns.victim. ADB entry to expire") + time.sleep(11) + + check_domain_hijack(ns4) + + +def test_bailiwick_spoofed_dname(servers: Dict[str, NamedInstance]) -> None: + set_spoofing_mode(ans1="none", ans2="dname") + + ns4 = servers["ns4"] + send_trigger_query(ns4, "trigger.victim.") + check_domain_hijack(ns4) diff -Nru bind9-9.18.41/bin/tests/system/chain/ans4/ans.py bind9-9.18.44/bin/tests/system/chain/ans4/ans.py --- bind9-9.18.41/bin/tests/system/chain/ans4/ans.py 2025-10-18 10:21:02.831252834 +0000 +++ bind9-9.18.44/bin/tests/system/chain/ans4/ans.py 2026-01-09 13:44:04.504033710 +0000 @@ -146,7 +146,7 @@ elif typename == "AAAA": final = "fd92:7065:b8e:ffff::4" elif typename == "TXT": - final = "Some\ text\ here" + final = "Some\\ text\\ here" elif typename == "NS": domain = qname final = "ns1.%s" % domain @@ -333,9 +333,9 @@ query6_udp.close() havev6 = False - query6_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + query6_tcp = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) try: - query6_tcp.bind((ip4, port)) + query6_tcp.bind((ip6, port)) query6_tcp.listen(1) query6_tcp.settimeout(1) except: diff -Nru bind9-9.18.41/bin/tests/system/checkds/tests_checkds.py bind9-9.18.44/bin/tests/system/checkds/tests_checkds.py --- bind9-9.18.41/bin/tests/system/checkds/tests_checkds.py 2025-10-18 10:21:02.860253610 +0000 +++ bind9-9.18.44/bin/tests/system/checkds/tests_checkds.py 2026-01-09 13:44:04.534034205 +0000 @@ -100,10 +100,10 @@ verifier = isctest.run.cmd(verify_cmd) - if verifier.returncode != 0: + if verifier.rc != 0: isctest.log.error(f"dnssec-verify {zone}. failed") - return verifier.returncode == 0 + return verifier.rc == 0 def read_statefile(server, zone): diff -Nru bind9-9.18.41/bin/tests/system/checkzone/zones/bad-cname-and-amtrelay.db bind9-9.18.44/bin/tests/system/checkzone/zones/bad-cname-and-amtrelay.db --- bind9-9.18.41/bin/tests/system/checkzone/zones/bad-cname-and-amtrelay.db 1970-01-01 00:00:00.000000000 +0000 +++ bind9-9.18.44/bin/tests/system/checkzone/zones/bad-cname-and-amtrelay.db 2026-01-09 13:44:04.537034255 +0000 @@ -0,0 +1,17 @@ +; 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. + +$TTL 600 +@ SOA ns hostmaster 2011012708 3600 1200 604800 1200 + NS ns +ns A 192.0.2.1 +bad AMTRELAY 0 0 0 . +bad CNAME @ diff -Nru bind9-9.18.41/bin/tests/system/checkzone/zones/bad-cname-and-atmrelay.db bind9-9.18.44/bin/tests/system/checkzone/zones/bad-cname-and-atmrelay.db --- bind9-9.18.41/bin/tests/system/checkzone/zones/bad-cname-and-atmrelay.db 2025-10-18 10:21:02.863253690 +0000 +++ bind9-9.18.44/bin/tests/system/checkzone/zones/bad-cname-and-atmrelay.db 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -; 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. - -$TTL 600 -@ SOA ns hostmaster 2011012708 3600 1200 604800 1200 - NS ns -ns A 192.0.2.1 -bad ATMRELAY 0 0 0 -bad CNAME @ diff -Nru bind9-9.18.41/bin/tests/system/conftest.py bind9-9.18.44/bin/tests/system/conftest.py --- bind9-9.18.41/bin/tests/system/conftest.py 2025-10-18 10:21:02.873253957 +0000 +++ bind9-9.18.44/bin/tests/system/conftest.py 2026-01-09 13:44:04.547034419 +0000 @@ -13,7 +13,7 @@ import filecmp import os from pathlib import Path -import re +from re import compile as Re import shutil import subprocess import tempfile @@ -49,7 +49,7 @@ XDIST_WORKER = os.environ.get("PYTEST_XDIST_WORKER", "") FILE_DIR = os.path.abspath(Path(__file__).parent) -ENV_RE = re.compile(b"([^=]+)=(.*)") +ENV_RE = Re(b"([^=]+)=(.*)") PORT_MIN = 5001 PORT_MAX = 32767 PORTS_PER_TEST = 20 @@ -62,10 +62,10 @@ "timeouts/", "upforwd/", ] -PRIORITY_TESTS_RE = re.compile("|".join(PRIORITY_TESTS)) +PRIORITY_TESTS_RE = Re("|".join(PRIORITY_TESTS)) SYSTEM_TEST_DIR_GIT_PATH = "bin/tests/system" -SYSTEM_TEST_NAME_RE = re.compile(f"{SYSTEM_TEST_DIR_GIT_PATH}" + r"/([^/]+)") -SYMLINK_REPLACEMENT_RE = re.compile(r"/tests(_.*)\.py") +SYSTEM_TEST_NAME_RE = Re(f"{SYSTEM_TEST_DIR_GIT_PATH}" + r"/([^/]+)") +SYMLINK_REPLACEMENT_RE = Re(r"/tests(_.*)\.py") # ---------------------- Module initialization --------------------------- @@ -366,7 +366,10 @@ "ns*/named.memstats", "ns*/named.run", "ns*/named.run.prev", + "core.[0-9]*-backtrace.txt", + "core.[0-9]*.gz", "pytest.log.txt", + "tsan.*.[0-9]*", ] if "USE_RR" in os.environ: @@ -656,14 +659,26 @@ pytest.skip("Prerequisites missing.") def setup_test(): - templates.render_auto() - try: - shell(f"{system_test_dir}/setup.sh") - except FileNotFoundError: - pass # setup.sh is optional - except subprocess.CalledProcessError as exc: - isctest.log.error("Failed to run test setup") - pytest.fail(f"setup.sh exited with {exc.returncode}") + template_data = None + bootstrap_fn = getattr(request.module, "bootstrap", None) + if bootstrap_fn: + isctest.log.debug("Running test bootstrap()") + try: + template_data = bootstrap_fn() + except Exception as exc: # pylint: disable=broad-exception-caught + isctest.log.error("Failed to run test bootstrap()") + kind = type(exc).__name__ + pytest.fail(f"bootstrap() failed with {kind}") + + templates.render_auto(template_data) + + setup_sh_path = f"{system_test_dir}/setup.sh" + if os.path.exists(setup_sh_path): + try: + shell(f"{system_test_dir}/setup.sh") + except subprocess.CalledProcessError as exc: + isctest.log.error("Failed to run test setup.sh") + pytest.fail(f"setup.sh exited with {exc.returncode}") def start_servers(): try: diff -Nru bind9-9.18.41/bin/tests/system/dlzexternal/driver/driver.c bind9-9.18.44/bin/tests/system/dlzexternal/driver/driver.c --- bind9-9.18.41/bin/tests/system/dlzexternal/driver/driver.c 2025-10-18 10:21:02.881254171 +0000 +++ bind9-9.18.44/bin/tests/system/dlzexternal/driver/driver.c 2026-01-09 13:44:04.554034535 +0000 @@ -49,13 +49,6 @@ dlz_dlopen_subrdataset_t dlz_subrdataset; dlz_dlopen_delrdataset_t dlz_delrdataset; -#define CHECK(x) \ - do { \ - result = (x); \ - if (result != ISC_R_SUCCESS) \ - goto failure; \ - } while (0) - #define loginfo(...) \ ({ \ if ((state != NULL) && (state->log != NULL)) \ @@ -258,7 +251,6 @@ const char *helper_name; va_list ap; char soa_data[sizeof("@ hostmaster.root 123 900 600 86400 3600")]; - isc_result_t result; size_t n; UNUSED(dlzname); @@ -306,7 +298,8 @@ } if (n >= sizeof(soa_data)) { - CHECK(ISC_R_NOSPACE); + free(state); + return ISC_R_NOSPACE; } add_name(state, &state->current[0], state->zone_name, "soa", 3600, @@ -320,10 +313,6 @@ *dbdata = state; return ISC_R_SUCCESS; - -failure: - free(state); - return result; } /* diff -Nru bind9-9.18.41/bin/tests/system/dnssec/ns2/example.db.in bind9-9.18.44/bin/tests/system/dnssec/ns2/example.db.in --- bind9-9.18.41/bin/tests/system/dnssec/ns2/example.db.in 2025-10-18 10:21:02.886254305 +0000 +++ bind9-9.18.44/bin/tests/system/dnssec/ns2/example.db.in 2026-01-09 13:44:04.560034634 +0000 @@ -168,4 +168,7 @@ revkey NS ns.revkey ns.revkey A 10.53.0.3 +extrabadkey NS ns3.extrabadkey +ns3.extrabadkey A 10.53.0.3 + dname-at-apex-nsec3 NS ns3 diff -Nru bind9-9.18.41/bin/tests/system/dnssec/ns2/sign.sh bind9-9.18.44/bin/tests/system/dnssec/ns2/sign.sh --- bind9-9.18.41/bin/tests/system/dnssec/ns2/sign.sh 2025-10-18 10:21:02.887254331 +0000 +++ bind9-9.18.44/bin/tests/system/dnssec/ns2/sign.sh 2026-01-09 13:44:04.561034650 +0000 @@ -62,7 +62,7 @@ ttlpatch split-dnssec split-smart expired expiring upper lower \ dnskey-unknown dnskey-unsupported dnskey-unsupported-2 \ dnskey-nsec3-unknown managed-future revkey \ - dname-at-apex-nsec3 occluded; do + dname-at-apex-nsec3 occluded extrabadkey; do cp "../ns3/dsset-$subdomain.example." . done diff -Nru bind9-9.18.41/bin/tests/system/dnssec/ns3/named.conf.in bind9-9.18.44/bin/tests/system/dnssec/ns3/named.conf.in --- bind9-9.18.41/bin/tests/system/dnssec/ns3/named.conf.in 2025-10-18 10:21:02.889254385 +0000 +++ bind9-9.18.44/bin/tests/system/dnssec/ns3/named.conf.in 2026-01-09 13:44:04.563034683 +0000 @@ -84,6 +84,12 @@ allow-update { any; }; }; +zone "extrabadkey.example" { + type primary; + file "extrabadkey.example.db.signed"; + allow-update { any; }; +}; + zone "insecure.nsec3.example" { type primary; file "insecure.nsec3.example.db"; diff -Nru bind9-9.18.41/bin/tests/system/dnssec/ns3/sign.sh bind9-9.18.44/bin/tests/system/dnssec/ns3/sign.sh --- bind9-9.18.41/bin/tests/system/dnssec/ns3/sign.sh 2025-10-18 10:21:02.891254438 +0000 +++ bind9-9.18.44/bin/tests/system/dnssec/ns3/sign.sh 2026-01-09 13:44:04.565034716 +0000 @@ -673,3 +673,34 @@ cat "$infile" "${kskname}.key" "${zskname}.key" "${keyname}.key" \ "${dnskeyname}.key" "dsset-delegation.${zone}." >"$zonefile" "$SIGNER" -P -o "$zone" "$zonefile" >/dev/null + +# +# +# +zone=extrabadkey.example. +infile=template.db.in +zonefile=extrabadkey.example.db + +# Add KSK and ZSK that we will mangle to RSAMD5 +ksk=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" -f KSK "$zone") +zsk=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" "$zone") +cat "$infile" "$ksk.key" "$zsk.key" >"$zonefile" +"$SIGNER" -g -O full -o "$zone" "$zonefile" >/dev/null 2>&1 + +# Mangle the signatures to RSAMD5 and save them for future use +sed -ne "s/\(IN[[:space:]]*RRSIG[[:space:]]*[A-Z]*\) $DEFAULT_ALGORITHM_NUMBER /\1 1 /p" <"$zonefile.signed" >"$zonefile.signed.rsamd5" + +# Now add normal KSK and ZSK to the zone file +ksk=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" -f KSK "$zone") +zsk=$("$KEYGEN" -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" "$zone") +cat "$infile" "$ksk.key" "$zsk.key" >"$zonefile" + +# Mangle the DNSKEY algorithm numbers and add them to the signed zone file +cat "$ksk.key" "$zsk.key" | sed -e "s/\(IN[[:space:]]*DNSKEY[[:space:]]*[0-9]* 3\) $DEFAULT_ALGORITHM_NUMBER /\1 1 /" >>"$zonefile" + +# Sign normally +"$SIGNER" -g -o "$zone" "$zonefile" >/dev/null 2>&1 + +# Add the mangled signatures to signed zone file +cat "$zonefile.signed.rsamd5" >>"$zonefile.signed" +rm "$zonefile.signed.rsamd5" diff -Nru bind9-9.18.41/bin/tests/system/dnssec/ns3/template.db.in bind9-9.18.44/bin/tests/system/dnssec/ns3/template.db.in --- bind9-9.18.41/bin/tests/system/dnssec/ns3/template.db.in 1970-01-01 00:00:00.000000000 +0000 +++ bind9-9.18.44/bin/tests/system/dnssec/ns3/template.db.in 2026-01-09 13:44:04.565034716 +0000 @@ -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. + +$TTL 300 ; 5 minutes +@ IN SOA mname1. . ( + 2000042407 ; serial + 20 ; refresh (20 seconds) + 20 ; retry (20 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) + NS ns3 +ns3 A 10.53.0.3 + +a A 10.0.0.1 +a.b A 10.0.0.1 +b A 10.0.0.2 +d A 10.0.0.4 +z A 10.0.0.26 diff -Nru bind9-9.18.41/bin/tests/system/dnssec/tests.sh bind9-9.18.44/bin/tests/system/dnssec/tests.sh --- bind9-9.18.41/bin/tests/system/dnssec/tests.sh 2025-10-18 10:21:02.896254572 +0000 +++ bind9-9.18.44/bin/tests/system/dnssec/tests.sh 2026-01-09 13:44:04.570034799 +0000 @@ -4631,5 +4631,16 @@ if [ "$ret" -ne 0 ]; then echo_i "failed"; fi status=$((status + ret)) +echo_i "checking extra-bad-algorithm positive validation ($n)" +ret=0 +dig_with_opts +noauth a.extrabadkey.example. @10.53.0.3 A >dig.out.ns3.test$n || ret=1 +dig_with_opts +noauth a.extrabadkey.example. @10.53.0.4 A >dig.out.ns4.test$n || ret=1 +digcomp --lc dig.out.ns3.test$n dig.out.ns4.test$n || ret=1 +grep "status: NOERROR" dig.out.ns4.test$n >/dev/null || ret=1 +grep "flags:.*ad.*QUERY" dig.out.ns4.test$n >/dev/null || ret=1 +n=$((n + 1)) +test "$ret" -eq 0 || echo_i "failed" +status=$((status + ret)) + echo_i "exit status: $status" [ $status -eq 0 ] || exit 1 diff -Nru bind9-9.18.41/bin/tests/system/dnssec/tests_sh_dnssec.py bind9-9.18.44/bin/tests/system/dnssec/tests_sh_dnssec.py --- bind9-9.18.41/bin/tests/system/dnssec/tests_sh_dnssec.py 2025-10-18 10:21:02.896254572 +0000 +++ bind9-9.18.44/bin/tests/system/dnssec/tests_sh_dnssec.py 2026-01-09 13:44:04.570034799 +0000 @@ -101,6 +101,7 @@ "ns3/example.bk", "ns3/expired.example.db", "ns3/expiring.example.db", + "ns3/extrabadkey.example.db", "ns3/future.example.db", "ns3/keyless.example.db", "ns3/kskonly.example.db", diff -Nru bind9-9.18.41/bin/tests/system/dnssec-malformed-dnskey/ns2/example.db.in bind9-9.18.44/bin/tests/system/dnssec-malformed-dnskey/ns2/example.db.in --- bind9-9.18.41/bin/tests/system/dnssec-malformed-dnskey/ns2/example.db.in 1970-01-01 00:00:00.000000000 +0000 +++ bind9-9.18.44/bin/tests/system/dnssec-malformed-dnskey/ns2/example.db.in 2026-01-09 13:44:04.558034601 +0000 @@ -0,0 +1,128 @@ +; 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. + +$TTL 300 +@ IN SOA mname1. . ( + 1 ; serial + 600 ; refresh + 600 ; retry + 1200 ; expire + 600 ; minimum + ) + +@ NS @ +@ A 10.53.0.2 + +; All of the following DNSKEYs are malformed and have the same key tag - 20071. +; The keys use invalid parameters for the ECDSA curve. + +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5Or0NNksES2iAAwmRfEEnH/hzk+8xF +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfjFg5Y9Ytl2+UR1JO/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sW2HoOfwFg5Y1ctl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5ZA8tl2+URx5O/UNNkogS2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rjJ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwghfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD47F1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URydO/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNkuMS2iAAwmRfEEnH/hzk2cv3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNi7iu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Yt0W+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeNcGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8vg +@ DNSKEY 256 3 14 rdZ2xb7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNky4S2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+FGHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5PE0NNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EFvr4ulinFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbkf1BvDhMNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtjlWd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNkuUS2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sS9HoOfwFg5Y9Ytl2+URx5O/UNNksETVCAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNkroS2iAHwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtmpWd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNAksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGQ8qmCKB+KmHS3YPgZjMZtl1W7/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmIe3YPgZjMZthFWd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Nr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGt8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cL1eMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHGAK8U3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4uli7FbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URqhO/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD6VF1z4ulhFFbCNdLiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulgyFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtIR/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAHOcqmCKB+KmHS3QXgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGasqmCKB+KmHS3dTgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfeVg5Y9Ytl2+URx5O/UNNksES2iAAwmRfV0nH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwsteqUnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1WEvfvtHF/3sU3HoOfwFieY9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9YtSW+UR2xO/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1WvPfvtHF/3sU3HoOfwFg5Y5Etl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPg5jMZtl1V9/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HnqfyVg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD6WF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sTlHoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BfD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfFEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgcjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk78v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD3HF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzleMv3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulinFbCNxbiu07nD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeJIGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFhnY9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCOE7iu1BvD9cNCeMAGu8qmCKB+KmGE3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtGB/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzlDMv3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EFwn4uliYFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cLTeMAGu8sVCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1WMvfvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iBFwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1JHD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfSlg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNf7iu1BvD9cNCeMAGu8qmCKB+KmHS3YPgrDMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFcCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fftHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fGtHF/3sU3HoOfwFg5Y9Ytl2+UR0dO/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjM7tl1Wd/fvtHF/3sU3HoOfnlg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbh71BvD9cN1eMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfYknH/crk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/hHtHF/3sU3HoOfwFg5Y9Ytl288Rx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF8z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB9umHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAHGsqmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y3ctl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4vlhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/frtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtopWd/fvtHF/3sU3HoOfwFgMY9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+lWHS3RjgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD4uF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgfDMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/gNtHF/3sU3HoOfwFg5Y9Ytl292Rx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/qMU3HoOfwFg5Y9YtzW+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytv2+URx5O/UMlksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr8HEQoEdD5EF1z4ulhFFYCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/6MU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEm9/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbkT1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmReq0nH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtCh/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzlRMv3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8rXCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzkysv3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPg2TMZtl1Wd/fvtHF/3sTEHoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOftVg5Y9Ytl2+URx5O/UNNksES2iAAwm9fEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cN2eMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sUDHoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr5aEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+p2HS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF0D4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOf3Fg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD6lF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCD9+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4jVhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtopWd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmI03YPgZjMZtftWd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtFp/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNktgS2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFZyNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwnhfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/gKtHF/w8U3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFfXY9Ytl2+URx5O/UNNksETPCAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNkeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/vMU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCN5Liu1BvD9cNCeMAGu8qmCKB+KmGz3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD4fF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9YtvG+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoE3z5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwflfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtjNWd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8wh +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1j4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfxFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ2+L7XEQoEdD5EF1z4ulhFFbCNxbiu1BvEL8NCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qOCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8wP +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbjh1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjLmtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjM2tl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2h/jwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCN4LiT1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr9DEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cLWeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFhYY9Ytl2+URx5O/UNNksES2iAAwmRe8UnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qlCKB+KmHS3YPgZjMatl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1n4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmdfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cODeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRez0nH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNqeMAGu8qmCKB+KmGq3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhrFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjLztl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF534ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2h+/wmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3QngZjMZtl1Wd/fvtHF/3sU3HoOfwFg5ZFAtl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 +@ DNSKEY 256 3 14 rdZ3Mr7XEQoEdD5EF1z4ulhFFbCNxbiu1BvD9cNCeMAGu8qmCKB+KmHS3YPgZjMZtl1Wd/fvtHF/3sU3HoOfwFg5Y9Ytl2+URx5O/UNNksES2iAAwmRfEEnH/hzk+8v3 + +malformed-dnskey A 10.53.0.2 +multiple-rrsigs A 10.53.0.2 diff -Nru bind9-9.18.41/bin/tests/system/dnssec-malformed-dnskey/ns2/named.conf.j2 bind9-9.18.44/bin/tests/system/dnssec-malformed-dnskey/ns2/named.conf.j2 --- bind9-9.18.41/bin/tests/system/dnssec-malformed-dnskey/ns2/named.conf.j2 1970-01-01 00:00:00.000000000 +0000 +++ bind9-9.18.44/bin/tests/system/dnssec-malformed-dnskey/ns2/named.conf.j2 2026-01-09 13:44:04.558034601 +0000 @@ -0,0 +1,42 @@ +/* + * 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 { + query-source address 10.53.0.2; + notify-source 10.53.0.2; + transfer-source 10.53.0.2; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.2; }; + listen-on-v6 { none; }; + allow-transfer { any; }; + recursion no; + dnssec-validation yes; + + /* Keep the order of RRSIGs in the response static. */ + rrset-order { + name "example." order none; + }; +}; + +zone example. { + type primary; + file "example.db.signed.malformed"; +}; + +zone truncated.selfsigned. { + type primary; + file "truncated.selfsigned.db.signed"; +}; + +include "trusted.conf"; diff -Nru bind9-9.18.41/bin/tests/system/dnssec-malformed-dnskey/ns2/truncated.selfsigned.db.signed bind9-9.18.44/bin/tests/system/dnssec-malformed-dnskey/ns2/truncated.selfsigned.db.signed --- bind9-9.18.41/bin/tests/system/dnssec-malformed-dnskey/ns2/truncated.selfsigned.db.signed 1970-01-01 00:00:00.000000000 +0000 +++ bind9-9.18.44/bin/tests/system/dnssec-malformed-dnskey/ns2/truncated.selfsigned.db.signed 2026-01-09 13:44:04.558034601 +0000 @@ -0,0 +1,40 @@ +; 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. + +$TTL 300 + +@ IN SOA mname1. . ( + 1 ; serial + 600 ; refresh + 600 ; retry + 1200 ; expire + 600 ; minimum + ) + +@ NS @ +@ A 10.53.0.2 + +; The following DNSKEY is revoked and truncated. To trigger the test +; condition, its key tag must be marked as trusted by the resolver. +; Since the key isn't valid, all the RRSIGs in this file are bogus. +@ DNSKEY 385 3 14 fQA= + +@ RRSIG SOA 14 2 86400 20950926153053 20251013153053 33167 @ xxxx5f7U0DiPvKFxpB83mTyqkAO0TfM0 xe4ZMYoJUQEPYdd0GTNkFzI6crsbU0lQ t/V1YOxAt5B+T1ch9n5dhYwt7ZTqluI2 mr6myKMesdPl1zp1hEgkmFpCG3NOXl2Z +@ RRSIG NS 14 2 86400 20950926153053 20251013153053 33167 @ xxxxLBPc05g7v/K5UfGuXsHH8xd29eQb 5qWe+Ei4Qn0GlmH0x/VIJiJMZXuxD5S+ VhP7DiX7uKIxi0QS2DOK1aOMXq/2WiUV 2VBmYAoSUilMlJY84I2XbzqD5iz5y+yp +@ RRSIG A 14 2 86400 20950926153053 20251013153053 33167 @ xxxx6UguMh8jgdVox2UVURjEsAP0D8o2 mFofnFOd6eYf+49QlWD+GX6x60X/hPVi f2XFsajouCvT/ZSmoXKWad3RC1DLHF/H TdOGMKlT4DfvbeJV+N5N0bgu2Wv3QRdM +@ RRSIG DNSKEY 14 2 86400 20950926153053 20251013153053 33167 @ xxxxqayRNsL32Km0c9AjwN0RNktt4iGb 97Dwi0uiHPcM4eVNZR2w68XMUh43+nR1 DA1QE2RqIqt7soEIwi1z4kAczf7W1wrP 7dcbEwjxS9D1CefuNRG1xnj9wGsqKecI +@ NSEC a A NS SOA RRSIG NSEC DNSKEY +@ RRSIG NSEC 14 2 0 20950926153053 20251013153053 33167 @ xxxx4Y6vqeOJHWEeg0T0OY4z7BdDrTkn BY9Yra8zSjFEGZvIX3irPd81+u5xlA0T 9waJO2Y9W42IMrOeKdQt++QXVHsLhOYn 4NAF6RotHSb4cqv1DXI1PSchMaJ5FWwD + +a A 10.53.0.2 +a RRSIG A 14 3 86400 20950926153053 20251013153053 33167 @ xxxxv31CNatB9xzj3AfTMlwiO0OqxbpJ cWrHN8zjj1ScXpqrHITfG/CZpoECDLWF wkXshDB/QMxHrnXkPKEcR2c9o5tcQT5R nHvtr7HT4Ob5PcY5DnItf3OWhE+bocmW +a NSEC @ A RRSIG NSEC +a RRSIG NSEC 14 3 0 20950926153053 20251013153053 33167 @ xxxxwMWbUxb3ScBKEVheQ2wFqujc6cyt 28GVCU0wPrBpK72HSsgdYme7IG8ZXGfa IWSU1Kf/om5+El7Tf2vDs7aI1yI7e7YG D5IxMejQg5v3/wtP7AJZXP5K9ICjq/ph diff -Nru bind9-9.18.41/bin/tests/system/dnssec-malformed-dnskey/ns2/trusted.conf.j2 bind9-9.18.44/bin/tests/system/dnssec-malformed-dnskey/ns2/trusted.conf.j2 --- bind9-9.18.41/bin/tests/system/dnssec-malformed-dnskey/ns2/trusted.conf.j2 1970-01-01 00:00:00.000000000 +0000 +++ bind9-9.18.44/bin/tests/system/dnssec-malformed-dnskey/ns2/trusted.conf.j2 2026-01-09 13:44:04.558034601 +0000 @@ -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. + */ + +trust-anchors { + example. static-key 257 3 14 "@ksk_public_key@"; + + /* + * The key tag in the trust anchor must match that of the revoked + * truncated self-signed key in the truncated.selfsigned. zone. + * + * The DNSKEY contents are intentionally different here, because the + * key doesn't have the revoked bit here and that flag is part of the + * key tag. The following decodes to key tag 33167, which is the same + * as the revoked truncated key in the zone file. + */ + truncated.selfsigned. static-key 257 3 14 "fYA="; +}; diff -Nru bind9-9.18.41/bin/tests/system/dnssec-malformed-dnskey/ns3/named.conf.j2 bind9-9.18.44/bin/tests/system/dnssec-malformed-dnskey/ns3/named.conf.j2 --- bind9-9.18.41/bin/tests/system/dnssec-malformed-dnskey/ns3/named.conf.j2 1970-01-01 00:00:00.000000000 +0000 +++ bind9-9.18.44/bin/tests/system/dnssec-malformed-dnskey/ns3/named.conf.j2 2026-01-09 13:44:04.558034601 +0000 @@ -0,0 +1,36 @@ +/* + * 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 { + query-source address 10.53.0.3; + notify-source 10.53.0.3; + transfer-source 10.53.0.3; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.3; }; + listen-on-v6 { none; }; + allow-transfer { any; }; + dnssec-validation yes; +}; + +zone "example." { + type static-stub; + server-addresses { 10.53.0.2; }; +}; + +zone "truncated.selfsigned." { + type static-stub; + server-addresses { 10.53.0.2; }; +}; + +include "trusted.conf"; diff -Nru bind9-9.18.41/bin/tests/system/dnssec-malformed-dnskey/ns3/trusted.conf.j2 bind9-9.18.44/bin/tests/system/dnssec-malformed-dnskey/ns3/trusted.conf.j2 --- bind9-9.18.41/bin/tests/system/dnssec-malformed-dnskey/ns3/trusted.conf.j2 1970-01-01 00:00:00.000000000 +0000 +++ bind9-9.18.44/bin/tests/system/dnssec-malformed-dnskey/ns3/trusted.conf.j2 2026-01-09 13:44:04.558034601 +0000 @@ -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. + */ + +trust-anchors { + example. static-key 257 3 14 "@ksk_public_key@"; + + /* + * The key tag in the trust anchor must match that of the revoked + * truncated self-signed key in the truncated.selfsigned. zone. + * + * The DNSKEY contents are intentionally different here, because the + * key doesn't have the revoked bit here and that flag is part of the + * key tag. The following decodes to key tag 33167, which is the same + * as the revoked truncated key in the zone file. + */ + truncated.selfsigned. static-key 257 3 14 "fYA="; +}; diff -Nru bind9-9.18.41/bin/tests/system/dnssec-malformed-dnskey/tests_malformed_dnskey.py bind9-9.18.44/bin/tests/system/dnssec-malformed-dnskey/tests_malformed_dnskey.py --- bind9-9.18.41/bin/tests/system/dnssec-malformed-dnskey/tests_malformed_dnskey.py 1970-01-01 00:00:00.000000000 +0000 +++ bind9-9.18.44/bin/tests/system/dnssec-malformed-dnskey/tests_malformed_dnskey.py 2026-01-09 13:44:04.558034601 +0000 @@ -0,0 +1,194 @@ +# 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 base64 +from re import compile as Re + +import pytest + +pytest.importorskip("cryptography") +pytest.importorskip( + "dns", minversion="2.7.0" +) # dns.dnssec.sign_zone(deterministic=...) needed + +from cryptography.hazmat.primitives.asymmetric import ec + +import dns +import dns.dnssec +import dns.zone +from dns.rdtypes.dnskeybase import Flag + +import isctest + + +def generate_key(): + algorithm = dns.dnssec.Algorithm.ECDSAP384SHA384 + ksk_private_key = ec.generate_private_key(ec.SECP384R1()) + try: + ksk_dnskey = dns.dnssec.make_dnskey( + public_key=ksk_private_key.public_key(), + algorithm=algorithm, + flags=Flag.ZONE | Flag.SEP, + ) + except ImportError as exc: + # if the cryptography package is too old, the make_dnskey() function + # will raise ImportError at runtime + pytest.skip(f"{exc}") + return ksk_private_key, ksk_dnskey + + +MALFORMED_ZSK_KEY_TAG = 20071 + + +def create_malformed_rr(rr, n=0): + malformed_rr = dns.rdtypes.ANY.RRSIG.RRSIG( + rdclass=rr.rdclass, + rdtype=rr.rdtype, + type_covered=rr.type_covered, + algorithm=rr.algorithm, + labels=rr.labels, + original_ttl=rr.original_ttl - n, # edit TTL so multiple RRSIGs can be added + expiration=rr.expiration, + inception=rr.inception, + key_tag=MALFORMED_ZSK_KEY_TAG, # overwrite with the malformed ZSKs + signer=rr.signer, + signature=rr.signature, + ) + return malformed_rr + + +def bootstrap(): + zone = dns.zone.from_file("ns2/example.db.in", origin="example.") + lifetime = 300 + + # geneate KSK, avoid key tag collision with ZSKs + while True: + ksk_private_key, ksk_dnskey = generate_key() + if dns.dnssec.key_id(ksk_dnskey) != MALFORMED_ZSK_KEY_TAG: + break + keys = [(ksk_private_key, ksk_dnskey)] + + # sign the zone (including the malformed ZSKs) with KSK + with zone.writer() as txn: + dns.dnssec.sign_zone( + zone=zone, + txn=txn, + keys=keys, + lifetime=lifetime, + add_dnskey=True, + deterministic=False, # for OpenSSL<3.2.0 compat + ) + + # force use of the malformed ZSKs for dnssec verification + # malformed-dnskey.example. has only one invalid RRSIG and is only signed + # with malformed ZSKs + malformed_rrset = zone.get_rdataset("malformed-dnskey", "RRSIG", "A") + rr = malformed_rrset.pop() + malformed_rrset.add(create_malformed_rr(rr)) + + # multiple-rrsigs.example. contains a lot of RRSIGS with the same invalid + # signature using malformed RRSIG, and one valid RRSIG + multiple_rrset = zone.get_rdataset("multiple-rrsigs", "RRSIG", "A") + rr = multiple_rrset.pop() + for i in range(99): + multiple_rrset.add(create_malformed_rr(rr, i)) + multiple_rrset.add(rr) + + zone.to_file("ns2/example.db.signed.malformed") + + return { + "ksk_public_key": base64.b64encode(ksk_dnskey.key).decode(), + } + + +@pytest.fixture(scope="module", autouse=True) +def after_servers_start(): + # prime the cache so we get consistent number of error messages in tests + msg = isctest.query.create("nxdomain.example", "A") + isctest.query.tcp(msg, "10.53.0.3") + + +def test_malformed_ecdsa(ns3): + log_validation_failed = Re(r"malformed-dnskey\.example/A\): validation failed") + log_openssl_failure = Re("EVP_PKEY_fromdata.*failed") + log_openssl_version = Re("linked to OpenSSL version: OpenSSL ([0-9]+)") + + msg = isctest.query.create("malformed-dnskey.example", "A") + + openssl_vers = ns3.log.grep(log_openssl_version) + if openssl_vers and int(openssl_vers[0].group(1)) >= 3: + # extra check for OpenSSL 3.0.0+ + with ns3.watch_log_from_here() as watcher: + res = isctest.query.tcp(msg, "10.53.0.3") + + # check the OpenSSL-specific log message appears just once + matches = watcher.wait_for_all( + [ + log_openssl_failure, + log_validation_failed, + ] + ) + assert len([m for m in matches if m.re == log_openssl_failure]) == 1 + else: + res = isctest.query.tcp(msg, "10.53.0.3") + + isctest.check.servfail(res) + + +def test_multiple_rrsigs(ns3): + log_validation_failed = Re(r"multiple-rrsigs\.example/A\): validation failed") + log_openssl_failure = Re("EVP_PKEY_fromdata.*failed") + log_openssl_version = Re("linked to OpenSSL version: OpenSSL ([0-9]+)") + + msg = isctest.query.create("multiple-rrsigs.example", "A") + + # Check the order of returned RRSIGs from auth. Due to rrset-order none; + # this should remain constant for the remainder of the test. + # Ensure the first two RRSIGs are malformed, otherwise skip the test. + res = isctest.query.tcp(msg, "10.53.0.2") + rrsigs = res.get_rrset( + res.answer, + dns.name.from_text("multiple-rrsigs.example."), + dns.rdataclass.IN, + dns.rdatatype.RRSIG, + dns.rdatatype.A, + ) + assert len(rrsigs) > 2 + if ( + rrsigs[0].key_tag != MALFORMED_ZSK_KEY_TAG + or rrsigs[1].key_tag != MALFORMED_ZSK_KEY_TAG + ): + pytest.skip("valid RRSIG listed first in response, re-run test") + + openssl_vers = ns3.log.grep(log_openssl_version) + if openssl_vers and int(openssl_vers[0].group(1)) >= 3: + # extra check for OpenSSL 3.0.0+ + with ns3.watch_log_from_here() as watcher: + res = isctest.query.tcp(msg, "10.53.0.3") + + # check the OpenSSL-specific log message appears exactly once + matches = watcher.wait_for_all( + [ + log_openssl_failure, + log_validation_failed, + ] + ) + assert len([m for m in matches if m.re == log_openssl_failure]) == 1 + else: + res = isctest.query.tcp(msg, "10.53.0.3") + + isctest.check.servfail(res) + + +def test_truncated_dnskey(): + msg = isctest.query.create("a.truncated.selfsigned.", "A") + res = isctest.query.tcp(msg, "10.53.0.3") + isctest.check.servfail(res) diff -Nru bind9-9.18.41/bin/tests/system/dnstap/tests_dnstap.py bind9-9.18.44/bin/tests/system/dnstap/tests_dnstap.py --- bind9-9.18.41/bin/tests/system/dnstap/tests_dnstap.py 2025-10-18 10:21:02.899254652 +0000 +++ bind9-9.18.44/bin/tests/system/dnstap/tests_dnstap.py 2026-01-09 13:44:04.573034848 +0000 @@ -13,7 +13,6 @@ import os import re -import subprocess import isctest import pytest @@ -30,20 +29,7 @@ ) -def run_rndc(server, rndc_command): - """ - Send the specified 'rndc_command' to 'server' with a timeout of 10 seconds - """ - rndc = os.getenv("RNDC") - port = os.getenv("CONTROLPORT") - - cmdline = [rndc, "-c", "../_common/rndc.conf", "-p", port, "-s", server] - cmdline.extend(rndc_command) - - subprocess.check_output(cmdline, stderr=subprocess.STDOUT, timeout=10) - - -def test_dnstap_dispatch_socket_addresses(): +def test_dnstap_dispatch_socket_addresses(ns3): # Send some query to ns3 so that it records something in its dnstap file. msg = isctest.query.create("mail.example.", "A") res = isctest.query.tcp(msg, "10.53.0.2", expected_rcode=dns.rcode.NOERROR) @@ -52,14 +38,14 @@ ] # Before continuing, roll dnstap file to ensure it is flushed to disk. - run_rndc("10.53.0.3", ["dnstap", "-roll", "1"]) + ns3.rndc("dnstap -roll 1") # Move the dnstap file aside so that it is retained for troubleshooting. os.rename(os.path.join("ns3", "dnstap.out.0"), "dnstap.out.resolver_addresses") # Read the contents of the dnstap file using dnstap-read. - output = subprocess.check_output( - [os.getenv("DNSTAPREAD"), "dnstap.out.resolver_addresses"], encoding="utf-8" + dnstapread = isctest.run.cmd( + [os.getenv("DNSTAPREAD"), "dnstap.out.resolver_addresses"], ) # Check whether all frames contain the expected addresses. @@ -74,7 +60,7 @@ bad_frames = [] inspected_frames = 0 addr_regex = r"^10\.53\.0\.[0-9]+:[0-9]{1,5}$" - for line in output.splitlines(): + for line in dnstapread.out.splitlines(): _, _, frame_type, addr1, _, addr2, _ = line.split(" ", 6) # Only inspect RESOLVER_QUERY and RESOLVER_RESPONSE frames. if frame_type not in ("RQ", "RR"): diff -Nru bind9-9.18.41/bin/tests/system/doth/conftest.py bind9-9.18.44/bin/tests/system/doth/conftest.py --- bind9-9.18.41/bin/tests/system/doth/conftest.py 2025-10-18 10:21:02.902254732 +0000 +++ bind9-9.18.44/bin/tests/system/doth/conftest.py 2026-01-09 13:44:04.577034914 +0000 @@ -25,10 +25,10 @@ pytest.skip("gnutls-cli not found in PATH") # Ensure gnutls-cli supports the --logfile command-line option. - output = isctest.run.cmd( + cmd = isctest.run.cmd( [executable, "--logfile=/dev/null"], log_stderr=False, raise_on_exception=False - ).stdout - if b"illegal option" in output: + ) + if "illegal option" in cmd.out: pytest.skip("gnutls-cli does not support the --logfile option") return executable diff -Nru bind9-9.18.41/bin/tests/system/doth/example.axfr.good bind9-9.18.44/bin/tests/system/doth/example.axfr.good --- bind9-9.18.41/bin/tests/system/doth/example.axfr.good 2025-10-18 10:21:02.903254759 +0000 +++ bind9-9.18.44/bin/tests/system/doth/example.axfr.good 2026-01-09 13:44:04.577034914 +0000 @@ -11,8 +11,8 @@ aaaa02.example. 3600 IN AAAA fd92:7065:b8e:ffff::5 afsdb01.example. 3600 IN AFSDB 0 hostname.example. afsdb02.example. 3600 IN AFSDB 65535 . -amtrelay01.example. 3600 IN AMTRELAY 0 0 0 -amtrelay02.example. 3600 IN AMTRELAY 0 1 0 +amtrelay01.example. 3600 IN AMTRELAY 0 0 0 . +amtrelay02.example. 3600 IN AMTRELAY 0 1 0 . amtrelay03.example. 3600 IN AMTRELAY 0 0 1 0.0.0.0 amtrelay04.example. 3600 IN AMTRELAY 0 0 2 :: amtrelay05.example. 3600 IN AMTRELAY 0 0 3 example.net. diff -Nru bind9-9.18.41/bin/tests/system/doth/example8.axfr.good bind9-9.18.44/bin/tests/system/doth/example8.axfr.good --- bind9-9.18.41/bin/tests/system/doth/example8.axfr.good 2025-10-18 10:21:02.903254759 +0000 +++ bind9-9.18.44/bin/tests/system/doth/example8.axfr.good 2026-01-09 13:44:04.578034931 +0000 @@ -11,8 +11,8 @@ aaaa02.example8. 3600 IN AAAA fd92:7065:b8e:ffff::5 afsdb01.example8. 3600 IN AFSDB 0 hostname.example8. afsdb02.example8. 3600 IN AFSDB 65535 . -amtrelay01.example8. 3600 IN AMTRELAY 0 0 0 -amtrelay02.example8. 3600 IN AMTRELAY 0 1 0 +amtrelay01.example8. 3600 IN AMTRELAY 0 0 0 . +amtrelay02.example8. 3600 IN AMTRELAY 0 1 0 . amtrelay03.example8. 3600 IN AMTRELAY 0 0 1 0.0.0.0 amtrelay04.example8. 3600 IN AMTRELAY 0 0 2 :: amtrelay05.example8. 3600 IN AMTRELAY 0 0 3 example.net. diff -Nru bind9-9.18.41/bin/tests/system/dyndb/driver/util.h bind9-9.18.44/bin/tests/system/dyndb/driver/util.h --- bind9-9.18.41/bin/tests/system/dyndb/driver/util.h 2025-10-18 10:21:02.909254919 +0000 +++ bind9-9.18.44/bin/tests/system/dyndb/driver/util.h 2026-01-09 13:44:04.583035013 +0000 @@ -39,19 +39,6 @@ #include "log.h" -#define CLEANUP_WITH(result_code) \ - do { \ - result = (result_code); \ - goto cleanup; \ - } while (0) - -#define CHECK(op) \ - do { \ - result = (op); \ - if (result != ISC_R_SUCCESS) \ - goto cleanup; \ - } while (0) - #define CHECKED_MEM_GET(m, target_ptr, s) \ do { \ (target_ptr) = isc_mem_get((m), (s)); \ diff -Nru bind9-9.18.41/bin/tests/system/dyndb/driver/zone.c bind9-9.18.44/bin/tests/system/dyndb/driver/zone.c --- bind9-9.18.41/bin/tests/system/dyndb/driver/zone.c 2025-10-18 10:21:02.909254919 +0000 +++ bind9-9.18.44/bin/tests/system/dyndb/driver/zone.c 2026-01-09 13:44:04.583035013 +0000 @@ -142,8 +142,8 @@ /* Return success if the zone is already in the view as expected. */ result = dns_view_findzone(inst->view, dns_zone_getorigin(zone), &zone_in_view); - if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) { - goto cleanup; + if (result != ISC_R_NOTFOUND) { + CHECK(result); } view_in_zone = dns_zone_getview(zone); @@ -151,7 +151,8 @@ /* Zone has a view set -> view should contain the same zone. */ if (zone_in_view == zone) { /* Zone is already published in the right view. */ - CLEANUP_WITH(ISC_R_SUCCESS); + result = ISC_R_SUCCESS; + goto cleanup; } else if (view_in_zone != inst->view) { /* * Un-published inactive zone will have @@ -161,7 +162,7 @@ dns_zone_log(zone, ISC_LOG_ERROR, "zone->view doesn't " "match data in the view"); - CLEANUP_WITH(ISC_R_UNEXPECTED); + CHECK(ISC_R_UNEXPECTED); } } @@ -169,7 +170,7 @@ dns_zone_log(zone, ISC_LOG_ERROR, "cannot publish zone: view already " "contains another zone with this name"); - CLEANUP_WITH(ISC_R_UNEXPECTED); + CHECK(ISC_R_UNEXPECTED); } run_exclusive_enter(inst, &lock_state); diff -Nru bind9-9.18.41/bin/tests/system/enginepkcs11/setup.sh bind9-9.18.44/bin/tests/system/enginepkcs11/setup.sh --- bind9-9.18.41/bin/tests/system/enginepkcs11/setup.sh 2025-10-18 10:21:02.913255026 +0000 +++ bind9-9.18.44/bin/tests/system/enginepkcs11/setup.sh 2026-01-09 13:44:04.588035096 +0000 @@ -42,7 +42,7 @@ dir="$4" shift 4 - $KEYFRLAB -K $dir -E pkcs11 -a $alg -l "token=softhsm2-enginepkcs11;object=${id}-${zone};pin-source=$PWD/pin" "$@" $zone >>keyfromlabel.out.$zone.$id 2>keyfromlabel.err.$zone.$id || return 1 + $KEYFRLAB -K $dir -E pkcs11 -a $alg -y -l "token=softhsm2-enginepkcs11;object=${id}-${zone};pin-source=$PWD/pin" "$@" $zone >>keyfromlabel.out.$zone.$id 2>keyfromlabel.err.$zone.$id || return 1 cat keyfromlabel.out.$zone.$id } diff -Nru bind9-9.18.41/bin/tests/system/genzone.sh bind9-9.18.44/bin/tests/system/genzone.sh --- bind9-9.18.41/bin/tests/system/genzone.sh 2025-10-18 10:21:02.925255347 +0000 +++ bind9-9.18.44/bin/tests/system/genzone.sh 2026-01-09 13:44:04.599035277 +0000 @@ -487,8 +487,8 @@ doa02 DOA 0 1 2 "" aHR0cHM6Ly93d3cuaXNjLm9yZy8= ; type 260 -amtrelay01 AMTRELAY 0 0 0 -amtrelay02 AMTRELAY 0 1 0 +amtrelay01 AMTRELAY 0 0 0 . +amtrelay02 AMTRELAY 0 1 0 . amtrelay03 AMTRELAY 0 0 1 0.0.0.0 amtrelay04 AMTRELAY 0 0 2 :: amtrelay05 AMTRELAY 0 0 3 example.net. diff -Nru bind9-9.18.41/bin/tests/system/hooks/driver/test-async.c bind9-9.18.44/bin/tests/system/hooks/driver/test-async.c --- bind9-9.18.41/bin/tests/system/hooks/driver/test-async.c 2025-10-18 10:21:02.928255427 +0000 +++ bind9-9.18.44/bin/tests/system/hooks/driver/test-async.c 2026-01-09 13:44:04.603035343 +0000 @@ -36,14 +36,6 @@ #include #include -#define CHECK(op) \ - do { \ - result = (op); \ - if (result != ISC_R_SUCCESS) { \ - goto cleanup; \ - } \ - } while (0) - /* * Persistent data for use by this module. This will be associated * with client object address in the hash table, and will remain diff -Nru bind9-9.18.41/bin/tests/system/isctest/__init__.py bind9-9.18.44/bin/tests/system/isctest/__init__.py --- bind9-9.18.41/bin/tests/system/isctest/__init__.py 2025-10-18 10:21:02.935255614 +0000 +++ bind9-9.18.44/bin/tests/system/isctest/__init__.py 2026-01-09 13:44:04.610035459 +0000 @@ -12,7 +12,6 @@ from . import check from . import instance from . import query -from . import rndc from . import run from . import template from . import log diff -Nru bind9-9.18.41/bin/tests/system/isctest/asyncserver.py bind9-9.18.44/bin/tests/system/isctest/asyncserver.py --- bind9-9.18.41/bin/tests/system/isctest/asyncserver.py 1970-01-01 00:00:00.000000000 +0000 +++ bind9-9.18.44/bin/tests/system/isctest/asyncserver.py 2026-01-09 13:44:04.610035459 +0000 @@ -0,0 +1,1530 @@ +""" +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. +""" + +from dataclasses import dataclass, field +from typing import ( + Any, + AsyncGenerator, + Callable, + Coroutine, + Dict, + List, + Optional, + Set, + Tuple, + Union, + cast, +) + +import abc +import asyncio +import contextlib +import copy +import enum +import functools +import logging +import os +import pathlib +import re +import signal +import struct +import sys + +import dns.exception +import dns.flags +import dns.message +import dns.name +import dns.node +import dns.rcode +import dns.rdata +import dns.rdataclass +import dns.rdataset +import dns.rdatatype +import dns.rrset +import dns.tsig +import dns.version +import dns.zone + + +_UdpHandler = Callable[ + [bytes, Tuple[str, int], asyncio.DatagramTransport], Coroutine[Any, Any, None] +] + + +_TcpHandler = Callable[ + [asyncio.StreamReader, asyncio.StreamWriter], Coroutine[Any, Any, None] +] + + +class _AsyncUdpHandler(asyncio.DatagramProtocol): + """ + Protocol implementation for handling UDP traffic using asyncio. + """ + + def __init__( + self, + handler: _UdpHandler, + ) -> None: + self._transport: Optional[asyncio.DatagramTransport] = None + self._handler: _UdpHandler = handler + + def connection_made(self, transport: asyncio.BaseTransport) -> None: + """ + Called by asyncio when a connection is made. + """ + self._transport = cast(asyncio.DatagramTransport, transport) + + def datagram_received(self, data: bytes, addr: Tuple[str, int]) -> None: + """ + Called by asyncio when a datagram is received. + """ + assert self._transport + handler_coroutine = self._handler(data, addr, self._transport) + try: + # Python >= 3.7 + asyncio.create_task(handler_coroutine) + except AttributeError: + # Python < 3.7 + loop = asyncio.get_event_loop() + loop.create_task(handler_coroutine) + + +class AsyncServer: + """ + A generic asynchronous server which may handle UDP and/or TCP traffic. + + Once the server is executed as asyncio coroutine, it will keep running + until a SIGINT/SIGTERM signal is received. + """ + + def __init__( + self, + udp_handler: Optional[_UdpHandler], + tcp_handler: Optional[_TcpHandler], + pidfile: Optional[str] = None, + ) -> None: + self._abort_if_on_dnspython_version_less_than_2_0_0() + logging.basicConfig( + format="%(asctime)s %(levelname)8s %(message)s", + level=os.environ.get("ANS_LOG_LEVEL", "INFO").upper(), + ) + try: + ipv4_address = sys.argv[1] + except IndexError: + ipv4_address = self._get_ipv4_address_from_directory_name() + + last_ipv4_address_octet = ipv4_address.split(".")[-1] + ipv6_address = f"fd92:7065:b8e:ffff::{last_ipv4_address_octet}" + + try: + port = int(sys.argv[2]) + except IndexError: + port = int(os.environ.get("PORT", 5300)) + + logging.info("Setting up IPv4 listener at %s:%d", ipv4_address, port) + logging.info("Setting up IPv6 listener at [%s]:%d", ipv6_address, port) + + self._ip_addresses: Tuple[str, str] = (ipv4_address, ipv6_address) + self._port: int = port + self._udp_handler: Optional[_UdpHandler] = udp_handler + self._tcp_handler: Optional[_TcpHandler] = tcp_handler + self._pidfile: Optional[str] = pidfile + self._work_done: Optional[asyncio.Future] = None + + @classmethod + def _abort_if_on_dnspython_version_less_than_2_0_0(cls) -> None: + if dns.version.MAJOR < 2: + error = f"Using {cls.__name__} requires dnspython >= 2.0.0; " + error += 'add `pytest.importorskip("dns", minversion="2.0.0")` ' + error += "to the test module to skip this test." + raise RuntimeError(error) + + def _get_ipv4_address_from_directory_name(self) -> str: + containing_directory = pathlib.Path().absolute().stem + match_result = re.match(r"ans(?P\d+)", containing_directory) + if not match_result: + raise RuntimeError("Unable to auto-determine the IPv4 address to use") + + return f"10.53.0.{match_result.group('index')}" + + def run(self) -> None: + """ + Start the server in an asynchronous coroutine. + """ + coroutine = self._run + try: + # Python >= 3.7 + asyncio.run(coroutine()) + except AttributeError: + # Python < 3.7 + loop = asyncio.get_event_loop() + loop.run_until_complete(coroutine()) + + async def _run(self) -> None: + self._setup_exception_handler() + self._setup_signals() + assert self._work_done + await self._listen_udp() + await self._listen_tcp() + self._write_pidfile() + await self._work_done + self._cleanup_pidfile() + + def _get_asyncio_loop(self) -> asyncio.AbstractEventLoop: + try: + # Python >= 3.7 + loop = asyncio.get_running_loop() + except AttributeError: + # Python < 3.7 + loop = asyncio.get_event_loop() + return loop + + def _setup_exception_handler(self) -> None: + loop = self._get_asyncio_loop() + self._work_done = loop.create_future() + loop.set_exception_handler(self._handle_exception) + + def _handle_exception( + self, _: asyncio.AbstractEventLoop, context: Dict[str, Any] + ) -> None: + assert self._work_done + exception = context.get("exception", RuntimeError(context["message"])) + try: + self._work_done.set_exception(exception) + except asyncio.InvalidStateError: + pass + + def _setup_signals(self) -> None: + loop = self._get_asyncio_loop() + loop.add_signal_handler(signal.SIGINT, functools.partial(self._signal_done)) + loop.add_signal_handler(signal.SIGTERM, functools.partial(self._signal_done)) + + def _signal_done(self) -> None: + assert self._work_done + try: + self._work_done.set_result(True) + except asyncio.InvalidStateError: + pass + + async def _listen_udp(self) -> None: + if not self._udp_handler: + return + loop = self._get_asyncio_loop() + for ip_address in self._ip_addresses: + await loop.create_datagram_endpoint( + lambda: _AsyncUdpHandler(cast(_UdpHandler, self._udp_handler)), + (ip_address, self._port), + ) + + async def _listen_tcp(self) -> None: + if not self._tcp_handler: + return + for ip_address in self._ip_addresses: + await asyncio.start_server( + self._tcp_handler, host=ip_address, port=self._port + ) + + def _write_pidfile(self) -> None: + if not self._pidfile: + return + logging.info("Writing PID to %s", self._pidfile) + with open(self._pidfile, "w", encoding="ascii") as pidfile: + print(f"{os.getpid()}", file=pidfile) + + def _cleanup_pidfile(self) -> None: + if not self._pidfile: + return + logging.info("Removing %s", self._pidfile) + os.unlink(self._pidfile) + + +class DnsProtocol(enum.Enum): + UDP = enum.auto() + TCP = enum.auto() + + +@dataclass(frozen=True) +class Peer: + """ + Pretty-printed connection endpoint. + """ + + host: str + port: int + + def __str__(self) -> str: + host = f"[{self.host}]" if ":" in self.host else self.host + return f"{host}:{self.port}" + + +@dataclass +class QueryContext: + """ + Context for the incoming query which may be used for preparing the response. + """ + + query: dns.message.Message + response: dns.message.Message + peer: Peer + protocol: DnsProtocol + zone: Optional[dns.zone.Zone] = field(default=None, init=False) + soa: Optional[dns.rrset.RRset] = field(default=None, init=False) + node: Optional[dns.node.Node] = field(default=None, init=False) + answer: Optional[dns.rdataset.Rdataset] = field(default=None, init=False) + alias: Optional[dns.name.Name] = field(default=None, init=False) + _initialized_response: Optional[dns.message.Message] = field( + default=None, init=False + ) + _initialized_response_with_zone_data: Optional[dns.message.Message] = field( + default=None, init=False + ) + + @property + def qname(self) -> dns.name.Name: + return self.query.question[0].name + + @property + def current_qname(self) -> dns.name.Name: + return self.alias or self.qname + + @property + def qclass(self) -> dns.rdataclass.RdataClass: + return self.query.question[0].rdclass + + @property + def qtype(self) -> dns.rdatatype.RdataType: + return self.query.question[0].rdtype + + def prepare_new_response( + self, /, with_zone_data: bool = True + ) -> dns.message.Message: + if with_zone_data: + assert self._initialized_response_with_zone_data + self.response = copy.deepcopy(self._initialized_response_with_zone_data) + else: + assert self._initialized_response + self.response = copy.deepcopy(self._initialized_response) + return self.response + + def save_initialized_response(self, /, with_zone_data: bool) -> None: + if with_zone_data: + self._initialized_response_with_zone_data = copy.deepcopy(self.response) + else: + self._initialized_response = copy.deepcopy(self.response) + + +@dataclass +class ResponseAction(abc.ABC): + """ + Base class for actions that can be taken in response to a query. + """ + + @abc.abstractmethod + async def perform(self) -> Optional[Union[dns.message.Message, bytes]]: + """ + This method is expected to carry out arbitrary actions (e.g. wait for a + specific amount of time, modify the answer, etc.) and then return the + DNS response to send (a dns.message.Message, a raw bytes object, or + None, which prevents any response from being sent). + """ + raise NotImplementedError + + +@dataclass +class DnsResponseSend(ResponseAction): + """ + Action which yields a dns.message.Message response. + + The response may be sent with a delay if requested. + + Depending on the value of the `authoritative` property, this class may set + the AA bit in the response (True), clear it (False), or not touch it at all + (None). + """ + + response: dns.message.Message + authoritative: Optional[bool] = None + delay: float = 0.0 + + async def perform(self) -> Optional[Union[dns.message.Message, bytes]]: + """ + Yield a potentially delayed response that is a dns.message.Message. + """ + assert isinstance(self.response, dns.message.Message) + if self.authoritative is not None: + if self.authoritative: + self.response.flags |= dns.flags.AA + else: + self.response.flags &= ~dns.flags.AA + if self.delay > 0: + logging.info( + "Delaying response (ID=%d) by %d ms", + self.response.id, + self.delay * 1000, + ) + await asyncio.sleep(self.delay) + return self.response + + +@dataclass +class BytesResponseSend(ResponseAction): + """ + Action which yields a raw response that is a sequence of bytes. + + The response may be sent with a delay if requested. + """ + + response: bytes + delay: float = 0.0 + + async def perform(self) -> Optional[Union[dns.message.Message, bytes]]: + """ + Yield a potentially delayed response that is a sequence of bytes. + """ + assert isinstance(self.response, bytes) + if self.delay > 0: + logging.info("Delaying raw response by %d ms", self.delay * 1000) + await asyncio.sleep(self.delay) + return self.response + + +@dataclass +class ResponseDrop(ResponseAction): + """ + Action which does nothing - as if a packet was dropped. + """ + + async def perform(self) -> Optional[Union[dns.message.Message, bytes]]: + return None + + +class _ConnectionTeardownRequested(Exception): + pass + + +@dataclass +class ResponseDropAndCloseConnection(ResponseAction): + """ + Action which makes the server close the connection after the DNS query is + received by the server (TCP only). + + The connection may be closed with a delay if requested. + """ + + delay: float = 0.0 + + async def perform(self) -> Optional[Union[dns.message.Message, bytes]]: + if self.delay > 0: + logging.info("Waiting %.1fs before closing TCP connection", self.delay) + await asyncio.sleep(self.delay) + raise _ConnectionTeardownRequested + + +class ConnectionHandler(abc.ABC): + """ + Base class for TCP connection handlers. + + An installed connection handler is called when a new TCP connection is + established. It may be used to perform arbitrary actions before + AsyncDnsServer processes DNS queries. + """ + + @abc.abstractmethod + async def handle( + self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter, peer: Peer + ) -> None: + """ + Handle the connection with the provided reader and writer. + """ + raise NotImplementedError + + +def block_reading(peer: Peer, writer_not_the_reader: asyncio.StreamWriter) -> None: + """ + Block reads for the reader associated with the provided writer. + + Yes, pass the writer, not the reader. See the comments below for details. + """ + + try: + # Python >= 3.7 + loop = asyncio.get_running_loop() + except AttributeError: + # Python < 3.7 + loop = asyncio.get_event_loop() + + logging.info("Blocking reads from %s", peer) + + # This is MichaÅ‚'s submission for the Ugliest Hack of the Year contest. + # (The alternative was implementing an asyncio transport from scratch.) + # + # In order to prevent the client socket from being read from, simply + # not calling `reader.read()` is not enough, because asyncio buffers + # incoming data itself on the transport level. However, `StreamReader` + # does not expose the underlying transport as a property. Therefore, + # cheat by extracting it from `StreamWriter` as it is the same + # bidirectional transport as for the read side (a `Transport`, which is + # a subclass of both `ReadTransport` and `WriteTransport`) and call + # `ReadTransport.pause_reading()` to remove the underlying socket from + # the set of descriptors monitored by the selector, thereby preventing + # any reads from happening on the client socket. However... + loop.call_soon(writer_not_the_reader.transport.pause_reading) # type: ignore + + # ...due to `AsyncDnsServer._handle_tcp()` being a coroutine, by the + # time it gets executed, asyncio transport code will already have added + # the client socket to the set of descriptors monitored by the + # selector. Therefore, if the client starts sending data immediately, + # a read from the socket will have already been scheduled by the time + # this handler gets executed. There is no way to prevent that from + # happening, so work around it by abusing the fact that the transport + # at hand is specifically an instance of `_SelectorSocketTransport` + # (from asyncio.selector_events) and set the size of its read buffer to + # just a single byte. This does give asyncio enough time to read that + # single byte from the client socket's buffer before that socket is + # removed from the set of monitored descriptors, but prevents the + # one-off read from emptying the client socket buffer _entirely_, which + # is enough to trigger sending an RST segment when the connection is + # closed shortly afterwards. + writer_not_the_reader.transport.max_size = 1 # type: ignore + + +@dataclass +class IgnoreAllConnections(ConnectionHandler): + """ + A connection handler that makes the server not read anything from the + client socket, effectively ignoring all incoming connections. + """ + + _connections: Set[asyncio.StreamWriter] = field(default_factory=set) + + async def handle( + self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter, peer: Peer + ) -> None: + block_reading(peer, writer) + # Due to the way various asyncio-related objects (tasks, streams, + # transports, selectors) are referencing each other, pausing reads for + # a TCP transport (which in practice means removing the client socket + # from the set of descriptors monitored by a selector) can cause the + # client task (AsyncDnsServer._handle_tcp()) to be prematurely + # garbage-collected, causing asyncio code to raise a "Task was + # destroyed but it is pending!" exception. Prevent that from happening + # by keeping a reference to each incoming TCP connection to protect its + # related asyncio objects from getting garbage-collected. This + # prevents AsyncDnsServer from closing any of the ignored TCP + # connections indefinitely, which is obviously a pretty brain-dead idea + # for a production-grade DNS server, but AsyncDnsServer was never meant + # to be one and this hack reliably solves the problem at hand. + self._connections.add(writer) + + +@dataclass +class ConnectionReset(ConnectionHandler): + """ + A connection handler that makes the server close the connection without + reading anything from the client socket. + + The connection may be closed with a delay if requested. + + The sole purpose of this handler is to trigger a connection reset, i.e. to + make the server send an RST segment; this happens when the server closes a + client's socket while there is still unread data in that socket's buffer. + If closing the connection _after_ the query is read by the server is enough + for a given use case, the ResponseDropAndCloseConnection response handler + should be used instead. + """ + + delay: float = 0.0 + + async def handle( + self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter, peer: Peer + ) -> None: + block_reading(peer, writer) + + if self.delay > 0: + logging.info( + "Waiting %.1fs before closing TCP connection from %s", self.delay, peer + ) + await asyncio.sleep(self.delay) + + raise _ConnectionTeardownRequested + + +class ResponseHandler(abc.ABC): + """ + Base class for generic response handlers. + + If a query passes the `match()` function logic, then it is handled by this + response handler and response(s) may be generated by the `get_responses()` + method. + """ + + # pylint: disable=unused-argument + def match(self, qctx: QueryContext) -> bool: + """ + Matching logic - the first handler whose `match()` method returns True + is used for handling the query. + + The default for each handler is to handle all queries. + """ + return True + + @abc.abstractmethod + async def get_responses( + self, qctx: QueryContext + ) -> AsyncGenerator[ResponseAction, None]: + """ + Custom handler which may produce response(s) to matching queries. + + The response prepared from zone data is passed to this method in + qctx.response. + """ + yield DnsResponseSend(qctx.response) + + def __str__(self) -> str: + return self.__class__.__name__ + + +class IgnoreAllQueries(ResponseHandler): + """ + Do not respond to any queries sent to the server. + """ + + async def get_responses( + self, qctx: QueryContext + ) -> AsyncGenerator[ResponseAction, None]: + yield ResponseDrop() + + +class QnameHandler(ResponseHandler): + """ + Base class used for deriving custom QNAME handlers. + + The derived class must specify a list of `qnames` that it wants to handle. + Queries for exactly these QNAMEs will then be passed to the + `get_response()` method in the derived class. + """ + + @property + @abc.abstractmethod + def qnames(self) -> List[str]: + """ + A list of QNAMEs handled by this class. + """ + raise NotImplementedError + + def __init__(self) -> None: + self._qnames: List[dns.name.Name] = [dns.name.from_text(d) for d in self.qnames] + + def __str__(self) -> str: + return f"{self.__class__.__name__}(QNAMEs: {', '.join(self.qnames)})" + + def match(self, qctx: QueryContext) -> bool: + """ + Handle queries whose QNAME matches any of the QNAMEs handled by this + class. + """ + return qctx.qname in self._qnames + + +class DomainHandler(ResponseHandler): + """ + Base class used for deriving custom domain handlers. + + The derived class must specify a list of `domains` that it wants to handle. + Queries for any of these domains (and their subdomains) will then be passed + to the `get_response()` method in the derived class. + """ + + @property + @abc.abstractmethod + def domains(self) -> List[str]: + """ + A list of domain names handled by this class. + """ + raise NotImplementedError + + def __init__(self) -> None: + self._domains: List[dns.name.Name] = [ + dns.name.from_text(d) for d in self.domains + ] + + def __str__(self) -> str: + return f"{self.__class__.__name__}(domains: {', '.join(self.domains)})" + + def match(self, qctx: QueryContext) -> bool: + """ + Handle queries whose QNAME matches any of the domains handled by this + class. + """ + for domain in self._domains: + if qctx.qname.is_subdomain(domain): + return True + return False + + +@dataclass +class _ZoneTreeNode: + """ + A node representing a zone with one origin. + """ + + zone: Optional[dns.zone.Zone] + children: List["_ZoneTreeNode"] = field(default_factory=list) + + +class _ZoneTree: + """ + Tree with independent zones. + + This zone tree is used as a backing structure for the DNS server. The + individual zones are independent to allow the (single) server to serve both + the parent zone and a child zone if needed. + """ + + def __init__(self) -> None: + self._root: _ZoneTreeNode = _ZoneTreeNode(None) + + def add(self, zone: dns.zone.Zone) -> None: + """ + Add a zone to the tree and rearrange sub-zones if necessary. + """ + assert zone.origin + best_match = self._find_best_match(zone.origin, self._root) + added_node = _ZoneTreeNode(zone) + self._move_children(best_match, added_node) + best_match.children.append(added_node) + + def _find_best_match( + self, name: dns.name.Name, start_node: _ZoneTreeNode + ) -> _ZoneTreeNode: + for child in start_node.children: + assert child.zone + assert child.zone.origin + if name.is_subdomain(child.zone.origin): + return self._find_best_match(name, child) + return start_node + + def _move_children(self, node_from: _ZoneTreeNode, node_to: _ZoneTreeNode) -> None: + assert node_to.zone + assert node_to.zone.origin + + children_to_move = [] + for child in node_from.children: + assert child.zone + assert child.zone.origin + if child.zone.origin.is_subdomain(node_to.zone.origin): + children_to_move.append(child) + + for child in children_to_move: + node_from.children.remove(child) + node_to.children.append(child) + + def find_best_zone(self, name: dns.name.Name) -> Optional[dns.zone.Zone]: + """ + Return the closest matching zone (if any) for the domain name. + """ + node = self._find_best_match(name, self._root) + return node.zone if node != self._root else None + + +class _DnsMessageWithTsigDisabled(dns.message.Message): + """ + A wrapper for `dns.message.Message` that works around a dnspython bug + causing exceptions to be raised when `make_response()` or `to_wire()` are + called for a message created using `dns.message.from_wire(keyring=False)`. + + See https://github.com/rthalley/dnspython/issues/1205 for more details. + """ + + class _DisableTsigHandling(contextlib.ContextDecorator): + def __init__(self, message: Optional[dns.message.Message] = None) -> None: + self.original_tsig_sign = dns.tsig.sign + self.original_tsig_validate = dns.tsig.validate + if message: + self.tsig = message.tsig + + def __enter__(self) -> None: + """ + Override the `dns.tsig.sign` and `dns.tsig.validate` functions to prevent them + from failing on messages initialized with `dns.message.from_wire(keyring=False)`. + """ + + def sign(*_: Any, **__: Any) -> Tuple[dns.rdata.Rdata, None]: + assert self.tsig + return self.tsig[0], None + + def validate(*_: Any, **__: Any) -> None: + return None + + dns.tsig.sign = sign + dns.tsig.validate = validate + + def __exit__(self, *_: Any, **__: Any) -> None: + dns.tsig.sign = self.original_tsig_sign + dns.tsig.validate = self.original_tsig_validate + + @classmethod + def from_wire(cls, wire: bytes) -> "_DnsMessageWithTsigDisabled": + with cls._DisableTsigHandling(): + message = dns.message.from_wire(wire, keyring=False) + message.__class__ = _DnsMessageWithTsigDisabled + + return cast(_DnsMessageWithTsigDisabled, message) + + @property + def had_tsig(self) -> bool: + """ + Override the `had_tsig()` method to always return False, to prevent + `make_response()` from crashing. + """ + return False + + def to_wire(self, *args: Any, **kwargs: Any) -> bytes: + """ + Override the `to_wire()` method to prevent it from trying to sign + the message with TSIG. + """ + with self._DisableTsigHandling(self): + return super().to_wire(*args, **kwargs) + + +class _NoKeyringType: + pass + + +class AsyncDnsServer(AsyncServer): + """ + DNS server which responds to queries based on zone data and/or custom + handlers. + + The server may use custom handlers which allow arbitrary query processing. + These don't need to be standards-compliant and can be used for testing all + sorts of scenarios, including delaying responses, synthesizing them based + on query contents etc. + + The server also loads any zone files (*.db) found in its directory and + serves them. Responses prepared using zone data can then be modified, + replaced, or suppressed by query handlers. Query handlers can also generate + response from scratch, without using zone data at all. + """ + + def __init__( + self, + /, + default_rcode: dns.rcode.Rcode = dns.rcode.REFUSED, + default_aa: bool = True, + keyring: Union[ + Dict[dns.name.Name, dns.tsig.Key], None, _NoKeyringType + ] = _NoKeyringType(), + acknowledge_manual_dname_handling: bool = False, + ) -> None: + super().__init__(self._handle_udp, self._handle_tcp, "ans.pid") + + self._zone_tree: _ZoneTree = _ZoneTree() + self._connection_handler: Optional[ConnectionHandler] = None + self._response_handlers: List[ResponseHandler] = [] + self._default_rcode = default_rcode + self._default_aa = default_aa + self._keyring = keyring + self._acknowledge_manual_dname_handling = acknowledge_manual_dname_handling + + self._load_zones() + + def install_response_handler( + self, handler: ResponseHandler, prepend: bool = False + ) -> None: + """ + Add a response handler that will be used to handle matching queries. + + Response handlers can modify, replace, or suppress the answers prepared + from zone file contents. + + The provided handler is installed at the end of the response handler + list unless `prepend` is set to True, in which case it is installed at + the beginning of the response handler list. + """ + logging.info("Installing response handler: %s", handler) + if prepend: + self._response_handlers.insert(0, handler) + else: + self._response_handlers.append(handler) + + def install_response_handlers(self, handlers: List[ResponseHandler]) -> None: + for handler in handlers: + self.install_response_handler(handler) + + def uninstall_response_handler(self, handler: ResponseHandler) -> None: + """ + Remove the specified handler from the list of response handlers. + """ + logging.info("Uninstalling response handler: %s", handler) + self._response_handlers.remove(handler) + + def install_connection_handler(self, handler: ConnectionHandler) -> None: + """ + Install a connection handler that will be called when a new TCP + connection is established. + """ + if self._connection_handler: + raise RuntimeError("Only one connection handler can be installed") + self._connection_handler = handler + + def _load_zones(self) -> None: + for entry in os.scandir(): + entry_path = pathlib.Path(entry.path) + if entry_path.suffix != ".db": + continue + zone = self._load_zone(entry_path) + self._zone_tree.add(zone) + + def _load_zone(self, zone_file_path: pathlib.Path) -> dns.zone.Zone: + logging.info("Loading zone file %s", zone_file_path) + zone = self._load_zone_file(zone_file_path) + self._abort_if_dname_found_unless_acknowledged(zone) + return zone + + def _load_zone_file(self, zone_file_path: pathlib.Path) -> dns.zone.Zone: + try: + zone = self._load_zone_file_with_origin(zone_file_path) + except dns.zone.UnknownOrigin: + zone = self._load_zone_file_without_origin(zone_file_path) + + return zone + + def _load_zone_file_with_origin( + self, zone_file_path: pathlib.Path + ) -> dns.zone.Zone: + zone = dns.zone.from_file(str(zone_file_path), origin=None, relativize=False) + if zone.origin != dns.name.root: + error = "only the root zone may use $ORIGIN in the zone file; " + error += "for every other zone, its origin is determined by " + error += "the name of the file it is loaded from" + raise ValueError(error) + return zone + + def _load_zone_file_without_origin( + self, zone_file_path: pathlib.Path + ) -> dns.zone.Zone: + origin = dns.name.from_text(zone_file_path.stem) + return dns.zone.from_file(str(zone_file_path), origin=origin, relativize=False) + + def _abort_if_dname_found_unless_acknowledged(self, zone: dns.zone.Zone) -> None: + if self._acknowledge_manual_dname_handling: + return + + error = f'DNAME records found in zone "{zone.origin}"; ' + error += "this server does not handle DNAME in a standards-compliant way; " + error += "pass `acknowledge_manual_dname_handling=True` to the " + error += "AsyncDnsServer constructor to acknowledge this and load zone anyway" + + for node in zone.nodes.values(): + for rdataset in node: + if rdataset.rdtype == dns.rdatatype.DNAME: + raise ValueError(error) + + async def _handle_udp( + self, wire: bytes, addr: Tuple[str, int], transport: asyncio.DatagramTransport + ) -> None: + logging.debug("Received UDP message: %s", wire.hex()) + peer = Peer(addr[0], addr[1]) + responses = self._handle_query(wire, peer, DnsProtocol.UDP) + async for response in responses: + logging.debug("Sending UDP message: %s", response.hex()) + transport.sendto(response, addr) + + async def _handle_tcp( + self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter + ) -> None: + peer_info = writer.get_extra_info("peername") + peer = Peer(peer_info[0], peer_info[1]) + logging.debug("Accepted TCP connection from %s", peer) + + try: + if self._connection_handler: + await self._connection_handler.handle(reader, writer, peer) + while True: + wire = await self._read_tcp_query(reader, peer) + if not wire: + break + await self._send_tcp_response(writer, peer, wire) + except _ConnectionTeardownRequested: + pass + except ConnectionResetError: + logging.error("TCP connection from %s reset by peer", peer) + return + + logging.debug("Closing TCP connection from %s", peer) + writer.close() + try: + # Python >= 3.7 + await writer.wait_closed() + except AttributeError: + # Python < 3.7 + pass + + async def _read_tcp_query( + self, reader: asyncio.StreamReader, peer: Peer + ) -> Optional[bytes]: + wire_length = await self._read_tcp_query_wire_length(reader, peer) + if not wire_length: + return None + + return await self._read_tcp_query_wire(reader, peer, wire_length) + + async def _read_tcp_query_wire_length( + self, reader: asyncio.StreamReader, peer: Peer + ) -> Optional[int]: + logging.debug("Receiving TCP message length from %s...", peer) + + wire_length_bytes = await self._read_tcp_octets(reader, peer, 2) + if not wire_length_bytes: + return None + + (wire_length,) = struct.unpack("!H", wire_length_bytes) + + return wire_length + + async def _read_tcp_query_wire( + self, reader: asyncio.StreamReader, peer: Peer, wire_length: int + ) -> Optional[bytes]: + logging.debug("Receiving TCP message (%d octets) from %s...", wire_length, peer) + + wire = await self._read_tcp_octets(reader, peer, wire_length) + if not wire: + return None + + logging.debug("Received complete TCP message from %s: %s", peer, wire.hex()) + + return wire + + async def _read_tcp_octets( + self, reader: asyncio.StreamReader, peer: Peer, expected: int + ) -> Optional[bytes]: + buffer = b"" + + while len(buffer) < expected: + chunk = await reader.read(expected - len(buffer)) + if not chunk: + if buffer: + logging.debug( + "Received short TCP message (%d octets) from %s: %s", + len(buffer), + peer, + buffer.hex(), + ) + else: + logging.debug("Received disconnect from %s", peer) + return None + + logging.debug("Received %d TCP octets from %s", len(chunk), peer) + buffer += chunk + + return buffer + + async def _send_tcp_response( + self, writer: asyncio.StreamWriter, peer: Peer, wire: bytes + ) -> None: + responses = self._handle_query(wire, peer, DnsProtocol.TCP) + async for response in responses: + logging.debug("Sending TCP response: %s", response.hex()) + writer.write(response) + await writer.drain() + + def _log_query(self, qctx: QueryContext, peer: Peer, protocol: DnsProtocol) -> None: + logging.info( + "Received %s/%s/%s (ID=%d) query from %s (%s)", + qctx.qname.to_text(omit_final_dot=True), + dns.rdataclass.to_text(qctx.qclass), + dns.rdatatype.to_text(qctx.qtype), + qctx.query.id, + peer, + protocol.name, + ) + logging.debug( + "\n".join([f"[IN] {l}" for l in [""] + str(qctx.query).splitlines()]) + ) + + def _log_response( + self, + qctx: QueryContext, + response: Optional[Union[dns.message.Message, bytes]], + peer: Peer, + protocol: DnsProtocol, + ) -> None: + if not response: + logging.info( + "Not sending a response to query (ID=%d) from %s (%s)", + qctx.query.id, + peer, + protocol.name, + ) + return + + if isinstance(response, dns.message.Message): + try: + qname = response.question[0].name.to_text(omit_final_dot=True) + qclass = dns.rdataclass.to_text(response.question[0].rdclass) + qtype = dns.rdatatype.to_text(response.question[0].rdtype) + except IndexError: + qname = "" + qclass = "-" + qtype = "-" + + logging.info( + "Sending %s/%s/%s (ID=%d) response (%d/%d/%d/%d) to a query (ID=%d) from %s (%s)", + qname, + qclass, + qtype, + response.id, + len(response.question), + len(response.answer), + len(response.authority), + len(response.additional), + qctx.query.id, + peer, + protocol.name, + ) + logging.debug( + "\n".join([f"[OUT] {l}" for l in [""] + str(response).splitlines()]) + ) + return + + logging.info( + "Sending response (%d bytes) to a query (ID=%d) from %s (%s)", + len(response), + qctx.query.id, + peer, + protocol.name, + ) + logging.debug("[OUT] %s", response.hex()) + + async def _handle_query( + self, wire: bytes, peer: Peer, protocol: DnsProtocol + ) -> AsyncGenerator[bytes, None]: + """ + Yield wire data to send as a response over the established transport. + """ + try: + query = self._parse_message(wire) + except dns.exception.DNSException as exc: + logging.error("Invalid query from %s (%s): %s", peer, wire.hex(), exc) + return + response_stub = dns.message.make_response(query) + qctx = QueryContext(query, response_stub, peer, protocol) + self._log_query(qctx, peer, protocol) + responses = self._prepare_responses(qctx) + async for response in responses: + self._log_response(qctx, response, peer, protocol) + if response: + if isinstance(response, dns.message.Message): + response = response.to_wire(max_size=65535) + if protocol == DnsProtocol.UDP: + yield response + else: + response_length = struct.pack("!H", len(response)) + yield response_length + response + + def _parse_message(self, wire: bytes) -> dns.message.Message: + try: + if isinstance(self._keyring, _NoKeyringType): + keyring = None + else: + keyring = self._keyring + return dns.message.from_wire(wire, keyring=keyring) + except dns.message.UnknownTSIGKey as exc: + if isinstance(self._keyring, _NoKeyringType): + error = "TSIG-signed query received but no `keyring` was provided; " + error += "either provide a keyring (in which case the server will " + error += "ignore any TSIG-invalid queries), or set `keyring=None` " + error += "explicitly to disable TSIG validation altogether. " + error += "This requires some hacking around a dnspython bug, " + error += "so there may be unexpected side effects." + raise ValueError(error) from exc + if self._keyring is None: + return _DnsMessageWithTsigDisabled.from_wire(wire) + raise + + async def _prepare_responses( + self, qctx: QueryContext + ) -> AsyncGenerator[Optional[Union[dns.message.Message, bytes]], None]: + """ + Yield response(s) either from response handlers or zone data. + """ + qctx.response.set_rcode(self._default_rcode) + if self._default_aa: + qctx.response.flags |= dns.flags.AA + qctx.save_initialized_response(with_zone_data=False) + + self._prepare_response_from_zone_data(qctx) + qctx.save_initialized_response(with_zone_data=True) + + response_handled = False + async for action in self._run_response_handlers(qctx): + yield await action.perform() + response_handled = True + + if not response_handled: + logging.debug("Responding based on zone data") + yield qctx.response + + def _prepare_response_from_zone_data(self, qctx: QueryContext) -> None: + """ + Prepare a response to the query based on the available zone data. + + The functionality is split across smaller functions that modify the + query context until a proper response is formed. + """ + if self._refused_response(qctx): + return + + if self._delegation_response(qctx): + return + + qctx.response.flags |= dns.flags.AA + + if self._ent_response(qctx): + return + + if self._nxdomain_response(qctx): + return + + if self._cname_response(qctx): + return + + if self._nodata_response(qctx): + return + + self._noerror_response(qctx) + + def _refused_response(self, qctx: QueryContext) -> bool: + zone = self._zone_tree.find_best_zone(qctx.current_qname) + if zone: + qctx.zone = zone + return False + + # RCODE is already set to self._default_rcode, i.e. REFUSED by default; + # it should also not be changed when following a CNAME chain + return True + + def _delegation_response(self, qctx: QueryContext) -> bool: + assert qctx.zone + + name = qctx.current_qname + delegation = None + + while name != qctx.zone.origin: + node = qctx.zone.get_node(name) + if node: + delegation = node.get_rdataset(qctx.qclass, dns.rdatatype.NS) + if delegation: + break + name = name.parent() + + if not delegation: + return False + + delegation_rrset = dns.rrset.RRset(name, qctx.qclass, dns.rdatatype.NS) + delegation_rrset.update(delegation) + + qctx.response.set_rcode(dns.rcode.NOERROR) + qctx.response.authority.append(delegation_rrset) + + self._delegation_response_additional(qctx) + + return True + + def _delegation_response_additional(self, qctx: QueryContext) -> None: + assert qctx.zone + assert qctx.response.authority[0] + + for nameserver in qctx.response.authority[0]: + if not nameserver.target.is_subdomain(qctx.response.authority[0].name): + continue + glue_a = qctx.zone.get_rrset(nameserver.target, dns.rdatatype.A) + if glue_a: + qctx.response.additional.append(glue_a) + glue_aaaa = qctx.zone.get_rrset(nameserver.target, dns.rdatatype.AAAA) + if glue_aaaa: + qctx.response.additional.append(glue_aaaa) + + def _ent_response(self, qctx: QueryContext) -> bool: + assert qctx.zone + assert qctx.zone.origin + + qctx.soa = qctx.zone.find_rrset(qctx.zone.origin, dns.rdatatype.SOA) + assert qctx.soa + + qctx.node = qctx.zone.get_node(qctx.current_qname) + if qctx.node or not any( + n for n in qctx.zone.nodes if n.is_subdomain(qctx.current_qname) + ): + return False + + qctx.response.set_rcode(dns.rcode.NOERROR) + qctx.response.authority.append(qctx.soa) + return True + + def _nxdomain_response(self, qctx: QueryContext) -> bool: + assert qctx.soa + + if qctx.node: + return False + + qctx.response.set_rcode(dns.rcode.NXDOMAIN) + qctx.response.authority.append(qctx.soa) + return True + + def _cname_response(self, qctx: QueryContext) -> bool: + assert qctx.node + + cname = qctx.node.get_rdataset(qctx.qclass, dns.rdatatype.CNAME) + if not cname: + return False + + qctx.response.set_rcode(dns.rcode.NOERROR) + cname_rrset = dns.rrset.RRset(qctx.current_qname, qctx.qclass, cname.rdtype) + cname_rrset.update(cname) + qctx.response.answer.append(cname_rrset) + + qctx.alias = cname[0].target + self._prepare_response_from_zone_data(qctx) + return True + + def _nodata_response(self, qctx: QueryContext) -> bool: + assert qctx.node + assert qctx.soa + + qctx.answer = qctx.node.get_rdataset(qctx.qclass, qctx.qtype) + if qctx.answer: + return False + + qctx.response.set_rcode(dns.rcode.NOERROR) + if not qctx.response.answer: + qctx.response.authority.append(qctx.soa) + return True + + def _noerror_response(self, qctx: QueryContext) -> None: + assert qctx.answer + + answer_rrset = dns.rrset.RRset(qctx.current_qname, qctx.qclass, qctx.qtype) + answer_rrset.update(qctx.answer) + + qctx.response.set_rcode(dns.rcode.NOERROR) + qctx.response.answer.append(answer_rrset) + + async def _run_response_handlers( + self, qctx: QueryContext + ) -> AsyncGenerator[ResponseAction, None]: + """ + Yield response(s) to the query from a matching query handler. + """ + for handler in self._response_handlers: + if handler.match(qctx): + logging.debug("Matched response handler: %s", handler) + async for response in handler.get_responses(qctx): + yield response + return + + +class ControllableAsyncDnsServer(AsyncDnsServer): + """ + An AsyncDnsServer whose behavior can be dynamically changed by sending TXT + queries to a "magic" domain. + """ + + _CONTROL_DOMAIN = "_control." + + @functools.cached_property + def _control_domain(self) -> dns.name.Name: + return dns.name.from_text(self._CONTROL_DOMAIN) + + @functools.cached_property + def _commands(self) -> Dict[dns.name.Name, "ControlCommand"]: + return {} + + def install_control_commands(self, commands: List["ControlCommand"]) -> None: + for command in commands: + self.install_control_command(command) + + def install_control_command(self, command: "ControlCommand") -> None: + command_subdomain = dns.name.Name([command.control_subdomain]) + control_subdomain = command_subdomain.concatenate(self._control_domain) + try: + existing_command = self._commands[control_subdomain] + except KeyError: + self._commands[control_subdomain] = command + else: + raise RuntimeError( + f"{control_subdomain} already handled by {existing_command}" + ) + + async def _prepare_responses( + self, qctx: QueryContext + ) -> AsyncGenerator[Optional[Union[dns.message.Message, bytes]], None]: + """ + Detect and handle control queries, falling back to normal processing + for non-control queries. + """ + control_response = self._handle_control_command(qctx) + if control_response: + yield await DnsResponseSend(response=control_response).perform() + return + + async for response in super()._prepare_responses(qctx): + yield response + + def _handle_control_command( + self, qctx: QueryContext + ) -> Optional[dns.message.Message]: + """ + Detect and handle control queries. + + A control query must be of type TXT; if it is not, a FORMERR response + is sent back. + + The list of commands that the server should respond to is passed to its + constructor. If the server is unable to handle the control query using + any of the enabled commands, an NXDOMAIN response is sent. + + Otherwise, the relevant command's handler is expected to provide the + response via qctx.response and/or return a string that is converted to + a TXT RRset inserted into the ANSWER section of the response to the + control query. The RCODE for a command-provided response defaults to + NOERROR, but can be overridden by the command's handler. + """ + if not qctx.qname.is_subdomain(self._control_domain): + return None + + if qctx.qtype != dns.rdatatype.TXT: + logging.error("Non-TXT control query %s from %s", qctx.qname, qctx.peer) + qctx.response.set_rcode(dns.rcode.FORMERR) + return qctx.response + + control_subdomain = dns.name.Name(qctx.qname.labels[-3:]) + try: + command = self._commands[control_subdomain] + except KeyError: + logging.error("Unhandled control query %s from %s", qctx.qname, qctx.peer) + qctx.response.set_rcode(dns.rcode.NXDOMAIN) + return qctx.response + + logging.info("Received control query %s from %s", qctx.qname, qctx.peer) + logging.debug("Handling control query %s using %s", qctx.qname, command) + qctx.response.set_rcode(dns.rcode.NOERROR) + qctx.response.flags |= dns.flags.AA + + command_qname = qctx.qname.relativize(control_subdomain) + try: + command_args = [l.decode("ascii") for l in command_qname.labels] + except UnicodeDecodeError: + logging.error("Non-ASCII control query %s from %s", qctx.qname, qctx.peer) + qctx.response.set_rcode(dns.rcode.FORMERR) + return qctx.response + + command_response = command.handle(command_args, self, qctx) + if command_response: + command_response_rrset = dns.rrset.from_text( + qctx.qname, 0, qctx.qclass, dns.rdatatype.TXT, f'"{command_response}"' + ) + qctx.response.answer.append(command_response_rrset) + + return qctx.response + + +class ControlCommand(abc.ABC): + """ + Base class for control commands. + + The derived class must define the control query subdomain that it handles + and the callback that handles the control queries. + """ + + @property + @abc.abstractmethod + def control_subdomain(self) -> str: + """ + The subdomain of the control domain handled by this command. Needs to + be defined as a string by the derived class. + """ + raise NotImplementedError + + @abc.abstractmethod + def handle( + self, args: List[str], server: ControllableAsyncDnsServer, qctx: QueryContext + ) -> Optional[str]: + """ + This method is expected to carry out arbitrary actions in response to a + control query. Note that it is invoked synchronously (it is not a + coroutine). + + `args` is a list of arguments for the command extracted from the + control query's QNAME; these arguments (and therefore the QNAME as + well) must only contain ASCII characters. For example, if a command's + subdomain is `my-command`, control query `foo.bar.my-command._control.` + causes `args` to be set to `["foo", "bar"]` while control query + `my-command._control.` causes `args` to be set to `[]`. + + `server` is the server instance that received the control query. This + method can change the server's behavior by altering its response + handler list using the appropriate methods. + + `qctx` is the query context for the control query. By operating on + qctx.response, this method can prepare the DNS response sent to + the client in response to the control query. Alternatively (or in + addition to the above), it can also return a string; if it does, the + returned string is converted to a TXT RRset that is inserted into the + ANSWER section of the response to the control query. + """ + raise NotImplementedError + + def __str__(self) -> str: + return self.__class__.__name__ + + +class ToggleResponsesCommand(ControlCommand): + """ + Disable/enable sending responses from the server. + """ + + control_subdomain = "send-responses" + + def __init__(self) -> None: + self._current_handler: Optional[IgnoreAllQueries] = None + + def handle( + self, args: List[str], server: ControllableAsyncDnsServer, qctx: QueryContext + ) -> Optional[str]: + if len(args) != 1: + logging.error("Invalid %s query %s", self, qctx.qname) + qctx.response.set_rcode(dns.rcode.SERVFAIL) + return "invalid query; use exactly one of 'enable' or 'disable' in QNAME" + + mode = args[0] + + if mode == "disable": + if self._current_handler: + return "sending responses already disabled" + self._current_handler = IgnoreAllQueries() + server.install_response_handler(self._current_handler, prepend=True) + return "sending responses disabled" + + if mode == "enable": + if not self._current_handler: + return "sending responses already enabled" + server.uninstall_response_handler(self._current_handler) + self._current_handler = None + return "sending responses enabled" + + logging.error("Unrecognized response sending mode '%s'", mode) + qctx.response.set_rcode(dns.rcode.SERVFAIL) + return f"unrecognized response sending mode '{mode}'" diff -Nru bind9-9.18.41/bin/tests/system/isctest/check.py bind9-9.18.44/bin/tests/system/isctest/check.py --- bind9-9.18.41/bin/tests/system/isctest/check.py 2025-10-18 10:21:02.935255614 +0000 +++ bind9-9.18.44/bin/tests/system/isctest/check.py 2026-01-09 13:44:04.610035459 +0000 @@ -10,15 +10,16 @@ # information regarding copyright ownership. import shutil -from typing import Optional +from typing import cast, List, Optional +import dns.edns import dns.flags import dns.message import dns.rcode import dns.zone import isctest.log -from isctest.compat import dns_rcode +from isctest.compat import dns_rcode, EDECode, EDEOption def rcode(message: dns.message.Message, expected_rcode) -> None: @@ -65,6 +66,54 @@ assert (message.flags & dns.flags.RA) == 0, str(message) +def _extract_ede_options( + message: dns.message.Message, +) -> List[EDEOption]: + """Extract EDE options from the DNS message.""" + return cast( + List[EDEOption], + [ + option + for option in message.options + if option.otype == dns.edns.OptionType.EDE + ], + ) + + +def noede(message: dns.message.Message) -> None: + """Check that message contains no EDE option.""" + if not hasattr(dns.edns, "EDECode"): + # dnspython<2.2.0 doesn't support EDE, skip check + return + + ede_options = _extract_ede_options(message) + assert not ede_options, f"unexpected EDE options {ede_options} in {message}" + + +def ede( + message: dns.message.Message, code: EDECode, text: Optional[str] = None +) -> None: + """Check if message contains expected EDE code (and its text).""" + if not hasattr(dns.edns, "EDECode"): + # dnspython<2.2.0 doesn't support EDE, skip check + return + + msg_opts = _extract_ede_options(message) + matching_opts = [opt for opt in msg_opts if opt.code == code] + + assert matching_opts, f"missing EDE code {code} in {message}" + + if text is None: + return + + # check at least one matching EDE option has the required text + for opt in matching_opts: + if opt.text == text: + return + opt_str = ", ".join([opt.to_text() for opt in matching_opts]) + assert False, f'EDE text "{text}" not found in [{opt_str}]' + + def section_equal(first_section: list, second_section: list) -> None: for rrset in first_section: assert ( diff -Nru bind9-9.18.41/bin/tests/system/isctest/compat.py bind9-9.18.44/bin/tests/system/isctest/compat.py --- bind9-9.18.41/bin/tests/system/isctest/compat.py 2025-10-18 10:21:02.935255614 +0000 +++ bind9-9.18.44/bin/tests/system/isctest/compat.py 2026-01-09 13:44:04.610035459 +0000 @@ -9,8 +9,9 @@ # See the COPYRIGHT file distributed with this work for additional # information regarding copyright ownership. -from typing import Any +from typing import Any, TYPE_CHECKING +import dns.edns import dns.rcode # compatiblity with dnspython<2.0.0 @@ -22,3 +23,34 @@ # In dnspython<2.0.0, selected rcodes are available as integers directly # from dns.rcode dns_rcode = dns.rcode + + +if TYPE_CHECKING: + EDECode = dns.edns.EDECode + EDEOption = dns.edns.EDEOption +else: + try: # compatiblity with dnspython<2.2.0 + EDECode = dns.edns.EDECode + except AttributeError: + # In dnspython<2.2.0, the dns.edns.EDECode doesn't exist. + # + # The primary use-case is for us to use existing EDECode objects from the + # class, e.g. EDECode.FILTERED. To mimick this behavior, use a string + # factory that just turns the attribute name into a string. + # + # The used compatibility hack doesn't really matter (as long as EDECode.xxx + # doesn't raise exception), as with dnspython versions prior to 2.2.0, any + # EDE checking will be skipped anyway. + class _CompatEDECode: + def __getattr__(self, name: str) -> str: + return name + + EDECode = _CompatEDECode() + try: + EDEOption = dns.edns.EDEOption + except AttributeError: + # In dnspython<2.2.0, the dns.edns.EDEOption doesn't exist, so we stub it to be + # able to use it in type annotations. + class EDEOption: + def __new__(cls, *args, **kwargs): + raise RuntimeError("Using EDEOption requires dnspython>=2.2.0") diff -Nru bind9-9.18.41/bin/tests/system/isctest/instance.py bind9-9.18.44/bin/tests/system/isctest/instance.py --- bind9-9.18.41/bin/tests/system/isctest/instance.py 2025-10-18 10:21:02.935255614 +0000 +++ bind9-9.18.44/bin/tests/system/isctest/instance.py 2026-01-09 13:44:04.610035459 +0000 @@ -11,14 +11,15 @@ # See the COPYRIGHT file distributed with this work for additional # information regarding copyright ownership. -from typing import NamedTuple, Optional +from typing import NamedTuple -import logging import os +from pathlib import Path import re -from .rndc import RNDCBinaryExecutor, RNDCException, RNDCExecutor -from .log import info, LogFile, WatchLogFromStart, WatchLogFromHere +from .log import WatchLogFromStart, WatchLogFromHere +from .run import CmdResult, EnvCmd +from .text import TextFile class NamedPorts(NamedTuple): @@ -42,8 +43,6 @@ self, identifier: str, ports: NamedPorts = NamedPorts(), - rndc_logger: Optional[logging.Logger] = None, - rndc_executor: Optional[RNDCExecutor] = None, ) -> None: """ `identifier` must be an `ns` string, where `` is an integer @@ -52,18 +51,18 @@ `ports` is the `NamedPorts` instance listing the UDP/TCP ports on which this `named` instance is listening for various types of traffic (both DNS traffic and RNDC commands). - - `rndc_logger` is the `logging.Logger` to use for logging RNDC - commands sent to this `named` instance. - - `rndc_executor` is an object implementing the `RNDCExecutor` interface - that is used for executing RNDC commands on this `named` instance. """ self.ip = self._identifier_to_ip(identifier) self.ports = ports - self.log = LogFile(os.path.join(identifier, "named.run")) - self._rndc_executor = rndc_executor or RNDCBinaryExecutor() - self._rndc_logger = rndc_logger + self.log = TextFile(os.path.join(identifier, "named.run")) + + self._rndc_conf = Path("../_common/rndc.conf").absolute() + self._rndc = EnvCmd("RNDC", self.rndc_args) + + @property + def rndc_args(self) -> str: + """Base arguments for calling RNDC to control the instance.""" + return f"-c {self._rndc_conf} -s {self.ip} -p {self.ports.rndc}" @staticmethod def _identifier_to_ip(identifier: str) -> str: @@ -72,52 +71,16 @@ raise ValueError("Invalid named instance identifier" + identifier) return "10.53.0." + regex_match.group("index") - def rndc(self, command: str, ignore_errors: bool = False, log: bool = True) -> str: + def rndc(self, command: str, timeout=10, **kwargs) -> CmdResult: """ Send `command` to this named instance using RNDC. Return the server's response. - If the RNDC command fails, an `RNDCException` is raised unless - `ignore_errors` is set to `True`. - - The RNDC command will be logged to `rndc.log` (along with the server's - response) unless `log` is set to `False`. - - ```python - def test_foo(servers): - # Send the "status" command to ns1. An `RNDCException` will be - # raised if the RNDC command fails. This command will be logged. - response = servers["ns1"].rndc("status") - - # Send the "thaw foo" command to ns2. No exception will be raised - # in case the RNDC command fails. This command will be logged - # (even if it fails). - response = servers["ns2"].rndc("thaw foo", ignore_errors=True) - - # Send the "stop" command to ns3. An `RNDCException` will be - # raised if the RNDC command fails, but this command will not be - # logged (the server's response will still be returned to the - # caller, though). - response = servers["ns3"].rndc("stop", log=False) - - # Send the "halt" command to ns4 in "fire & forget mode": no - # exceptions will be raised and no logging will take place (the - # server's response will still be returned to the caller, though). - response = servers["ns4"].rndc("stop", ignore_errors=True, log=False) - ``` - """ - try: - response = self._rndc_executor.call(self.ip, self.ports.rndc, command) - if log: - self._rndc_log(command, response) - except RNDCException as exc: - response = str(exc) - if log: - self._rndc_log(command, response) - if not ignore_errors: - raise - - return response + To suppress exceptions, redirect outputs, control logging change + timeout etc. use keyword arguments which are passed to + isctest.cmd.run(). + """ + return self._rndc(command, timeout=timeout, **kwargs) def watch_log_from_start( self, timeout: float = WatchLogFromStart.DEFAULT_TIMEOUT @@ -137,28 +100,12 @@ """ return WatchLogFromHere(self.log.path, timeout) - def reconfigure(self, **kwargs) -> None: + def reconfigure(self, **kwargs) -> CmdResult: """ Reconfigure this named `instance` and wait until reconfiguration is - finished. Raise an `RNDCException` if reconfiguration fails. + finished. """ with self.watch_log_from_here() as watcher: - self.rndc("reconfig", **kwargs) + cmd = self.rndc("reconfig", **kwargs) watcher.wait_for_line("any newly configured zones are now loaded") - - def _rndc_log(self, command: str, response: str) -> None: - """ - Log an `rndc` invocation (and its output) to the `rndc.log` file in the - current working directory. - """ - fmt = '%(ip)s: "%(command)s"\n%(separator)s\n%(response)s%(separator)s' - args = { - "ip": self.ip, - "command": command, - "separator": "-" * 80, - "response": response, - } - if self._rndc_logger is None: - info(fmt, args) - else: - self._rndc_logger.info(fmt, args) + return cmd diff -Nru bind9-9.18.41/bin/tests/system/isctest/log/__init__.py bind9-9.18.44/bin/tests/system/isctest/log/__init__.py --- bind9-9.18.41/bin/tests/system/isctest/log/__init__.py 2025-10-18 10:21:02.936255641 +0000 +++ bind9-9.18.44/bin/tests/system/isctest/log/__init__.py 2026-01-09 13:44:04.611035475 +0000 @@ -21,4 +21,4 @@ critical, ) -from .watchlog import LogFile, WatchLogFromStart, WatchLogFromHere +from .watchlog import WatchLogFromStart, WatchLogFromHere diff -Nru bind9-9.18.41/bin/tests/system/isctest/log/watchlog.py bind9-9.18.44/bin/tests/system/isctest/log/watchlog.py --- bind9-9.18.41/bin/tests/system/isctest/log/watchlog.py 2025-10-18 10:21:02.936255641 +0000 +++ bind9-9.18.44/bin/tests/system/isctest/log/watchlog.py 2026-01-09 13:44:04.611035475 +0000 @@ -9,15 +9,15 @@ # See the COPYRIGHT file distributed with this work for additional # information regarding copyright ownership. -from typing import Any, Iterator, List, Match, Optional, Pattern, TextIO, TypeVar, Union +from typing import Any, List, Match, Optional, Pattern, TextIO, TypeVar, Union import abc import os -import re import time +from isctest.text import compile_pattern, FlexPattern, LineReader + -FlexPattern = Union[str, Pattern] T = TypeVar("T") OneOrMore = Union[T, List[T]] @@ -30,128 +30,6 @@ pass -class LogFile: - """ - Log file wrapper with a path and means to find a string in its contents. - """ - - def __init__(self, path: str): - self.path = path - - @property - def _lines(self) -> Iterator[str]: - with open(self.path, encoding="utf-8") as f: - yield from f - - def __contains__(self, substring: str) -> bool: - """ - Return whether any of the lines in the log contains a given string. - """ - for line in self._lines: - if substring in line: - return True - return False - - def expect(self, msg: str): - """Check the string is present anywhere in the log file.""" - if msg in self: - return - assert False, f"log message not found in log {self.path}: {msg}" - - def prohibit(self, msg: str): - """Check the string is not present in the entire log file.""" - if msg in self: - assert False, f"forbidden message appeared in log {self.path}: {msg}" - - -class LineReader: - """ - >>> import io - - >>> file = io.StringIO("complete line\\n") - >>> line_reader = LineReader(file) - >>> for line in line_reader.readlines(): - ... print(line.strip()) - complete line - - >>> file = io.StringIO("complete line\\nand then incomplete line") - >>> line_reader = LineReader(file) - >>> for line in line_reader.readlines(): - ... print(line.strip()) - complete line - - >>> file = io.StringIO("complete line\\nand then another complete line\\n") - >>> line_reader = LineReader(file) - >>> for line in line_reader.readlines(): - ... print(line.strip()) - complete line - and then another complete line - - >>> file = io.StringIO() - >>> line_reader = LineReader(file) - >>> for chunk in ( - ... "first line\\nsecond line\\nthi", - ... "rd ", - ... "line\\nfour", - ... "th line\\n\\nfifth line\\n" - ... ): - ... print("=== OUTER ITERATION ===") - ... pos = file.tell() - ... print(chunk, end="", file=file) - ... _ = file.seek(pos) - ... for line in line_reader.readlines(): - ... print("--- inner iteration ---") - ... print(line.strip() or "") - === OUTER ITERATION === - --- inner iteration --- - first line - --- inner iteration --- - second line - === OUTER ITERATION === - === OUTER ITERATION === - --- inner iteration --- - third line - === OUTER ITERATION === - --- inner iteration --- - fourth line - --- inner iteration --- - - --- inner iteration --- - fifth line - """ - - def __init__(self, stream: TextIO): - self._stream = stream - self._linebuf = "" - - def readline(self) -> Optional[str]: - """ - Wrapper around io.readline() function to handle unfinished lines. - - If a line ends with newline character, it's returned immediately. - If a line doesn't end with a newline character, the read contents are - buffered until the next call of this function and None is returned - instead. - """ - read = self._stream.readline() - if not read.endswith("\n"): - self._linebuf += read - return None - read = self._linebuf + read - self._linebuf = "" - return read - - def readlines(self) -> Iterator[str]: - """ - Wrapper around io.readline() which only returns finished lines. - """ - while True: - line = self.readline() - if line is None: - return - yield line - - class WatchLog(abc.ABC): """ Wait for a log message to appear in a text file. @@ -210,15 +88,7 @@ if not isinstance(strings, list): strings = [strings] for string in strings: - if isinstance(string, Pattern): - patterns.append(string) - elif isinstance(string, str): - pattern = re.compile(re.escape(string)) - patterns.append(pattern) - else: - raise WatchLogException( - "only string and re.Pattern allowed for matching" - ) + patterns.append(compile_pattern(string)) return patterns def _wait_for_match(self, regexes: List[Pattern]) -> Match: @@ -256,13 +126,14 @@ Recommended use: ```python + from re import compile as Re import isctest def test_foo(servers): with servers["ns1"].watch_log_from_start() as watcher: watcher.wait_for_line("all zones loaded") - pattern = re.compile(r"next key event in ([0-9]+) seconds") + pattern = Re(r"next key event in ([0-9]+) seconds") with servers["ns1"].watch_log_from_here() as watcher: # ... do stuff here ... match = watcher.wait_for_line(pattern) @@ -321,7 +192,8 @@ >>> # Different values must be returned depending on which line is >>> # found in the log file. >>> import tempfile - >>> patterns = [re.compile(r"bar ([0-9])"), "qux"] + >>> from re import compile as Re + >>> patterns = [Re(r"bar ([0-9])"), "qux"] >>> with tempfile.NamedTemporaryFile("w") as file: ... print("foo bar 3", file=file, flush=True) ... with WatchLogFromStart(file.name) as watcher: @@ -443,7 +315,8 @@ >>> assert ret[1].group(0) == "foo" >>> import tempfile - >>> bar_pattern = re.compile('bar') + >>> from re import compile as Re + >>> bar_pattern = Re('bar') >>> patterns = ['foo', bar_pattern] >>> with tempfile.NamedTemporaryFile("w") as file: ... print("bar", file=file, flush=True) diff -Nru bind9-9.18.41/bin/tests/system/isctest/rndc.py bind9-9.18.44/bin/tests/system/isctest/rndc.py --- bind9-9.18.41/bin/tests/system/isctest/rndc.py 2025-10-18 10:21:02.936255641 +0000 +++ bind9-9.18.44/bin/tests/system/isctest/rndc.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,69 +0,0 @@ -# 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 abc -import os -import subprocess - - -class RNDCExecutor(abc.ABC): - """ - An interface which RNDC executors have to implement in order for the - `NamedInstance` class to be able to use them. - """ - - @abc.abstractmethod - def call(self, ip: str, port: int, command: str) -> str: - """ - Send RNDC `command` to the `named` instance at `ip:port` and return the - server's response. - """ - - -class RNDCException(Exception): - """ - Raised by classes implementing the `RNDCExecutor` interface when sending an - RNDC command fails for any reason. - """ - - -class RNDCBinaryExecutor(RNDCExecutor): - """ - An `RNDCExecutor` which sends RNDC commands to servers using the `rndc` - binary. - """ - - def __init__(self) -> None: - """ - This class needs the `RNDC` environment variable to be set to the path - to the `rndc` binary to use. - """ - rndc_path = os.environ.get("RNDC", "/bin/false") - rndc_conf = os.path.join("..", "_common", "rndc.conf") - self._base_cmdline = [rndc_path, "-c", rndc_conf] - - def call(self, ip: str, port: int, command: str) -> str: - """ - Send RNDC `command` to the `named` instance at `ip:port` and return the - server's response. - """ - cmdline = self._base_cmdline[:] - cmdline.extend(["-s", ip]) - cmdline.extend(["-p", str(port)]) - cmdline.extend(command.split()) - - try: - return subprocess.check_output( - cmdline, stderr=subprocess.STDOUT, timeout=10, encoding="utf-8" - ) - except subprocess.SubprocessError as exc: - msg = getattr(exc, "output", "RNDC exception occurred") - raise RNDCException(msg) from exc diff -Nru bind9-9.18.41/bin/tests/system/isctest/run.py bind9-9.18.44/bin/tests/system/isctest/run.py --- bind9-9.18.41/bin/tests/system/isctest/run.py 2025-10-18 10:21:02.936255641 +0000 +++ bind9-9.18.44/bin/tests/system/isctest/run.py 2026-01-09 13:44:04.611035475 +0000 @@ -15,11 +15,24 @@ from typing import Optional import isctest.log +import isctest.text from isctest.compat import dns_rcode import dns.message +class CmdResult: + def __init__(self, proc=None): + self.proc = proc + self.rc = self.proc.returncode + self.out = isctest.text.Text("") + self.err = isctest.text.Text("") + if self.proc.stdout: + self.out = isctest.text.Text(self.proc.stdout.decode("utf-8")) + if self.proc.stderr: + self.err = isctest.text.Text(self.proc.stderr.decode("utf-8")) + + def cmd( args, cwd=None, @@ -31,7 +44,7 @@ input_text: Optional[bytes] = None, raise_on_exception=True, env: Optional[dict] = None, -): +) -> CmdResult: """Execute a command with given args as subprocess.""" isctest.log.debug(f"isctest.run.cmd(): {' '.join(args)}") @@ -61,24 +74,26 @@ env=env, ) print_debug_logs(proc) - return proc + return CmdResult(proc) except subprocess.CalledProcessError as exc: print_debug_logs(exc) isctest.log.debug(f"isctest.run.cmd(): (return code) {exc.returncode}") if raise_on_exception: raise exc - return exc + return CmdResult(exc) + + +class EnvCmd: + """Helper for executing binaries from env with optional base parameters.""" + def __init__(self, name: str, base_params: str = ""): + self.bin_path = os.environ[name] + self.base_params = base_params.split() -class Dig: - def __init__(self, base_params: str = ""): - self.base_params = base_params - - def __call__(self, params: str) -> str: - """Run the dig command with the given parameters and return the decoded output.""" - return cmd( - [os.environ.get("DIG")] + f"{self.base_params} {params}".split(), - ).stdout.decode("utf-8") + def __call__(self, params: str, **kwargs) -> CmdResult: + """Call the command. Keyword arguments from isctest.run.cmd() are supported.""" + args = self.base_params + params.split() + return cmd([self.bin_path] + args, **kwargs) def retry_with_timeout(func, timeout, delay=1, msg=None): diff -Nru bind9-9.18.41/bin/tests/system/isctest/template.py bind9-9.18.44/bin/tests/system/isctest/template.py --- bind9-9.18.41/bin/tests/system/isctest/template.py 2025-10-18 10:21:02.936255641 +0000 +++ bind9-9.18.44/bin/tests/system/isctest/template.py 2026-01-09 13:44:04.611035475 +0000 @@ -87,14 +87,14 @@ stream = self.j2env.get_template(template).stream(data) stream.dump(output, encoding="utf-8") - def render_auto(self): + def render_auto(self, data: Optional[Dict[str, Any]] = None): """ - Render all *.j2 templates with default values and write the output to - files without the .j2 extensions. + Render all *.j2 templates with default (and optionally the provided) + values and write the output to files without the .j2 extensions. """ templates = [ str(filepath.relative_to(self.directory)) for filepath in self.directory.rglob("*.j2") ] for template in templates: - self.render(template[:-3]) + self.render(template[:-3], data) diff -Nru bind9-9.18.41/bin/tests/system/isctest/text.py bind9-9.18.44/bin/tests/system/isctest/text.py --- bind9-9.18.41/bin/tests/system/isctest/text.py 1970-01-01 00:00:00.000000000 +0000 +++ bind9-9.18.44/bin/tests/system/isctest/text.py 2026-01-09 13:44:04.611035475 +0000 @@ -0,0 +1,178 @@ +#!/usr/bin/python3 + +# 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 abc +import re +from re import compile as Re +from typing import Iterator, List, Match, Optional, Pattern, TextIO, Union + + +FlexPattern = Union[str, Pattern] + + +def compile_pattern(string: FlexPattern) -> Pattern: + if isinstance(string, Pattern): + return string + if isinstance(string, str): + return Re(re.escape(string)) + raise TypeError("only string and re.Pattern allowed") + + +class Grep(abc.ABC): + """ + Implement a grep-like interface for pattern matching in texts and files. + """ + + @abc.abstractmethod + def readlines(self) -> Iterator[str]: + raise NotImplementedError + + def igrep(self, pattern: FlexPattern) -> Iterator[Match]: + """ + Iterate over the lines matching the pattern. + """ + regex = compile_pattern(pattern) + + for line in self.readlines(): + match = regex.search(line) + if match: + yield match + + def grep(self, pattern: FlexPattern) -> List[Match]: + """ + Get list of lines matching the pattern. + """ + return list(self.igrep(pattern)) + + def __contains__(self, pattern: FlexPattern) -> bool: + """ + Return whether any of the lines in the log contains matches the pattern. + """ + try: + next(self.igrep(pattern)) + except StopIteration: + return False + return True + + +class Text(Grep, str): # type: ignore + """ + Wrapper around classic string with grep support. + """ + + def readlines(self): + yield from self.splitlines(keepends=True) + + +class TextFile(Grep): + """ + Text file wrapper with grep support. + """ + + def __init__(self, path: str): + self.path = path + + def readlines(self) -> Iterator[str]: + with open(self.path, encoding="utf-8") as f: + yield from f + + def __repr__(self): + return self.path + + +class LineReader(Grep): + """ + >>> import io + + >>> file = io.StringIO("complete line\\n") + >>> line_reader = LineReader(file) + >>> for line in line_reader.readlines(): + ... print(line.strip()) + complete line + + >>> file = io.StringIO("complete line\\nand then incomplete line") + >>> line_reader = LineReader(file) + >>> for line in line_reader.readlines(): + ... print(line.strip()) + complete line + + >>> file = io.StringIO("complete line\\nand then another complete line\\n") + >>> line_reader = LineReader(file) + >>> for line in line_reader.readlines(): + ... print(line.strip()) + complete line + and then another complete line + + >>> file = io.StringIO() + >>> line_reader = LineReader(file) + >>> for chunk in ( + ... "first line\\nsecond line\\nthi", + ... "rd ", + ... "line\\nfour", + ... "th line\\n\\nfifth line\\n" + ... ): + ... print("=== OUTER ITERATION ===") + ... pos = file.tell() + ... print(chunk, end="", file=file) + ... _ = file.seek(pos) + ... for line in line_reader.readlines(): + ... print("--- inner iteration ---") + ... print(line.strip() or "") + === OUTER ITERATION === + --- inner iteration --- + first line + --- inner iteration --- + second line + === OUTER ITERATION === + === OUTER ITERATION === + --- inner iteration --- + third line + === OUTER ITERATION === + --- inner iteration --- + fourth line + --- inner iteration --- + + --- inner iteration --- + fifth line + """ + + def __init__(self, stream: TextIO): + self._stream = stream + self._linebuf = "" + + def readline(self) -> Optional[str]: + """ + Wrapper around io.readline() function to handle unfinished lines. + + If a line ends with newline character, it's returned immediately. + If a line doesn't end with a newline character, the read contents are + buffered until the next call of this function and None is returned + instead. + """ + read = self._stream.readline() + if not read.endswith("\n"): + self._linebuf += read + return None + read = self._linebuf + read + self._linebuf = "" + return read + + def readlines(self) -> Iterator[str]: + """ + Wrapper around io.readline() which only returns finished lines. + """ + while True: + line = self.readline() + if line is None: + return + yield line diff -Nru bind9-9.18.41/bin/tests/system/ixfr/tests.sh bind9-9.18.44/bin/tests/system/ixfr/tests.sh --- bind9-9.18.41/bin/tests/system/ixfr/tests.sh 2025-10-18 10:21:02.937255668 +0000 +++ bind9-9.18.44/bin/tests/system/ixfr/tests.sh 2026-01-09 13:44:04.612035492 +0000 @@ -243,7 +243,7 @@ grep -q -F "serial 4, fallback AXFR" dig.out.test$n.2 || ret=1 # Ensure the expected error is logged. -nextpart ns1/named.run | grep -q -F "SOA name mismatch" || ret=1 +nextpart ns1/named.run | grep -F "SOA name mismatch" >/dev/null || ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status + ret)) diff -Nru bind9-9.18.41/bin/tests/system/keepalive/tests_keepalive.py bind9-9.18.44/bin/tests/system/keepalive/tests_keepalive.py --- bind9-9.18.41/bin/tests/system/keepalive/tests_keepalive.py 2025-10-18 10:21:02.944255855 +0000 +++ bind9-9.18.44/bin/tests/system/keepalive/tests_keepalive.py 2026-01-09 13:44:04.619035607 +0000 @@ -20,7 +20,7 @@ def test_dig_tcp_keepalive_handling(named_port, servers): def get_keepalive_options_received(): - servers["ns2"].rndc("stats", log=False) + servers["ns2"].rndc("stats") options_received = 0 with open("ns2/named.stats", "r", encoding="utf-8") as ns2_stats_file: for line in ns2_stats_file: @@ -28,38 +28,41 @@ options_received = line.split()[0] return int(options_received) - dig = isctest.run.Dig(f"-p {str(named_port)}") + dig = isctest.run.EnvCmd("DIG", f"-p {str(named_port)}") isctest.log.info("check that dig handles TCP keepalive in query") - assert "; TCP KEEPALIVE" in dig("+qr +keepalive foo.example. @10.53.0.2") + assert "; TCP KEEPALIVE" in dig("+qr +keepalive foo.example. @10.53.0.2").out isctest.log.info("check that dig added TCP keepalive was received") assert get_keepalive_options_received() == 1 isctest.log.info("check that TCP keepalive is added for TCP responses") - assert "; TCP KEEPALIVE" in dig("+tcp +keepalive foo.example. @10.53.0.2") + assert "; TCP KEEPALIVE" in dig("+tcp +keepalive foo.example. @10.53.0.2").out isctest.log.info("check that TCP keepalive requires TCP") - assert "; TCP KEEPALIVE" not in dig("+keepalive foo.example. @10.53.0.2") + assert "; TCP KEEPALIVE" not in dig("+keepalive foo.example. @10.53.0.2").out isctest.log.info("check the default keepalive value") - assert "; TCP KEEPALIVE: 30.0 secs" in dig( - "+tcp +keepalive foo.example. @10.53.0.3" + assert ( + "; TCP KEEPALIVE: 30.0 secs" + in dig("+tcp +keepalive foo.example. @10.53.0.3").out ) isctest.log.info("check a keepalive configured value") - assert "; TCP KEEPALIVE: 15.0 secs" in dig( - "+tcp +keepalive foo.example. @10.53.0.2" + assert ( + "; TCP KEEPALIVE: 15.0 secs" + in dig("+tcp +keepalive foo.example. @10.53.0.2").out ) isctest.log.info("check a re-configured keepalive value") - response = servers["ns2"].rndc("tcp-timeouts 300 300 300 200", log=False) - assert "tcp-initial-timeout=300" in response - assert "tcp-idle-timeout=300" in response - assert "tcp-keepalive-timeout=300" in response - assert "tcp-advertised-timeout=200" in response - assert "; TCP KEEPALIVE: 20.0 secs" in dig( - "+tcp +keepalive foo.example. @10.53.0.2" + response = servers["ns2"].rndc("tcp-timeouts 300 300 300 200") + assert "tcp-initial-timeout=300" in response.out + assert "tcp-idle-timeout=300" in response.out + assert "tcp-keepalive-timeout=300" in response.out + assert "tcp-advertised-timeout=200" in response.out + assert ( + "; TCP KEEPALIVE: 20.0 secs" + in dig("+tcp +keepalive foo.example. @10.53.0.2").out ) isctest.log.info("check server config entry") diff -Nru bind9-9.18.41/bin/tests/system/keyfromlabel/tests_keyfromlabel.py bind9-9.18.44/bin/tests/system/keyfromlabel/tests_keyfromlabel.py --- bind9-9.18.41/bin/tests/system/keyfromlabel/tests_keyfromlabel.py 2025-10-18 10:21:02.944255855 +0000 +++ bind9-9.18.44/bin/tests/system/keyfromlabel/tests_keyfromlabel.py 2026-01-09 13:44:04.620035624 +0000 @@ -11,7 +11,7 @@ import hashlib import os -import re +from re import compile as Re import shutil import pytest @@ -75,18 +75,16 @@ ) try: - output = isctest.run.cmd( - token_init_command, env=EMPTY_OPENSSL_CONF_ENV - ).stdout.decode("utf-8") - assert "The token has been initialized and is reassigned to slot" in output + cmd = isctest.run.cmd(token_init_command, env=EMPTY_OPENSSL_CONF_ENV) + assert "The token has been initialized and is reassigned to slot" in cmd.out yield finally: - output = isctest.run.cmd( + cmd = isctest.run.cmd( token_cleanup_command, env=EMPTY_OPENSSL_CONF_ENV, raise_on_exception=False, - ).stdout.decode("utf-8") - assert re.search("Found token (.*) with matching token label", output) + ) + assert Re("Found token (.*) with matching token label") in cmd.out # pylint: disable-msg=too-many-locals @@ -126,11 +124,9 @@ HSMPIN, ] - output = isctest.run.cmd( - pkcs11_command, env=EMPTY_OPENSSL_CONF_ENV - ).stdout.decode("utf-8") + cmd = isctest.run.cmd(pkcs11_command, env=EMPTY_OPENSSL_CONF_ENV) - assert "Key pair generated" in output + assert "Key pair generated" in cmd.out def keyfromlabel(alg_name, zone, key_id, key_flag): key_flag = key_flag.split() if key_flag else [] @@ -141,24 +137,25 @@ "pkcs11", "-a", alg_name, + "-y", "-l", f"pkcs11:token=softhsm2-keyfromlabel;object={key_id}-{zone};pin-source=pin", *key_flag, zone, ] - output = isctest.run.cmd(keyfrlab_command) - output_decoded = output.stdout.decode("utf-8").rstrip() + ".key" + cmd = isctest.run.cmd(keyfrlab_command) + keyfile = cmd.out.rstrip() + ".key" - assert os.path.exists(output_decoded) + assert os.path.exists(keyfile) - return output_decoded + return keyfile if ( isctest.run.cmd( [os.environ["SHELL"], "../testcrypto.sh", alg_name], raise_on_exception=False, - ).returncode + ).rc != 0 ): pytest.skip(f"{alg_name} is not supported") diff -Nru bind9-9.18.41/bin/tests/system/masterfile/tests_masterfile.py bind9-9.18.44/bin/tests/system/masterfile/tests_masterfile.py --- bind9-9.18.41/bin/tests/system/masterfile/tests_masterfile.py 2025-10-18 10:21:02.953256095 +0000 +++ bind9-9.18.44/bin/tests/system/masterfile/tests_masterfile.py 2026-01-09 13:44:04.628035756 +0000 @@ -96,7 +96,7 @@ def test_masterfile_owner_inheritance(): """Test owner inheritance after $INCLUDE""" - checker_output = isctest.run.cmd( + cmd = isctest.run.cmd( [ os.environ["CHECKZONE"], "-D", @@ -104,12 +104,12 @@ "example", "zone/inheritownerafterinclude.db", ] - ).stdout.decode("utf-8") + ) owner_inheritance_zone = """ example. 0 IN SOA . . 0 0 0 0 0 example. 0 IN TXT "this should be at the zone apex" example. 0 IN NS . """ - checker_zone = dns.zone.from_text(checker_output, origin="example.") + checker_zone = dns.zone.from_text(cmd.out, origin="example.") expected = dns.zone.from_text(owner_inheritance_zone, origin="example.") isctest.check.zones_equal(checker_zone, expected, compare_ttl=True) diff -Nru bind9-9.18.41/bin/tests/system/optout/ns2/named.conf.j2 bind9-9.18.44/bin/tests/system/optout/ns2/named.conf.j2 --- bind9-9.18.41/bin/tests/system/optout/ns2/named.conf.j2 1970-01-01 00:00:00.000000000 +0000 +++ bind9-9.18.44/bin/tests/system/optout/ns2/named.conf.j2 2026-01-09 13:44:04.645036036 +0000 @@ -0,0 +1,57 @@ +/* + * 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 reconfiged = reconfiged | default(False) %} +{% set policy = "optout" if not reconfiged else "nsec" %} + +options { + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.2; }; + listen-on-v6 { none; }; + allow-transfer { any; }; + recursion no; + dnssec-validation no; + ixfr-from-differences yes; + sig-signing-nodes 900; + sig-signing-signatures 900; +}; + +include "controls.conf"; + +dnssec-policy "optout" { + keys { + csk lifetime unlimited algorithm ecdsa256; + }; + nsec3param iterations 0 optout yes salt-length 0; +}; + +dnssec-policy "nsec" { + keys { + csk lifetime unlimited algorithm ecdsa256; + }; +}; + +zone "test" { + type primary; + file "test.db"; + dnssec-policy "optout"; + inline-signing yes; +}; + +zone "small.test" { + type primary; + file "small.test.db"; + dnssec-policy "@policy@"; + inline-signing yes; +}; diff -Nru bind9-9.18.41/bin/tests/system/optout/ns2/small.test.db bind9-9.18.44/bin/tests/system/optout/ns2/small.test.db --- bind9-9.18.41/bin/tests/system/optout/ns2/small.test.db 1970-01-01 00:00:00.000000000 +0000 +++ bind9-9.18.44/bin/tests/system/optout/ns2/small.test.db 2026-01-09 13:44:04.645036036 +0000 @@ -0,0 +1,25 @@ +; 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. + +$TTL 3600 +@ IN SOA ns2.small.test. hostmaster.small.test. 1 7200 3600 24796800 3600 + IN NS ns2 + +ns2 IN A 10.53.0.2 + +a IN A 127.0.0.1 + +dname IN DNAME branch.example. +under.dname IN TXT "occluded" + +$GENERATE 1-10 child$ IN NS ns.example. + +child5 IN DS 7250 13 2 A30B3F78B6DDE9A4A9A2AD0C805518B4F49EC62E7D3F4531D33DE697 CDA01CB2 diff -Nru bind9-9.18.41/bin/tests/system/optout/ns2/test.db bind9-9.18.44/bin/tests/system/optout/ns2/test.db --- bind9-9.18.41/bin/tests/system/optout/ns2/test.db 1970-01-01 00:00:00.000000000 +0000 +++ bind9-9.18.44/bin/tests/system/optout/ns2/test.db 2026-01-09 13:44:04.645036036 +0000 @@ -0,0 +1,25 @@ +; 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. + +$TTL 3600 +@ IN SOA ns2.test. hostmaster.test. 1 7200 3600 24796800 3600 + IN NS ns2 + +ns2 IN A 10.53.0.2 + +a IN A 127.0.0.1 + +dname IN DNAME branch.example. +under.dname IN TXT "occluded" + +$GENERATE 1-50000 child$ IN NS ns.example. + +child303 IN DS 7250 13 2 A30B3F78B6DDE9A4A9A2AD0C805518B4F49EC62E7D3F4531D33DE697 CDA01CB2 diff -Nru bind9-9.18.41/bin/tests/system/optout/setup.sh bind9-9.18.44/bin/tests/system/optout/setup.sh --- bind9-9.18.41/bin/tests/system/optout/setup.sh 1970-01-01 00:00:00.000000000 +0000 +++ bind9-9.18.44/bin/tests/system/optout/setup.sh 2026-01-09 13:44:04.646036053 +0000 @@ -0,0 +1,16 @@ +#!/bin/sh -e + +# 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. + +. ../conf.sh + +copy_setports ../_common/controls.conf.in ns2/controls.conf diff -Nru bind9-9.18.41/bin/tests/system/optout/tests_optout.py bind9-9.18.44/bin/tests/system/optout/tests_optout.py --- bind9-9.18.41/bin/tests/system/optout/tests_optout.py 1970-01-01 00:00:00.000000000 +0000 +++ bind9-9.18.44/bin/tests/system/optout/tests_optout.py 2026-01-09 13:44:04.646036053 +0000 @@ -0,0 +1,145 @@ +#!/usr/bin/python3 + +# 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 os +import re +import sys + +import isctest +import pytest + +pytest.importorskip("dns", minversion="2.0.0") +import dns.exception +import dns.message +import dns.name +import dns.query +import dns.rcode +import dns.rdataclass +import dns.rdatatype + + +pytestmark = [ + pytest.mark.skipif( + sys.version_info < (3, 7), reason="Python >= 3.7 required [GL #3001]" + ), + pytest.mark.extra_artifacts( + [ + "*.out", + "ns2/*.infile", + "ns2/*.signed", + "ns2/*.jnl", + "ns2/*.jbk", + "ns2/controls.conf", + "ns2/dsset-*", + "ns2/K*", + ] + ), +] + + +def has_nsec3param(zone, response): + match = rf"{re.escape(zone)}\.\s+\d+\s+IN\s+NSEC3PARAM\s+1\s+0\s+0\s+-" + + for rr in response.answer: + if re.search(match, rr.to_text()): + return True + + return False + + +def do_query(server, qname, qtype, tcp=False): + msg = isctest.query.create(qname, qtype) + query_func = isctest.query.tcp if tcp else isctest.query.udp + response = query_func(msg, server.ip, expected_rcode=dns.rcode.NOERROR) + return response + + +def do_xfr(server, qname): + xfr = dns.zone.Zone(origin=f"{qname}.", relativize=False) + dns.query.inbound_xfr( + where=server.ip, txn_manager=xfr, port=int(os.environ["PORT"]) + ) + return xfr + + +def verify_zone(zone, transfer): + verify = os.getenv("VERIFY") + assert verify is not None + + filename = f"{zone}.out" + with open(filename, "w", encoding="utf-8") as file: + file.write(transfer.to_text()) + + # dnssec-verify command with default arguments. + verify_cmd = [verify, "-z", "-o", zone, filename] + + verifier = isctest.run.cmd(verify_cmd) + + if verifier.rc != 0: + isctest.log.error(f"dnssec-verify {zone}. failed") + + return verifier.rc == 0 + + +def test_optout(ns2): + zone = "test" + expect_nsec3param = True + + # Wait until the provided zone is signed and then verify its DNSSEC data. + def check_nsec3param(): + response = do_query(ns2, zone, "NSEC3PARAM") + if expect_nsec3param: + return has_nsec3param(zone, response) + return not has_nsec3param(zone, response) + + # check zone is fully signed. + isctest.run.retry_with_timeout(check_nsec3param, timeout=100) + + # check if zone if DNSSEC valid. + transfer = do_xfr(ns2, zone) + assert verify_zone(zone, transfer) + + +def test_optout_to_nsec(ns2, templates): + zone = "small.test" + expect_nsec3param = True + + # Wait until the provided zone is signed and then verify its DNSSEC data. + def check_nsec3param(): + response = do_query(ns2, zone, "NSEC3PARAM") + if expect_nsec3param: + return has_nsec3param(zone, response) + return not has_nsec3param(zone, response) + + # check zone is fully signed. + isctest.run.retry_with_timeout(check_nsec3param, timeout=100) + + # check if zone if DNSSEC valid. + transfer = do_xfr(ns2, zone) + assert verify_zone(zone, transfer) + + # reconfigure to NSEC. + data = { + "reconfiged": True, + } + templates.render("ns2/named.conf", data) + ns2.reconfigure() + + # wait until NSEC3PARAM is removed. + expect_nsec3param = False + isctest.run.retry_with_timeout(check_nsec3param, timeout=100) + + # check if zone if DNSSEC valid. + transfer = do_xfr(ns2, zone) + assert verify_zone(zone, transfer) diff -Nru bind9-9.18.41/bin/tests/system/pipelined/pipequeries.c bind9-9.18.44/bin/tests/system/pipelined/pipequeries.c --- bind9-9.18.41/bin/tests/system/pipelined/pipequeries.c 2025-10-18 10:21:02.973256630 +0000 +++ bind9-9.18.44/bin/tests/system/pipelined/pipequeries.c 2026-01-09 13:44:04.649036102 +0000 @@ -45,7 +45,7 @@ #include #include -#define CHECK(str, x) \ +#define CHECKM(str, x) \ { \ if ((x) != ISC_R_SUCCESS) { \ fprintf(stderr, "I:%s: %s\n", (str), \ @@ -91,7 +91,7 @@ result = dns_request_getresponse(reqev->request, response, DNS_MESSAGEPARSE_PRESERVEORDER); - CHECK("dns_request_getresponse", result); + CHECKM("dns_request_getresponse", result); if (response->rcode != dns_rcode_noerror) { result = dns_result_fromrcode(response->rcode); @@ -108,7 +108,7 @@ result = dns_message_sectiontotext( response, DNS_SECTION_ANSWER, &dns_master_style_simple, DNS_MESSAGETEXTFLAG_NOCOMMENTS, &outbuf); - CHECK("dns_message_sectiontotext", result); + CHECKM("dns_message_sectiontotext", result); printf("%.*s", (int)isc_buffer_usedlength(&outbuf), (char *)isc_buffer_base(&outbuf)); fflush(stdout); @@ -148,7 +148,7 @@ isc_buffer_add(&buf, strlen(host)); result = dns_name_fromtext(dns_fixedname_name(&queryname), &buf, dns_rootname, 0, NULL); - CHECK("dns_name_fromtext", result); + CHECKM("dns_name_fromtext", result); dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER, &message); @@ -158,10 +158,10 @@ message->id = (unsigned short)(random() & 0xFFFF); result = dns_message_gettempname(message, &qname); - CHECK("dns_message_gettempname", result); + CHECKM("dns_message_gettempname", result); result = dns_message_gettemprdataset(message, &qrdataset); - CHECK("dns_message_gettemprdataset", result); + CHECKM("dns_message_gettemprdataset", result); dns_name_clone(dns_fixedname_name(&queryname), qname); dns_rdataset_makequestion(qrdataset, dns_rdataclass_in, @@ -173,7 +173,7 @@ have_src ? &srcaddr : NULL, &dstaddr, DNS_REQUESTOPT_TCP, NULL, TIMEOUT, 0, 0, task, recvresponse, message, &request); - CHECK("dns_request_create", result); + CHECKM("dns_request_create", result); return ISC_R_SUCCESS; } @@ -248,13 +248,13 @@ result = ISC_R_FAILURE; if (inet_pton(AF_INET, "10.53.0.7", &inaddr) != 1) { - CHECK("inet_pton", result); + CHECKM("inet_pton", result); } isc_sockaddr_fromin(&srcaddr, &inaddr, 0); result = ISC_R_FAILURE; if (inet_pton(AF_INET, "10.53.0.4", &inaddr) != 1) { - CHECK("inet_pton", result); + CHECKM("inet_pton", result); } isc_sockaddr_fromin(&dstaddr, &inaddr, port); diff -Nru bind9-9.18.41/bin/tests/system/re_compile_checker.py bind9-9.18.44/bin/tests/system/re_compile_checker.py --- bind9-9.18.41/bin/tests/system/re_compile_checker.py 1970-01-01 00:00:00.000000000 +0000 +++ bind9-9.18.44/bin/tests/system/re_compile_checker.py 2026-01-09 13:44:04.651036135 +0000 @@ -0,0 +1,46 @@ +# 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. + +# pylint: disable=unknown-option-value,re-compile-alias + +import re + +from astroid import nodes + +from pylint.checkers import BaseRawFileChecker +from pylint.lint import PyLinter + + +class ReCompileChecker(BaseRawFileChecker): + + name = "custom_raw" + msgs = { + "R9901": ( + "Replace re.compile() with Re() using `from re import compile as Re`", + "re-compile-alias", + ( + "Use a Re() alias instead of re.compile() by importing the " + "re.compile() function as Re()" + ), + ), + } + options = () + + def process_module(self, node: nodes.Module) -> None: + pattern = re.compile(r"re\.compile\(") + with node.stream() as stream: + for lineno, line in enumerate(stream): + if pattern.search(line.decode("utf-8")): + self.add_message("re-compile-alias", line=lineno) + + +def register(linter: PyLinter) -> None: + linter.register_checker(ReCompileChecker(linter)) diff -Nru bind9-9.18.41/bin/tests/system/rpz/tests.sh bind9-9.18.44/bin/tests/system/rpz/tests.sh --- bind9-9.18.41/bin/tests/system/rpz/tests.sh 2025-10-18 10:21:02.992257138 +0000 +++ bind9-9.18.44/bin/tests/system/rpz/tests.sh 2026-01-09 13:44:04.669036432 +0000 @@ -622,7 +622,7 @@ if [ "$MODE" = dnsrps ]; then addr 12.12.12.12 as-ns.tld5. # 17 qname-as-ns fi -nextpart ns3/named.run | grep -q "unrecognized NS rpz_rrset_find() failed: glue" \ +nextpart ns3/named.run | grep -F "unrecognized NS rpz_rrset_find() failed: glue" >/dev/null \ && setret "seen: unrecognized NS rpz_rrset_find() failed: glue" end_group if [ "$MODE" = dnsrps ]; then @@ -645,7 +645,7 @@ if [ "$MODE" = dnsrps ]; then addr 12.12.12.12 as-ns.tld5. # 9 ip-as-ns fi -nextpart ns3/named.run | grep -q "unrecognized NS rpz_rrset_find() failed: glue" \ +nextpart ns3/named.run | grep -F "unrecognized NS rpz_rrset_find() failed: glue" >/dev/null \ && setret "seen: unrecognized NS rpz_rrset_find() failed: glue" end_group diff -Nru bind9-9.18.41/bin/tests/system/rrchecker/tests_rrchecker.py bind9-9.18.44/bin/tests/system/rrchecker/tests_rrchecker.py --- bind9-9.18.41/bin/tests/system/rrchecker/tests_rrchecker.py 2025-10-18 10:21:02.997257272 +0000 +++ bind9-9.18.44/bin/tests/system/rrchecker/tests_rrchecker.py 2026-01-09 13:44:04.674036515 +0000 @@ -121,22 +121,18 @@ ], ) def test_rrchecker_list_standard_names(option, expected_result): - stdout = isctest.run.cmd([os.environ["RRCHECKER"], option]).stdout.decode("utf-8") - values = [line for line in stdout.split("\n") if line.strip()] + cmd = isctest.run.cmd([os.environ["RRCHECKER"], option]) + values = [line for line in cmd.out.split("\n") if line.strip()] assert sorted(values) == sorted(expected_result) def run_rrchecker(option, rr_class, rr_type, rr_rest): - rrchecker_output = ( - isctest.run.cmd( - [os.environ["RRCHECKER"], option], - input_text=f"{rr_class} {rr_type} {rr_rest}".encode("utf-8"), - ) - .stdout.decode("utf-8") - .strip() + cmd = isctest.run.cmd( + [os.environ["RRCHECKER"], option], + input_text=f"{rr_class} {rr_type} {rr_rest}".encode("utf-8"), ) - return rrchecker_output.split() + return cmd.out.strip().split() @pytest.mark.parametrize( @@ -162,7 +158,7 @@ ".", tempzone_file, ], - ).stdout.decode("utf-8") + ).out checkzone_output = [ line for line in checkzone_output.splitlines() if not line.startswith(";") ] diff -Nru bind9-9.18.41/bin/tests/system/rsabigexponent/bigkey.c bind9-9.18.44/bin/tests/system/rsabigexponent/bigkey.c --- bind9-9.18.41/bin/tests/system/rsabigexponent/bigkey.c 2025-10-18 10:21:03.001257379 +0000 +++ bind9-9.18.44/bin/tests/system/rsabigexponent/bigkey.c 2026-01-09 13:44:04.678036580 +0000 @@ -58,7 +58,7 @@ BIGNUM *e; EVP_PKEY *pkey; -#define CHECK(op, msg) \ +#define CHECKM(op, msg) \ do { \ result = (op); \ if (result != ISC_R_SUCCESS) { \ @@ -127,22 +127,20 @@ name = dns_fixedname_initname(&fname); isc_buffer_constinit(&buf, "example.", strlen("example.")); isc_buffer_add(&buf, strlen("example.")); - CHECK(dns_name_fromtext(name, &buf, dns_rootname, 0, NULL), "dns_name_" - "fromtext(" - "\"example." - "\")"); - - CHECK(dst_key_buildinternal(name, DNS_KEYALG_RSASHA256, bits, - DNS_KEYOWNER_ZONE, DNS_KEYPROTO_DNSSEC, - dns_rdataclass_in, pkey, mctx, &key), - "dst_key_buildinternal(...)"); + CHECKM(dns_name_fromtext(name, &buf, dns_rootname, 0, NULL), + "dns_name_fromtext(\"example.\")"); - CHECK(dst_key_tofile(key, DST_TYPE_PRIVATE | DST_TYPE_PUBLIC, NULL), - "dst_key_tofile()"); + CHECKM(dst_key_buildinternal(name, DNS_KEYALG_RSASHA256, bits, + DNS_KEYOWNER_ZONE, DNS_KEYPROTO_DNSSEC, + dns_rdataclass_in, pkey, mctx, &key), + "dst_key_buildinternal(...)"); + + CHECKM(dst_key_tofile(key, DST_TYPE_PRIVATE | DST_TYPE_PUBLIC, NULL), + "dst_key_tofile()"); isc_buffer_init(&buf, filename, sizeof(filename) - 1); isc_buffer_clear(&buf); - CHECK(dst_key_buildfilename(key, 0, NULL, &buf), "dst_key_" - "buildfilename()"); + CHECKM(dst_key_buildfilename(key, 0, NULL, &buf), + "dst_key_buildfilename()"); printf("%s\n", filename); dst_key_free(&key); diff -Nru bind9-9.18.41/bin/tests/system/serve-stale/ans2/ans.pl bind9-9.18.44/bin/tests/system/serve-stale/ans2/ans.pl --- bind9-9.18.41/bin/tests/system/serve-stale/ans2/ans.pl 2025-10-18 10:21:03.005257486 +0000 +++ bind9-9.18.44/bin/tests/system/serve-stale/ans2/ans.pl 2026-01-09 13:44:04.682036647 +0000 @@ -70,6 +70,7 @@ my $TARGET = "target.example 9 IN A $localaddr"; my $SHORTCNAME = "shortttl.cname.example 1 IN CNAME longttl.target.example"; my $LONGTARGET = "longttl.target.example 600 IN A $localaddr"; +my $OUTCNAME = "out-cname.example 600 IN CNAME serve.stale"; sub reply_handler { my ($qname, $qclass, $qtype) = @_; @@ -105,6 +106,15 @@ } $rcode = "NOERROR"; return ($rcode, \@ans, \@auth, \@add, { aa => 1 }); + } elsif ($qname eq "normal" ) { + if ($qtype eq "TXT") { + $send_response = 1; + $slow_response = 0; + my $rr = new Net::DNS::RR("$qname 0 $qclass TXT \"$send_response\""); + push @ans, $rr; + } + $rcode = "NOERROR"; + return ($rcode, \@ans, \@auth, \@add, { aa => 1 }); } # If we are not responding to queries we are done. @@ -217,6 +227,15 @@ push @ans, $rr; } else { my $rr = new Net::DNS::RR($negSOA); + push @auth, $rr; + } + $rcode = "NOERROR"; + } elsif ($qname eq "out-cname.example") { + if ($qtype eq "A") { + my $rr = new Net::DNS::RR($OUTCNAME); + push @ans, $rr; + } else { + my $rr = new Net::DNS::RR($negSOA); push @auth, $rr; } $rcode = "NOERROR"; diff -Nru bind9-9.18.41/bin/tests/system/serve-stale/ns3/named10.conf.in bind9-9.18.44/bin/tests/system/serve-stale/ns3/named10.conf.in --- bind9-9.18.41/bin/tests/system/serve-stale/ns3/named10.conf.in 1970-01-01 00:00:00.000000000 +0000 +++ bind9-9.18.44/bin/tests/system/serve-stale/ns3/named10.conf.in 2026-01-09 13:44:04.682036647 +0000 @@ -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. + */ + +key rndc_key { + secret "1234abcd8765"; + algorithm @DEFAULT_HMAC@; +}; + +controls { + inet 10.53.0.3 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +options { + query-source address 10.53.0.3; + notify-source 10.53.0.3; + transfer-source 10.53.0.3; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.3; }; + listen-on-v6 { none; }; + recursion yes; + dnssec-validation no; + stale-answer-enable yes; + stale-cache-enable yes; + stale-answer-ttl 3; + stale-answer-client-timeout 0; +}; + +zone "." { + type hint; + file "root.db"; +}; + +zone "serve.stale" IN { + type primary; + notify no; + file "serve.stale.db"; +}; diff -Nru bind9-9.18.41/bin/tests/system/serve-stale/tests.sh bind9-9.18.44/bin/tests/system/serve-stale/tests.sh --- bind9-9.18.41/bin/tests/system/serve-stale/tests.sh 2025-10-18 10:21:03.007257539 +0000 +++ bind9-9.18.44/bin/tests/system/serve-stale/tests.sh 2026-01-09 13:44:04.684036679 +0000 @@ -2758,5 +2758,37 @@ if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status + ret)) +# disable delaying auth answering +n=$((n + 1)) +echo_i "disable delaying responses from authoritative server ($n)" +ret=0 +$DIG -p ${PORT} @10.53.0.2 txt normal >dig.out.test$n || ret=1 +grep "ANSWER: 1," dig.out.test$n >/dev/null || ret=1 +grep "TXT.\"1\"" dig.out.test$n >/dev/null || ret=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +# configure ns3 with stale-answer-client-timeout 0 and a auth zone +copy_setports ns3/named10.conf.in ns3/named.conf +rndc_reload ns3 10.53.0.3 + +# GL#5383 +n=$((n + 1)) +echo_i "check serve-stale (stale-answer-client-timeout 0) with a CNAME targeting a cached auth zone ($n)" +ret=0 +# flush cache, make sure serve-stale is on +$RNDCCMD 10.53.0.3 flush >rndc.out.test$n.1 2>&1 || ret=1 +$RNDCCMD 10.53.0.3 serve-stale on >rndc.out.test$n.2 2>&1 || ret=1 +# prime the cache with the A response +$DIG -p ${PORT} @10.53.0.3 out-cname.example >dig.out.1.test$n || ret=1 +grep -F "status: NOERROR" dig.out.1.test$n >/dev/null || ret=1 +grep -F "QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1" dig.out.1.test$n >/dev/null || ret=1 +# resend the query; we should immediately get a cached answer +$DIG -p ${PORT} @10.53.0.3 out-cname.example >dig.out.2.test$n || ret=1 +grep -F "status: NOERROR" dig.out.2.test$n >/dev/null || ret=1 +grep -F "QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1" dig.out.2.test$n >/dev/null || ret=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + echo_i "exit status: $status" [ $status -eq 0 ] || exit 1 diff -Nru bind9-9.18.41/bin/tests/system/shutdown/tests_shutdown.py bind9-9.18.44/bin/tests/system/shutdown/tests_shutdown.py --- bind9-9.18.41/bin/tests/system/shutdown/tests_shutdown.py 2025-10-18 10:21:03.009257593 +0000 +++ bind9-9.18.44/bin/tests/system/shutdown/tests_shutdown.py 2026-01-09 13:44:04.686036712 +0000 @@ -71,11 +71,8 @@ # helper function, 'command' is the rndc command to run def launch_rndc(command): - try: - instance.rndc(command, log=False) - return 0 - except isctest.rndc.RNDCException: - return -1 + ret = instance.rndc(command, raise_on_exception=False) + return 0 if ret.rc == 0 else -1 # We're going to execute queries in parallel by means of a thread pool. # dnspython functions block, so we need to circumvent that. diff -Nru bind9-9.18.41/bin/tests/system/spf/tests_spf_zones.py bind9-9.18.44/bin/tests/system/spf/tests_spf_zones.py --- bind9-9.18.41/bin/tests/system/spf/tests_spf_zones.py 2025-10-18 10:21:03.010257619 +0000 +++ bind9-9.18.44/bin/tests/system/spf/tests_spf_zones.py 2026-01-09 13:44:04.687036729 +0000 @@ -13,7 +13,7 @@ @pytest.mark.requires_zones_loaded("ns1") -def test_spf_log(servers): +def test_spf_log(ns1): for msg in ( "zone spf/IN: 'y.spf' found type SPF record but no SPF TXT record found", "zone warn/IN: 'y.warn' found type SPF record but no SPF TXT record found", @@ -21,7 +21,7 @@ "zone warn/IN: loaded serial 0", "zone nowarn/IN: loaded serial 0", ): - servers["ns1"].log.expect(msg) + assert msg in ns1.log for msg in ( "zone nowarn/IN: 'y.nowarn' found type SPF record but no SPF TXT record found", @@ -29,4 +29,4 @@ "zone warn/IN: 'warn' found type SPF record but no SPF TXT record found", "zone nowarn/IN: 'nowarn' found type SPF record but no SPF TXT record found", ): - servers["ns1"].log.prohibit(msg) + assert msg not in ns1.log diff -Nru bind9-9.18.41/bin/tests/system/start.pl bind9-9.18.44/bin/tests/system/start.pl --- bind9-9.18.41/bin/tests/system/start.pl 2025-10-18 10:21:03.010257619 +0000 +++ bind9-9.18.44/bin/tests/system/start.pl 2026-01-09 13:44:04.687036729 +0000 @@ -325,6 +325,7 @@ } if (-e "$testdir/$server/ans.py") { + $ENV{'PYTHONPATH'} = $testdir . ":" . $srcdir; $command = "$PYTHON -u ans.py 10.53.0.$n $queryport"; } elsif (-e "$testdir/$server/ans.pl") { $command = "$PERL ans.pl"; diff -Nru bind9-9.18.41/bin/tests/system/stress/tests_stress_update.py bind9-9.18.44/bin/tests/system/stress/tests_stress_update.py --- bind9-9.18.41/bin/tests/system/stress/tests_stress_update.py 2025-10-18 10:21:03.018257833 +0000 +++ bind9-9.18.44/bin/tests/system/stress/tests_stress_update.py 2026-01-09 13:44:04.696036877 +0000 @@ -10,7 +10,6 @@ # information regarding copyright ownership. import concurrent.futures -import os import time import dns.update @@ -28,22 +27,8 @@ def rndc_loop(test_state, server): - rndc = os.getenv("RNDC") - port = os.getenv("CONTROLPORT") - - cmdline = [ - rndc, - "-c", - "../_common/rndc.conf", - "-p", - port, - "-s", - server, - "reload", - ] - while not test_state["finished"]: - isctest.run.cmd(cmdline, raise_on_exception=False) + server.rndc("reload", raise_on_exception=False) time.sleep(1) diff -Nru bind9-9.18.41/bin/tests/system/tkey/keycreate.c bind9-9.18.44/bin/tests/system/tkey/keycreate.c --- bind9-9.18.41/bin/tests/system/tkey/keycreate.c 2025-10-18 10:21:03.024257993 +0000 +++ bind9-9.18.44/bin/tests/system/tkey/keycreate.c 2026-01-09 13:44:04.703036993 +0000 @@ -40,7 +40,7 @@ #include #include -#define CHECK(str, x) \ +#define CHECKM(str, x) \ { \ if ((x) != ISC_R_SUCCESS) { \ fprintf(stderr, "I:%s: %s\n", (str), \ @@ -90,7 +90,7 @@ result = dns_request_getresponse(reqev->request, response, DNS_MESSAGEPARSE_PRESERVEORDER); - CHECK("dns_request_getresponse", result); + CHECKM("dns_request_getresponse", result); if (response->rcode != dns_rcode_noerror) { result = dns_result_fromrcode(response->rcode); @@ -101,19 +101,19 @@ result = dns_tkey_processdhresponse(query, response, ourkey, &nonce, &tsigkey, ring); - CHECK("dns_tkey_processdhresponse", result); + CHECKM("dns_tkey_processdhresponse", result); /* * Yes, this is a hack. */ isc_buffer_init(&keynamebuf, keyname, sizeof(keyname)); result = dst_key_buildfilename(tsigkey->key, 0, "", &keynamebuf); - CHECK("dst_key_buildfilename", result); + CHECKM("dst_key_buildfilename", result); printf("%.*s\n", (int)isc_buffer_usedlength(&keynamebuf), (char *)isc_buffer_base(&keynamebuf)); type = DST_TYPE_PRIVATE | DST_TYPE_PUBLIC | DST_TYPE_KEY; result = dst_key_tofile(tsigkey->key, type, ""); - CHECK("dst_key_tofile", result); + CHECKM("dst_key_tofile", result); dns_message_detach(&query); dns_message_detach(&response); @@ -141,7 +141,7 @@ result = ISC_R_FAILURE; if (inet_pton(AF_INET, ip_address, &inaddr) != 1) { - CHECK("inet_pton", result); + CHECKM("inet_pton", result); } isc_sockaddr_fromin(&address, &inaddr, port); @@ -150,18 +150,18 @@ isc_buffer_add(&namestr, 9); result = dns_name_fromtext(dns_fixedname_name(&keyname), &namestr, NULL, 0, NULL); - CHECK("dns_name_fromtext", result); + CHECKM("dns_name_fromtext", result); dns_fixedname_init(&ownername); isc_buffer_constinit(&namestr, ownername_str, strlen(ownername_str)); isc_buffer_add(&namestr, strlen(ownername_str)); result = dns_name_fromtext(dns_fixedname_name(&ownername), &namestr, NULL, 0, NULL); - CHECK("dns_name_fromtext", result); + CHECKM("dns_name_fromtext", result); isc_buffer_init(&keybuf, keydata, 9); result = isc_base64_decodestring(keystr, &keybuf); - CHECK("isc_base64_decodestring", result); + CHECKM("isc_base64_decodestring", result); isc_buffer_usedregion(&keybuf, &r); @@ -169,19 +169,19 @@ dns_fixedname_name(&keyname), DNS_TSIG_HMACMD5_NAME, isc_buffer_base(&keybuf), isc_buffer_usedlength(&keybuf), false, NULL, 0, 0, mctx, ring, &initialkey); - CHECK("dns_tsigkey_create", result); + CHECKM("dns_tsigkey_create", result); dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER, &query); result = dns_tkey_builddhquery(query, ourkey, dns_fixedname_name(&ownername), DNS_TSIG_HMACMD5_NAME, &nonce, 3600); - CHECK("dns_tkey_builddhquery", result); + CHECKM("dns_tkey_builddhquery", result); result = dns_request_create(requestmgr, query, NULL, &address, DNS_REQUESTOPT_TCP, initialkey, TIMEOUT, 0, 0, task, recvquery, query, &request); - CHECK("dns_request_create", result); + CHECKM("dns_request_create", result); } int @@ -242,7 +242,7 @@ type = DST_TYPE_PUBLIC | DST_TYPE_PRIVATE | DST_TYPE_KEY; result = dst_key_fromnamedfile(ourkeyname, NULL, type, mctx, &ourkey); - CHECK("dst_key_fromnamedfile", result); + CHECKM("dst_key_fromnamedfile", result); isc_buffer_init(&nonce, noncedata, sizeof(noncedata)); isc_nonce_buf(noncedata, sizeof(noncedata)); diff -Nru bind9-9.18.41/bin/tests/system/tkey/keydelete.c bind9-9.18.44/bin/tests/system/tkey/keydelete.c --- bind9-9.18.41/bin/tests/system/tkey/keydelete.c 2025-10-18 10:21:03.024257993 +0000 +++ bind9-9.18.44/bin/tests/system/tkey/keydelete.c 2026-01-09 13:44:04.703036993 +0000 @@ -39,7 +39,7 @@ #include #include -#define CHECK(str, x) \ +#define CHECKM(str, x) \ { \ if ((x) != ISC_R_SUCCESS) { \ fprintf(stderr, "I:%s: %s\n", (str), \ @@ -81,7 +81,7 @@ result = dns_request_getresponse(reqev->request, response, DNS_MESSAGEPARSE_PRESERVEORDER); - CHECK("dns_request_getresponse", result); + CHECKM("dns_request_getresponse", result); if (response->rcode != dns_rcode_noerror) { result = dns_result_fromrcode(response->rcode); @@ -91,7 +91,7 @@ } result = dns_tkey_processdeleteresponse(query, response, ring); - CHECK("dns_tkey_processdhresponse", result); + CHECKM("dns_tkey_processdhresponse", result); dns_message_detach(&query); dns_message_detach(&response); @@ -113,19 +113,19 @@ result = ISC_R_FAILURE; if (inet_pton(AF_INET, ip_address, &inaddr) != 1) { - CHECK("inet_pton", result); + CHECKM("inet_pton", result); } isc_sockaddr_fromin(&address, &inaddr, port); dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER, &query); result = dns_tkey_builddeletequery(query, tsigkey); - CHECK("dns_tkey_builddeletequery", result); + CHECKM("dns_tkey_builddeletequery", result); result = dns_request_create(requestmgr, query, NULL, &address, DNS_REQUESTOPT_TCP, tsigkey, TIMEOUT, 0, 0, task, recvquery, query, &request); - CHECK("dns_request_create", result); + CHECKM("dns_request_create", result); } int @@ -184,12 +184,12 @@ type = DST_TYPE_PUBLIC | DST_TYPE_PRIVATE | DST_TYPE_KEY; result = dst_key_fromnamedfile(keyname, NULL, type, mctx, &dstkey); - CHECK("dst_key_fromnamedfile", result); + CHECKM("dst_key_fromnamedfile", result); result = dns_tsigkey_createfromkey(dst_key_name(dstkey), DNS_TSIG_HMACMD5_NAME, dstkey, true, NULL, 0, 0, mctx, ring, &tsigkey); dst_key_free(&dstkey); - CHECK("dns_tsigkey_createfromkey", result); + CHECKM("dns_tsigkey_createfromkey", result); (void)isc_app_run(); diff -Nru bind9-9.18.41/bin/tests/system/tools/tests_tools_nsec3hash.py bind9-9.18.44/bin/tests/system/tools/tests_tools_nsec3hash.py --- bind9-9.18.41/bin/tests/system/tools/tests_tools_nsec3hash.py 2025-10-18 10:21:03.025258020 +0000 +++ bind9-9.18.44/bin/tests/system/tools/tests_tools_nsec3hash.py 2026-01-09 13:44:04.704037009 +0000 @@ -51,16 +51,12 @@ algorithm = "1" iterations = "12" - output = isctest.run.cmd( - [NSEC3HASH, salt, algorithm, iterations, domain] - ).stdout.decode("utf-8") - assert nsec3hash in output + cmd = isctest.run.cmd([NSEC3HASH, salt, algorithm, iterations, domain]) + assert nsec3hash in cmd.out flags = "0" - output = isctest.run.cmd( - [NSEC3HASH, "-r", algorithm, flags, iterations, salt, domain] - ).stdout.decode("utf-8") - assert nsec3hash in output + cmd = isctest.run.cmd([NSEC3HASH, "-r", algorithm, flags, iterations, salt, domain]) + assert nsec3hash in cmd.out @pytest.mark.parametrize( @@ -77,11 +73,11 @@ iterations = "0" domain = "com" - output = isctest.run.cmd( + cmd = isctest.run.cmd( [NSEC3HASH] + salt_emptiness_args + [algorithm, iterations, domain] - ).stdout.decode("utf-8") - assert "CK0POJMG874LJREF7EFN8430QVIT8BSM" in output - assert "salt=-" in output + ) + assert "CK0POJMG874LJREF7EFN8430QVIT8BSM" in cmd.out + assert "salt=-" in cmd.out @pytest.mark.parametrize( @@ -97,7 +93,7 @@ iterations = "0" domain = "com" - output = isctest.run.cmd( + cmd = isctest.run.cmd( [ NSEC3HASH, "-r", @@ -107,8 +103,8 @@ salt_emptiness_arg, domain, ] - ).stdout.decode("utf-8") - assert " - CK0POJMG874LJREF7EFN8430QVIT8BSM" in output + ) + assert " - CK0POJMG874LJREF7EFN8430QVIT8BSM" in cmd.out @pytest.mark.parametrize( @@ -144,10 +140,8 @@ ) # calculate the hash using nsec3hash: - output = isctest.run.cmd( - [NSEC3HASH, salt_text, "1", str(it), str(domain)] - ).stdout.decode("ascii") - hash2 = output.partition(" ")[0] + cmd = isctest.run.cmd([NSEC3HASH, salt_text, "1", str(it), str(domain)]) + hash2 = cmd.out.partition(" ")[0] assert hash1 == hash2 diff -Nru bind9-9.18.41/bin/tests/system/verify/tests_verify.py bind9-9.18.44/bin/tests/system/verify/tests_verify.py --- bind9-9.18.41/bin/tests/system/verify/tests_verify.py 2025-10-18 10:21:03.034258261 +0000 +++ bind9-9.18.44/bin/tests/system/verify/tests_verify.py 2026-01-09 13:44:04.713037158 +0000 @@ -11,6 +11,7 @@ import os import re +from re import compile as Re import pytest @@ -61,14 +62,14 @@ ) -def get_bad_zone_output(zone): - only_opt = ["-z"] if re.match(r"[zk]sk-only", zone) else [] - output = isctest.run.cmd( +def verify_bad_zone(zone): + only_opt = ["-z"] if re.search(r"^[zk]sk-only", zone) else [] + cmd = isctest.run.cmd( [VERIFY, *only_opt, "-o", zone, f"zones/{zone}.bad"], raise_on_exception=False, ) - stream = (output.stdout + output.stderr).decode("utf-8").replace("\n", "") - return stream + assert cmd.rc != 0 + return cmd @pytest.mark.parametrize( @@ -80,7 +81,8 @@ ], ) def test_verify_bad_zone_files_dnskeyonly(zone): - assert re.match(r".*DNSKEY is not signed.*", get_bad_zone_output(zone)) + cmd = verify_bad_zone(zone) + assert "DNSKEY is not signed" in cmd.err @pytest.mark.parametrize( @@ -97,10 +99,8 @@ ], ) def test_verify_bad_zone_files_expired(zone): - assert re.match( - r".*signature has expired.*|.*No self-signed .*DNSKEY found.*", - get_bad_zone_output(zone), - ) + cmd = verify_bad_zone(zone) + assert Re("signature has expired|No self-signed DNSKEY found") in cmd.err @pytest.mark.parametrize( @@ -112,40 +112,33 @@ ], ) def test_verify_bad_zone_files_unexpected_nsec_rrset(zone): - assert re.match(r".*unexpected NSEC RRset at.*", get_bad_zone_output(zone)) + cmd = verify_bad_zone(zone) + assert "unexpected NSEC RRset at" in cmd.err def test_verify_bad_zone_files_bad_nsec_record(): - assert re.match( - r".*Bad NSEC record for.*, next name mismatch.*", - get_bad_zone_output("ksk+zsk.nsec.broken-chain"), - ) + cmd = verify_bad_zone("ksk+zsk.nsec.broken-chain") + assert Re("Bad NSEC record for.*, next name mismatch") in cmd.err def test_verify_bad_zone_files_bad_bitmap(): - assert re.match( - r".*bit map mismatch.*", get_bad_zone_output("ksk+zsk.nsec.bad-bitmap") - ) + cmd = verify_bad_zone("ksk+zsk.nsec.bad-bitmap") + assert "bit map mismatch" in cmd.err def test_verify_bad_zone_files_missing_nsec3_record(): - assert re.match( - r".*Missing NSEC3 record for.*", - get_bad_zone_output("ksk+zsk.nsec3.missing-empty"), - ) + cmd = verify_bad_zone("ksk+zsk.nsec3.missing-empty") + assert "Missing NSEC3 record for" in cmd.err def test_verify_bad_zone_files_no_dnssec_keys(): - assert re.match( - r".*Zone contains no DNSSEC keys.*", get_bad_zone_output("unsigned") - ) + cmd = verify_bad_zone("unsigned") + assert "Zone contains no DNSSEC keys" in cmd.err def test_verify_bad_zone_files_unequal_nsec3_chains(): - assert re.match( - r".*Expected and found NSEC3 chains not equal.*", - get_bad_zone_output("ksk+zsk.nsec3.extra-nsec3"), - ) + cmd = verify_bad_zone("ksk+zsk.nsec3.extra-nsec3") + assert "Expected and found NSEC3 chains not equal" in cmd.err # checking error message when -o is not used @@ -153,19 +146,17 @@ def test_verify_soa_not_at_top_error(): # when -o is not used, origin is set to zone file name, # which should cause an error in this case - output = isctest.run.cmd( - [VERIFY, "zones/ksk+zsk.nsec.good"], raise_on_exception=False - ).stderr.decode("utf-8") - assert "not at top of zone" in output - assert "use -o to specify a different zone origin" in output + cmd = isctest.run.cmd([VERIFY, "zones/ksk+zsk.nsec.good"], raise_on_exception=False) + assert "not at top of zone" in cmd.err + assert "use -o to specify a different zone origin" in cmd.err # checking error message when an invalid -o is specified # and a SOA record not at top of zone is found def test_verify_invalid_o_option_soa_not_at_top_error(): - output = isctest.run.cmd( + cmd = isctest.run.cmd( [VERIFY, "-o", "invalid.origin", "zones/ksk+zsk.nsec.good"], raise_on_exception=False, - ).stderr.decode("utf-8") - assert "not at top of zone" in output - assert "use -o to specify a different zone origin" not in output + ) + assert "not at top of zone" in cmd.err + assert "use -o to specify a different zone origin" not in cmd.err diff -Nru bind9-9.18.41/bin/tests/system/xfer/dig1.good bind9-9.18.44/bin/tests/system/xfer/dig1.good --- bind9-9.18.41/bin/tests/system/xfer/dig1.good 2025-10-18 10:21:03.039258394 +0000 +++ bind9-9.18.44/bin/tests/system/xfer/dig1.good 2026-01-09 13:44:04.718037240 +0000 @@ -12,8 +12,8 @@ aaaa02.example. 3600 IN AAAA fd92:7065:b8e:ffff::5 afsdb01.example. 3600 IN AFSDB 0 hostname.example. afsdb02.example. 3600 IN AFSDB 65535 . -amtrelay01.example. 3600 IN AMTRELAY 0 0 0 -amtrelay02.example. 3600 IN AMTRELAY 0 1 0 +amtrelay01.example. 3600 IN AMTRELAY 0 0 0 . +amtrelay02.example. 3600 IN AMTRELAY 0 1 0 . amtrelay03.example. 3600 IN AMTRELAY 0 0 1 0.0.0.0 amtrelay04.example. 3600 IN AMTRELAY 0 0 2 :: amtrelay05.example. 3600 IN AMTRELAY 0 0 3 example.net. diff -Nru bind9-9.18.41/bin/tests/system/xfer/dig2.good bind9-9.18.44/bin/tests/system/xfer/dig2.good --- bind9-9.18.41/bin/tests/system/xfer/dig2.good 2025-10-18 10:21:03.039258394 +0000 +++ bind9-9.18.44/bin/tests/system/xfer/dig2.good 2026-01-09 13:44:04.718037240 +0000 @@ -12,8 +12,8 @@ aaaa02.example. 3600 IN AAAA fd92:7065:b8e:ffff::5 afsdb01.example. 3600 IN AFSDB 0 hostname.example. afsdb02.example. 3600 IN AFSDB 65535 . -amtrelay01.example. 3600 IN AMTRELAY 0 0 0 -amtrelay02.example. 3600 IN AMTRELAY 0 1 0 +amtrelay01.example. 3600 IN AMTRELAY 0 0 0 . +amtrelay02.example. 3600 IN AMTRELAY 0 1 0 . amtrelay03.example. 3600 IN AMTRELAY 0 0 1 0.0.0.1 amtrelay04.example. 3600 IN AMTRELAY 0 0 2 :: amtrelay05.example. 3600 IN AMTRELAY 0 0 3 example.net. diff -Nru bind9-9.18.41/bin/tests/system/xferquota/tests_xferquota.py bind9-9.18.44/bin/tests/system/xferquota/tests_xferquota.py --- bind9-9.18.41/bin/tests/system/xferquota/tests_xferquota.py 2025-10-18 10:21:03.042258475 +0000 +++ bind9-9.18.44/bin/tests/system/xferquota/tests_xferquota.py 2026-01-09 13:44:04.721037290 +0000 @@ -12,6 +12,7 @@ import glob import os import re +from re import compile as Re import shutil import signal import time @@ -71,7 +72,7 @@ isctest.check.rrsets_equal(ns1response.answer, ns2response.answer) query_and_compare(axfr_msg) - pattern = re.compile( + pattern = Re( f"transfer of 'changing/IN' from 10.53.0.1#{named_port}: " f"Transfer completed: .*\\(serial 2\\)" ) diff -Nru bind9-9.18.41/bin/tools/mdig.c bind9-9.18.44/bin/tools/mdig.c --- bind9-9.18.41/bin/tools/mdig.c 2025-10-18 10:21:03.046258582 +0000 +++ bind9-9.18.44/bin/tools/mdig.c 2026-01-09 13:44:04.725037356 +0000 @@ -56,7 +56,7 @@ #include -#define CHECK(str, x) \ +#define CHECKM(str, x) \ { \ if ((x) != ISC_R_SUCCESS) { \ fprintf(stderr, "mdig: %s failed with %s\n", (str), \ @@ -222,7 +222,7 @@ msgbuf = dns_request_getanswer(reqev->request); result = dns_request_getresponse(reqev->request, response, parseflags); - CHECK("dns_request_getresponse", result); + CHECKM("dns_request_getresponse", result); styleflags |= DNS_STYLEFLAG_REL_OWNER; if (yaml) { @@ -278,7 +278,7 @@ 48, 80, 8, display_splitwidth, mctx); } - CHECK("dns_master_stylecreate2", result); + CHECKM("dns_master_stylecreate2", result); flags = 0; if (!display_headers) { @@ -342,7 +342,7 @@ printf(" %s:\n", "response_message_data"); result = dns_message_headertotext(response, style, flags, buf); - CHECK("dns_message_headertotext", result); + CHECKM("dns_message_headertotext", result); } else if (display_comments && !display_short_form) { printf(";; Got answer:\n"); @@ -405,7 +405,7 @@ isc_buffer_allocate(mctx, &buf, len); goto repopulate_buffer; } - CHECK("dns_message_pseudosectiontotext", result); + CHECKM("dns_message_pseudosectiontotext", result); } if (display_question && display_headers && !display_short_form) { @@ -414,7 +414,7 @@ if (result == ISC_R_NOSPACE) { goto buftoosmall; } - CHECK("dns_message_sectiontotext", result); + CHECKM("dns_message_sectiontotext", result); } if (display_answer && !display_short_form) { @@ -423,7 +423,7 @@ if (result == ISC_R_NOSPACE) { goto buftoosmall; } - CHECK("dns_message_sectiontotext", result); + CHECKM("dns_message_sectiontotext", result); } else if (display_answer) { dns_name_t *name; dns_rdataset_t *rdataset; @@ -442,14 +442,14 @@ dns_name_init(&empty_name, NULL); result = dns_message_firstname(response, DNS_SECTION_ANSWER); if (result != ISC_R_NOMORE) { - CHECK("dns_message_firstname", result); + CHECKM("dns_message_firstname", result); } for (;;) { if (result == ISC_R_NOMORE) { break; } - CHECK("dns_message_nextname", result); + CHECKM("dns_message_nextname", result); name = NULL; dns_message_currentname(response, DNS_SECTION_ANSWER, &name); @@ -467,7 +467,7 @@ if (result == ISC_R_NOSPACE) { goto buftoosmall; } - CHECK("dns_rdata_tofmttext", result); + CHECKM("dns_rdata_tofmttext", result); loopresult = dns_rdataset_next(rdataset); dns_rdata_reset(&rdata); @@ -490,7 +490,7 @@ if (result == ISC_R_NOSPACE) { goto buftoosmall; } - CHECK("dns_message_sectiontotext", result); + CHECKM("dns_message_sectiontotext", result); } if (display_additional && !display_short_form) { @@ -499,7 +499,7 @@ if (result == ISC_R_NOSPACE) { goto buftoosmall; } - CHECK("dns_message_sectiontotext", result); + CHECKM("dns_message_sectiontotext", result); } if (display_additional && !display_short_form && display_headers) { @@ -511,13 +511,13 @@ if (result == ISC_R_NOSPACE) { goto buftoosmall; } - CHECK("dns_message_pseudosectiontotext", result); + CHECKM("dns_message_pseudosectiontotext", result); result = dns_message_pseudosectiontotext( response, DNS_PSEUDOSECTION_SIG0, style, flags, buf); if (result == ISC_R_NOSPACE) { goto buftoosmall; } - CHECK("dns_message_pseudosectiontotext", result); + CHECKM("dns_message_pseudosectiontotext", result); } if (display_headers && display_comments && !display_short_form && !yaml) @@ -562,9 +562,9 @@ result = dns_message_buildopt(msg, &rdataset, edns, udpsize, flags, opts, count); - CHECK("dns_message_buildopt", result); + CHECKM("dns_message_buildopt", result); result = dns_message_setopt(msg, rdataset); - CHECK("dns_message_setopt", result); + CHECKM("dns_message_setopt", result); } static void @@ -592,7 +592,7 @@ isc_buffer_add(&buf, strlen(query->textname)); result = dns_name_fromtext(dns_fixedname_name(&queryname), &buf, dns_rootname, 0, NULL); - CHECK("dns_name_fromtext", result); + CHECKM("dns_name_fromtext", result); dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER, &message); @@ -616,10 +616,10 @@ message->id = (unsigned short)(random() & 0xFFFF); result = dns_message_gettempname(message, &qname); - CHECK("dns_message_gettempname", result); + CHECKM("dns_message_gettempname", result); result = dns_message_gettemprdataset(message, &qrdataset); - CHECK("dns_message_gettemprdataset", result); + CHECKM("dns_message_gettemprdataset", result); dns_name_clone(dns_fixedname_name(&queryname), qname); dns_rdataset_makequestion(qrdataset, query->rdclass, query->rdtype); @@ -668,7 +668,7 @@ INSIST(i < DNS_EDNSOPTIONS); opts[i].code = DNS_OPT_CLIENT_SUBNET; opts[i].length = (uint16_t)addrl + 4; - CHECK("isc_buffer_allocate", result); + CHECKM("isc_buffer_allocate", result); isc_buffer_init(&b, ecsbuf, sizeof(ecsbuf)); if (sa->sa_family == AF_INET) { family = 1; @@ -713,7 +713,7 @@ isc_buffer_init(&b, cookie, sizeof(cookie)); result = isc_hex_decodestring(query->cookie, &b); - CHECK("isc_hex_decodestring", result); + CHECKM("isc_hex_decodestring", result); opts[i].value = isc_buffer_base(&b); opts[i].length = isc_buffer_usedlength(&b); } else { @@ -756,7 +756,7 @@ requestmgr, message, have_src ? &srcaddr : NULL, &dstaddr, options, NULL, query->timeout, query->udptimeout, query->udpretries, task, recvresponse, message, &request); - CHECK("dns_request_create", result); + CHECKM("dns_request_create", result); return ISC_R_SUCCESS; } @@ -973,7 +973,7 @@ buf = isc_mem_allocate(mctx, strlen(value) / 2 + 1); isc_buffer_init(&b, buf, strlen(value) / 2 + 1); result = isc_hex_decodestring(value, &b); - CHECK("isc_hex_decodestring", result); + CHECKM("isc_hex_decodestring", result); query->ednsopts[query->ednsoptscnt].value = isc_buffer_base(&b); query->ednsopts[query->ednsoptscnt].length = isc_buffer_usedlength(&b); @@ -1070,9 +1070,9 @@ if (dot != NULL) { isc_result_t result; result = reverse_octets(dot + 1, p, end); - CHECK("reverse_octets", result); + CHECKM("reverse_octets", result); result = append(".", 1, p, end); - CHECK("append", result); + CHECKM("append", result); len = (int)(dot - in); } else { len = strlen(in); @@ -1096,7 +1096,7 @@ name = dns_fixedname_initname(&fname); result = dns_byaddr_createptrname(&addr, options, name); - CHECK("dns_byaddr_createptrname2", result); + CHECKM("dns_byaddr_createptrname2", result); dns_name_format(name, reverse, (unsigned int)len); return; } else { @@ -1110,10 +1110,10 @@ char *p = reverse; char *end = reverse + len; result = reverse_octets(value, &p, end); - CHECK("reverse_octets", result); + CHECKM("reverse_octets", result); /* Append .in-addr.arpa. and a terminating NUL. */ result = append(".in-addr.arpa.", 15, &p, end); - CHECK("append", result); + CHECKM("append", result); return; } } @@ -1230,7 +1230,7 @@ } result = parse_uint(&num, value, COMMSIZE, "buffer size"); - CHECK("parse_uint(buffer size)", result); + CHECKM("parse_uint(buffer size)", result); query->udpsize = num; break; case 'r': /* burst */ @@ -1346,8 +1346,8 @@ result = parse_uint(&num, value, 255, "edns"); - CHECK("parse_uint(edns)", - result); + CHECKM("parse_uint(edns)", + result); query->edns = num; break; case 'f': @@ -1363,8 +1363,8 @@ result = parse_xint( &num, value, 0xffff, "ednsflags"); - CHECK("parse_xint(ednsflags)", - result); + CHECKM("parse_xint(ednsflags)", + result); if (query->edns == -1) { query->edns = 1; } @@ -1446,7 +1446,7 @@ } result = parse_uint(&query->udpretries, value, MAXTRIES - 1, "udpretries"); - CHECK("parse_uint(udpretries)", result); + CHECKM("parse_uint(udpretries)", result); break; default: goto invalid_option; @@ -1510,7 +1510,7 @@ if (display_splitwidth) { display_splitwidth += 3; } - CHECK("parse_uint(split)", result); + CHECKM("parse_uint(split)", result); break; case 'u': /* subnet */ FULLCHECK("subnet"); @@ -1528,7 +1528,7 @@ query->edns = 0; } result = parse_netprefix(&query->ecs_addr, value); - CHECK("parse_netprefix", result); + CHECKM("parse_netprefix", result); break; default: goto invalid_option; @@ -1551,7 +1551,7 @@ } result = parse_uint(&query->timeout, value, MAXTIMEOUT, "timeout"); - CHECK("parse_uint(timeout)", result); + CHECKM("parse_uint(timeout)", result); if (query->timeout == 0) { query->timeout = 1; } @@ -1566,7 +1566,7 @@ } result = parse_uint(&query->udpretries, value, MAXTRIES, "udpretries"); - CHECK("parse_uint(udpretries)", result); + CHECKM("parse_uint(udpretries)", result); if (query->udpretries > 0) { query->udpretries -= 1; } @@ -1611,7 +1611,7 @@ } result = parse_uint(&query->udptimeout, value, MAXTIMEOUT, "udptimeout"); - CHECK("parse_uint(udptimeout)", result); + CHECKM("parse_uint(udptimeout)", result); break; case 'n': FULLCHECK("unknownformat"); @@ -1630,7 +1630,7 @@ FULLCHECK("yaml"); yaml = state; if (state) { - display_rrcomments = state; + display_rrcomments = 1; } break; case 'z': /* zflag */ @@ -1742,7 +1742,7 @@ if (hash != NULL) { result = parse_uint(&num, hash + 1, MAXPORT, "port number"); - CHECK("parse_uint(srcport)", result); + CHECKM("parse_uint(srcport)", result); srcport = num; *hash = '\0'; } else { @@ -1770,7 +1770,7 @@ tr.length = strlen(value); result = dns_rdataclass_fromtext(&rdclass, (isc_textregion_t *)&tr); - CHECK("dns_rdataclass_fromtext", result); + CHECKM("dns_rdataclass_fromtext", result); query->rdclass = rdclass; return value_from_next; case 'f': @@ -1779,7 +1779,7 @@ case 'p': GLOBAL(); result = parse_uint(&num, value, MAXPORT, "port number"); - CHECK("parse_uint(port)", result); + CHECKM("parse_uint(port)", result); port = num; return value_from_next; case 't': @@ -1787,7 +1787,7 @@ tr.length = strlen(value); result = dns_rdatatype_fromtext(&rdtype, (isc_textregion_t *)&tr); - CHECK("dns_rdatatype_fromtext", result); + CHECKM("dns_rdatatype_fromtext", result); query->rdtype = rdtype; return value_from_next; case 'x': diff -Nru bind9-9.18.41/configure bind9-9.18.44/configure --- bind9-9.18.41/configure 2025-10-18 10:21:42.026275596 +0000 +++ bind9-9.18.44/configure 2026-01-09 13:45:06.614152475 +0000 @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.72 for BIND 9.18.41. +# Generated by GNU Autoconf 2.72 for BIND 9.18.44. # # Report bugs to . # @@ -615,8 +615,8 @@ # Identity of this package. PACKAGE_NAME='BIND' PACKAGE_TARNAME='bind' -PACKAGE_VERSION='9.18.41' -PACKAGE_STRING='BIND 9.18.41' +PACKAGE_VERSION='9.18.44' +PACKAGE_STRING='BIND 9.18.44' PACKAGE_BUGREPORT='https://gitlab.isc.org/isc-projects/bind9/-/issues/new?issuable_template=Bug' PACKAGE_URL='https://www.isc.org/downloads/' @@ -1544,7 +1544,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -'configure' configures BIND 9.18.41 to adapt to many kinds of systems. +'configure' configures BIND 9.18.44 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1616,7 +1616,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of BIND 9.18.41:";; + short | recursive ) echo "Configuration of BIND 9.18.44:";; esac cat <<\_ACEOF @@ -1842,7 +1842,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -BIND configure 9.18.41 +BIND configure 9.18.44 generated by GNU Autoconf 2.72 Copyright (C) 2023 Free Software Foundation, Inc. @@ -2262,7 +2262,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by BIND $as_me 9.18.41, which was +It was created by BIND $as_me 9.18.44, which was generated by GNU Autoconf 2.72. Invocation command line was $ $0$ac_configure_args_raw @@ -3046,7 +3046,7 @@ printf "%s\n" "#define PACKAGE_VERSION_MINOR \"18\"" >>confdefs.h -printf "%s\n" "#define PACKAGE_VERSION_PATCH \"41\"" >>confdefs.h +printf "%s\n" "#define PACKAGE_VERSION_PATCH \"44\"" >>confdefs.h printf "%s\n" "#define PACKAGE_VERSION_EXTRA \"\"" >>confdefs.h @@ -3055,7 +3055,7 @@ printf "%s\n" "#define PACKAGE_DESCRIPTION \" (Extended Support Version)\"" >>confdefs.h -printf "%s\n" "#define PACKAGE_SRCID \"e8adafa\"" >>confdefs.h +printf "%s\n" "#define PACKAGE_SRCID \"2e74eea\"" >>confdefs.h bind_CONFIGARGS="${ac_configure_args:-default}" @@ -3890,7 +3890,7 @@ # Define the identity of the package. PACKAGE='bind' - VERSION='9.18.41' + VERSION='9.18.44' printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h @@ -29899,7 +29899,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by BIND $as_me 9.18.41, which was +This file was extended by BIND $as_me 9.18.44, which was generated by GNU Autoconf 2.72. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -29968,7 +29968,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ -BIND config.status 9.18.41 +BIND config.status 9.18.44 configured by $0, generated by GNU Autoconf 2.72, with options \\"\$ac_cs_config\\" diff -Nru bind9-9.18.41/configure.ac bind9-9.18.44/configure.ac --- bind9-9.18.41/configure.ac 2025-10-18 10:21:03.049258662 +0000 +++ bind9-9.18.44/configure.ac 2026-01-09 13:44:04.728037405 +0000 @@ -16,7 +16,7 @@ # m4_define([bind_VERSION_MAJOR], 9)dnl m4_define([bind_VERSION_MINOR], 18)dnl -m4_define([bind_VERSION_PATCH], 41)dnl +m4_define([bind_VERSION_PATCH], 44)dnl m4_define([bind_VERSION_EXTRA], )dnl m4_define([bind_DESCRIPTION], [(Extended Support Version)])dnl m4_define([bind_SRCID], [m4_esyscmd_s([git rev-parse --short HEAD | cut -b1-7])])dnl diff -Nru bind9-9.18.41/debian/changelog bind9-9.18.44/debian/changelog --- bind9-9.18.41/debian/changelog 2025-10-22 15:38:58.000000000 +0000 +++ bind9-9.18.44/debian/changelog 2026-01-22 07:24:36.000000000 +0000 @@ -1,3 +1,11 @@ +bind9 (1:9.18.44-1~deb12u1) bookworm-security; urgency=high + + * New upstream version 9.18.44 + + [CVE-2025-13878]: Fix incorrect length checks for BRID and HHIT + records. + + -- OndÅ™ej Surý Thu, 22 Jan 2026 08:24:36 +0100 + bind9 (1:9.18.41-1~deb12u1) bookworm-security; urgency=high * New upstream version 9.18.41 diff -Nru bind9-9.18.41/doc/arm/changelog.rst bind9-9.18.44/doc/arm/changelog.rst --- bind9-9.18.41/doc/arm/changelog.rst 2025-10-18 10:21:03.052258742 +0000 +++ bind9-9.18.44/doc/arm/changelog.rst 2026-01-09 13:44:04.731037455 +0000 @@ -18,6 +18,9 @@ development. Regular users should refer to :ref:`Release Notes ` for changes relevant to them. +.. include:: ../changelog/changelog-9.18.44.rst +.. include:: ../changelog/changelog-9.18.43.rst +.. include:: ../changelog/changelog-9.18.42.rst .. include:: ../changelog/changelog-9.18.41.rst .. include:: ../changelog/changelog-9.18.40.rst .. include:: ../changelog/changelog-9.18.39.rst diff -Nru bind9-9.18.41/doc/arm/notes.rst bind9-9.18.44/doc/arm/notes.rst --- bind9-9.18.41/doc/arm/notes.rst 2025-10-18 10:21:03.056258849 +0000 +++ bind9-9.18.44/doc/arm/notes.rst 2026-01-09 13:44:04.736037537 +0000 @@ -45,6 +45,9 @@ found at https://gitlab.isc.org/isc-projects/bind9/-/wikis/Known-Issues-in-BIND-9.18 +.. include:: ../notes/notes-9.18.44.rst +.. include:: ../notes/notes-9.18.43.rst +.. include:: ../notes/notes-9.18.42.rst .. include:: ../notes/notes-9.18.41.rst .. include:: ../notes/notes-9.18.40.rst .. include:: ../notes/notes-9.18.39.rst diff -Nru bind9-9.18.41/doc/arm/platforms.inc.rst bind9-9.18.44/doc/arm/platforms.inc.rst --- bind9-9.18.41/doc/arm/platforms.inc.rst 2025-10-18 10:21:03.056258849 +0000 +++ bind9-9.18.44/doc/arm/platforms.inc.rst 2026-01-09 13:44:04.736037537 +0000 @@ -47,10 +47,10 @@ - Debian 12, 13 - Ubuntu LTS 22.04, 24.04 -- Fedora 42 +- Fedora 43 - Red Hat Enterprise Linux / CentOS / AlmaLinux 8, 9, 10 -- FreeBSD 13.4, 14.2 -- Alpine Linux 3.22 +- FreeBSD 13, 14, 15 +- Alpine Linux 3.23 The amd64 CPU architecture is fully supported and regularly tested. diff -Nru bind9-9.18.41/doc/arm/reference.rst bind9-9.18.44/doc/arm/reference.rst --- bind9-9.18.41/doc/arm/reference.rst 2025-10-18 10:21:03.059258929 +0000 +++ bind9-9.18.44/doc/arm/reference.rst 2026-01-09 13:44:04.738037570 +0000 @@ -5828,7 +5828,7 @@ increasing the packet size to a multiple of the specified block size. Valid block sizes range from 0 (the default, which disables the use of EDNS Padding) to 512 bytes. Larger values are reduced to 512, with a - logged warning. Note: this option is not currently compatible with no + logged warning. Note: this option is not currently compatible with TSIG or SIG(0), as the EDNS OPT record containing the padding would have to be added to the packet after it had already been signed. diff -Nru bind9-9.18.41/doc/arm/troubleshooting.inc.rst bind9-9.18.44/doc/arm/troubleshooting.inc.rst --- bind9-9.18.41/doc/arm/troubleshooting.inc.rst 2025-10-18 10:21:03.060258956 +0000 +++ bind9-9.18.44/doc/arm/troubleshooting.inc.rst 2026-01-09 13:44:04.739037587 +0000 @@ -119,7 +119,7 @@ performance. .. _Wireshark: https://www.wireshark.org/ -.. _solution: https://wiki.wireshark.org/TLS#tls-decryption +.. _solution: https://wiki.wireshark.org/TLS Incrementing and Changing the Serial Number ------------------------------------------- diff -Nru bind9-9.18.41/doc/changelog/changelog-9.18.42.rst bind9-9.18.44/doc/changelog/changelog-9.18.42.rst --- bind9-9.18.41/doc/changelog/changelog-9.18.42.rst 1970-01-01 00:00:00.000000000 +0000 +++ bind9-9.18.44/doc/changelog/changelog-9.18.42.rst 2026-01-09 13:44:04.741037620 +0000 @@ -0,0 +1,26 @@ +.. 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. + +BIND 9.18.42 +------------ + +Bug Fixes +~~~~~~~~~ + +- Skip unsupported algorithms when looking for signing key. + ``2882dbfc803`` + + A mix of supported and unsupported DNSSEC algorithms in the same zone + could have caused validation failures. Ignore the DNSSEC keys with + unsupported algorithm when looking for the signing keys. :gl:`#5622` + :gl:`!11211` + + diff -Nru bind9-9.18.41/doc/changelog/changelog-9.18.43.rst bind9-9.18.44/doc/changelog/changelog-9.18.43.rst --- bind9-9.18.41/doc/changelog/changelog-9.18.43.rst 1970-01-01 00:00:00.000000000 +0000 +++ bind9-9.18.44/doc/changelog/changelog-9.18.43.rst 2026-01-09 13:44:04.741037620 +0000 @@ -0,0 +1,61 @@ +.. 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. + +BIND 9.18.43 +------------ + +New Features +~~~~~~~~~~~~ + +- Add spatch to detect implicit bool/int/result cast. ``cce6c2dd0c`` + + Detection of implicit cast from a boolean into an int, or an + isc_result_t into a boolean (either in an assignement or return + position). + + If such pattern is found, a warning comment is added into the code + (and the CI will fails) so the error can be spotted and manually + fixed. :gl:`!11238` + +Bug Fixes +~~~~~~~~~ + +- AMTRELAY type 0 presentation format handling was wrong. ``e5025baf93`` + + RFC 8777 specifies a placeholder value of "." for the gateway field + when the gateway type is 0 (no gateway). This was not being checked + for nor emitted when displaying the record. This has been corrected. + + Instances of this record will need the placeholder period added to + them when upgrading. :gl:`#5639` :gl:`!11256` + +- Adding NSEC3 opt-out records could leave invalid records in chain. + ``335be0e079`` + + When creating an NSEC3 opt-out chain, a node in the chain could be + removed too soon, causing the previous NSEC3 being unable to be found, + resulting in invalid NSEC3 records to be left in the zone. This has + been fixed. :gl:`#5671` :gl:`!11341` + +- Standardize CHECK and RETERR macros. ``83163f39d5`` + + previously, there were over 40 separate definitions of CHECK macros, + of which most used "goto cleanup", and the rest "goto failure" or + "goto out". there were another 10 definitions of RETERR, of which most + were identical to CHECK, but some simply returned a result code + instead of jumping to a cleanup label. + + this has now been standardized throughout the code base: RETERR is for + returning an error code in the case of an error, and CHECK is for + jumping to a cleanup tag, which is now always called "cleanup". both + macros are defined in isc/util.h. :gl:`!11080` + + diff -Nru bind9-9.18.41/doc/changelog/changelog-9.18.44.rst bind9-9.18.44/doc/changelog/changelog-9.18.44.rst --- bind9-9.18.41/doc/changelog/changelog-9.18.44.rst 1970-01-01 00:00:00.000000000 +0000 +++ bind9-9.18.44/doc/changelog/changelog-9.18.44.rst 2026-01-09 13:44:04.741037620 +0000 @@ -0,0 +1,53 @@ +.. 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. + +BIND 9.18.44 +------------ + +Security Fixes +~~~~~~~~~~~~~~ + +- [CVE-2025-13878] Fix incorrect length checks for BRID and HHIT + records. ``d556bde123`` + + Malformed BRID and HHIT records could trigger an assertion failure. + This has been fixed. + + ISC would like to thank Vlatko Kosturjak from Marlink Cyber for + bringing this vulnerability to our attention. :gl:`#5616` + +Feature Changes +~~~~~~~~~~~~~~~ + +- Support compilation with cmocka 2.0.0+ ``184df12da4`` + + The `assert_in_range()` function was deprecated in favor of + `assert_int_in_range()` and `assert_uint_in_range()`. Add + compatibility shims for cmocka<2.0.0 and use the new functions. + :gl:`#5699` :gl:`!11438` + +Bug Fixes +~~~~~~~~~ + +- Allow glue in delegations with QTYPE=ANY. ``21ad0222b7`` + + When a query for type ANY triggered a delegation response, all + additional data was omitted from the response, including mandatory + glue. This has been corrected. :gl:`#5659` :gl:`!11368` + +- Reconfigure NSEC3 opt-out zone to NSEC causes zone to be invalid. + ``53cfe984e3`` + + A zone that is signed with NSEC3, opt-out enabled, and then + reconfigured to use NSEC, causes the zone to be published with missing + NSEC records. This has been fixed. :gl:`#5679` :gl:`!11402` + + diff -Nru bind9-9.18.41/doc/man/arpaname.1in bind9-9.18.44/doc/man/arpaname.1in --- bind9-9.18.41/doc/man/arpaname.1in 2025-10-18 10:22:29.829518333 +0000 +++ bind9-9.18.44/doc/man/arpaname.1in 2026-01-09 13:46:03.126233616 +0000 @@ -43,6 +43,6 @@ .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT -2025, Internet Systems Consortium +2026, Internet Systems Consortium .\" Generated by docutils manpage writer. . diff -Nru bind9-9.18.41/doc/man/ddns-confgen.8in bind9-9.18.44/doc/man/ddns-confgen.8in --- bind9-9.18.41/doc/man/ddns-confgen.8in 2025-10-18 10:22:29.833518437 +0000 +++ bind9-9.18.44/doc/man/ddns-confgen.8in 2026-01-09 13:46:03.130233693 +0000 @@ -107,6 +107,6 @@ .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT -2025, Internet Systems Consortium +2026, Internet Systems Consortium .\" Generated by docutils manpage writer. . diff -Nru bind9-9.18.41/doc/man/delv.1in bind9-9.18.44/doc/man/delv.1in --- bind9-9.18.41/doc/man/delv.1in 2025-10-18 10:22:29.845518750 +0000 +++ bind9-9.18.44/doc/man/delv.1in 2026-01-09 13:46:03.142233923 +0000 @@ -404,6 +404,6 @@ .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT -2025, Internet Systems Consortium +2026, Internet Systems Consortium .\" Generated by docutils manpage writer. . diff -Nru bind9-9.18.41/doc/man/dig.1in bind9-9.18.44/doc/man/dig.1in --- bind9-9.18.41/doc/man/dig.1in 2025-10-18 10:22:29.873519479 +0000 +++ bind9-9.18.44/doc/man/dig.1in 2026-01-09 13:46:03.171234478 +0000 @@ -925,6 +925,6 @@ .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT -2025, Internet Systems Consortium +2026, Internet Systems Consortium .\" Generated by docutils manpage writer. . diff -Nru bind9-9.18.41/doc/man/dnssec-cds.1in bind9-9.18.44/doc/man/dnssec-cds.1in --- bind9-9.18.41/doc/man/dnssec-cds.1in 2025-10-18 10:22:29.879519636 +0000 +++ bind9-9.18.44/doc/man/dnssec-cds.1in 2026-01-09 13:46:03.178234612 +0000 @@ -247,6 +247,6 @@ .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT -2025, Internet Systems Consortium +2026, Internet Systems Consortium .\" Generated by docutils manpage writer. . diff -Nru bind9-9.18.41/doc/man/dnssec-dsfromkey.1in bind9-9.18.44/doc/man/dnssec-dsfromkey.1in --- bind9-9.18.41/doc/man/dnssec-dsfromkey.1in 2025-10-18 10:22:29.885519792 +0000 +++ bind9-9.18.44/doc/man/dnssec-dsfromkey.1in 2026-01-09 13:46:03.184234727 +0000 @@ -181,6 +181,6 @@ .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT -2025, Internet Systems Consortium +2026, Internet Systems Consortium .\" Generated by docutils manpage writer. . diff -Nru bind9-9.18.41/doc/man/dnssec-importkey.1in bind9-9.18.44/doc/man/dnssec-importkey.1in --- bind9-9.18.41/doc/man/dnssec-importkey.1in 2025-10-18 10:22:29.890519922 +0000 +++ bind9-9.18.44/doc/man/dnssec-importkey.1in 2026-01-09 13:46:03.189234822 +0000 @@ -48,6 +48,11 @@ possible to set publication (\fI\%\-P\fP) and deletion (\fI\%\-D\fP) times for the key, which means the public key can be added to and removed from the DNSKEY RRset on schedule even if the true private key is stored offline. +.sp +When using \fBdnssec\-policy\fP, do not use \fBdnssec\-importkey\fP to +import key files that cannot be used for signing. In this case, simply publish the +imported DNSKEY record in the zone, and make sure that the files are outside +the configured \fBkey\-directory\fP\&. .SH OPTIONS .INDENT 0.0 .TP @@ -147,6 +152,6 @@ .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT -2025, Internet Systems Consortium +2026, Internet Systems Consortium .\" Generated by docutils manpage writer. . diff -Nru bind9-9.18.41/doc/man/dnssec-keyfromlabel.1in bind9-9.18.44/doc/man/dnssec-keyfromlabel.1in --- bind9-9.18.41/doc/man/dnssec-keyfromlabel.1in 2025-10-18 10:22:29.899520156 +0000 +++ bind9-9.18.44/doc/man/dnssec-keyfromlabel.1in 2026-01-09 13:46:03.199235014 +0000 @@ -318,6 +318,6 @@ .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT -2025, Internet Systems Consortium +2026, Internet Systems Consortium .\" Generated by docutils manpage writer. . diff -Nru bind9-9.18.41/doc/man/dnssec-keygen.1in bind9-9.18.44/doc/man/dnssec-keygen.1in --- bind9-9.18.41/doc/man/dnssec-keygen.1in 2025-10-18 10:22:29.909520417 +0000 +++ bind9-9.18.44/doc/man/dnssec-keygen.1in 2026-01-09 13:46:03.209235205 +0000 @@ -385,6 +385,6 @@ .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT -2025, Internet Systems Consortium +2026, Internet Systems Consortium .\" Generated by docutils manpage writer. . diff -Nru bind9-9.18.41/doc/man/dnssec-revoke.1in bind9-9.18.44/doc/man/dnssec-revoke.1in --- bind9-9.18.41/doc/man/dnssec-revoke.1in 2025-10-18 10:22:29.912520495 +0000 +++ bind9-9.18.44/doc/man/dnssec-revoke.1in 2026-01-09 13:46:03.212235263 +0000 @@ -92,6 +92,6 @@ .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT -2025, Internet Systems Consortium +2026, Internet Systems Consortium .\" Generated by docutils manpage writer. . diff -Nru bind9-9.18.41/doc/man/dnssec-settime.1in bind9-9.18.44/doc/man/dnssec-settime.1in --- bind9-9.18.41/doc/man/dnssec-settime.1in 2025-10-18 10:22:29.920520703 +0000 +++ bind9-9.18.44/doc/man/dnssec-settime.1in 2026-01-09 13:46:03.222235454 +0000 @@ -291,6 +291,6 @@ .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT -2025, Internet Systems Consortium +2026, Internet Systems Consortium .\" Generated by docutils manpage writer. . diff -Nru bind9-9.18.41/doc/man/dnssec-signzone.1in bind9-9.18.44/doc/man/dnssec-signzone.1in --- bind9-9.18.41/doc/man/dnssec-signzone.1in 2025-10-18 10:22:29.932521016 +0000 +++ bind9-9.18.44/doc/man/dnssec-signzone.1in 2026-01-09 13:46:03.233235664 +0000 @@ -514,6 +514,6 @@ .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT -2025, Internet Systems Consortium +2026, Internet Systems Consortium .\" Generated by docutils manpage writer. . diff -Nru bind9-9.18.41/doc/man/dnssec-verify.1in bind9-9.18.44/doc/man/dnssec-verify.1in --- bind9-9.18.41/doc/man/dnssec-verify.1in 2025-10-18 10:22:29.936521120 +0000 +++ bind9-9.18.44/doc/man/dnssec-verify.1in 2026-01-09 13:46:03.237235741 +0000 @@ -123,6 +123,6 @@ .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT -2025, Internet Systems Consortium +2026, Internet Systems Consortium .\" Generated by docutils manpage writer. . diff -Nru bind9-9.18.41/doc/man/dnstap-read.1in bind9-9.18.44/doc/man/dnstap-read.1in --- bind9-9.18.41/doc/man/dnstap-read.1in 2025-10-18 10:22:29.938521172 +0000 +++ bind9-9.18.44/doc/man/dnstap-read.1in 2026-01-09 13:46:03.240235799 +0000 @@ -68,6 +68,6 @@ .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT -2025, Internet Systems Consortium +2026, Internet Systems Consortium .\" Generated by docutils manpage writer. . diff -Nru bind9-9.18.41/doc/man/filter-a.8in bind9-9.18.44/doc/man/filter-a.8in --- bind9-9.18.41/doc/man/filter-a.8in 2025-10-18 10:22:29.943521302 +0000 +++ bind9-9.18.44/doc/man/filter-a.8in 2026-01-09 13:46:03.245235894 +0000 @@ -99,6 +99,6 @@ .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT -2025, Internet Systems Consortium +2026, Internet Systems Consortium .\" Generated by docutils manpage writer. . diff -Nru bind9-9.18.41/doc/man/filter-aaaa.8in bind9-9.18.44/doc/man/filter-aaaa.8in --- bind9-9.18.41/doc/man/filter-aaaa.8in 2025-10-18 10:22:29.941521250 +0000 +++ bind9-9.18.44/doc/man/filter-aaaa.8in 2026-01-09 13:46:03.242235837 +0000 @@ -103,6 +103,6 @@ .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT -2025, Internet Systems Consortium +2026, Internet Systems Consortium .\" Generated by docutils manpage writer. . diff -Nru bind9-9.18.41/doc/man/host.1in bind9-9.18.44/doc/man/host.1in --- bind9-9.18.41/doc/man/host.1in 2025-10-18 10:22:29.952521537 +0000 +++ bind9-9.18.44/doc/man/host.1in 2026-01-09 13:46:03.255236086 +0000 @@ -215,6 +215,6 @@ .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT -2025, Internet Systems Consortium +2026, Internet Systems Consortium .\" Generated by docutils manpage writer. . diff -Nru bind9-9.18.41/doc/man/mdig.1in bind9-9.18.44/doc/man/mdig.1in --- bind9-9.18.41/doc/man/mdig.1in 2025-10-18 10:22:29.963521823 +0000 +++ bind9-9.18.44/doc/man/mdig.1in 2026-01-09 13:46:03.266236296 +0000 @@ -431,6 +431,6 @@ .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT -2025, Internet Systems Consortium +2026, Internet Systems Consortium .\" Generated by docutils manpage writer. . diff -Nru bind9-9.18.41/doc/man/named-checkconf.1in bind9-9.18.44/doc/man/named-checkconf.1in --- bind9-9.18.41/doc/man/named-checkconf.1in 2025-10-18 10:22:29.967521927 +0000 +++ bind9-9.18.44/doc/man/named-checkconf.1in 2026-01-09 13:46:03.270236373 +0000 @@ -123,6 +123,6 @@ .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT -2025, Internet Systems Consortium +2026, Internet Systems Consortium .\" Generated by docutils manpage writer. . diff -Nru bind9-9.18.41/doc/man/named-checkzone.1in bind9-9.18.44/doc/man/named-checkzone.1in --- bind9-9.18.41/doc/man/named-checkzone.1in 2025-10-18 10:22:30.048524037 +0000 +++ bind9-9.18.44/doc/man/named-checkzone.1in 2026-01-09 13:46:03.352237942 +0000 @@ -251,6 +251,6 @@ .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT -2025, Internet Systems Consortium +2026, Internet Systems Consortium .\" Generated by docutils manpage writer. . diff -Nru bind9-9.18.41/doc/man/named-compilezone.1in bind9-9.18.44/doc/man/named-compilezone.1in --- bind9-9.18.41/doc/man/named-compilezone.1in 2025-10-18 10:22:30.056524246 +0000 +++ bind9-9.18.44/doc/man/named-compilezone.1in 2026-01-09 13:46:03.361238115 +0000 @@ -253,6 +253,6 @@ .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT -2025, Internet Systems Consortium +2026, Internet Systems Consortium .\" Generated by docutils manpage writer. . diff -Nru bind9-9.18.41/doc/man/named-journalprint.1in bind9-9.18.44/doc/man/named-journalprint.1in --- bind9-9.18.41/doc/man/named-journalprint.1in 2025-10-18 10:22:30.058524298 +0000 +++ bind9-9.18.44/doc/man/named-journalprint.1in 2026-01-09 13:46:03.364238172 +0000 @@ -74,6 +74,6 @@ .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT -2025, Internet Systems Consortium +2026, Internet Systems Consortium .\" Generated by docutils manpage writer. . diff -Nru bind9-9.18.41/doc/man/named-nzd2nzf.1in bind9-9.18.44/doc/man/named-nzd2nzf.1in --- bind9-9.18.41/doc/man/named-nzd2nzf.1in 2025-10-18 10:22:30.060524350 +0000 +++ bind9-9.18.44/doc/man/named-nzd2nzf.1in 2026-01-09 13:46:03.365238191 +0000 @@ -52,6 +52,6 @@ .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT -2025, Internet Systems Consortium +2026, Internet Systems Consortium .\" Generated by docutils manpage writer. . diff -Nru bind9-9.18.41/doc/man/named-rrchecker.1in bind9-9.18.44/doc/man/named-rrchecker.1in --- bind9-9.18.41/doc/man/named-rrchecker.1in 2025-10-18 10:22:30.073524688 +0000 +++ bind9-9.18.44/doc/man/named-rrchecker.1in 2026-01-09 13:46:03.379238459 +0000 @@ -368,6 +368,6 @@ .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT -2025, Internet Systems Consortium +2026, Internet Systems Consortium .\" Generated by docutils manpage writer. . diff -Nru bind9-9.18.41/doc/man/named.8in bind9-9.18.44/doc/man/named.8in --- bind9-9.18.41/doc/man/named.8in 2025-10-18 10:22:30.084524975 +0000 +++ bind9-9.18.44/doc/man/named.8in 2026-01-09 13:46:03.391238689 +0000 @@ -303,6 +303,6 @@ .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT -2025, Internet Systems Consortium +2026, Internet Systems Consortium .\" Generated by docutils manpage writer. . diff -Nru bind9-9.18.41/doc/man/named.conf.5in bind9-9.18.44/doc/man/named.conf.5in --- bind9-9.18.41/doc/man/named.conf.5in 2025-10-18 10:22:30.076524767 +0000 +++ bind9-9.18.44/doc/man/named.conf.5in 2026-01-09 13:46:03.382238517 +0000 @@ -1006,6 +1006,6 @@ .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT -2025, Internet Systems Consortium +2026, Internet Systems Consortium .\" Generated by docutils manpage writer. . diff -Nru bind9-9.18.41/doc/man/nsec3hash.1in bind9-9.18.44/doc/man/nsec3hash.1in --- bind9-9.18.41/doc/man/nsec3hash.1in 2025-10-18 10:22:30.086525027 +0000 +++ bind9-9.18.44/doc/man/nsec3hash.1in 2026-01-09 13:46:03.393238727 +0000 @@ -81,6 +81,6 @@ .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT -2025, Internet Systems Consortium +2026, Internet Systems Consortium .\" Generated by docutils manpage writer. . diff -Nru bind9-9.18.41/doc/man/nslookup.1in bind9-9.18.44/doc/man/nslookup.1in --- bind9-9.18.41/doc/man/nslookup.1in 2025-10-18 10:22:30.092525183 +0000 +++ bind9-9.18.44/doc/man/nslookup.1in 2026-01-09 13:46:03.399238842 +0000 @@ -218,6 +218,6 @@ .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT -2025, Internet Systems Consortium +2026, Internet Systems Consortium .\" Generated by docutils manpage writer. . diff -Nru bind9-9.18.41/doc/man/nsupdate.1in bind9-9.18.44/doc/man/nsupdate.1in --- bind9-9.18.41/doc/man/nsupdate.1in 2025-10-18 10:22:30.106525548 +0000 +++ bind9-9.18.44/doc/man/nsupdate.1in 2026-01-09 13:46:03.414239129 +0000 @@ -428,6 +428,6 @@ .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT -2025, Internet Systems Consortium +2026, Internet Systems Consortium .\" Generated by docutils manpage writer. . diff -Nru bind9-9.18.41/doc/man/rndc-confgen.8in bind9-9.18.44/doc/man/rndc-confgen.8in --- bind9-9.18.41/doc/man/rndc-confgen.8in 2025-10-18 10:22:30.112525704 +0000 +++ bind9-9.18.44/doc/man/rndc-confgen.8in 2026-01-09 13:46:03.420239244 +0000 @@ -136,6 +136,6 @@ .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT -2025, Internet Systems Consortium +2026, Internet Systems Consortium .\" Generated by docutils manpage writer. . diff -Nru bind9-9.18.41/doc/man/rndc.8in bind9-9.18.44/doc/man/rndc.8in --- bind9-9.18.41/doc/man/rndc.8in 2025-10-18 10:22:30.135526303 +0000 +++ bind9-9.18.44/doc/man/rndc.8in 2026-01-09 13:46:03.445239723 +0000 @@ -138,10 +138,10 @@ .INDENT 0.0 .TP .B addzone zone [class [view]] configuration -This command adds a zone while the server is running. This command requires the -\fBallow\-new\-zones\fP option to be set to \fByes\fP\&. The configuration -string specified on the command line is the zone configuration text -that would ordinarily be placed in \fI\%named.conf\fP\&. +This command adds a zone while the server is running. This command +requires the \fBallow\-new\-zones\fP option to be set to \fByes\fP\&. The +configuration string specified on the command line is the zone +configuration text that would ordinarily be placed in \fI\%named.conf\fP\&. .sp The configuration is saved in a file called \fBviewname.nzf\fP (or, if \fI\%named\fP is compiled with liblmdb, an LMDB database file called @@ -328,10 +328,10 @@ .INDENT 0.0 .TP .B modzone zone [class [view]] configuration -This command modifies the configuration of a zone while the server is running. This -command requires the \fBallow\-new\-zones\fP option to be set to \fByes\fP\&. -As with \fBaddzone\fP, the configuration string specified on the -command line is the zone configuration text that would ordinarily be +This command modifies the configuration of a zone while the server is +running. This command requires the \fBallow\-new\-zones\fP option to be set +to \fByes\fP\&. As with \fBaddzone\fP, the configuration string specified on +the command line is the zone configuration text that would ordinarily be placed in \fI\%named.conf\fP\&. .sp If the zone was originally added via \fI\%rndc addzone\fP, the @@ -522,9 +522,11 @@ .INDENT 0.0 .TP .B showzone zone [class [view]] -This command prints the configuration of a running zone. +If the server is configured with \fBallow\-new\-zones\fP set to \fByes\fP, +then this command prints the configuration of a running zone. .sp -See also \fI\%rndc zonestatus\fP\&. +See also \fI\%rndc addzone\fP, \fI\%rndc modzone\fP\&. +and \fI\%rndc delzone\fP\&. .UNINDENT .INDENT 0.0 .TP @@ -724,6 +726,6 @@ .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT -2025, Internet Systems Consortium +2026, Internet Systems Consortium .\" Generated by docutils manpage writer. . diff -Nru bind9-9.18.41/doc/man/rndc.conf.5in bind9-9.18.44/doc/man/rndc.conf.5in --- bind9-9.18.41/doc/man/rndc.conf.5in 2025-10-18 10:22:30.116525809 +0000 +++ bind9-9.18.44/doc/man/rndc.conf.5in 2026-01-09 13:46:03.424239321 +0000 @@ -181,6 +181,6 @@ .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT -2025, Internet Systems Consortium +2026, Internet Systems Consortium .\" Generated by docutils manpage writer. . diff -Nru bind9-9.18.41/doc/man/tsig-keygen.8in bind9-9.18.44/doc/man/tsig-keygen.8in --- bind9-9.18.41/doc/man/tsig-keygen.8in 2025-10-18 10:22:30.137526356 +0000 +++ bind9-9.18.44/doc/man/tsig-keygen.8in 2026-01-09 13:46:03.447239761 +0000 @@ -62,6 +62,6 @@ .SH AUTHOR Internet Systems Consortium .SH COPYRIGHT -2025, Internet Systems Consortium +2026, Internet Systems Consortium .\" Generated by docutils manpage writer. . diff -Nru bind9-9.18.41/doc/notes/notes-9.18.42.rst bind9-9.18.44/doc/notes/notes-9.18.42.rst --- bind9-9.18.41/doc/notes/notes-9.18.42.rst 1970-01-01 00:00:00.000000000 +0000 +++ bind9-9.18.44/doc/notes/notes-9.18.42.rst 2026-01-09 13:44:04.767038049 +0000 @@ -0,0 +1,24 @@ +.. 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. + +Notes for BIND 9.18.42 +---------------------- + +Bug Fixes +~~~~~~~~~ + +- Skip unsupported algorithms when looking for a signing key. + + A mix of supported and unsupported DNSSEC algorithms in the same zone + could cause validation failures. Unsupported algorithms are now + ignored when looking for signing keys. :gl:`#5622` + + diff -Nru bind9-9.18.41/doc/notes/notes-9.18.43.rst bind9-9.18.44/doc/notes/notes-9.18.43.rst --- bind9-9.18.41/doc/notes/notes-9.18.43.rst 1970-01-01 00:00:00.000000000 +0000 +++ bind9-9.18.44/doc/notes/notes-9.18.43.rst 2026-01-09 13:44:04.767038049 +0000 @@ -0,0 +1,33 @@ +.. 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. + +Notes for BIND 9.18.43 +---------------------- + +Bug Fixes +~~~~~~~~~ + +- Adding NSEC3 opt-out records could leave invalid records in chain. + + When creating an NSEC3 opt-out chain, a node in the chain could be + removed too soon. The previous NSEC3 would therefore not be found, + resulting in invalid NSEC3 records being left in the zone. This has + been fixed. :gl:`#5671` + +- ``AMTRELAY`` type 0 presentation format handling was wrong. + + :rfc:`8777` specifies a placeholder value of ``.`` for the gateway field + when the gateway type is 0 (no gateway). This was not being checked + for, nor was it emitted when displaying the record. This has been corrected. + + Instances of this record will need the placeholder period added to + them when upgrading. :gl:`#5639` + diff -Nru bind9-9.18.41/doc/notes/notes-9.18.44.rst bind9-9.18.44/doc/notes/notes-9.18.44.rst --- bind9-9.18.41/doc/notes/notes-9.18.44.rst 1970-01-01 00:00:00.000000000 +0000 +++ bind9-9.18.44/doc/notes/notes-9.18.44.rst 2026-01-09 13:44:04.767038049 +0000 @@ -0,0 +1,43 @@ +.. 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. + +Notes for BIND 9.18.44 +---------------------- + +Security Fixes +~~~~~~~~~~~~~~ + +- Fix incorrect length checks for BRID and HHIT records. + :cve:`2025-13878` + + Malformed BRID and HHIT records could trigger an assertion + failure. This has been fixed. + + ISC would like to thank Vlatko Kosturjak from Marlink Cyber for + bringing this vulnerability to our attention. :gl:`#5616` + +Bug Fixes +~~~~~~~~~ + +- Allow glue in delegations with QTYPE=ANY. + + When a query for type ANY triggered a delegation response, all + additional data was omitted from the response, including mandatory + glue. This has been fixed. :gl:`#5659` + +- Reconfiguring an NSEC3 opt-out zone to NSEC caused the zone to be + invalid. + + A zone that was signed with NSEC3, had opt-out enabled, and was then + reconfigured to use NSEC, was published with missing NSEC records. + This has been fixed. :gl:`#5679` + + diff -Nru bind9-9.18.41/fuzz/dns_rdata_fromwire_text.c bind9-9.18.44/fuzz/dns_rdata_fromwire_text.c --- bind9-9.18.41/fuzz/dns_rdata_fromwire_text.c 2025-10-18 10:21:03.116260453 +0000 +++ bind9-9.18.44/fuzz/dns_rdata_fromwire_text.c 2026-01-09 13:44:04.796038527 +0000 @@ -47,7 +47,7 @@ isc_lexspecials_t specials; isc_mem_create(&mctx); - CHECK(isc_lex_create(mctx, 64, &lex)); + RETERR(isc_lex_create(mctx, 64, &lex)); memset(specials, 0, sizeof(specials)); specials[0] = 1; @@ -215,5 +215,6 @@ assert(target.used == size); assert(!memcmp(target.base, data, size)); +cleanup: return 0; } diff -Nru bind9-9.18.41/fuzz/fuzz.h bind9-9.18.44/fuzz/fuzz.h --- bind9-9.18.41/fuzz/fuzz.h 2025-10-18 10:21:03.119260533 +0000 +++ bind9-9.18.44/fuzz/fuzz.h 2026-01-09 13:44:04.799038577 +0000 @@ -37,9 +37,4 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); -#define CHECK(x) \ - if ((x) != ISC_R_SUCCESS) { \ - return 0; \ - } - ISC_LANG_ENDDECLS diff -Nru bind9-9.18.41/fuzz/isc_lex_getmastertoken.c bind9-9.18.44/fuzz/isc_lex_getmastertoken.c --- bind9-9.18.41/fuzz/isc_lex_getmastertoken.c 2025-10-18 10:21:03.119260533 +0000 +++ bind9-9.18.44/fuzz/isc_lex_getmastertoken.c 2026-01-09 13:44:04.799038577 +0000 @@ -77,5 +77,6 @@ result = isc_lex_getmastertoken(lex, &token, expect, eol); } while (result == ISC_R_SUCCESS && token.type != isc_tokentype_eof); +cleanup: return 0; } diff -Nru bind9-9.18.41/fuzz/isc_lex_gettoken.c bind9-9.18.44/fuzz/isc_lex_gettoken.c --- bind9-9.18.41/fuzz/isc_lex_gettoken.c 2025-10-18 10:21:03.119260533 +0000 +++ bind9-9.18.44/fuzz/isc_lex_gettoken.c 2026-01-09 13:44:04.799038577 +0000 @@ -55,5 +55,6 @@ result = isc_lex_gettoken(lex, 0, &token); } while (result == ISC_R_SUCCESS); +cleanup: return 0; } diff -Nru bind9-9.18.41/lib/dns/client.c bind9-9.18.44/lib/dns/client.c --- bind9-9.18.41/lib/dns/client.c 2025-10-18 10:21:03.123260640 +0000 +++ bind9-9.18.44/lib/dns/client.c 2026-01-09 13:44:04.803038643 +0000 @@ -60,13 +60,6 @@ #define UCTX_MAGIC ISC_MAGIC('U', 'c', 't', 'x') #define UCTX_VALID(c) ISC_MAGIC_VALID(c, UCTX_MAGIC) -#define CHECK(r) \ - do { \ - result = (r); \ - if (result != ISC_R_SUCCESS) \ - goto cleanup; \ - } while (0) - /*% * DNS client object */ diff -Nru bind9-9.18.41/lib/dns/diff.c bind9-9.18.44/lib/dns/diff.c --- bind9-9.18.41/lib/dns/diff.c 2025-10-18 10:21:03.123260640 +0000 +++ bind9-9.18.44/lib/dns/diff.c 2026-01-09 13:44:04.804038659 +0000 @@ -35,13 +35,6 @@ #include #include -#define CHECK(op) \ - do { \ - result = (op); \ - if (result != ISC_R_SUCCESS) \ - goto failure; \ - } while (0) - #define DIFF_COMMON_LOGARGS \ dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_DIFF @@ -486,7 +479,7 @@ } return ISC_R_SUCCESS; -failure: +cleanup: if (node != NULL) { dns_db_detachnode(db, &node); } @@ -569,7 +562,7 @@ } } result = ISC_R_SUCCESS; -failure: +cleanup: return result; } diff -Nru bind9-9.18.41/lib/dns/dnssec.c bind9-9.18.44/lib/dns/dnssec.c --- bind9-9.18.41/lib/dns/dnssec.c 2025-10-18 10:21:03.124260666 +0000 +++ bind9-9.18.44/lib/dns/dnssec.c 2026-01-09 13:44:04.805038676 +0000 @@ -46,13 +46,6 @@ #define is_response(msg) ((msg->flags & DNS_MESSAGEFLAG_QR) != 0) -#define RETERR(x) \ - do { \ - result = (x); \ - if (result != ISC_R_SUCCESS) \ - goto failure; \ - } while (0) - #define TYPE_SIGN 0 #define TYPE_VERIFY 1 @@ -752,13 +745,13 @@ *nkeys = 0; memset(keys, 0, sizeof(*keys) * maxkeys); dns_rdataset_init(&rdataset); - RETERR(dns_db_findrdataset(db, node, ver, dns_rdatatype_dnskey, 0, 0, - &rdataset, NULL)); - RETERR(dns_rdataset_first(&rdataset)); + CHECK(dns_db_findrdataset(db, node, ver, dns_rdatatype_dnskey, 0, 0, + &rdataset, NULL)); + CHECK(dns_rdataset_first(&rdataset)); while (result == ISC_R_SUCCESS && count < maxkeys) { pubkey = NULL; dns_rdataset_current(&rdataset, &rdata); - RETERR(dns_dnssec_keyfromrdata(name, &rdata, mctx, &pubkey)); + CHECK(dns_dnssec_keyfromrdata(name, &rdata, mctx, &pubkey)); dst_key_setttl(pubkey, rdataset.ttl); if (!is_zone_key(pubkey) || @@ -845,9 +838,7 @@ goto next; } - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(result); /* * If a key is marked inactive, skip it @@ -881,7 +872,7 @@ result = dns_rdataset_next(&rdataset); } if (result != ISC_R_NOMORE) { - goto failure; + goto cleanup; } if (count == 0) { result = ISC_R_NOTFOUND; @@ -889,7 +880,7 @@ result = ISC_R_SUCCESS; } -failure: +cleanup: if (dns_rdataset_isassociated(&rdataset)) { dns_rdataset_disassociate(&rdataset); } @@ -961,25 +952,25 @@ isc_buffer_init(&databuf, data, sizeof(data)); - RETERR(dst_context_create(key, mctx, DNS_LOGCATEGORY_DNSSEC, true, 0, - &ctx)); + CHECK(dst_context_create(key, mctx, DNS_LOGCATEGORY_DNSSEC, true, 0, + &ctx)); /* * Digest the fields of the SIG - we can cheat and use * dns_rdata_fromstruct. Since siglen is 0, the digested data * is identical to dns format. */ - RETERR(dns_rdata_fromstruct(NULL, dns_rdataclass_any, - dns_rdatatype_sig /* SIG(0) */, &sig, - &databuf)); + CHECK(dns_rdata_fromstruct(NULL, dns_rdataclass_any, + dns_rdatatype_sig /* SIG(0) */, &sig, + &databuf)); isc_buffer_usedregion(&databuf, &r); - RETERR(dst_context_adddata(ctx, &r)); + CHECK(dst_context_adddata(ctx, &r)); /* * If this is a response, digest the query. */ if (is_response(msg)) { - RETERR(dst_context_adddata(ctx, &msg->query)); + CHECK(dst_context_adddata(ctx, &msg->query)); } /* @@ -988,48 +979,48 @@ isc_buffer_init(&headerbuf, header, sizeof(header)); dns_message_renderheader(msg, &headerbuf); isc_buffer_usedregion(&headerbuf, &r); - RETERR(dst_context_adddata(ctx, &r)); + CHECK(dst_context_adddata(ctx, &r)); /* * Digest the remainder of the message. */ isc_buffer_usedregion(msg->buffer, &r); isc_region_consume(&r, DNS_MESSAGE_HEADERLEN); - RETERR(dst_context_adddata(ctx, &r)); + CHECK(dst_context_adddata(ctx, &r)); - RETERR(dst_key_sigsize(key, &sigsize)); + CHECK(dst_key_sigsize(key, &sigsize)); sig.siglen = sigsize; sig.signature = isc_mem_get(mctx, sig.siglen); isc_buffer_init(&sigbuf, sig.signature, sig.siglen); - RETERR(dst_context_sign(ctx, &sigbuf)); + CHECK(dst_context_sign(ctx, &sigbuf)); dst_context_destroy(&ctx); rdata = NULL; - RETERR(dns_message_gettemprdata(msg, &rdata)); + CHECK(dns_message_gettemprdata(msg, &rdata)); isc_buffer_allocate(msg->mctx, &dynbuf, 1024); - RETERR(dns_rdata_fromstruct(rdata, dns_rdataclass_any, - dns_rdatatype_sig /* SIG(0) */, &sig, - dynbuf)); + CHECK(dns_rdata_fromstruct(rdata, dns_rdataclass_any, + dns_rdatatype_sig /* SIG(0) */, &sig, + dynbuf)); isc_mem_put(mctx, sig.signature, sig.siglen); dns_message_takebuffer(msg, &dynbuf); datalist = NULL; - RETERR(dns_message_gettemprdatalist(msg, &datalist)); + CHECK(dns_message_gettemprdatalist(msg, &datalist)); datalist->rdclass = dns_rdataclass_any; datalist->type = dns_rdatatype_sig; /* SIG(0) */ ISC_LIST_APPEND(datalist->rdata, rdata, link); dataset = NULL; - RETERR(dns_message_gettemprdataset(msg, &dataset)); + CHECK(dns_message_gettemprdataset(msg, &dataset)); RUNTIME_CHECK(dns_rdatalist_tordataset(datalist, dataset) == ISC_R_SUCCESS); msg->sig0 = dataset; return ISC_R_SUCCESS; -failure: +cleanup: if (dynbuf != NULL) { isc_buffer_free(&dynbuf); } @@ -1075,21 +1066,19 @@ isc_buffer_usedregion(source, &source_r); - RETERR(dns_rdataset_first(msg->sig0)); + CHECK(dns_rdataset_first(msg->sig0)); dns_rdataset_current(msg->sig0, &rdata); - RETERR(dns_rdata_tostruct(&rdata, &sig, NULL)); + CHECK(dns_rdata_tostruct(&rdata, &sig, NULL)); signeedsfree = true; if (sig.labels != 0) { - result = DNS_R_SIGINVALID; - goto failure; + CHECK(DNS_R_SIGINVALID); } if (isc_serial_lt(sig.timeexpire, sig.timesigned)) { - result = DNS_R_SIGINVALID; msg->sig0status = dns_tsigerror_badtime; - goto failure; + CHECK(DNS_R_SIGINVALID); } if (msg->fuzzing) { @@ -1099,36 +1088,33 @@ } if (isc_serial_lt((uint32_t)now, sig.timesigned)) { - result = DNS_R_SIGFUTURE; msg->sig0status = dns_tsigerror_badtime; - goto failure; + CHECK(DNS_R_SIGFUTURE); } else if (isc_serial_lt(sig.timeexpire, (uint32_t)now)) { - result = DNS_R_SIGEXPIRED; msg->sig0status = dns_tsigerror_badtime; - goto failure; + CHECK(DNS_R_SIGEXPIRED); } if (!dns_name_equal(dst_key_name(key), &sig.signer)) { - result = DNS_R_SIGINVALID; msg->sig0status = dns_tsigerror_badkey; - goto failure; + CHECK(DNS_R_SIGINVALID); } - RETERR(dst_context_create(key, mctx, DNS_LOGCATEGORY_DNSSEC, false, 0, - &ctx)); + CHECK(dst_context_create(key, mctx, DNS_LOGCATEGORY_DNSSEC, false, 0, + &ctx)); /* * Digest the SIG(0) record, except for the signature. */ dns_rdata_toregion(&rdata, &r); r.length -= sig.siglen; - RETERR(dst_context_adddata(ctx, &r)); + CHECK(dst_context_adddata(ctx, &r)); /* * If this is a response, digest the query. */ if (is_response(msg)) { - RETERR(dst_context_adddata(ctx, &msg->query)); + CHECK(dst_context_adddata(ctx, &msg->query)); } /* @@ -1149,21 +1135,21 @@ */ header_r.base = (unsigned char *)header; header_r.length = DNS_MESSAGE_HEADERLEN; - RETERR(dst_context_adddata(ctx, &header_r)); + CHECK(dst_context_adddata(ctx, &header_r)); /* * Digest all non-SIG(0) records. */ r.base = source_r.base + DNS_MESSAGE_HEADERLEN; r.length = msg->sigstart - DNS_MESSAGE_HEADERLEN; - RETERR(dst_context_adddata(ctx, &r)); + CHECK(dst_context_adddata(ctx, &r)); sig_r.base = sig.signature; sig_r.length = sig.siglen; result = dst_context_verify(ctx, &sig_r); if (result != ISC_R_SUCCESS) { msg->sig0status = dns_tsigerror_badsig; - goto failure; + goto cleanup; } msg->verified_sig = 1; @@ -1174,7 +1160,7 @@ return ISC_R_SUCCESS; -failure: +cleanup: if (signeedsfree) { dns_rdata_freestruct(&sig); } @@ -1395,14 +1381,14 @@ isc_dir_init(&dir); isc_buffer_init(&b, namebuf, sizeof(namebuf) - 1); - RETERR(dns_name_tofilenametext(origin, false, &b)); + CHECK(dns_name_tofilenametext(origin, false, &b)); len = isc_buffer_usedlength(&b); namebuf[len] = '\0'; if (directory == NULL) { directory = "."; } - RETERR(isc_dir_open(&dir, directory)); + CHECK(isc_dir_open(&dir, directory)); dir_open = true; while (isc_dir_read(&dir) == ISC_R_SUCCESS) { @@ -1479,7 +1465,7 @@ continue; } - RETERR(dns_dnsseckey_create(mctx, &dstkey, &key)); + CHECK(dns_dnsseckey_create(mctx, &dstkey, &key)); key->source = dns_keysource_repository; dns_dnssec_get_hints(key, now); @@ -1498,7 +1484,7 @@ result = ISC_R_NOTFOUND; } -failure: +cleanup: if (dir_open) { isc_dir_close(&dir); } @@ -1658,7 +1644,7 @@ goto skip; } - RETERR(dns_dnssec_keyfromrdata(origin, &rdata, mctx, &dnskey)); + CHECK(dns_dnssec_keyfromrdata(origin, &rdata, mctx, &dnskey)); dst_key_setttl(dnskey, keys.ttl); if (!is_zone_key(dnskey)) { @@ -1671,7 +1657,7 @@ } if (publickey) { - RETERR(addkey(keylist, &dnskey, savekeys, mctx)); + CHECK(addkey(keylist, &dnskey, savekeys, mctx)); goto skip; } @@ -1683,7 +1669,7 @@ if (result == ISC_R_FILENOTFOUND || result == ISC_R_NOPERM) { result = ISC_R_SUCCESS; } - RETERR(result); + CHECK(result); /* Now read the private key. */ result = dst_key_fromfile( @@ -1754,15 +1740,13 @@ if (result == ISC_R_FILENOTFOUND || result == ISC_R_NOPERM) { if (pubkey != NULL) { - RETERR(addkey(keylist, &pubkey, savekeys, - mctx)); + CHECK(addkey(keylist, &pubkey, savekeys, mctx)); } else { - RETERR(addkey(keylist, &dnskey, savekeys, - mctx)); + CHECK(addkey(keylist, &dnskey, savekeys, mctx)); } goto skip; } - RETERR(result); + CHECK(result); /* * Whatever the key's default TTL may have @@ -1770,7 +1754,7 @@ */ dst_key_setttl(privkey, dst_key_getttl(dnskey)); - RETERR(addkey(keylist, &privkey, savekeys, mctx)); + CHECK(addkey(keylist, &privkey, savekeys, mctx)); skip: if (dnskey != NULL) { dst_key_free(&dnskey); @@ -1784,20 +1768,20 @@ } if (result != ISC_R_NOMORE) { - RETERR(result); + CHECK(result); } if (keysigs != NULL && dns_rdataset_isassociated(keysigs)) { - RETERR(mark_active_keys(keylist, keysigs)); + CHECK(mark_active_keys(keylist, keysigs)); } if (soasigs != NULL && dns_rdataset_isassociated(soasigs)) { - RETERR(mark_active_keys(keylist, soasigs)); + CHECK(mark_active_keys(keylist, soasigs)); } result = ISC_R_SUCCESS; -failure: +cleanup: if (dns_rdataset_isassociated(&keys)) { dns_rdataset_disassociate(&keys); } @@ -1836,29 +1820,25 @@ static isc_result_t addrdata(dns_rdata_t *rdata, dns_diff_t *diff, const dns_name_t *origin, dns_ttl_t ttl, isc_mem_t *mctx) { - isc_result_t result; dns_difftuple_t *tuple = NULL; RETERR(dns_difftuple_create(mctx, DNS_DIFFOP_ADD, origin, ttl, rdata, &tuple)); dns_diff_appendminimal(diff, &tuple); -failure: - return result; + return ISC_R_SUCCESS; } static isc_result_t delrdata(dns_rdata_t *rdata, dns_diff_t *diff, const dns_name_t *origin, dns_ttl_t ttl, isc_mem_t *mctx) { - isc_result_t result; dns_difftuple_t *tuple = NULL; RETERR(dns_difftuple_create(mctx, DNS_DIFFOP_DEL, origin, ttl, rdata, &tuple)); dns_diff_appendminimal(diff, &tuple); -failure: - return result; + return ISC_R_SUCCESS; } static isc_result_t @@ -1871,7 +1851,7 @@ dns_rdata_t dnskey = DNS_RDATA_INIT; dns_rdata_reset(&dnskey); - RETERR(dns_dnssec_make_dnskey(key->key, buf, sizeof(buf), &dnskey)); + CHECK(dns_dnssec_make_dnskey(key->key, buf, sizeof(buf), &dnskey)); dst_key_format(key->key, keystr, sizeof(keystr)); report("Fetching %s (%s) from key %s.", keystr, @@ -1892,7 +1872,7 @@ /* publish key */ result = addrdata(&dnskey, diff, origin, ttl, mctx); -failure: +cleanup: return result; } @@ -1911,10 +1891,10 @@ report("Removing %s key %s/%d/%s from DNSKEY RRset.", reason, namebuf, dst_key_id(key->key), alg); - RETERR(dns_dnssec_make_dnskey(key->key, buf, sizeof(buf), &dnskey)); + CHECK(dns_dnssec_make_dnskey(key->key, buf, sizeof(buf), &dnskey)); result = delrdata(&dnskey, diff, origin, ttl, mctx); -failure: +cleanup: return result; } @@ -1972,8 +1952,8 @@ dns_rdata_t cdnskeyrdata = DNS_RDATA_INIT; dns_name_t *origin = dst_key_name(key->key); - RETERR(dns_dnssec_make_dnskey(key->key, keybuf, sizeof(keybuf), - &cdnskeyrdata)); + CHECK(dns_dnssec_make_dnskey(key->key, keybuf, sizeof(keybuf), + &cdnskeyrdata)); /* * We construct the SHA-1 version of the record so we can @@ -1983,11 +1963,11 @@ * XXXMPA we need to be able to specify the DS algorithms * to be used here and below with rmkeys. */ - RETERR(dns_ds_buildrdata(origin, &cdnskeyrdata, - DNS_DSDIGEST_SHA1, dsbuf1, &cds_sha1)); - RETERR(dns_ds_buildrdata(origin, &cdnskeyrdata, - DNS_DSDIGEST_SHA256, dsbuf2, - &cds_sha256)); + CHECK(dns_ds_buildrdata(origin, &cdnskeyrdata, + DNS_DSDIGEST_SHA1, dsbuf1, &cds_sha1)); + CHECK(dns_ds_buildrdata(origin, &cdnskeyrdata, + DNS_DSDIGEST_SHA256, dsbuf2, + &cds_sha256)); /* * Now that the we have created the DS records convert @@ -2009,8 +1989,8 @@ DNS_LOGMODULE_DNSSEC, ISC_LOG_INFO, "CDNSKEY for key %s is now published", keystr); - RETERR(addrdata(&cdnskeyrdata, diff, origin, - cdnskeyttl, mctx)); + CHECK(addrdata(&cdnskeyrdata, diff, origin, + cdnskeyttl, mctx)); } /* Only publish SHA-256 (SHA-1 is deprecated) */ if (!dns_rdataset_isassociated(cds) || @@ -2021,8 +2001,8 @@ ISC_LOG_INFO, "CDS for key %s is now published", keystr); - RETERR(addrdata(&cds_sha256, diff, origin, - cdsttl, mctx)); + CHECK(addrdata(&cds_sha256, diff, origin, + cdsttl, mctx)); } } @@ -2040,8 +2020,8 @@ "CDS (SHA-1) for key %s " "is now deleted", keystr); - RETERR(delrdata(&cds_sha1, diff, origin, - cds->ttl, mctx)); + CHECK(delrdata(&cds_sha1, diff, origin, + cds->ttl, mctx)); } if (exists(cds, &cds_sha256)) { isc_log_write(dns_lctx, @@ -2051,9 +2031,8 @@ "CDS (SHA-256) for key " "%s is now deleted", keystr); - RETERR(delrdata(&cds_sha256, diff, - origin, cds->ttl, - mctx)); + CHECK(delrdata(&cds_sha256, diff, + origin, cds->ttl, mctx)); } } @@ -2066,9 +2045,9 @@ "CDNSKEY for key %s is " "now deleted", keystr); - RETERR(delrdata(&cdnskeyrdata, diff, - origin, cdnskey->ttl, - mctx)); + CHECK(delrdata(&cdnskeyrdata, diff, + origin, cdnskey->ttl, + mctx)); } } } @@ -2094,24 +2073,24 @@ char keystr[DST_KEY_FORMATSIZE]; dst_key_format(key->key, keystr, sizeof(keystr)); - RETERR(dns_dnssec_make_dnskey(key->key, keybuf, sizeof(keybuf), - &cdnskeyrdata)); + CHECK(dns_dnssec_make_dnskey(key->key, keybuf, sizeof(keybuf), + &cdnskeyrdata)); if (dns_rdataset_isassociated(cds)) { - RETERR(dns_ds_buildrdata(origin, &cdnskeyrdata, - DNS_DSDIGEST_SHA1, dsbuf1, - &cds_sha1)); - RETERR(dns_ds_buildrdata(origin, &cdnskeyrdata, - DNS_DSDIGEST_SHA256, dsbuf2, - &cds_sha256)); + CHECK(dns_ds_buildrdata(origin, &cdnskeyrdata, + DNS_DSDIGEST_SHA1, dsbuf1, + &cds_sha1)); + CHECK(dns_ds_buildrdata(origin, &cdnskeyrdata, + DNS_DSDIGEST_SHA256, dsbuf2, + &cds_sha256)); if (exists(cds, &cds_sha1)) { isc_log_write( dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_DNSSEC, ISC_LOG_INFO, "CDS (SHA-1) for key %s is now deleted", keystr); - RETERR(delrdata(&cds_sha1, diff, origin, - cds->ttl, mctx)); + CHECK(delrdata(&cds_sha1, diff, origin, + cds->ttl, mctx)); } if (exists(cds, &cds_sha256)) { isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, @@ -2120,8 +2099,8 @@ "CDS (SHA-256) for key %s is now " "deleted", keystr); - RETERR(delrdata(&cds_sha256, diff, origin, - cds->ttl, mctx)); + CHECK(delrdata(&cds_sha256, diff, origin, + cds->ttl, mctx)); } } @@ -2132,15 +2111,15 @@ DNS_LOGMODULE_DNSSEC, ISC_LOG_INFO, "CDNSKEY for key %s is now deleted", keystr); - RETERR(delrdata(&cdnskeyrdata, diff, origin, - cdnskey->ttl, mctx)); + CHECK(delrdata(&cdnskeyrdata, diff, origin, + cdnskey->ttl, mctx)); } } } result = ISC_R_SUCCESS; -failure: +cleanup: return result; } @@ -2149,13 +2128,13 @@ dns_name_t *origin, dns_rdataclass_t zclass, dns_ttl_t ttl, dns_diff_t *diff, isc_mem_t *mctx, bool expect_cds_delete, bool expect_cdnskey_delete) { + isc_result_t result; unsigned char dsbuf[5] = { 0, 0, 0, 0, 0 }; /* CDS DELETE rdata */ unsigned char keybuf[5] = { 0, 0, 3, 0, 0 }; /* CDNSKEY DELETE rdata */ char namebuf[DNS_NAME_FORMATSIZE]; dns_rdata_t cds_delete = DNS_RDATA_INIT; dns_rdata_t cdnskey_delete = DNS_RDATA_INIT; isc_region_t r; - isc_result_t result; r.base = keybuf; r.length = sizeof(keybuf); @@ -2177,7 +2156,7 @@ "CDS (DELETE) for zone %s is now " "published", namebuf); - RETERR(addrdata(&cds_delete, diff, origin, ttl, mctx)); + CHECK(addrdata(&cds_delete, diff, origin, ttl, mctx)); } } else { if (dns_rdataset_isassociated(cds) && exists(cds, &cds_delete)) @@ -2187,8 +2166,8 @@ "CDS (DELETE) for zone %s is now " "deleted", namebuf); - RETERR(delrdata(&cds_delete, diff, origin, cds->ttl, - mctx)); + CHECK(delrdata(&cds_delete, diff, origin, cds->ttl, + mctx)); } } @@ -2201,8 +2180,8 @@ "CDNSKEY (DELETE) for zone %s is now " "published", namebuf); - RETERR(addrdata(&cdnskey_delete, diff, origin, ttl, - mctx)); + CHECK(addrdata(&cdnskey_delete, diff, origin, ttl, + mctx)); } } else { if (dns_rdataset_isassociated(cdnskey) && @@ -2213,15 +2192,13 @@ "CDNSKEY (DELETE) for zone %s is now " "deleted", namebuf); - RETERR(delrdata(&cdnskey_delete, diff, origin, - cdnskey->ttl, mctx)); + CHECK(delrdata(&cdnskey_delete, diff, origin, + cdnskey->ttl, mctx)); } } - result = ISC_R_SUCCESS; - -failure: - return result; +cleanup: + return ISC_R_SUCCESS; } /* @@ -2255,8 +2232,8 @@ if (key->source == dns_keysource_user && (key->hint_publish || key->force_publish)) { - RETERR(publish_key(diff, key, origin, ttl, mctx, - report)); + CHECK(publish_key(diff, key, origin, ttl, mctx, + report)); } if (key->source == dns_keysource_zoneapex) { ttl = dst_key_getttl(key->key); @@ -2330,8 +2307,8 @@ if (key1->source != dns_keysource_zoneapex && (key1->hint_publish || key1->force_publish)) { - RETERR(publish_key(diff, key1, origin, ttl, - mctx, report)); + CHECK(publish_key(diff, key1, origin, ttl, mctx, + report)); isc_log_write( dns_lctx, DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_DNSSEC, ISC_LOG_INFO, @@ -2366,8 +2343,8 @@ /* Match found: remove or update it as needed */ if (key1->hint_remove) { - RETERR(remove_key(diff, key2, origin, ttl, mctx, - "expired", report)); + CHECK(remove_key(diff, key2, origin, ttl, mctx, + "expired", report)); ISC_LIST_UNLINK(*keys, key2, link); if (removed != NULL) { @@ -2390,8 +2367,8 @@ * We need to remove the old version and pull * in the new one. */ - RETERR(remove_key(diff, key2, origin, ttl, mctx, - "revoked", report)); + CHECK(remove_key(diff, key2, origin, ttl, mctx, + "revoked", report)); ISC_LIST_UNLINK(*keys, key2, link); if (removed != NULL) { ISC_LIST_APPEND(*removed, key2, link); @@ -2408,8 +2385,8 @@ dns_dnsseckey_destroy(mctx, &key2); } - RETERR(publish_key(diff, key1, origin, ttl, mctx, - report)); + CHECK(publish_key(diff, key1, origin, ttl, mctx, + report)); ISC_LIST_UNLINK(*newkeys, key1, link); ISC_LIST_APPEND(*keys, key1, link); @@ -2460,7 +2437,7 @@ result = ISC_R_SUCCESS; -failure: +cleanup: return result; } diff -Nru bind9-9.18.41/lib/dns/dnstap.c bind9-9.18.44/lib/dns/dnstap.c --- bind9-9.18.41/lib/dns/dnstap.c 2025-10-18 10:21:03.124260666 +0000 +++ bind9-9.18.44/lib/dns/dnstap.c 2026-01-09 13:44:04.805038676 +0000 @@ -124,13 +124,6 @@ isc_stats_t *stats; }; -#define CHECK(x) \ - do { \ - result = (x); \ - if (result != ISC_R_SUCCESS) \ - goto cleanup; \ - } while (0) - typedef struct ioq { unsigned int generation; struct fstrm_iothr_queue *ioq; diff -Nru bind9-9.18.41/lib/dns/dst_api.c bind9-9.18.44/lib/dns/dst_api.c --- bind9-9.18.41/lib/dns/dst_api.c 2025-10-18 10:21:03.125260693 +0000 +++ bind9-9.18.44/lib/dns/dst_api.c 2026-01-09 13:44:04.806038692 +0000 @@ -68,35 +68,35 @@ #define DST_AS_STR(t) ((t).value.as_textregion.base) -#define NEXTTOKEN(lex, opt, token) \ - { \ - ret = isc_lex_gettoken(lex, opt, token); \ - if (ret != ISC_R_SUCCESS) \ - goto cleanup; \ +#define NEXTTOKEN(lex, opt, token) \ + { \ + CHECK(isc_lex_gettoken(lex, opt, token)); \ } -#define NEXTTOKEN_OR_EOF(lex, opt, token) \ - do { \ - ret = isc_lex_gettoken(lex, opt, token); \ - if (ret == ISC_R_EOF) \ - break; \ - if (ret != ISC_R_SUCCESS) \ - goto cleanup; \ +#define NEXTTOKEN_OR_EOF(lex, opt, token) \ + do { \ + result = isc_lex_gettoken(lex, opt, token); \ + if (result == ISC_R_EOF) { \ + break; \ + } \ + if (result != ISC_R_SUCCESS) { \ + goto cleanup; \ + } \ } while ((*token).type == isc_tokentype_eol); -#define READLINE(lex, opt, token) \ - do { \ - ret = isc_lex_gettoken(lex, opt, token); \ - if (ret == ISC_R_EOF) \ - break; \ - if (ret != ISC_R_SUCCESS) \ - goto cleanup; \ +#define READLINE(lex, opt, token) \ + do { \ + result = isc_lex_gettoken(lex, opt, token); \ + if (result == ISC_R_EOF) \ + break; \ + if (result != ISC_R_SUCCESS) \ + goto cleanup; \ } while ((*token).type != isc_tokentype_eol) -#define BADTOKEN() \ - { \ - ret = ISC_R_UNEXPECTEDTOKEN; \ - goto cleanup; \ +#define BADTOKEN() \ + { \ + result = ISC_R_UNEXPECTEDTOKEN; \ + goto cleanup; \ } #define NUMERIC_NTAGS (DST_MAX_NUMERIC + 1) @@ -174,13 +174,6 @@ addsuffix(char *filename, int len, const char *dirname, const char *ofilename, const char *suffix); -#define RETERR(x) \ - do { \ - result = (x); \ - if (result != ISC_R_SUCCESS) \ - goto out; \ - } while (0) - #define CHECKALG(alg) \ do { \ isc_result_t _r; \ @@ -199,39 +192,39 @@ UNUSED(engine); memset(dst_t_func, 0, sizeof(dst_t_func)); - RETERR(dst__hmacmd5_init(&dst_t_func[DST_ALG_HMACMD5])); - RETERR(dst__hmacsha1_init(&dst_t_func[DST_ALG_HMACSHA1])); - RETERR(dst__hmacsha224_init(&dst_t_func[DST_ALG_HMACSHA224])); - RETERR(dst__hmacsha256_init(&dst_t_func[DST_ALG_HMACSHA256])); - RETERR(dst__hmacsha384_init(&dst_t_func[DST_ALG_HMACSHA384])); - RETERR(dst__hmacsha512_init(&dst_t_func[DST_ALG_HMACSHA512])); - RETERR(dst__openssl_init(engine)); - RETERR(dst__openssldh_init(&dst_t_func[DST_ALG_DH])); - RETERR(dst__opensslrsa_init(&dst_t_func[DST_ALG_RSASHA1], - DST_ALG_RSASHA1)); - RETERR(dst__opensslrsa_init(&dst_t_func[DST_ALG_NSEC3RSASHA1], - DST_ALG_NSEC3RSASHA1)); - RETERR(dst__opensslrsa_init(&dst_t_func[DST_ALG_RSASHA256], - DST_ALG_RSASHA256)); - RETERR(dst__opensslrsa_init(&dst_t_func[DST_ALG_RSASHA512], - DST_ALG_RSASHA512)); - RETERR(dst__opensslecdsa_init(&dst_t_func[DST_ALG_ECDSA256])); - RETERR(dst__opensslecdsa_init(&dst_t_func[DST_ALG_ECDSA384])); + CHECK(dst__hmacmd5_init(&dst_t_func[DST_ALG_HMACMD5])); + CHECK(dst__hmacsha1_init(&dst_t_func[DST_ALG_HMACSHA1])); + CHECK(dst__hmacsha224_init(&dst_t_func[DST_ALG_HMACSHA224])); + CHECK(dst__hmacsha256_init(&dst_t_func[DST_ALG_HMACSHA256])); + CHECK(dst__hmacsha384_init(&dst_t_func[DST_ALG_HMACSHA384])); + CHECK(dst__hmacsha512_init(&dst_t_func[DST_ALG_HMACSHA512])); + CHECK(dst__openssl_init(engine)); + CHECK(dst__openssldh_init(&dst_t_func[DST_ALG_DH])); + CHECK(dst__opensslrsa_init(&dst_t_func[DST_ALG_RSASHA1], + DST_ALG_RSASHA1)); + CHECK(dst__opensslrsa_init(&dst_t_func[DST_ALG_NSEC3RSASHA1], + DST_ALG_NSEC3RSASHA1)); + CHECK(dst__opensslrsa_init(&dst_t_func[DST_ALG_RSASHA256], + DST_ALG_RSASHA256)); + CHECK(dst__opensslrsa_init(&dst_t_func[DST_ALG_RSASHA512], + DST_ALG_RSASHA512)); + CHECK(dst__opensslecdsa_init(&dst_t_func[DST_ALG_ECDSA256])); + CHECK(dst__opensslecdsa_init(&dst_t_func[DST_ALG_ECDSA384])); #ifdef HAVE_OPENSSL_ED25519 - RETERR(dst__openssleddsa_init(&dst_t_func[DST_ALG_ED25519])); + CHECK(dst__openssleddsa_init(&dst_t_func[DST_ALG_ED25519])); #endif /* ifdef HAVE_OPENSSL_ED25519 */ #ifdef HAVE_OPENSSL_ED448 - RETERR(dst__openssleddsa_init(&dst_t_func[DST_ALG_ED448])); + CHECK(dst__openssleddsa_init(&dst_t_func[DST_ALG_ED448])); #endif /* ifdef HAVE_OPENSSL_ED448 */ #if HAVE_GSSAPI - RETERR(dst__gssapi_init(&dst_t_func[DST_ALG_GSSAPI])); + CHECK(dst__gssapi_init(&dst_t_func[DST_ALG_GSSAPI])); #endif /* HAVE_GSSAPI */ dst_initialized = true; return ISC_R_SUCCESS; -out: +cleanup: /* avoid immediate crash! */ dst_initialized = true; dst_lib_destroy(); @@ -428,9 +421,6 @@ isc_result_t dst_key_tofile(const dst_key_t *key, int type, const char *directory) { - isc_result_t ret = ISC_R_SUCCESS; - - REQUIRE(dst_initialized); REQUIRE(VALID_KEY(key)); REQUIRE((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC | DST_TYPE_STATE)) != 0); @@ -442,17 +432,11 @@ } if ((type & DST_TYPE_PUBLIC) != 0) { - ret = write_public_key(key, type, directory); - if (ret != ISC_R_SUCCESS) { - return ret; - } + RETERR(write_public_key(key, type, directory)); } if ((type & DST_TYPE_STATE) != 0) { - ret = write_key_state(key, type, directory); - if (ret != ISC_R_SUCCESS) { - return ret; - } + RETERR(write_key_state(key, type, directory)); } if (((type & DST_TYPE_PRIVATE) != 0) && @@ -545,32 +529,20 @@ key = NULL; isc_buffer_init(&buf, filename, NAME_MAX); - result = dst_key_getfilename(name, id, alg, type, NULL, mctx, &buf); - if (result != ISC_R_SUCCESS) { - goto out; - } - - result = dst_key_fromnamedfile(filename, directory, type, mctx, &key); - if (result != ISC_R_SUCCESS) { - goto out; - } - - result = computeid(key); - if (result != ISC_R_SUCCESS) { - goto out; - } + CHECK(dst_key_getfilename(name, id, alg, type, NULL, mctx, &buf)); + CHECK(dst_key_fromnamedfile(filename, directory, type, mctx, &key)); + CHECK(computeid(key)); if (!dns_name_equal(name, key->key_name) || id != key->key_id || alg != key->key_alg) { - result = DST_R_INVALIDPRIVATEKEY; - goto out; + CHECK(DST_R_INVALIDPRIVATEKEY); } *keyp = key; result = ISC_R_SUCCESS; -out: +cleanup: if ((key != NULL) && (result != ISC_R_SUCCESS)) { dst_key_free(&key); } @@ -607,7 +579,7 @@ ".key"); INSIST(result == ISC_R_SUCCESS); - RETERR(dst_key_read_public(newfilename, type, mctx, &pubkey)); + CHECK(dst_key_read_public(newfilename, type, mctx, &pubkey)); isc_mem_put(mctx, newfilename, newfilenamelen); /* @@ -633,31 +605,31 @@ /* Having no state is valid. */ result = ISC_R_SUCCESS; } - RETERR(result); + CHECK(result); } if ((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) == DST_TYPE_PUBLIC || (pubkey->key_flags & DNS_KEYFLAG_TYPEMASK) == DNS_KEYTYPE_NOKEY) { - RETERR(computeid(pubkey)); + CHECK(computeid(pubkey)); pubkey->modified = false; *keyp = pubkey; pubkey = NULL; - goto out; + goto cleanup; } - RETERR(algorithm_status(pubkey->key_alg)); + CHECK(algorithm_status(pubkey->key_alg)); key = get_key_struct(pubkey->key_name, pubkey->key_alg, pubkey->key_flags, pubkey->key_proto, pubkey->key_size, pubkey->key_class, pubkey->key_ttl, mctx); if (key == NULL) { - RETERR(ISC_R_NOMEMORY); + CHECK(ISC_R_NOMEMORY); } if (key->func->parse == NULL) { - RETERR(DST_R_UNSUPPORTEDALG); + CHECK(DST_R_UNSUPPORTEDALG); } newfilenamelen = strlen(filename) + 9; @@ -669,11 +641,11 @@ ".private"); INSIST(result == ISC_R_SUCCESS); - RETERR(isc_lex_create(mctx, 1500, &lex)); - RETERR(isc_lex_openfile(lex, newfilename)); + CHECK(isc_lex_create(mctx, 1500, &lex)); + CHECK(isc_lex_openfile(lex, newfilename)); isc_mem_put(mctx, newfilename, newfilenamelen); - RETERR(key->func->parse(key, lex, pubkey)); + CHECK(key->func->parse(key, lex, pubkey)); isc_lex_destroy(&lex); key->kasp = false; @@ -685,20 +657,20 @@ /* Having no state is valid. */ result = ISC_R_SUCCESS; } - RETERR(result); + CHECK(result); } - RETERR(computeid(key)); + CHECK(computeid(key)); if (pubkey->key_id != key->key_id) { - RETERR(DST_R_INVALIDPRIVATEKEY); + CHECK(DST_R_INVALIDPRIVATEKEY); } key->modified = false; *keyp = key; key = NULL; -out: +cleanup: if (pubkey != NULL) { dst_key_free(&pubkey); } @@ -853,13 +825,13 @@ REQUIRE(buffer != NULL); if (key->func->parse == NULL) { - RETERR(DST_R_UNSUPPORTEDALG); + CHECK(DST_R_UNSUPPORTEDALG); } - RETERR(isc_lex_create(key->mctx, 1500, &lex)); - RETERR(isc_lex_openbuffer(lex, buffer)); - RETERR(key->func->parse(key, lex, NULL)); -out: + CHECK(isc_lex_create(key->mctx, 1500, &lex)); + CHECK(isc_lex_openbuffer(lex, buffer)); + CHECK(key->func->parse(key, lex, NULL)); +cleanup: if (lex != NULL) { isc_lex_destroy(&lex); } @@ -895,13 +867,13 @@ */ isc_buffer_allocate(key->mctx, &key->key_tkeytoken, intoken->length); - RETERR(isc_buffer_copyregion(key->key_tkeytoken, intoken)); + CHECK(isc_buffer_copyregion(key->key_tkeytoken, intoken)); } key->keydata.gssctx = gssctx; *keyp = key; result = ISC_R_SUCCESS; -out: +cleanup: if (result != ISC_R_SUCCESS) { dst_key_free(&key); } @@ -1050,7 +1022,7 @@ dns_rdataclass_t rdclass, isc_mem_t *mctx, dst_key_t **keyp, void (*callback)(int)) { dst_key_t *key; - isc_result_t ret; + isc_result_t result; REQUIRE(dst_initialized); REQUIRE(dns_name_isabsolute(name)); @@ -1076,16 +1048,16 @@ return DST_R_UNSUPPORTEDALG; } - ret = key->func->generate(key, param, callback); - if (ret != ISC_R_SUCCESS) { + result = key->func->generate(key, param, callback); + if (result != ISC_R_SUCCESS) { dst_key_free(&key); - return ret; + return result; } - ret = computeid(key); - if (ret != ISC_R_SUCCESS) { + result = computeid(key); + if (result != ISC_R_SUCCESS) { dst_key_free(&key); - return ret; + return result; } *keyp = key; @@ -1653,13 +1625,12 @@ dns_fixedname_t name; isc_lex_t *lex = NULL; isc_token_t token; - isc_result_t ret; + isc_result_t result; dns_rdata_t rdata = DNS_RDATA_INIT; unsigned int opt = ISC_LEXOPT_DNSMULTILINE | ISC_LEXOPT_ESCAPE; dns_rdataclass_t rdclass = dns_rdataclass_in; isc_lexspecials_t specials; uint32_t ttl = 0; - isc_result_t result; dns_rdatatype_t keytype; /* @@ -1670,10 +1641,7 @@ */ /* 1500 should be large enough for any key */ - ret = isc_lex_create(mctx, 1500, &lex); - if (ret != ISC_R_SUCCESS) { - goto cleanup; - } + CHECK(isc_lex_create(mctx, 1500, &lex)); memset(specials, 0, sizeof(specials)); specials['('] = 1; @@ -1682,10 +1650,7 @@ isc_lex_setspecials(lex, specials); isc_lex_setcomments(lex, ISC_LEXCOMMENT_DNSMASTERFILE); - ret = isc_lex_openfile(lex, filename); - if (ret != ISC_R_SUCCESS) { - goto cleanup; - } + CHECK(isc_lex_openfile(lex, filename)); /* Read the domain name */ NEXTTOKEN(lex, opt, &token); @@ -1703,11 +1668,8 @@ dns_fixedname_init(&name); isc_buffer_init(&b, DST_AS_STR(token), strlen(DST_AS_STR(token))); isc_buffer_add(&b, strlen(DST_AS_STR(token))); - ret = dns_name_fromtext(dns_fixedname_name(&name), &b, dns_rootname, 0, - NULL); - if (ret != ISC_R_SUCCESS) { - goto cleanup; - } + CHECK(dns_name_fromtext(dns_fixedname_name(&name), &b, dns_rootname, 0, + NULL)); /* Read the next word: either TTL, class, or 'KEY' */ NEXTTOKEN(lex, opt, &token); @@ -1726,8 +1688,8 @@ BADTOKEN(); } - ret = dns_rdataclass_fromtext(&rdclass, &token.value.as_textregion); - if (ret == ISC_R_SUCCESS) { + result = dns_rdataclass_fromtext(&rdclass, &token.value.as_textregion); + if (result == ISC_R_SUCCESS) { NEXTTOKEN(lex, opt, &token); } @@ -1746,22 +1708,16 @@ if (((type & DST_TYPE_KEY) != 0 && keytype != dns_rdatatype_key) || ((type & DST_TYPE_KEY) == 0 && keytype != dns_rdatatype_dnskey)) { - ret = DST_R_BADKEYTYPE; + result = DST_R_BADKEYTYPE; goto cleanup; } isc_buffer_init(&b, rdatabuf, sizeof(rdatabuf)); - ret = dns_rdata_fromtext(&rdata, rdclass, keytype, lex, NULL, false, - mctx, &b, NULL); - if (ret != ISC_R_SUCCESS) { - goto cleanup; - } + CHECK(dns_rdata_fromtext(&rdata, rdclass, keytype, lex, NULL, false, + mctx, &b, NULL)); - ret = dst_key_fromdns(dns_fixedname_name(&name), rdclass, &b, mctx, - keyp); - if (ret != ISC_R_SUCCESS) { - goto cleanup; - } + CHECK(dst_key_fromdns(dns_fixedname_name(&name), rdclass, &b, mctx, + keyp)); dst_key_setttl(*keyp, ttl); @@ -1769,7 +1725,7 @@ if (lex != NULL) { isc_lex_destroy(&lex); } - return ret; + return result; } static int @@ -1820,19 +1776,13 @@ dst_key_read_state(const char *filename, isc_mem_t *mctx, dst_key_t **keyp) { isc_lex_t *lex = NULL; isc_token_t token; - isc_result_t ret; + isc_result_t result; unsigned int opt = ISC_LEXOPT_EOL; - ret = isc_lex_create(mctx, 1500, &lex); - if (ret != ISC_R_SUCCESS) { - goto cleanup; - } + CHECK(isc_lex_create(mctx, 1500, &lex)); isc_lex_setcomments(lex, ISC_LEXCOMMENT_DNSMASTERFILE); - ret = isc_lex_openfile(lex, filename); - if (ret != ISC_R_SUCCESS) { - goto cleanup; - } + CHECK(isc_lex_openfile(lex, filename)); /* * Read the comment line. @@ -1884,7 +1834,7 @@ int tag; NEXTTOKEN_OR_EOF(lex, opt, &token); - if (ret == ISC_R_EOF) { + if (result == ISC_R_EOF) { break; } if (token.type != isc_tokentype_string) { @@ -1937,10 +1887,7 @@ BADTOKEN(); } - ret = dns_time32_fromtext(DST_AS_STR(token), &when); - if (ret != ISC_R_SUCCESS) { - goto cleanup; - } + CHECK(dns_time32_fromtext(DST_AS_STR(token), &when)); dst_key_settime(*keyp, tag, when); goto next; @@ -1958,10 +1905,7 @@ BADTOKEN(); } - ret = keystate_fromtext(DST_AS_STR(token), &state); - if (ret != ISC_R_SUCCESS) { - goto cleanup; - } + CHECK(keystate_fromtext(DST_AS_STR(token), &state)); dst_key_setstate(*keyp, tag, state); goto next; @@ -1972,13 +1916,13 @@ } /* Done, successfully parsed the whole file. */ - ret = ISC_R_SUCCESS; + result = ISC_R_SUCCESS; cleanup: if (lex != NULL) { isc_lex_destroy(&lex); } - return ret; + return result; } static bool @@ -2342,13 +2286,9 @@ isc_buffer_t dnsbuf; unsigned char dns_array[DST_KEY_MAXSIZE]; isc_region_t r; - isc_result_t ret; isc_buffer_init(&dnsbuf, dns_array, sizeof(dns_array)); - ret = dst_key_todns(key, &dnsbuf); - if (ret != ISC_R_SUCCESS) { - return ret; - } + RETERR(dst_key_todns(key, &dnsbuf)); isc_buffer_usedregion(&dnsbuf, &r); key->key_id = dst_region_computeid(&r); @@ -2362,7 +2302,7 @@ isc_buffer_t *source, isc_mem_t *mctx, bool no_rdata, dst_key_t **keyp) { dst_key_t *key; - isc_result_t ret; + isc_result_t result; REQUIRE(dns_name_isabsolute(name)); REQUIRE(source != NULL); @@ -2375,10 +2315,10 @@ } if (isc_buffer_remaininglength(source) > 0) { - ret = algorithm_status(alg); - if (ret != ISC_R_SUCCESS) { + result = algorithm_status(alg); + if (result != ISC_R_SUCCESS) { dst_key_free(&key); - return ret; + return result; } if (key->func->fromdns == NULL) { dst_key_free(&key); @@ -2386,10 +2326,10 @@ } if (!no_rdata) { - ret = key->func->fromdns(key, source); - if (ret != ISC_R_SUCCESS) { + result = key->func->fromdns(key, source); + if (result != ISC_R_SUCCESS) { dst_key_free(&key); - return ret; + return result; } } } diff -Nru bind9-9.18.41/lib/dns/dst_parse.c bind9-9.18.44/lib/dns/dst_parse.c --- bind9-9.18.41/lib/dns/dst_parse.c 2025-10-18 10:21:03.125260693 +0000 +++ bind9-9.18.44/lib/dns/dst_parse.c 2026-01-09 13:44:04.806038692 +0000 @@ -417,7 +417,7 @@ unsigned char *data = NULL; unsigned int opt = ISC_LEXOPT_EOL; isc_stdtime_t when; - isc_result_t ret; + isc_result_t result; bool external = false; REQUIRE(priv != NULL); @@ -425,20 +425,19 @@ priv->nelements = 0; memset(priv->elements, 0, sizeof(priv->elements)); -#define NEXTTOKEN(lex, opt, token) \ - do { \ - ret = isc_lex_gettoken(lex, opt, token); \ - if (ret != ISC_R_SUCCESS) \ - goto fail; \ +#define NEXTTOKEN(lex, opt, token) \ + do { \ + CHECK(isc_lex_gettoken(lex, opt, token)); \ } while (0) -#define READLINE(lex, opt, token) \ - do { \ - ret = isc_lex_gettoken(lex, opt, token); \ - if (ret == ISC_R_EOF) \ - break; \ - else if (ret != ISC_R_SUCCESS) \ - goto fail; \ +#define READLINE(lex, opt, token) \ + do { \ + result = isc_lex_gettoken(lex, opt, token); \ + if (result == ISC_R_EOF) { \ + break; \ + } else if (result != ISC_R_SUCCESS) { \ + goto cleanup; \ + } \ } while ((*token).type != isc_tokentype_eol) /* @@ -448,24 +447,24 @@ if (token.type != isc_tokentype_string || strcmp(DST_AS_STR(token), PRIVATE_KEY_STR) != 0) { - ret = DST_R_INVALIDPRIVATEKEY; - goto fail; + result = DST_R_INVALIDPRIVATEKEY; + goto cleanup; } NEXTTOKEN(lex, opt, &token); if (token.type != isc_tokentype_string || (DST_AS_STR(token))[0] != 'v') { - ret = DST_R_INVALIDPRIVATEKEY; - goto fail; + result = DST_R_INVALIDPRIVATEKEY; + goto cleanup; } if (sscanf(DST_AS_STR(token), "v%d.%d", &major, &minor) != 2) { - ret = DST_R_INVALIDPRIVATEKEY; - goto fail; + result = DST_R_INVALIDPRIVATEKEY; + goto cleanup; } if (major > DST_MAJOR_VERSION) { - ret = DST_R_INVALIDPRIVATEKEY; - goto fail; + result = DST_R_INVALIDPRIVATEKEY; + goto cleanup; } /* @@ -482,16 +481,16 @@ if (token.type != isc_tokentype_string || strcmp(DST_AS_STR(token), ALGORITHM_STR) != 0) { - ret = DST_R_INVALIDPRIVATEKEY; - goto fail; + result = DST_R_INVALIDPRIVATEKEY; + goto cleanup; } NEXTTOKEN(lex, opt | ISC_LEXOPT_NUMBER, &token); if (token.type != isc_tokentype_number || token.value.as_ulong != (unsigned long)dst_key_alg(key)) { - ret = DST_R_INVALIDPRIVATEKEY; - goto fail; + result = DST_R_INVALIDPRIVATEKEY; + goto cleanup; } READLINE(lex, opt, &token); @@ -503,18 +502,18 @@ int tag; isc_region_t r; do { - ret = isc_lex_gettoken(lex, opt, &token); - if (ret == ISC_R_EOF) { + result = isc_lex_gettoken(lex, opt, &token); + if (result == ISC_R_EOF) { goto done; } - if (ret != ISC_R_SUCCESS) { - goto fail; + if (result != ISC_R_SUCCESS) { + goto cleanup; } } while (token.type == isc_tokentype_eol); if (token.type != isc_tokentype_string) { - ret = DST_R_INVALIDPRIVATEKEY; - goto fail; + result = DST_R_INVALIDPRIVATEKEY; + goto cleanup; } if (strcmp(DST_AS_STR(token), "External:") == 0) { @@ -529,8 +528,8 @@ NEXTTOKEN(lex, opt | ISC_LEXOPT_NUMBER, &token); if (token.type != isc_tokentype_number) { - ret = DST_R_INVALIDPRIVATEKEY; - goto fail; + result = DST_R_INVALIDPRIVATEKEY; + goto cleanup; } dst_key_setnum(key, tag, token.value.as_ulong); @@ -544,14 +543,11 @@ NEXTTOKEN(lex, opt, &token); if (token.type != isc_tokentype_string) { - ret = DST_R_INVALIDPRIVATEKEY; - goto fail; + result = DST_R_INVALIDPRIVATEKEY; + goto cleanup; } - ret = dns_time32_fromtext(DST_AS_STR(token), &when); - if (ret != ISC_R_SUCCESS) { - goto fail; - } + CHECK(dns_time32_fromtext(DST_AS_STR(token), &when)); dst_key_settime(key, tag, when); @@ -563,8 +559,8 @@ if (tag < 0 && minor > DST_MINOR_VERSION) { goto next; } else if (tag < 0) { - ret = DST_R_INVALIDPRIVATEKEY; - goto fail; + result = DST_R_INVALIDPRIVATEKEY; + goto cleanup; } priv->elements[n].tag = tag; @@ -572,10 +568,7 @@ data = isc_mem_get(mctx, MAXFIELDSIZE); isc_buffer_init(&b, data, MAXFIELDSIZE); - ret = isc_base64_tobuffer(lex, &b, -1); - if (ret != ISC_R_SUCCESS) { - goto fail; - } + CHECK(isc_base64_tobuffer(lex, &b, -1)); isc_buffer_usedregion(&b, &r); priv->elements[n].length = r.length; @@ -589,30 +582,30 @@ done: if (external && priv->nelements != 0) { - ret = DST_R_INVALIDPRIVATEKEY; - goto fail; + result = DST_R_INVALIDPRIVATEKEY; + goto cleanup; } check = check_data(priv, alg, true, external); if (check < 0) { - ret = DST_R_INVALIDPRIVATEKEY; - goto fail; + result = DST_R_INVALIDPRIVATEKEY; + goto cleanup; } else if (check != ISC_R_SUCCESS) { - ret = check; - goto fail; + result = check; + goto cleanup; } key->external = external; return ISC_R_SUCCESS; -fail: +cleanup: dst__privstruct_free(priv, mctx); if (data != NULL) { isc_mem_put(mctx, data, MAXFIELDSIZE); } - return ret; + return result; } isc_result_t diff -Nru bind9-9.18.41/lib/dns/dyndb.c bind9-9.18.44/lib/dns/dyndb.c --- bind9-9.18.41/lib/dns/dyndb.c 2025-10-18 10:21:03.125260693 +0000 +++ bind9-9.18.44/lib/dns/dyndb.c 2026-01-09 13:44:04.806038692 +0000 @@ -30,13 +30,6 @@ #include #include -#define CHECK(op) \ - do { \ - result = (op); \ - if (result != ISC_R_SUCCESS) \ - goto cleanup; \ - } while (0) - typedef struct dyndb_implementation dyndb_implementation_t; struct dyndb_implementation { isc_mem_t *mctx; diff -Nru bind9-9.18.41/lib/dns/gssapictx.c bind9-9.18.44/lib/dns/gssapictx.c --- bind9-9.18.41/lib/dns/gssapictx.c 2025-10-18 10:21:03.126260720 +0000 +++ bind9-9.18.44/lib/dns/gssapictx.c 2026-01-09 13:44:04.807038709 +0000 @@ -93,13 +93,6 @@ (r).base = (gb).value; \ } while (0) -#define RETERR(x) \ - do { \ - result = (x); \ - if (result != ISC_R_SUCCESS) \ - goto out; \ - } while (0) - static void name_to_gbuffer(const dns_name_t *name, isc_buffer_t *buffer, gss_buffer_desc *gbuffer) { @@ -589,8 +582,7 @@ gret = gss_import_name(&minor, &gnamebuf, GSS_C_NO_OID, &gname); if (gret != GSS_S_COMPLETE) { gss_err_message(mctx, gret, minor, err_message); - result = ISC_R_FAILURE; - goto out; + CHECK(ISC_R_FAILURE); } if (intoken != NULL) { @@ -621,8 +613,7 @@ gss_log(3, "Failure initiating security context"); } - result = ISC_R_FAILURE; - goto out; + CHECK(ISC_R_FAILURE); } /* @@ -635,7 +626,7 @@ */ if (gouttoken.length != 0U) { GBUFFER_TO_REGION(gouttoken, r); - RETERR(isc_buffer_copyregion(outtoken, &r)); + CHECK(isc_buffer_copyregion(outtoken, &r)); } if (gret == GSS_S_COMPLETE) { @@ -644,7 +635,7 @@ result = DNS_R_CONTINUE; } -out: +cleanup: if (gouttoken.length != 0U) { (void)gss_release_buffer(&minor, &gouttoken); } @@ -749,7 +740,7 @@ isc_buffer_allocate(mctx, outtoken, (unsigned int)gouttoken.length); GBUFFER_TO_REGION(gouttoken, r); - RETERR(isc_buffer_copyregion(*outtoken, &r)); + CHECK(isc_buffer_copyregion(*outtoken, &r)); (void)gss_release_buffer(&minor, &gouttoken); } @@ -759,7 +750,7 @@ gss_log(3, "failed gss_display_name: %s", gss_error_tostring(gret, minor, buf, sizeof(buf))); - RETERR(ISC_R_FAILURE); + CHECK(ISC_R_FAILURE); } /* @@ -781,8 +772,8 @@ isc_buffer_init(&namebuf, r.base, r.length); isc_buffer_add(&namebuf, r.length); - RETERR(dns_name_fromtext(principal, &namebuf, dns_rootname, 0, - NULL)); + CHECK(dns_name_fromtext(principal, &namebuf, dns_rootname, 0, + NULL)); if (gnamebuf.length != 0U) { gret = gss_release_buffer(&minor, &gnamebuf); @@ -798,7 +789,7 @@ *ctxout = context; -out: +cleanup: if (gname != NULL) { gret = gss_release_name(&minor, &gname); if (gret != GSS_S_COMPLETE) { diff -Nru bind9-9.18.41/lib/dns/include/dns/librpz.h bind9-9.18.44/lib/dns/include/dns/librpz.h --- bind9-9.18.41/lib/dns/include/dns/librpz.h 2025-10-18 10:21:03.131260854 +0000 +++ bind9-9.18.44/lib/dns/include/dns/librpz.h 2026-01-09 13:44:04.812038791 +0000 @@ -170,8 +170,8 @@ librpz_dznum_t dznum; /* dnsrpzd zone number */ librpz_cznum_t cznum; /* librpz client zone number */ librpz_trig_t trig : LIBRPZ_TRIG_SIZE; - bool log : 1; /* log rewrite given librpz_log_level - * */ + bool log : 1; /* log rewrite given librpz_log_level + * */ } librpz_result_t; /** diff -Nru bind9-9.18.41/lib/dns/include/dns/rrl.h bind9-9.18.44/lib/dns/include/dns/rrl.h --- bind9-9.18.41/lib/dns/include/dns/rrl.h 2025-10-18 10:21:03.134260934 +0000 +++ bind9-9.18.44/lib/dns/include/dns/rrl.h 2026-01-09 13:44:04.815038841 +0000 @@ -112,11 +112,11 @@ unsigned int log_qname : DNS_RRL_QNAMES_BITS; #define DNS_RRL_TS_GEN_BITS 2 - unsigned int ts_gen : DNS_RRL_TS_GEN_BITS; + unsigned int ts_gen : DNS_RRL_TS_GEN_BITS; unsigned int ts_valid : 1; #define DNS_RRL_HASH_GEN_BITS 1 unsigned int hash_gen : DNS_RRL_HASH_GEN_BITS; - unsigned int logged : 1; + unsigned int logged : 1; #define DNS_RRL_LOG_BITS 11 unsigned int log_secs : DNS_RRL_LOG_BITS; diff -Nru bind9-9.18.41/lib/dns/journal.c bind9-9.18.44/lib/dns/journal.c --- bind9-9.18.41/lib/dns/journal.c 2025-10-18 10:21:03.137261014 +0000 +++ bind9-9.18.44/lib/dns/journal.c 2026-01-09 13:44:04.819038907 +0000 @@ -86,25 +86,6 @@ #define JOURNAL_DEBUG_LOGARGS(n) JOURNAL_COMMON_LOGARGS, ISC_LOG_DEBUG(n) -/*% - * It would be non-sensical (or at least obtuse) to use FAIL() with an - * ISC_R_SUCCESS code, but the test is there to keep the Solaris compiler - * from complaining about "end-of-loop code not reached". - */ -#define FAIL(code) \ - do { \ - result = (code); \ - if (result != ISC_R_SUCCESS) \ - goto failure; \ - } while (0) - -#define CHECK(op) \ - do { \ - result = (op); \ - if (result != ISC_R_SUCCESS) \ - goto failure; \ - } while (0) - #define JOURNAL_SERIALSET 0x01U static isc_result_t @@ -648,14 +629,14 @@ */ result = isc_stdio_open(j->filename, "rb+", &fp); } else { - FAIL(ISC_R_NOTFOUND); + CHECK(ISC_R_NOTFOUND); } } if (result != ISC_R_SUCCESS) { isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, "%s: open: %s", j->filename, isc_result_totext(result)); - FAIL(ISC_R_UNEXPECTED); + CHECK(ISC_R_UNEXPECTED); } j->fp = fp; @@ -693,7 +674,7 @@ } else { isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, "%s: journal format not recognized", j->filename); - FAIL(ISC_R_UNEXPECTED); + CHECK(ISC_R_UNEXPECTED); } journal_header_decode(&rawheader, &j->header); @@ -745,7 +726,7 @@ *journalp = j; return ISC_R_SUCCESS; -failure: +cleanup: j->magic = 0; if (j->rawindex != NULL) { isc_mem_put(j->mctx, j->rawindex, @@ -921,7 +902,7 @@ j->recovered = true; } -failure: +cleanup: return result; } @@ -1001,7 +982,7 @@ pos->serial = xhdr.serial1; return ISC_R_SUCCESS; -failure: +cleanup: return result; } @@ -1181,7 +1162,7 @@ j->state = JOURNAL_STATE_TRANSACTION; result = ISC_R_SUCCESS; -failure: +cleanup: return result; } @@ -1272,7 +1253,7 @@ result = ISC_R_SUCCESS; -failure: +cleanup: if (mem != NULL) { isc_mem_put(j->mctx, mem, size); } @@ -1415,7 +1396,7 @@ result = ISC_R_SUCCESS; -failure: +cleanup: return result; } @@ -1428,7 +1409,7 @@ CHECK(dns_journal_writediff(j, diff)); CHECK(dns_journal_commit(j)); result = ISC_R_SUCCESS; -failure: +cleanup: return result; } @@ -1566,7 +1547,7 @@ "%s: journal file corrupt: missing " "initial SOA", j->filename); - FAIL(ISC_R_UNEXPECTED); + CHECK(ISC_R_UNEXPECTED); } if ((options & DNS_JOURNALOPT_RESIGN) != 0) { op = (n_soa == 1) ? DNS_DIFFOP_DELRESIGN @@ -1603,7 +1584,7 @@ dns_diff_clear(&diff); } -failure: +cleanup: if (ver != NULL) { dns_db_closeversion(db, &ver, result == ISC_R_SUCCESS ? true : false); @@ -1709,7 +1690,7 @@ "%s: journal file corrupt: missing " "initial SOA", j->filename); - FAIL(ISC_R_UNEXPECTED); + CHECK(ISC_R_UNEXPECTED); } if (print) { @@ -1751,13 +1732,13 @@ result = dns_diff_print(&diff, file); dns_diff_clear(&diff); } - goto cleanup; + goto done; -failure: +cleanup: isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, "%s: cannot print: journal file corrupt", j->filename); -cleanup: +done: if (source.base != NULL) { isc_mem_put(j->mctx, source.base, source.length); } @@ -1921,7 +1902,7 @@ } result = ISC_R_SUCCESS; -failure: +cleanup: j->it.result = result; return j->it.result; } @@ -1942,7 +1923,7 @@ return read_one_rr(j); -failure: +cleanup: return result; } @@ -1976,7 +1957,7 @@ isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, "%s: journal corrupt: empty transaction", j->filename); - FAIL(ISC_R_UNEXPECTED); + CHECK(ISC_R_UNEXPECTED); } if (j->header_ver1) { @@ -1992,7 +1973,7 @@ "expected serial %u, got %u", j->filename, j->it.current_serial, xhdr.serial0); - FAIL(ISC_R_UNEXPECTED); + CHECK(ISC_R_UNEXPECTED); } j->it.xsize = xhdr.size; @@ -2014,7 +1995,7 @@ "%s: journal corrupt: impossible RR size " "(%d bytes)", j->filename, rrhdr.size); - FAIL(ISC_R_UNEXPECTED); + CHECK(ISC_R_UNEXPECTED); } CHECK(size_buffer(j->mctx, &j->it.source, rrhdr.size)); @@ -2043,7 +2024,7 @@ * Check that the RR header is there, and parse it. */ if (isc_buffer_remaininglength(&j->it.source) < 10) { - FAIL(DNS_R_FORMERR); + CHECK(DNS_R_FORMERR); } rdtype = isc_buffer_getuint16(&j->it.source); @@ -2056,14 +2037,14 @@ "%s: journal corrupt: impossible rdlen " "(%u bytes)", j->filename, rdlen); - FAIL(ISC_R_FAILURE); + CHECK(ISC_R_FAILURE); } /* * Parse the rdata. */ if (isc_buffer_remaininglength(&j->it.source) != rdlen) { - FAIL(DNS_R_FORMERR); + CHECK(DNS_R_FORMERR); } isc_buffer_setactive(&j->it.source, rdlen); dns_rdata_reset(&j->it.rdata); @@ -2079,7 +2060,7 @@ result = ISC_R_SUCCESS; -failure: +cleanup: j->it.result = result; return result; } @@ -2255,7 +2236,7 @@ ISC_LIST_APPENDLIST(r->tuples, del, link); ISC_LIST_APPENDLIST(r->tuples, add, link); result = ISC_R_SUCCESS; -failure: +cleanup: return result; } @@ -2347,16 +2328,16 @@ next:; } if (itresult[0] != ISC_R_NOMORE) { - FAIL(itresult[0]); + CHECK(itresult[0]); } if (itresult[1] != ISC_R_NOMORE) { - FAIL(itresult[1]); + CHECK(itresult[1]); } INSIST(ISC_LIST_EMPTY(diff[0].tuples)); INSIST(ISC_LIST_EMPTY(diff[1].tuples)); -failure: +cleanup: dns_dbiterator_destroy(&dbit[1]); cleanup_iterator: @@ -2412,7 +2393,7 @@ } } -failure: +cleanup: if (journal != NULL) { dns_journal_destroy(&journal); } @@ -2796,7 +2777,7 @@ if (result != ISC_R_SUCCESS && result != ISC_R_FILENOTFOUND) { - goto failure; + CHECK(result); } if (rename(filename, backup) == -1) { goto maperrno; @@ -2807,14 +2788,13 @@ (void)isc_file_remove(backup); } else { maperrno: - result = ISC_R_FAILURE; - goto failure; + CHECK(ISC_R_FAILURE); } } result = ISC_R_SUCCESS; -failure: +cleanup: (void)isc_file_remove(newname); if (buf != NULL) { isc_mem_put(mctx, buf, size); @@ -2851,6 +2831,6 @@ CHECK(journal_seek(j, sizeof(journal_rawheader_t))); CHECK(journal_write(j, j->rawindex, rawbytes)); } -failure: +cleanup: return result; } diff -Nru bind9-9.18.41/lib/dns/keymgr.c bind9-9.18.44/lib/dns/keymgr.c --- bind9-9.18.41/lib/dns/keymgr.c 2025-10-18 10:21:03.138261041 +0000 +++ bind9-9.18.44/lib/dns/keymgr.c 2026-01-09 13:44:04.819038907 +0000 @@ -34,13 +34,6 @@ #include -#define RETERR(x) \ - do { \ - result = (x); \ - if (result != ISC_R_SUCCESS) \ - goto failure; \ - } while (0) - /* * Set key state to `target` state and change last changed * to `time`, only if key state has not been set before. @@ -494,9 +487,9 @@ if (dns_kasp_key_ksk(kkey)) { keyflags |= DNS_KEYFLAG_KSK; } - RETERR(dst_key_generate(origin, algo, size, 0, keyflags, - DNS_KEYPROTO_DNSSEC, rdclass, mctx, - &newkey, NULL)); + CHECK(dst_key_generate(origin, algo, size, 0, keyflags, + DNS_KEYPROTO_DNSSEC, rdclass, mctx, + &newkey, NULL)); /* Key collision? */ conflict = keymgr_keyid_conflict(newkey, keylist); @@ -520,7 +513,7 @@ *dst_key = newkey; return ISC_R_SUCCESS; -failure: +cleanup: return result; } @@ -2238,7 +2231,7 @@ } /* See if this key requires a rollover. */ - RETERR(keymgr_key_rollover( + CHECK(keymgr_key_rollover( kkey, active_key, keyring, &newkeys, origin, rdclass, kasp, lifetime, rollover_allowed, now, nexttime, mctx)); } @@ -2268,14 +2261,14 @@ } if (modified && !dkey->purge) { dns_dnssec_get_hints(dkey, now); - RETERR(dst_key_tofile(dkey->key, options, directory)); + CHECK(dst_key_tofile(dkey->key, options, directory)); } dst_key_setmodified(dkey->key, false); } result = ISC_R_SUCCESS; -failure: +cleanup: if (dir_open) { isc_dir_close(&dir); } @@ -2437,7 +2430,7 @@ rollover_status(dns_dnsseckey_t *dkey, dns_kasp_t *kasp, isc_stdtime_t now, isc_buffer_t *buf, bool zsk) { char timestr[26]; /* Minimal buf as per ctime_r() spec. */ - isc_result_t ret = ISC_R_SUCCESS; + isc_result_t result = ISC_R_SUCCESS; isc_stdtime_t active_time = 0; dst_key_state_t state = NA, goal = NA; int rrsig, active, retire; @@ -2469,9 +2462,9 @@ state = NA; (void)dst_key_getstate(key, DST_KEY_DNSKEY, &state); if (state == RUMOURED || state == OMNIPRESENT) { - ret = dst_key_gettime(key, DST_TIME_DELETE, - &remove_time); - if (ret == ISC_R_SUCCESS) { + result = dst_key_gettime(key, DST_TIME_DELETE, + &remove_time); + if (result == ISC_R_SUCCESS) { isc_buffer_printf(buf, " Key is retired, will " "be removed on "); isc_stdtime_tostring(remove_time, timestr, @@ -2484,8 +2477,8 @@ } } else { isc_stdtime_t retire_time = 0; - ret = dst_key_gettime(key, retire, &retire_time); - if (ret == ISC_R_SUCCESS) { + result = dst_key_gettime(key, retire, &retire_time); + if (result == ISC_R_SUCCESS) { if (now < retire_time) { if (goal == OMNIPRESENT) { isc_buffer_printf(buf, diff -Nru bind9-9.18.41/lib/dns/masterdump.c bind9-9.18.44/lib/dns/masterdump.c --- bind9-9.18.41/lib/dns/masterdump.c 2025-10-18 10:21:03.139261067 +0000 +++ bind9-9.18.44/lib/dns/masterdump.c 2026-01-09 13:44:04.820038923 +0000 @@ -52,19 +52,6 @@ #define DNS_DCTX_MAGIC ISC_MAGIC('D', 'c', 't', 'x') #define DNS_DCTX_VALID(d) ISC_MAGIC_VALID(d, DNS_DCTX_MAGIC) -#define RETERR(x) \ - do { \ - isc_result_t _r = (x); \ - if (_r != ISC_R_SUCCESS) \ - return ((_r)); \ - } while (0) - -#define CHECK(x) \ - do { \ - if ((x) != ISC_R_SUCCESS) \ - goto cleanup; \ - } while (0) - struct dns_master_style { dns_masterstyle_flags_t flags; /* DNS_STYLEFLAG_* */ unsigned int ttl_column; diff -Nru bind9-9.18.41/lib/dns/nsec.c bind9-9.18.44/lib/dns/nsec.c --- bind9-9.18.41/lib/dns/nsec.c 2025-10-18 10:21:03.140261094 +0000 +++ bind9-9.18.44/lib/dns/nsec.c 2026-01-09 13:44:04.821038940 +0000 @@ -30,13 +30,6 @@ #include -#define RETERR(x) \ - do { \ - result = (x); \ - if (result != ISC_R_SUCCESS) \ - goto failure; \ - } while (0) - void dns_nsec_setbit(unsigned char *array, unsigned int type, unsigned int bit) { unsigned int shift, mask; @@ -189,20 +182,20 @@ dns_rdataset_init(&rdataset); dns_rdata_init(&rdata); - RETERR(dns_nsec_buildrdata(db, version, node, target, data, &rdata)); + CHECK(dns_nsec_buildrdata(db, version, node, target, data, &rdata)); dns_rdatalist_init(&rdatalist); rdatalist.rdclass = dns_db_class(db); rdatalist.type = dns_rdatatype_nsec; rdatalist.ttl = ttl; ISC_LIST_APPEND(rdatalist.rdata, &rdata, link); - RETERR(dns_rdatalist_tordataset(&rdatalist, &rdataset)); + CHECK(dns_rdatalist_tordataset(&rdatalist, &rdataset)); result = dns_db_addrdataset(db, node, version, 0, &rdataset, 0, NULL); if (result == DNS_R_UNCHANGED) { result = ISC_R_SUCCESS; } -failure: +cleanup: if (dns_rdataset_isassociated(&rdataset)) { dns_rdataset_disassociate(&rdataset); } diff -Nru bind9-9.18.41/lib/dns/nsec3.c bind9-9.18.44/lib/dns/nsec3.c --- bind9-9.18.41/lib/dns/nsec3.c 2025-10-18 10:21:03.140261094 +0000 +++ bind9-9.18.44/lib/dns/nsec3.c 2026-01-09 13:44:04.822038956 +0000 @@ -41,13 +41,6 @@ #include -#define CHECK(x) \ - do { \ - result = (x); \ - if (result != ISC_R_SUCCESS) \ - goto failure; \ - } while (0) - #define OPTOUT(x) (((x) & DNS_NSEC3FLAG_OPTOUT) != 0) #define CREATE(x) (((x) & DNS_NSEC3FLAG_CREATE) != 0) #define INITIAL(x) (((x) & DNS_NSEC3FLAG_INITIAL) != 0) @@ -444,22 +437,16 @@ continue; } - result = dns_difftuple_create(diff->mctx, DNS_DIFFOP_DEL, name, - rdataset.ttl, &rdata, &tuple); - if (result != ISC_R_SUCCESS) { - goto failure; - } - result = do_one_tuple(&tuple, db, version, diff); - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_DEL, name, + rdataset.ttl, &rdata, &tuple)); + CHECK(do_one_tuple(&tuple, db, version, diff)); } if (result != ISC_R_NOMORE) { - goto failure; + goto cleanup; } result = ISC_R_SUCCESS; -failure: +cleanup: dns_rdataset_disassociate(&rdataset); cleanup_node: dns_db_detachnode(db, &node); @@ -532,7 +519,7 @@ break; } } -failure: +cleanup: return result; } @@ -640,14 +627,14 @@ } else if (CREATE(nsec3param->flags) && OPTOUT(flags)) { result = dns_nsec3_delnsec3(db, version, name, nsec3param, diff); - goto failure; + goto cleanup; } else { maybe_remove_unsecure = true; } } else { dns_rdataset_disassociate(&rdataset); if (result != ISC_R_NOMORE) { - goto failure; + goto cleanup; } } } @@ -677,9 +664,7 @@ dns_rdataset_disassociate(&rdataset); continue; } - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(result); if (maybe_remove_unsecure) { dns_rdataset_disassociate(&rdataset); @@ -691,7 +676,7 @@ if (OPTOUT(nsec3.flags)) { result = dns_nsec3_delnsec3(db, version, name, nsec3param, diff); - goto failure; + goto cleanup; } goto addnsec3; } else { @@ -701,7 +686,7 @@ */ if (OPTOUT(nsec3.flags) && unsecure) { dns_rdataset_disassociate(&rdataset); - goto failure; + goto cleanup; } } @@ -795,7 +780,7 @@ break; } if (result != ISC_R_NOMORE) { - goto failure; + goto cleanup; } } @@ -824,9 +809,7 @@ dns_rdataset_disassociate(&rdataset); continue; } - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(result); old_next = nsec3.next; old_length = nsec3.next_length; @@ -886,7 +869,7 @@ /* result cannot be ISC_R_NOMORE here */ INSIST(result != ISC_R_NOMORE); -failure: +cleanup: if (dbit != NULL) { dns_dbiterator_destroy(&dbit); } @@ -960,7 +943,7 @@ result = ISC_R_SUCCESS; } -failure: +cleanup: if (dns_rdataset_isassociated(&rdataset)) { dns_rdataset_disassociate(&rdataset); } @@ -1036,7 +1019,7 @@ if (result == ISC_R_NOTFOUND) { *flag = false; result = ISC_R_SUCCESS; - goto failure; + goto cleanup; } for (result = dns_rdataset_first(&rdataset); result == ISC_R_SUCCESS; @@ -1056,7 +1039,7 @@ result = ISC_R_SUCCESS; } -failure: +cleanup: if (node != NULL) { dns_db_detachnode(db, &node); } @@ -1128,9 +1111,7 @@ if (result == ISC_R_NOTFOUND) { goto try_private; } - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(result); for (result = dns_rdataset_first(&rdataset); result == ISC_R_SUCCESS; result = dns_rdataset_next(&rdataset)) @@ -1157,23 +1138,23 @@ dns_rdata_reset(&rdata); } if (result != ISC_R_NOMORE) { - goto failure; + goto cleanup; } dns_rdataset_disassociate(&rdataset); try_private: if (privatetype == 0) { - goto success; + result = ISC_R_SUCCESS; + goto cleanup; } result = dns_db_findrdataset(db, node, ver, privatetype, 0, (isc_stdtime_t)0, &rdataset, NULL); if (result == ISC_R_NOTFOUND) { - goto success; - } - if (result != ISC_R_SUCCESS) { - goto failure; + result = ISC_R_SUCCESS; + goto cleanup; } + CHECK(result); for (result = dns_rdataset_first(&rdataset); result == ISC_R_SUCCESS; result = dns_rdataset_next(&rdataset)) @@ -1215,12 +1196,12 @@ } } if (result != ISC_R_NOMORE) { - goto failure; + goto cleanup; } -success: + result = ISC_R_SUCCESS; -failure: +cleanup: if (dns_rdataset_isassociated(&rdataset)) { dns_rdataset_disassociate(&rdataset); } @@ -1252,7 +1233,7 @@ result = dns_db_findrdataset(db, node, version, type, 0, 0, &prdataset, NULL); if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) { - goto failure; + CHECK(result); } result = dns_db_findrdataset(db, node, version, @@ -1261,9 +1242,7 @@ if (result == ISC_R_NOTFOUND) { goto try_private; } - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(result); /* * Update each active NSEC3 chain. @@ -1287,15 +1266,17 @@ nsecttl, unsecure, diff)); } if (result != ISC_R_NOMORE) { - goto failure; + goto cleanup; } dns_rdataset_disassociate(&rdataset); try_private: if (!dns_rdataset_isassociated(&prdataset)) { - goto success; + result = ISC_R_SUCCESS; + goto cleanup; } + /* * Update each active NSEC3 chain. */ @@ -1328,10 +1309,10 @@ nsecttl, unsecure, diff)); } if (result == ISC_R_NOMORE) { - success: result = ISC_R_SUCCESS; } -failure: + +cleanup: if (dns_rdataset_isassociated(&rdataset)) { dns_rdataset_disassociate(&rdataset); } @@ -1439,9 +1420,7 @@ if (result == ISC_R_NOTFOUND || result == DNS_R_PARTIALMATCH) { goto cleanup_orphaned_ents; } - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(result); CHECK(dns_dbiterator_current(dbit, &node, NULL)); CHECK(dns_dbiterator_pause(dbit)); @@ -1451,9 +1430,7 @@ if (result == ISC_R_NOTFOUND) { goto cleanup_orphaned_ents; } - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(result); /* * If we find a existing NSEC3 for this chain then save the @@ -1467,11 +1444,9 @@ } dns_rdataset_disassociate(&rdataset); if (result == ISC_R_NOMORE) { - goto success; - } - if (result != ISC_R_SUCCESS) { - goto failure; + result = ISC_R_SUCCESS; } + CHECK(result); /* * Find the previous NSEC3 and update it. @@ -1497,9 +1472,7 @@ dns_rdataset_disassociate(&rdataset); continue; } - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(result); /* * Delete the old previous NSEC3. @@ -1553,11 +1526,10 @@ salt_length)); result = dns_dbiterator_seek(dbit, hashname); if (result == ISC_R_NOTFOUND || result == DNS_R_PARTIALMATCH) { - goto success; - } - if (result != ISC_R_SUCCESS) { - goto failure; + result = ISC_R_SUCCESS; + goto cleanup; } + CHECK(result); CHECK(dns_dbiterator_current(dbit, &node, NULL)); CHECK(dns_dbiterator_pause(dbit)); @@ -1566,11 +1538,10 @@ (isc_stdtime_t)0, &rdataset, NULL); dns_db_detachnode(db, &node); if (result == ISC_R_NOTFOUND) { - goto success; - } - if (result != ISC_R_SUCCESS) { - goto failure; + result = ISC_R_SUCCESS; + goto cleanup; } + CHECK(result); result = find_nsec3(&nsec3, &rdataset, nsec3param); if (result == ISC_R_SUCCESS) { @@ -1580,11 +1551,9 @@ } dns_rdataset_disassociate(&rdataset); if (result == ISC_R_NOMORE) { - goto success; - } - if (result != ISC_R_SUCCESS) { - goto failure; + result = ISC_R_SUCCESS; } + CHECK(result); pass = 0; do { @@ -1607,9 +1576,7 @@ dns_rdataset_disassociate(&rdataset); continue; } - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(result); /* * Delete the old previous NSEC3. @@ -1642,10 +1609,9 @@ CHECK(delnsec3(db, version, hashname, nsec3param, diff)); } while (1); -success: result = ISC_R_SUCCESS; -failure: +cleanup: if (dbit != NULL) { dns_dbiterator_destroy(&dbit); } @@ -1689,9 +1655,7 @@ if (result == ISC_R_NOTFOUND) { goto try_private; } - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(result); /* * Update each active NSEC3 chain. @@ -1716,16 +1680,16 @@ try_private: if (privatetype == 0) { - goto success; + result = ISC_R_SUCCESS; + goto cleanup; } result = dns_db_findrdataset(db, node, version, privatetype, 0, 0, &rdataset, NULL); if (result == ISC_R_NOTFOUND) { - goto success; - } - if (result != ISC_R_SUCCESS) { - goto failure; + result = ISC_R_SUCCESS; + goto cleanup; } + CHECK(result); /* * Update each NSEC3 chain being built. @@ -1758,11 +1722,10 @@ CHECK(dns_nsec3_delnsec3(db, version, name, &nsec3param, diff)); } if (result == ISC_R_NOMORE) { - success: result = ISC_R_SUCCESS; } -failure: +cleanup: if (dns_rdataset_isassociated(&rdataset)) { dns_rdataset_disassociate(&rdataset); } diff -Nru bind9-9.18.41/lib/dns/opensslecdsa_link.c bind9-9.18.44/lib/dns/opensslecdsa_link.c --- bind9-9.18.41/lib/dns/opensslecdsa_link.c 2025-10-18 10:21:03.141261121 +0000 +++ bind9-9.18.44/lib/dns/opensslecdsa_link.c 2026-01-09 13:44:04.823038972 +0000 @@ -51,17 +51,17 @@ #error "P-384 group is not known (NID_secp384r1)" #endif /* ifndef NID_secp384r1 */ -#define DST_RET(a) \ - { \ - ret = a; \ - goto err; \ +#define DST_RET(a) \ + { \ + result = a; \ + goto cleanup; \ } #if OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_API_LEVEL >= 30000 static isc_result_t raw_key_to_ossl(unsigned int key_alg, int private, const unsigned char *key, size_t key_len, EVP_PKEY **pkey) { - isc_result_t ret; + isc_result_t result; int status; const char *groupname; OSSL_PARAM_BLD *bld = NULL; @@ -141,9 +141,9 @@ DST_R_OPENSSLFAILURE)); } - ret = ISC_R_SUCCESS; + result = ISC_R_SUCCESS; -err: +cleanup: if (params != NULL) { OSSL_PARAM_free(params); } @@ -157,14 +157,14 @@ BN_clear_free(priv); } - return ret; + return result; } #endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_API_LEVEL >= 30000 \ */ static isc_result_t opensslecdsa_createctx(dst_key_t *key, dst_context_t *dctx) { - isc_result_t ret = ISC_R_SUCCESS; + isc_result_t result = ISC_R_SUCCESS; EVP_MD_CTX *evp_md_ctx; const EVP_MD *type = NULL; @@ -205,8 +205,8 @@ dctx->ctxdata.evp_md_ctx = evp_md_ctx; -err: - return ret; +cleanup: + return result; } static void @@ -225,7 +225,7 @@ static isc_result_t opensslecdsa_adddata(dst_context_t *dctx, const isc_region_t *data) { - isc_result_t ret = ISC_R_SUCCESS; + isc_result_t result = ISC_R_SUCCESS; EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx; REQUIRE(dctx->key->key_alg == DST_ALG_ECDSA256 || @@ -250,8 +250,8 @@ } } -err: - return ret; +cleanup: + return result; } static int @@ -269,7 +269,7 @@ static isc_result_t opensslecdsa_sign(dst_context_t *dctx, isc_buffer_t *sig) { - isc_result_t ret; + isc_result_t result; dst_key_t *key = dctx->key; isc_region_t region; EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx; @@ -320,19 +320,19 @@ isc_region_consume(®ion, siglen / 2); ECDSA_SIG_free(ecdsasig); isc_buffer_add(sig, siglen); - ret = ISC_R_SUCCESS; + result = ISC_R_SUCCESS; -err: +cleanup: if (sigder != NULL && sigder_alloced != 0) { isc_mem_put(dctx->mctx, sigder, sigder_alloced); } - return ret; + return result; } static isc_result_t opensslecdsa_verify(dst_context_t *dctx, const isc_region_t *sig) { - isc_result_t ret; + isc_result_t result; dst_key_t *key = dctx->key; int status; unsigned char *cp = sig->base; @@ -388,19 +388,19 @@ switch (status) { case 1: - ret = ISC_R_SUCCESS; + result = ISC_R_SUCCESS; break; case 0: - ret = dst__openssl_toresult(DST_R_VERIFYFAILURE); + result = dst__openssl_toresult(DST_R_VERIFYFAILURE); break; default: - ret = dst__openssl_toresult3(dctx->category, - "EVP_DigestVerifyFinal", - DST_R_VERIFYFAILURE); + result = dst__openssl_toresult3(dctx->category, + "EVP_DigestVerifyFinal", + DST_R_VERIFYFAILURE); break; } -err: +cleanup: if (ecdsasig != NULL) { ECDSA_SIG_free(ecdsasig); } @@ -408,12 +408,12 @@ isc_mem_put(dctx->mctx, sigder, sigder_alloced); } - return ret; + return result; } static bool opensslecdsa_compare(const dst_key_t *key1, const dst_key_t *key2) { - bool ret; + bool result; EVP_PKEY *pkey1 = key1->keydata.pkey; EVP_PKEY *pkey2 = key2->keydata.pkey; #if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 @@ -464,9 +464,9 @@ ERR_clear_error(); } - ret = true; + result = true; -err: +cleanup: #if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 if (eckey1 != NULL) { EC_KEY_free(eckey1); @@ -483,12 +483,12 @@ } #endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */ - return ret; + return result; } static isc_result_t opensslecdsa_generate(dst_key_t *key, int unused, void (*callback)(int)) { - isc_result_t ret; + isc_result_t result; int status; EVP_PKEY *pkey = NULL; #if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 @@ -577,9 +577,9 @@ key->keydata.pkey = pkey; pkey = NULL; - ret = ISC_R_SUCCESS; + result = ISC_R_SUCCESS; -err: +cleanup: if (pkey != NULL) { EVP_PKEY_free(pkey); } @@ -596,12 +596,12 @@ } #endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */ - return ret; + return result; } static bool opensslecdsa_isprivate(const dst_key_t *key) { - bool ret; + bool result; EVP_PKEY *pkey; #if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 EC_KEY *eckey; @@ -620,22 +620,22 @@ #if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 eckey = EVP_PKEY_get1_EC_KEY(pkey); - ret = (eckey != NULL && EC_KEY_get0_private_key(eckey) != NULL); + result = (eckey != NULL && EC_KEY_get0_private_key(eckey) != NULL); if (eckey != NULL) { EC_KEY_free(eckey); } else { ERR_clear_error(); } #else - ret = (EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY, &priv) == - 1 && - priv != NULL); + result = (EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY, + &priv) == 1 && + priv != NULL); if (priv != NULL) { BN_clear_free(priv); } #endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */ - return ret; + return result; } static void @@ -650,7 +650,7 @@ static isc_result_t opensslecdsa_todns(const dst_key_t *key, isc_buffer_t *data) { - isc_result_t ret; + isc_result_t result; EVP_PKEY *pkey; #if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 EC_KEY *eckey = NULL; @@ -719,9 +719,9 @@ #endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */ isc_buffer_add(data, len); - ret = ISC_R_SUCCESS; + result = ISC_R_SUCCESS; -err: +cleanup: #if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 if (eckey != NULL) { EC_KEY_free(eckey); @@ -735,12 +735,12 @@ } #endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */ - return ret; + return result; } static isc_result_t opensslecdsa_fromdns(dst_key_t *key, isc_buffer_t *data) { - isc_result_t ret; + isc_result_t result; EVP_PKEY *pkey = NULL; isc_region_t r; #if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 @@ -803,29 +803,29 @@ DST_RET(dst__openssl_toresult(ISC_R_FAILURE)); } #else - ret = raw_key_to_ossl(key->key_alg, 0, r.base, len, &pkey); - if (ret != ISC_R_SUCCESS) { - DST_RET(ret); + result = raw_key_to_ossl(key->key_alg, 0, r.base, len, &pkey); + if (result != ISC_R_SUCCESS) { + DST_RET(result); } #endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */ isc_buffer_forward(data, len); key->keydata.pkey = pkey; key->key_size = len * 4; - ret = ISC_R_SUCCESS; + result = ISC_R_SUCCESS; -err: +cleanup: #if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 if (eckey != NULL) { EC_KEY_free(eckey); } #endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */ - return ret; + return result; } static isc_result_t opensslecdsa_tofile(const dst_key_t *key, const char *directory) { - isc_result_t ret; + isc_result_t result; EVP_PKEY *pkey; #if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 EC_KEY *eckey = NULL; @@ -895,9 +895,9 @@ } priv.nelements = i; - ret = dst__privstruct_writefile(key, &priv, directory); + result = dst__privstruct_writefile(key, &priv, directory); -err: +cleanup: if (buf != NULL && privkey != NULL) { isc_mem_put(key->mctx, buf, BN_num_bytes(privkey)); } @@ -911,7 +911,7 @@ } #endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */ - return ret; + return result; } #if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 @@ -940,7 +940,7 @@ #else static isc_result_t ecdsa_check(EVP_PKEY **pkey, EVP_PKEY *pubpkey) { - isc_result_t ret = ISC_R_FAILURE; + isc_result_t result = ISC_R_FAILURE; int status; size_t pkey_len = 0; BIGNUM *x = NULL; @@ -1057,7 +1057,7 @@ DST_RET(ISC_R_SUCCESS); } -err: +cleanup: if (ctx != NULL) { EVP_PKEY_CTX_free(ctx); } @@ -1077,7 +1077,7 @@ BN_clear_free(y); } - return ret; + return result; } #endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */ @@ -1186,7 +1186,7 @@ static isc_result_t opensslecdsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) { dst_private_t priv; - isc_result_t ret; + isc_result_t result; #if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 EC_KEY *eckey = NULL; EC_KEY *pubeckey = NULL; @@ -1200,11 +1200,8 @@ key->key_alg == DST_ALG_ECDSA384); /* read private key file */ - ret = dst__privstruct_parse(key, DST_ALG_ECDSA256, lexer, key->mctx, - &priv); - if (ret != ISC_R_SUCCESS) { - goto err; - } + CHECK(dst__privstruct_parse(key, DST_ALG_ECDSA256, lexer, key->mctx, + &priv)); if (key->external) { if (priv.nelements != 0 || pub == NULL) { @@ -1236,10 +1233,7 @@ } if (label != NULL) { - ret = opensslecdsa_fromlabel(key, engine, label, NULL); - if (ret != ISC_R_SUCCESS) { - goto err; - } + CHECK(opensslecdsa_fromlabel(key, engine, label, NULL)); #if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 eckey = EVP_PKEY_get1_EC_KEY(key->keydata.pkey); @@ -1249,28 +1243,22 @@ #endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */ } else { #if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 - ret = dst__key_to_eckey(key, &eckey); - if (ret != ISC_R_SUCCESS) { - goto err; - } + CHECK(dst__key_to_eckey(key, &eckey)); - ret = load_privkey_from_privstruct(eckey, &priv, privkey_index); + CHECK(load_privkey_from_privstruct(eckey, &priv, + privkey_index)); #else if (key->keydata.pkey != NULL) { EVP_PKEY_free(key->keydata.pkey); key->keydata.pkey = NULL; } - ret = raw_key_to_ossl(key->key_alg, 1, + CHECK(raw_key_to_ossl(key->key_alg, 1, priv.elements[privkey_index].data, priv.elements[privkey_index].length, - &key->keydata.pkey); + &key->keydata.pkey)); #endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */ - if (ret != ISC_R_SUCCESS) { - goto err; - } - finalize_key = true; } @@ -1284,7 +1272,7 @@ } if (finalize_key) { - ret = finalize_eckey(key, eckey, engine, label); + result = finalize_eckey(key, eckey, engine, label); } #else if (ecdsa_check(&key->keydata.pkey, @@ -1295,11 +1283,11 @@ } if (finalize_key) { - ret = finalize_eckey(key, engine, label); + result = finalize_eckey(key, engine, label); } #endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */ -err: +cleanup: #if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 if (pubeckey != NULL) { EC_KEY_free(pubeckey); @@ -1308,21 +1296,21 @@ EC_KEY_free(eckey); } #endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */ - if (ret != ISC_R_SUCCESS) { + if (result != ISC_R_SUCCESS) { key->keydata.generic = NULL; } dst__privstruct_free(&priv, key->mctx); isc_safe_memwipe(&priv, sizeof(priv)); - return ret; + return result; } static isc_result_t opensslecdsa_fromlabel(dst_key_t *key, const char *engine, const char *label, const char *pin) { #if !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000 - isc_result_t ret = ISC_R_SUCCESS; + isc_result_t result = ISC_R_SUCCESS; ENGINE *e; EC_KEY *eckey = NULL; EC_KEY *pubeckey = NULL; @@ -1395,7 +1383,7 @@ key->keydata.pkey = pkey; pkey = NULL; -err: +cleanup: if (pubpkey != NULL) { EVP_PKEY_free(pubpkey); } @@ -1409,7 +1397,7 @@ EC_KEY_free(eckey); } - return ret; + return result; #else UNUSED(key); UNUSED(engine); diff -Nru bind9-9.18.41/lib/dns/openssleddsa_link.c bind9-9.18.44/lib/dns/openssleddsa_link.c --- bind9-9.18.41/lib/dns/openssleddsa_link.c 2025-10-18 10:21:03.141261121 +0000 +++ bind9-9.18.44/lib/dns/openssleddsa_link.c 2026-01-09 13:44:04.823038972 +0000 @@ -38,10 +38,10 @@ #include "dst_parse.h" #include "openssl_shim.h" -#define DST_RET(a) \ - { \ - ret = a; \ - goto err; \ +#define DST_RET(a) \ + { \ + result = a; \ + goto cleanup; \ } #if HAVE_OPENSSL_ED25519 @@ -59,7 +59,7 @@ static isc_result_t raw_key_to_ossl(unsigned int key_alg, int private, const unsigned char *key, size_t *key_len, EVP_PKEY **pkey) { - isc_result_t ret; + isc_result_t result; int pkey_type = EVP_PKEY_NONE; size_t len = 0; @@ -79,9 +79,9 @@ return ISC_R_NOTIMPLEMENTED; } - ret = (private ? DST_R_INVALIDPRIVATEKEY : DST_R_INVALIDPUBLICKEY); + result = (private ? DST_R_INVALIDPRIVATEKEY : DST_R_INVALIDPUBLICKEY); if (*key_len < len) { - return ret; + return result; } if (private) { @@ -90,7 +90,7 @@ *pkey = EVP_PKEY_new_raw_public_key(pkey_type, NULL, key, len); } if (*pkey == NULL) { - return dst__openssl_toresult(ret); + return dst__openssl_toresult(result); } *key_len = len; @@ -156,7 +156,7 @@ static isc_result_t openssleddsa_sign(dst_context_t *dctx, isc_buffer_t *sig) { - isc_result_t ret; + isc_result_t result; dst_key_t *key = dctx->key; isc_region_t tbsreg; isc_region_t sigreg; @@ -196,19 +196,19 @@ DST_R_SIGNFAILURE)); } isc_buffer_add(sig, (unsigned int)siglen); - ret = ISC_R_SUCCESS; + result = ISC_R_SUCCESS; -err: +cleanup: EVP_MD_CTX_free(ctx); isc_buffer_free(&buf); dctx->ctxdata.generic = NULL; - return ret; + return result; } static isc_result_t openssleddsa_verify(dst_context_t *dctx, const isc_region_t *sig) { - isc_result_t ret; + isc_result_t result; dst_key_t *key = dctx->key; int status; isc_region_t tbsreg; @@ -254,23 +254,24 @@ switch (status) { case 1: - ret = ISC_R_SUCCESS; + result = ISC_R_SUCCESS; break; case 0: - ret = dst__openssl_toresult(DST_R_VERIFYFAILURE); + result = dst__openssl_toresult(DST_R_VERIFYFAILURE); break; default: - ret = dst__openssl_toresult3(dctx->category, "EVP_DigestVerify", - DST_R_VERIFYFAILURE); + result = dst__openssl_toresult3(dctx->category, + "EVP_DigestVerify", + DST_R_VERIFYFAILURE); break; } -err: +cleanup: EVP_MD_CTX_free(ctx); isc_buffer_free(&buf); dctx->ctxdata.generic = NULL; - return ret; + return result; } static bool @@ -294,7 +295,7 @@ static isc_result_t openssleddsa_generate(dst_key_t *key, int unused, void (*callback)(int)) { - isc_result_t ret; + isc_result_t result; EVP_PKEY *pkey = NULL; EVP_PKEY_CTX *ctx = NULL; int nid = 0, status; @@ -339,11 +340,11 @@ } key->keydata.pkey = pkey; - ret = ISC_R_SUCCESS; + result = ISC_R_SUCCESS; -err: +cleanup: EVP_PKEY_CTX_free(ctx); - return ret; + return result; } static bool @@ -412,7 +413,6 @@ static isc_result_t openssleddsa_fromdns(dst_key_t *key, isc_buffer_t *data) { - isc_result_t ret; isc_region_t r; size_t len; EVP_PKEY *pkey; @@ -426,10 +426,7 @@ } len = r.length; - ret = raw_key_to_ossl(key->key_alg, 0, r.base, &len, &pkey); - if (ret != ISC_R_SUCCESS) { - return ret; - } + RETERR(raw_key_to_ossl(key->key_alg, 0, r.base, &len, &pkey)); isc_buffer_forward(data, len); key->keydata.pkey = pkey; @@ -439,7 +436,7 @@ static isc_result_t openssleddsa_tofile(const dst_key_t *key, const char *directory) { - isc_result_t ret; + isc_result_t result; dst_private_t priv; unsigned char *buf = NULL; size_t len; @@ -492,13 +489,13 @@ } priv.nelements = i; - ret = dst__privstruct_writefile(key, &priv, directory); + result = dst__privstruct_writefile(key, &priv, directory); -err: +cleanup: if (buf != NULL) { isc_mem_put(key->mctx, buf, len); } - return ret; + return result; } static isc_result_t @@ -515,7 +512,7 @@ static isc_result_t openssleddsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) { dst_private_t priv; - isc_result_t ret; + isc_result_t result; int i, privkey_index = -1; const char *engine = NULL, *label = NULL; EVP_PKEY *pkey = NULL, *pubpkey = NULL; @@ -526,10 +523,7 @@ key->key_alg == DST_ALG_ED448); /* read private key file */ - ret = dst__privstruct_parse(key, DST_ALG_ED25519, lexer, mctx, &priv); - if (ret != ISC_R_SUCCESS) { - goto err; - } + CHECK(dst__privstruct_parse(key, DST_ALG_ED25519, lexer, mctx, &priv)); if (key->external) { if (priv.nelements != 0) { @@ -566,10 +560,7 @@ } if (label != NULL) { - ret = openssleddsa_fromlabel(key, engine, label, NULL); - if (ret != ISC_R_SUCCESS) { - goto err; - } + CHECK(openssleddsa_fromlabel(key, engine, label, NULL)); if (eddsa_check(key->keydata.pkey, pubpkey) != ISC_R_SUCCESS) { DST_RET(DST_R_INVALIDPRIVATEKEY); } @@ -581,30 +572,27 @@ } len = priv.elements[privkey_index].length; - ret = raw_key_to_ossl(key->key_alg, 1, - priv.elements[privkey_index].data, &len, &pkey); - if (ret != ISC_R_SUCCESS) { - goto err; - } + CHECK(raw_key_to_ossl(key->key_alg, 1, + priv.elements[privkey_index].data, &len, &pkey)); if (eddsa_check(pkey, pubpkey) != ISC_R_SUCCESS) { EVP_PKEY_free(pkey); DST_RET(DST_R_INVALIDPRIVATEKEY); } key->keydata.pkey = pkey; key->key_size = len * 8; - ret = ISC_R_SUCCESS; + result = ISC_R_SUCCESS; -err: +cleanup: dst__privstruct_free(&priv, mctx); isc_safe_memwipe(&priv, sizeof(priv)); - return ret; + return result; } static isc_result_t openssleddsa_fromlabel(dst_key_t *key, const char *engine, const char *label, const char *pin) { #if !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000 - isc_result_t ret; + isc_result_t result; ENGINE *e; EVP_PKEY *pkey = NULL, *pubpkey = NULL; int baseid = EVP_PKEY_NONE; @@ -654,16 +642,16 @@ key->key_size = EVP_PKEY_bits(pkey); key->keydata.pkey = pkey; pkey = NULL; - ret = ISC_R_SUCCESS; + result = ISC_R_SUCCESS; -err: +cleanup: if (pubpkey != NULL) { EVP_PKEY_free(pubpkey); } if (pkey != NULL) { EVP_PKEY_free(pkey); } - return ret; + return result; #else /* if !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000 */ UNUSED(key); UNUSED(engine); diff -Nru bind9-9.18.41/lib/dns/opensslrsa_link.c bind9-9.18.44/lib/dns/opensslrsa_link.c --- bind9-9.18.41/lib/dns/opensslrsa_link.c 2025-10-18 10:21:03.142261148 +0000 +++ bind9-9.18.44/lib/dns/opensslrsa_link.c 2026-01-09 13:44:04.823038972 +0000 @@ -42,10 +42,10 @@ #include "dst_parse.h" #include "openssl_shim.h" -#define DST_RET(a) \ - { \ - ret = a; \ - goto err; \ +#define DST_RET(a) \ + { \ + result = a; \ + goto cleanup; \ } static isc_result_t @@ -256,7 +256,7 @@ static bool opensslrsa_compare(const dst_key_t *key1, const dst_key_t *key2) { - bool ret; + bool result; int status; EVP_PKEY *pkey1 = key1->keydata.pkey; EVP_PKEY *pkey2 = key2->keydata.pkey; @@ -323,9 +323,9 @@ } } - ret = true; + result = true; -err: +cleanup: #if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 if (rsa1 != NULL) { RSA_free(rsa1); @@ -354,7 +354,7 @@ } #endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */ - return ret; + return result; } #if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 @@ -392,7 +392,7 @@ static isc_result_t opensslrsa_generate(dst_key_t *key, int exp, void (*callback)(int)) { - isc_result_t ret; + isc_result_t result; union { void *dptr; void (*fptr)(int); @@ -500,9 +500,9 @@ key->keydata.pkey = pkey; pkey = NULL; - ret = ISC_R_SUCCESS; + result = ISC_R_SUCCESS; -err: +cleanup: if (pkey != NULL) { EVP_PKEY_free(pkey); } @@ -521,7 +521,7 @@ if (e != NULL) { BN_free(e); } - return ret; + return result; } static bool @@ -584,7 +584,7 @@ isc_region_t r; unsigned int e_bytes; unsigned int mod_bytes; - isc_result_t ret; + isc_result_t result; EVP_PKEY *pkey; #if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 RSA *rsa; @@ -641,8 +641,8 @@ isc_buffer_add(data, e_bytes + mod_bytes); - ret = ISC_R_SUCCESS; -err: + result = ISC_R_SUCCESS; +cleanup: #if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 if (rsa != NULL) { RSA_free(rsa); @@ -655,12 +655,12 @@ BN_free(n); } #endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */ - return ret; + return result; } static isc_result_t opensslrsa_fromdns(dst_key_t *key, isc_buffer_t *data) { - isc_result_t ret; + isc_result_t result; int status; isc_region_t r; unsigned int e_bytes; @@ -778,9 +778,9 @@ key->keydata.pkey = pkey; pkey = NULL; - ret = ISC_R_SUCCESS; + result = ISC_R_SUCCESS; -err: +cleanup: #if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 if (rsa != NULL) { @@ -807,12 +807,12 @@ EVP_PKEY_free(pkey); } - return ret; + return result; } static isc_result_t opensslrsa_tofile(const dst_key_t *key, const char *directory) { - isc_result_t ret; + isc_result_t result; dst_private_t priv = { 0 }; unsigned char *bufs[8] = { NULL }; unsigned short i = 0; @@ -952,9 +952,9 @@ } priv.nelements = i; - ret = dst__privstruct_writefile(key, &priv, directory); + result = dst__privstruct_writefile(key, &priv, directory); -err: +cleanup: for (i = 0; i < ARRAY_SIZE(bufs); i++) { if (bufs[i] != NULL) { isc_mem_put(key->mctx, bufs[i], @@ -990,7 +990,7 @@ } #endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */ - return ret; + return result; } #if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 @@ -1053,7 +1053,7 @@ #else static isc_result_t rsa_check(EVP_PKEY *pkey, EVP_PKEY *pubpkey) { - isc_result_t ret = ISC_R_FAILURE; + isc_result_t result = ISC_R_FAILURE; int status; BIGNUM *n1 = NULL, *n2 = NULL; BIGNUM *e1 = NULL, *e2 = NULL; @@ -1101,7 +1101,7 @@ DST_RET(ISC_R_SUCCESS); } -err: +cleanup: if (n1 != NULL) { BN_free(n1); } @@ -1115,14 +1115,14 @@ BN_free(e2); } - return ret; + return result; } #endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */ static isc_result_t opensslrsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) { dst_private_t priv; - isc_result_t ret; + isc_result_t result; int i; #if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 RSA *rsa = NULL, *pubrsa = NULL; @@ -1152,10 +1152,7 @@ mctx = key->mctx; /* read private key file */ - ret = dst__privstruct_parse(key, DST_ALG_RSA, lexer, mctx, &priv); - if (ret != ISC_R_SUCCESS) { - goto err; - } + CHECK(dst__privstruct_parse(key, DST_ALG_RSA, lexer, mctx, &priv)); if (key->external) { if (priv.nelements != 0 || pub == NULL) { @@ -1405,7 +1402,7 @@ key->keydata.pkey = pkey; pkey = NULL; -err: +cleanup: if (pkey != NULL) { EVP_PKEY_free(pkey); } @@ -1451,14 +1448,14 @@ BN_clear_free(iqmp); } #endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */ - if (ret != ISC_R_SUCCESS) { + if (result != ISC_R_SUCCESS) { key->keydata.generic = NULL; } dst__privstruct_free(&priv, mctx); isc_safe_memwipe(&priv, sizeof(priv)); - return ret; + return result; } static isc_result_t @@ -1466,7 +1463,7 @@ const char *pin) { #if !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000 ENGINE *e = NULL; - isc_result_t ret = ISC_R_SUCCESS; + isc_result_t result = ISC_R_SUCCESS; EVP_PKEY *pkey = NULL, *pubpkey = NULL; RSA *rsa = NULL, *pubrsa = NULL; const BIGNUM *ex = NULL; @@ -1520,7 +1517,7 @@ key->keydata.pkey = pkey; pkey = NULL; -err: +cleanup: if (rsa != NULL) { RSA_free(rsa); } @@ -1533,7 +1530,7 @@ if (pubpkey != NULL) { EVP_PKEY_free(pubpkey); } - return ret; + return result; #else /* if !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000 */ UNUSED(key); UNUSED(engine); @@ -1651,7 +1648,7 @@ const EVP_MD *type = NULL; const unsigned char *sig = NULL; int status; - isc_result_t ret = ISC_R_SUCCESS; + isc_result_t result = ISC_R_SUCCESS; size_t len; #if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 RSA *rsa = NULL; @@ -1769,7 +1766,7 @@ DST_RET(ISC_R_NOTIMPLEMENTED); } -err: +cleanup: BN_free(e); BN_free(n); #if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 @@ -1794,7 +1791,7 @@ EVP_MD_CTX_destroy(evp_md_ctx); } ERR_clear_error(); - return ret; + return result; } isc_result_t diff -Nru bind9-9.18.41/lib/dns/private.c bind9-9.18.44/lib/dns/private.c --- bind9-9.18.41/lib/dns/private.c 2025-10-18 10:21:03.142261148 +0000 +++ bind9-9.18.44/lib/dns/private.c 2026-01-09 13:44:04.823038972 +0000 @@ -44,13 +44,6 @@ #define INITIAL(x) (((x) & DNS_NSEC3FLAG_INITIAL) != 0) #define NONSEC(x) (((x) & DNS_NSEC3FLAG_NONSEC) != 0) -#define CHECK(x) \ - do { \ - result = (x); \ - if (result != ISC_R_SUCCESS) \ - goto failure; \ - } while (0) - /* * Work out if 'param' should be ignored or not (i.e. it is in the process * of being removed). @@ -126,14 +119,14 @@ result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec, 0, (isc_stdtime_t)0, &nsecset, NULL); - if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) { - goto failure; + if (result != ISC_R_NOTFOUND) { + CHECK(result); } result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3param, 0, (isc_stdtime_t)0, &nsec3paramset, NULL); - if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) { - goto failure; + if (result != ISC_R_NOTFOUND) { + CHECK(result); } if (dns_rdataset_isassociated(&nsecset) && @@ -152,8 +145,8 @@ result = dns_db_findrdataset(db, node, ver, privatetype, 0, (isc_stdtime_t)0, &privateset, NULL); - if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) { - goto failure; + if (result != ISC_R_NOTFOUND) { + CHECK(result); } } @@ -318,7 +311,7 @@ success: result = ISC_R_SUCCESS; -failure: +cleanup: if (dns_rdataset_isassociated(&nsecset)) { dns_rdataset_disassociate(&nsecset); } @@ -412,6 +405,6 @@ isc_buffer_putuint8(buf, 0); result = ISC_R_SUCCESS; -failure: +cleanup: return result; } diff -Nru bind9-9.18.41/lib/dns/rbt.c bind9-9.18.44/lib/dns/rbt.c --- bind9-9.18.41/lib/dns/rbt.c 2025-10-18 10:21:03.142261148 +0000 +++ bind9-9.18.44/lib/dns/rbt.c 2026-01-09 13:44:04.824038989 +0000 @@ -42,13 +42,6 @@ #include #include -#define CHECK(x) \ - do { \ - result = (x); \ - if (result != ISC_R_SUCCESS) \ - goto cleanup; \ - } while (0) - #define RBT_MAGIC ISC_MAGIC('R', 'B', 'T', '+') #define VALID_RBT(rbt) ISC_MAGIC_VALID(rbt, RBT_MAGIC) diff -Nru bind9-9.18.41/lib/dns/rbtdb.c bind9-9.18.44/lib/dns/rbtdb.c --- bind9-9.18.41/lib/dns/rbtdb.c 2025-10-18 10:21:03.143261174 +0000 +++ bind9-9.18.44/lib/dns/rbtdb.c 2026-01-09 13:44:04.825039005 +0000 @@ -65,13 +65,6 @@ #define RBTDB_MAGIC ISC_MAGIC('R', 'B', 'D', '4') -#define CHECK(op) \ - do { \ - result = (op); \ - if (result != ISC_R_SUCCESS) \ - goto failure; \ - } while (0) - /*% * Note that "impmagic" is not the first four bytes of the struct, so * ISC_MAGIC_VALID cannot be used. @@ -9589,11 +9582,12 @@ resume_iteration(rbtdbiter); } - dereference_iter_node(rbtdbiter); - name = dns_fixedname_name(&rbtdbiter->name); origin = dns_fixedname_name(&rbtdbiter->origin); result = dns_rbtnodechain_prev(rbtdbiter->current, name, origin); + + dereference_iter_node(rbtdbiter); + if (rbtdbiter->current == &rbtdbiter->nsec3chain && (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN)) { diff -Nru bind9-9.18.41/lib/dns/rcode.c bind9-9.18.44/lib/dns/rcode.c --- bind9-9.18.41/lib/dns/rcode.c 2025-10-18 10:21:03.144261201 +0000 +++ bind9-9.18.44/lib/dns/rcode.c 2026-01-09 13:44:04.825039005 +0000 @@ -37,13 +37,6 @@ #include #include -#define RETERR(x) \ - do { \ - isc_result_t _r = (x); \ - if (_r != ISC_R_SUCCESS) \ - return ((_r)); \ - } while (0) - #define NUMBERSIZE sizeof("037777777777") /* 2^32-1 octal + NUL */ #define TOTEXTONLY 0x01 diff -Nru bind9-9.18.41/lib/dns/rdata/generic/amtrelay_260.c bind9-9.18.44/lib/dns/rdata/generic/amtrelay_260.c --- bind9-9.18.41/lib/dns/rdata/generic/amtrelay_260.c 2025-10-18 10:21:03.145261228 +0000 +++ bind9-9.18.44/lib/dns/rdata/generic/amtrelay_260.c 2026-01-09 13:44:04.826039022 +0000 @@ -68,21 +68,22 @@ RETERR(uint8_tobuffer(token.value.as_ulong | (discovery << 7), target)); gateway = token.value.as_ulong; - if (gateway == 0) { - return ISC_R_SUCCESS; - } - - if (gateway > 3) { - return ISC_R_NOTIMPLEMENTED; - } - /* - * Gateway. + * Gateway (must exist). */ RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string, false)); + if (gateway > 3) { + return ISC_R_NOTIMPLEMENTED; + } + switch (gateway) { + case 0: + if (strcmp(DNS_AS_STR(token), ".") != 0) { + RETTOK(DNS_R_SYNTAX); + } + return ISC_R_SUCCESS; case 1: if (inet_pton(AF_INET, DNS_AS_STR(token), &addr) != 1) { RETTOK(DNS_R_BADDOTTEDQUAD); @@ -128,7 +129,6 @@ unsigned char precedence; unsigned char discovery; unsigned char gateway; - const char *space; UNUSED(tctx); @@ -154,9 +154,8 @@ gateway = uint8_fromregion(®ion); discovery = gateway >> 7; gateway &= 0x7f; - space = (gateway != 0U) ? " " : ""; isc_region_consume(®ion, 1); - snprintf(buf, sizeof(buf), "%u %u%s", discovery, gateway, space); + snprintf(buf, sizeof(buf), "%u %u ", discovery, gateway); RETERR(str_totext(buf, target)); /* @@ -164,7 +163,8 @@ */ switch (gateway) { case 0: - break; + return str_totext(".", target); + case 1: return inet_totext(AF_INET, tctx->flags, ®ion, target); diff -Nru bind9-9.18.41/lib/dns/rdata/generic/brid_68.c bind9-9.18.44/lib/dns/rdata/generic/brid_68.c --- bind9-9.18.41/lib/dns/rdata/generic/brid_68.c 2025-10-18 10:21:03.145261228 +0000 +++ bind9-9.18.44/lib/dns/rdata/generic/brid_68.c 2026-01-09 13:44:04.827039039 +0000 @@ -85,7 +85,7 @@ static isc_result_t towire_brid(ARGS_TOWIRE) { REQUIRE(rdata->type == dns_rdatatype_brid); - REQUIRE(rdata->length >= 3); + REQUIRE(rdata->length > 0); UNUSED(cctx); diff -Nru bind9-9.18.41/lib/dns/rdata/generic/hhit_67.c bind9-9.18.44/lib/dns/rdata/generic/hhit_67.c --- bind9-9.18.41/lib/dns/rdata/generic/hhit_67.c 2025-10-18 10:21:03.149261335 +0000 +++ bind9-9.18.44/lib/dns/rdata/generic/hhit_67.c 2026-01-09 13:44:04.830039088 +0000 @@ -85,7 +85,7 @@ static isc_result_t towire_hhit(ARGS_TOWIRE) { REQUIRE(rdata->type == dns_rdatatype_hhit); - REQUIRE(rdata->length >= 3); + REQUIRE(rdata->length > 0); UNUSED(cctx); diff -Nru bind9-9.18.41/lib/dns/rdata.c bind9-9.18.44/lib/dns/rdata.c --- bind9-9.18.41/lib/dns/rdata.c 2025-10-18 10:21:03.144261201 +0000 +++ bind9-9.18.44/lib/dns/rdata.c 2026-01-09 13:44:04.825039005 +0000 @@ -48,13 +48,6 @@ #include #include -#define RETERR(x) \ - do { \ - isc_result_t _r = (x); \ - if (_r != ISC_R_SUCCESS) \ - return ((_r)); \ - } while (0) - #define RETTOK(x) \ do { \ isc_result_t _r = (x); \ @@ -64,13 +57,6 @@ } \ } while (0) -#define CHECK(op) \ - do { \ - result = (op); \ - if (result != ISC_R_SUCCESS) \ - goto cleanup; \ - } while (0) - #define CHECKTOK(op) \ do { \ result = (op); \ diff -Nru bind9-9.18.41/lib/dns/tkey.c bind9-9.18.44/lib/dns/tkey.c --- bind9-9.18.41/lib/dns/tkey.c 2025-10-18 10:21:03.167261816 +0000 +++ bind9-9.18.44/lib/dns/tkey.c 2026-01-09 13:44:04.848039385 +0000 @@ -54,13 +54,6 @@ #define TEMP_BUFFER_SZ 8192 #define TKEY_RANDOM_AMOUNT 16 -#define RETERR(x) \ - do { \ - result = (x); \ - if (result != ISC_R_SUCCESS) \ - goto failure; \ - } while (0) - static void tkey_log(const char *fmt, ...) ISC_FORMAT_PRINTF(1, 2); @@ -167,7 +160,7 @@ dns_rdataset_t *newset = NULL; isc_buffer_t *tmprdatabuf = NULL; - RETERR(dns_message_gettemprdata(msg, &newrdata)); + CHECK(dns_message_gettemprdata(msg, &newrdata)); dns_rdata_toregion(rdata, &r); isc_buffer_allocate(msg->mctx, &tmprdatabuf, r.length); @@ -176,17 +169,17 @@ dns_rdata_fromregion(newrdata, rdata->rdclass, rdata->type, &newr); dns_message_takebuffer(msg, &tmprdatabuf); - RETERR(dns_message_gettempname(msg, &newname)); + CHECK(dns_message_gettempname(msg, &newname)); dns_name_copy(name, newname); - RETERR(dns_message_gettemprdatalist(msg, &newlist)); + CHECK(dns_message_gettemprdatalist(msg, &newlist)); newlist->rdclass = newrdata->rdclass; newlist->type = newrdata->type; newlist->ttl = ttl; ISC_LIST_APPEND(newlist->rdata, newrdata, link); - RETERR(dns_message_gettemprdataset(msg, &newset)); - RETERR(dns_rdatalist_tordataset(newlist, newset)); + CHECK(dns_message_gettemprdataset(msg, &newset)); + CHECK(dns_rdatalist_tordataset(newlist, newset)); ISC_LIST_INIT(newname->list); ISC_LIST_APPEND(newname->list, newset, link); @@ -195,7 +188,7 @@ return ISC_R_SUCCESS; -failure: +cleanup: if (newrdata != NULL) { if (ISC_LINK_LINKED(newrdata, link)) { INSIST(newlist != NULL); @@ -430,10 +423,10 @@ } } - RETERR(add_rdata_to_list(msg, keyname, &keyrdata, ttl, namelist)); + CHECK(add_rdata_to_list(msg, keyname, &keyrdata, ttl, namelist)); isc_buffer_init(&ourkeybuf, keydata, sizeof(keydata)); - RETERR(dst_key_todns(tctx->dhkey, &ourkeybuf)); + CHECK(dst_key_todns(tctx->dhkey, &ourkeybuf)); isc_buffer_usedregion(&ourkeybuf, &ourkeyr); dns_rdata_fromregion(&ourkeyrdata, dns_rdataclass_any, dns_rdatatype_key, &ourkeyr); @@ -444,16 +437,16 @@ /* * XXXBEW The TTL should be obtained from the database, if it exists. */ - RETERR(add_rdata_to_list(msg, &ourname, &ourkeyrdata, 0, namelist)); + CHECK(add_rdata_to_list(msg, &ourname, &ourkeyrdata, 0, namelist)); - RETERR(dst_key_secretsize(tctx->dhkey, &sharedsize)); + CHECK(dst_key_secretsize(tctx->dhkey, &sharedsize)); isc_buffer_allocate(msg->mctx, &shared, sharedsize); result = dst_key_computesecret(pubkey, tctx->dhkey, shared); if (result != ISC_R_SUCCESS) { tkey_log("process_dhtkey: failed to compute shared secret: %s", isc_result_totext(result)); - goto failure; + goto cleanup; } dst_key_free(&pubkey); @@ -467,10 +460,10 @@ r.length = TKEY_RANDOM_AMOUNT; r2.base = tkeyin->key; r2.length = tkeyin->keylen; - RETERR(compute_secret(shared, &r2, &r, &secret)); + CHECK(compute_secret(shared, &r2, &r, &secret)); isc_buffer_free(&shared); - RETERR(dns_tsigkey_create( + CHECK(dns_tsigkey_create( name, &tkeyin->algorithm, isc_buffer_base(&secret), isc_buffer_usedlength(&secret), true, signer, tkeyin->inception, tkeyin->expire, ring->mctx, ring, NULL)); @@ -484,7 +477,7 @@ return ISC_R_SUCCESS; -failure: +cleanup: if (!ISC_LIST_EMPTY(*namelist)) { free_namelist(msg, namelist); } @@ -566,7 +559,7 @@ return ISC_R_SUCCESS; } if (result != DNS_R_CONTINUE && result != ISC_R_SUCCESS) { - goto failure; + CHECK(result); } /* * XXXDCL Section 4.1.3: Limit GSS_S_CONTINUE_NEEDED to 10 times. @@ -584,8 +577,8 @@ #endif /* HAVE_GSSAPI */ uint32_t expire; - RETERR(dst_key_fromgssapi(name, gss_ctx, ring->mctx, &dstkey, - &intoken)); + CHECK(dst_key_fromgssapi(name, gss_ctx, ring->mctx, &dstkey, + &intoken)); /* * Limit keys to 1 hour or the context's lifetime whichever * is smaller. @@ -597,7 +590,7 @@ expire = now + lifetime; } #endif /* HAVE_GSSAPI */ - RETERR(dns_tsigkey_createfromkey( + CHECK(dns_tsigkey_createfromkey( name, &tkeyin->algorithm, dstkey, true, principal, now, expire, ring->mctx, ring, &tsigkey)); dst_key_free(&dstkey); @@ -639,7 +632,7 @@ return ISC_R_SUCCESS; -failure: +cleanup: if (tsigkey != NULL) { dns_tsigkey_detach(&tsigkey); } @@ -743,26 +736,23 @@ dns_rdatatype_tkey, 0, &name, &tkeyset) != ISC_R_SUCCESS) { - result = DNS_R_FORMERR; tkey_log("dns_tkey_processquery: couldn't find a TKEY " "matching the question"); - goto failure; + CHECK(DNS_R_FORMERR); } } result = dns_rdataset_first(tkeyset); if (result != ISC_R_SUCCESS) { - result = DNS_R_FORMERR; - goto failure; + CHECK(DNS_R_FORMERR); } dns_rdata_init(&rdata); dns_rdataset_current(tkeyset, &rdata); - RETERR(dns_rdata_tostruct(&rdata, &tkeyin, NULL)); + CHECK(dns_rdata_tostruct(&rdata, &tkeyin, NULL)); freetkeyin = true; if (tkeyin.error != dns_rcode_noerror) { - result = DNS_R_FORMERR; - goto failure; + CHECK(DNS_R_FORMERR); } /* @@ -779,8 +769,7 @@ } else { tkey_log("dns_tkey_processquery: query was not " "properly signed - rejecting"); - result = DNS_R_FORMERR; - goto failure; + CHECK(DNS_R_FORMERR); } } else { signer = &tsigner; @@ -814,8 +803,7 @@ if (tctx->domain == NULL && tkeyin.mode != DNS_TKEYMODE_GSSAPI) { tkey_log("dns_tkey_processquery: tkey-domain not set"); - result = DNS_R_REFUSED; - goto failure; + CHECK(DNS_R_REFUSED); } keyname = dns_fixedname_initname(&fkeyname); @@ -843,35 +831,25 @@ } isc_buffer_init(&b, randomtext, sizeof(randomtext)); isc_buffer_add(&b, sizeof(randomtext)); - result = dns_name_fromtext(keyname, &b, NULL, 0, NULL); - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(dns_name_fromtext(keyname, &b, NULL, 0, NULL)); } if (tkeyin.mode == DNS_TKEYMODE_GSSAPI) { /* Yup. This is a hack */ - result = dns_name_concatenate(keyname, dns_rootname, - keyname, NULL); - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(dns_name_concatenate(keyname, dns_rootname, + keyname, NULL)); } else { - result = dns_name_concatenate(keyname, tctx->domain, - keyname, NULL); - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(dns_name_concatenate(keyname, tctx->domain, + keyname, NULL)); } result = dns_tsigkey_find(&tsigkey, keyname, NULL, ring); - if (result == ISC_R_SUCCESS) { tkeyout.error = dns_tsigerror_badname; dns_tsigkey_detach(&tsigkey); goto failure_with_tkey; } else if (result != ISC_R_NOTFOUND) { - goto failure; + CHECK(result); } } else { keyname = qname; @@ -880,23 +858,23 @@ switch (tkeyin.mode) { case DNS_TKEYMODE_DIFFIEHELLMAN: tkeyout.error = dns_rcode_noerror; - RETERR(process_dhtkey(msg, signer, keyname, &tkeyin, tctx, - &tkeyout, ring, &namelist)); + CHECK(process_dhtkey(msg, signer, keyname, &tkeyin, tctx, + &tkeyout, ring, &namelist)); break; case DNS_TKEYMODE_GSSAPI: tkeyout.error = dns_rcode_noerror; - RETERR(process_gsstkey(msg, keyname, &tkeyin, tctx, &tkeyout, - ring)); + CHECK(process_gsstkey(msg, keyname, &tkeyin, tctx, &tkeyout, + ring)); break; case DNS_TKEYMODE_DELETE: tkeyout.error = dns_rcode_noerror; - RETERR(process_deletetkey(signer, keyname, &tkeyin, &tkeyout, - ring)); + CHECK(process_deletetkey(signer, keyname, &tkeyin, &tkeyout, + ring)); break; case DNS_TKEYMODE_SERVERASSIGNED: case DNS_TKEYMODE_RESOLVERASSIGNED: result = DNS_R_NOTIMP; - goto failure; + goto cleanup; default: tkeyout.error = dns_tsigerror_badmode; } @@ -920,13 +898,11 @@ if (tkeyout.other != NULL) { isc_mem_put(tkeyout.mctx, tkeyout.other, tkeyout.otherlen); } - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(result); - RETERR(add_rdata_to_list(msg, keyname, &rdata, 0, &namelist)); + CHECK(add_rdata_to_list(msg, keyname, &rdata, 0, &namelist)); - RETERR(dns_message_reply(msg, true)); + CHECK(dns_message_reply(msg, true)); name = ISC_LIST_HEAD(namelist); while (name != NULL) { @@ -938,7 +914,7 @@ return ISC_R_SUCCESS; -failure: +cleanup: if (freetkeyin) { dns_rdata_freestruct(&tkeyin); @@ -964,28 +940,28 @@ REQUIRE(name != NULL); REQUIRE(tkey != NULL); - RETERR(dns_message_gettempname(msg, &qname)); - RETERR(dns_message_gettempname(msg, &aname)); + CHECK(dns_message_gettempname(msg, &qname)); + CHECK(dns_message_gettempname(msg, &aname)); - RETERR(dns_message_gettemprdataset(msg, &question)); + CHECK(dns_message_gettemprdataset(msg, &question)); dns_rdataset_makequestion(question, dns_rdataclass_any, dns_rdatatype_tkey); len = 16 + tkey->algorithm.length + tkey->keylen + tkey->otherlen; isc_buffer_allocate(msg->mctx, &dynbuf, len); - RETERR(dns_message_gettemprdata(msg, &rdata)); + CHECK(dns_message_gettemprdata(msg, &rdata)); - RETERR(dns_rdata_fromstruct(rdata, dns_rdataclass_any, - dns_rdatatype_tkey, tkey, dynbuf)); + CHECK(dns_rdata_fromstruct(rdata, dns_rdataclass_any, + dns_rdatatype_tkey, tkey, dynbuf)); dns_message_takebuffer(msg, &dynbuf); - RETERR(dns_message_gettemprdatalist(msg, &tkeylist)); + CHECK(dns_message_gettemprdatalist(msg, &tkeylist)); tkeylist->rdclass = dns_rdataclass_any; tkeylist->type = dns_rdatatype_tkey; ISC_LIST_APPEND(tkeylist->rdata, rdata, link); - RETERR(dns_message_gettemprdataset(msg, &tkeyset)); - RETERR(dns_rdatalist_tordataset(tkeylist, tkeyset)); + CHECK(dns_message_gettemprdataset(msg, &tkeyset)); + CHECK(dns_rdatalist_tordataset(tkeylist, tkeyset)); dns_name_copy(name, qname); dns_name_copy(name, aname); @@ -1007,7 +983,7 @@ return ISC_R_SUCCESS; -failure: +cleanup: if (qname != NULL) { dns_message_puttempname(msg, &qname); } @@ -1079,11 +1055,11 @@ tkey.other = NULL; tkey.otherlen = 0; - RETERR(buildquery(msg, name, &tkey, false)); + CHECK(buildquery(msg, name, &tkey, false)); - RETERR(dns_message_gettemprdata(msg, &rdata)); + CHECK(dns_message_gettemprdata(msg, &rdata)); isc_buffer_allocate(msg->mctx, &dynbuf, 1024); - RETERR(dst_key_todns(key, dynbuf)); + CHECK(dst_key_todns(key, dynbuf)); isc_buffer_usedregion(dynbuf, &r); dns_rdata_fromregion(rdata, dns_rdataclass_any, dns_rdatatype_key, &r); dns_message_takebuffer(msg, &dynbuf); @@ -1092,7 +1068,7 @@ dns_name_clone(dst_key_name(key), &keyname); ISC_LIST_INIT(namelist); - RETERR(add_rdata_to_list(msg, &keyname, rdata, 0, &namelist)); + CHECK(add_rdata_to_list(msg, &keyname, rdata, 0, &namelist)); item = ISC_LIST_HEAD(namelist); while (item != NULL) { dns_name_t *next = ISC_LIST_NEXT(item, link); @@ -1103,7 +1079,7 @@ return ISC_R_SUCCESS; -failure: +cleanup: if (dynbuf != NULL) { isc_buffer_free(&dynbuf); @@ -1242,12 +1218,12 @@ if (rmsg->rcode != dns_rcode_noerror) { return dns_result_fromrcode(rmsg->rcode); } - RETERR(find_tkey(rmsg, &tkeyname, &rtkeyrdata, DNS_SECTION_ANSWER)); - RETERR(dns_rdata_tostruct(&rtkeyrdata, &rtkey, NULL)); + CHECK(find_tkey(rmsg, &tkeyname, &rtkeyrdata, DNS_SECTION_ANSWER)); + CHECK(dns_rdata_tostruct(&rtkeyrdata, &rtkey, NULL)); freertkey = true; - RETERR(find_tkey(qmsg, &tempname, &qtkeyrdata, DNS_SECTION_ADDITIONAL)); - RETERR(dns_rdata_tostruct(&qtkeyrdata, &qtkey, NULL)); + CHECK(find_tkey(qmsg, &tempname, &qtkeyrdata, DNS_SECTION_ADDITIONAL)); + CHECK(dns_rdata_tostruct(&qtkeyrdata, &qtkey, NULL)); if (rtkey.error != dns_rcode_noerror || rtkey.mode != DNS_TKEYMODE_DIFFIEHELLMAN || @@ -1257,9 +1233,8 @@ { tkey_log("dns_tkey_processdhresponse: tkey mode invalid " "or error set(1)"); - result = DNS_R_INVALIDTKEY; dns_rdata_freestruct(&qtkey); - goto failure; + CHECK(DNS_R_INVALIDTKEY); } dns_rdata_freestruct(&qtkey); @@ -1269,9 +1244,9 @@ ourkeyname = NULL; ourkeyset = NULL; - RETERR(dns_message_findname(rmsg, DNS_SECTION_ANSWER, &keyname, - dns_rdatatype_key, 0, &ourkeyname, - &ourkeyset)); + CHECK(dns_message_findname(rmsg, DNS_SECTION_ANSWER, &keyname, + dns_rdatatype_key, 0, &ourkeyname, + &ourkeyset)); result = dns_message_firstname(rmsg, DNS_SECTION_ANSWER); while (result == ISC_R_SUCCESS) { @@ -1285,7 +1260,7 @@ result = dns_message_findtype(theirkeyname, dns_rdatatype_key, 0, &theirkeyset); if (result == ISC_R_SUCCESS) { - RETERR(dns_rdataset_first(theirkeyset)); + CHECK(dns_rdataset_first(theirkeyset)); break; } next: @@ -1295,18 +1270,17 @@ if (theirkeyset == NULL) { tkey_log("dns_tkey_processdhresponse: failed to find server " "key"); - result = ISC_R_NOTFOUND; - goto failure; + CHECK(ISC_R_NOTFOUND); } dns_rdataset_current(theirkeyset, &theirkeyrdata); - RETERR(dns_dnssec_keyfromrdata(theirkeyname, &theirkeyrdata, rmsg->mctx, - &theirkey)); + CHECK(dns_dnssec_keyfromrdata(theirkeyname, &theirkeyrdata, rmsg->mctx, + &theirkey)); - RETERR(dst_key_secretsize(key, &sharedsize)); + CHECK(dst_key_secretsize(key, &sharedsize)); isc_buffer_allocate(rmsg->mctx, &shared, sharedsize); - RETERR(dst_key_computesecret(theirkey, key, shared)); + CHECK(dst_key_computesecret(theirkey, key, shared)); isc_buffer_init(&secret, secretdata, sizeof(secretdata)); @@ -1318,7 +1292,7 @@ r2.base = NULL; r2.length = 0; } - RETERR(compute_secret(shared, &r2, &r, &secret)); + CHECK(compute_secret(shared, &r2, &r, &secret)); isc_buffer_usedregion(&secret, &r); result = dns_tsigkey_create(tkeyname, &rtkey.algorithm, r.base, @@ -1329,7 +1303,7 @@ dst_key_free(&theirkey); return result; -failure: +cleanup: if (shared != NULL) { isc_buffer_free(&shared); } @@ -1370,8 +1344,8 @@ if (rmsg->rcode != dns_rcode_noerror) { return dns_result_fromrcode(rmsg->rcode); } - RETERR(find_tkey(rmsg, &tkeyname, &rtkeyrdata, DNS_SECTION_ANSWER)); - RETERR(dns_rdata_tostruct(&rtkeyrdata, &rtkey, NULL)); + CHECK(find_tkey(rmsg, &tkeyname, &rtkeyrdata, DNS_SECTION_ANSWER)); + CHECK(dns_rdata_tostruct(&rtkeyrdata, &rtkey, NULL)); /* * Win2k puts the item in the ANSWER section, while the RFC @@ -1384,11 +1358,9 @@ result = find_tkey(qmsg, &tkeyname, &qtkeyrdata, DNS_SECTION_ANSWER); } - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(result); - RETERR(dns_rdata_tostruct(&qtkeyrdata, &qtkey, NULL)); + CHECK(dns_rdata_tostruct(&qtkeyrdata, &qtkey, NULL)); if (rtkey.error != dns_rcode_noerror || rtkey.mode != DNS_TKEYMODE_GSSAPI || @@ -1399,26 +1371,25 @@ rtkey.error); dumpmessage(qmsg); dumpmessage(rmsg); - result = DNS_R_INVALIDTKEY; - goto failure; + CHECK(DNS_R_INVALIDTKEY); } isc_buffer_init(outtoken, array, sizeof(array)); isc_buffer_init(&intoken, rtkey.key, rtkey.keylen); - RETERR(dst_gssapi_initctx(gname, &intoken, outtoken, context, - ring->mctx, err_message)); + CHECK(dst_gssapi_initctx(gname, &intoken, outtoken, context, ring->mctx, + err_message)); - RETERR(dst_key_fromgssapi(dns_rootname, *context, rmsg->mctx, &dstkey, - NULL)); + CHECK(dst_key_fromgssapi(dns_rootname, *context, rmsg->mctx, &dstkey, + NULL)); - RETERR(dns_tsigkey_createfromkey( + CHECK(dns_tsigkey_createfromkey( tkeyname, DNS_TSIG_GSSAPI_NAME, dstkey, false, NULL, rtkey.inception, rtkey.expire, ring->mctx, ring, outkey)); dst_key_free(&dstkey); dns_rdata_freestruct(&rtkey); return result; -failure: +cleanup: /* * XXXSRA This probably leaks memory from rtkey and qtkey. */ @@ -1444,11 +1415,11 @@ return dns_result_fromrcode(rmsg->rcode); } - RETERR(find_tkey(rmsg, &tkeyname, &rtkeyrdata, DNS_SECTION_ANSWER)); - RETERR(dns_rdata_tostruct(&rtkeyrdata, &rtkey, NULL)); + CHECK(find_tkey(rmsg, &tkeyname, &rtkeyrdata, DNS_SECTION_ANSWER)); + CHECK(dns_rdata_tostruct(&rtkeyrdata, &rtkey, NULL)); - RETERR(find_tkey(qmsg, &tempname, &qtkeyrdata, DNS_SECTION_ADDITIONAL)); - RETERR(dns_rdata_tostruct(&qtkeyrdata, &qtkey, NULL)); + CHECK(find_tkey(qmsg, &tempname, &qtkeyrdata, DNS_SECTION_ADDITIONAL)); + CHECK(dns_rdata_tostruct(&qtkeyrdata, &qtkey, NULL)); if (rtkey.error != dns_rcode_noerror || rtkey.mode != DNS_TKEYMODE_DELETE || rtkey.mode != qtkey.mode || @@ -1457,15 +1428,14 @@ { tkey_log("dns_tkey_processdeleteresponse: tkey mode invalid " "or error set(3)"); - result = DNS_R_INVALIDTKEY; dns_rdata_freestruct(&qtkey); dns_rdata_freestruct(&rtkey); - goto failure; + CHECK(DNS_R_INVALIDTKEY); } dns_rdata_freestruct(&qtkey); - RETERR(dns_tsigkey_find(&tsigkey, tkeyname, &rtkey.algorithm, ring)); + CHECK(dns_tsigkey_find(&tsigkey, tkeyname, &rtkey.algorithm, ring)); dns_rdata_freestruct(&rtkey); @@ -1478,7 +1448,7 @@ */ dns_tsigkey_detach(&tsigkey); -failure: +cleanup: return result; } @@ -1507,19 +1477,19 @@ return dns_result_fromrcode(rmsg->rcode); } - RETERR(find_tkey(rmsg, &tkeyname, &rtkeyrdata, DNS_SECTION_ANSWER)); - RETERR(dns_rdata_tostruct(&rtkeyrdata, &rtkey, NULL)); + CHECK(find_tkey(rmsg, &tkeyname, &rtkeyrdata, DNS_SECTION_ANSWER)); + CHECK(dns_rdata_tostruct(&rtkeyrdata, &rtkey, NULL)); freertkey = true; if (win2k) { - RETERR(find_tkey(qmsg, &tkeyname, &qtkeyrdata, - DNS_SECTION_ANSWER)); + CHECK(find_tkey(qmsg, &tkeyname, &qtkeyrdata, + DNS_SECTION_ANSWER)); } else { - RETERR(find_tkey(qmsg, &tkeyname, &qtkeyrdata, - DNS_SECTION_ADDITIONAL)); + CHECK(find_tkey(qmsg, &tkeyname, &qtkeyrdata, + DNS_SECTION_ADDITIONAL)); } - RETERR(dns_rdata_tostruct(&qtkeyrdata, &qtkey, NULL)); + CHECK(dns_rdata_tostruct(&qtkeyrdata, &qtkey, NULL)); if (rtkey.error != dns_rcode_noerror || rtkey.mode != DNS_TKEYMODE_GSSAPI || @@ -1527,8 +1497,7 @@ { tkey_log("dns_tkey_processdhresponse: tkey mode invalid " "or error set(4)"); - result = DNS_R_INVALIDTKEY; - goto failure; + CHECK(DNS_R_INVALIDTKEY); } isc_buffer_init(&intoken, rtkey.key, rtkey.keylen); @@ -1569,12 +1538,12 @@ tkey.otherlen = 0; dns_message_reset(qmsg, DNS_MESSAGE_INTENTRENDER); - RETERR(buildquery(qmsg, tkeyname, &tkey, win2k)); + CHECK(buildquery(qmsg, tkeyname, &tkey, win2k)); return DNS_R_CONTINUE; } - RETERR(dst_key_fromgssapi(dns_rootname, *context, rmsg->mctx, &dstkey, - NULL)); + CHECK(dst_key_fromgssapi(dns_rootname, *context, rmsg->mctx, &dstkey, + NULL)); /* * XXXSRA This seems confused. If we got CONTINUE from initctx, @@ -1582,7 +1551,7 @@ * anything yet. */ - RETERR(dns_tsigkey_createfromkey( + CHECK(dns_tsigkey_createfromkey( tkeyname, win2k ? DNS_TSIG_GSSAPIMS_NAME : DNS_TSIG_GSSAPI_NAME, dstkey, true, NULL, rtkey.inception, rtkey.expire, ring->mctx, ring, outkey)); @@ -1590,7 +1559,7 @@ dns_rdata_freestruct(&rtkey); return result; -failure: +cleanup: /* * XXXSRA This probably leaks memory from qtkey. */ diff -Nru bind9-9.18.41/lib/dns/ttl.c bind9-9.18.44/lib/dns/ttl.c --- bind9-9.18.41/lib/dns/ttl.c 2025-10-18 10:21:03.167261816 +0000 +++ bind9-9.18.44/lib/dns/ttl.c 2026-01-09 13:44:04.849039401 +0000 @@ -30,13 +30,6 @@ #include -#define RETERR(x) \ - do { \ - isc_result_t _r = (x); \ - if (_r != ISC_R_SUCCESS) \ - return ((_r)); \ - } while (0) - static isc_result_t bind_ttl(isc_textregion_t *source, uint32_t *ttl); diff -Nru bind9-9.18.41/lib/dns/update.c bind9-9.18.44/lib/dns/update.c --- bind9-9.18.41/lib/dns/update.c 2025-10-18 10:21:03.167261816 +0000 +++ bind9-9.18.44/lib/dns/update.c 2026-01-09 13:44:04.849039401 +0000 @@ -73,116 +73,6 @@ */ #define LOGLEVEL_DEBUG ISC_LOG_DEBUG(8) -/*% - * Check an operation for failure. These macros all assume that - * the function using them has a 'result' variable and a 'failure' - * label. - */ -#define CHECK(op) \ - do { \ - result = (op); \ - if (result != ISC_R_SUCCESS) \ - goto failure; \ - } while (0) - -/*% - * Fail unconditionally with result 'code', which must not - * be ISC_R_SUCCESS. The reason for failure presumably has - * been logged already. - * - * The test against ISC_R_SUCCESS is there to keep the Solaris compiler - * from complaining about "end-of-loop code not reached". - */ - -#define FAIL(code) \ - do { \ - result = (code); \ - if (result != ISC_R_SUCCESS) \ - goto failure; \ - } while (0) - -/*% - * Fail unconditionally and log as a client error. - * The test against ISC_R_SUCCESS is there to keep the Solaris compiler - * from complaining about "end-of-loop code not reached". - */ -#define FAILC(code, msg) \ - do { \ - const char *_what = "failed"; \ - result = (code); \ - switch (result) { \ - case DNS_R_NXDOMAIN: \ - case DNS_R_YXDOMAIN: \ - case DNS_R_YXRRSET: \ - case DNS_R_NXRRSET: \ - _what = "unsuccessful"; \ - } \ - update_log(log, zone, LOGLEVEL_PROTOCOL, "update %s: %s (%s)", \ - _what, msg, isc_result_totext(result)); \ - if (result != ISC_R_SUCCESS) \ - goto failure; \ - } while (0) - -#define FAILN(code, name, msg) \ - do { \ - const char *_what = "failed"; \ - result = (code); \ - switch (result) { \ - case DNS_R_NXDOMAIN: \ - case DNS_R_YXDOMAIN: \ - case DNS_R_YXRRSET: \ - case DNS_R_NXRRSET: \ - _what = "unsuccessful"; \ - } \ - if (isc_log_wouldlog(dns_lctx, LOGLEVEL_PROTOCOL)) { \ - char _nbuf[DNS_NAME_FORMATSIZE]; \ - dns_name_format(name, _nbuf, sizeof(_nbuf)); \ - update_log(log, zone, LOGLEVEL_PROTOCOL, \ - "update %s: %s: %s (%s)", _what, _nbuf, \ - msg, isc_result_totext(result)); \ - } \ - if (result != ISC_R_SUCCESS) \ - goto failure; \ - } while (0) - -#define FAILNT(code, name, type, msg) \ - do { \ - const char *_what = "failed"; \ - result = (code); \ - switch (result) { \ - case DNS_R_NXDOMAIN: \ - case DNS_R_YXDOMAIN: \ - case DNS_R_YXRRSET: \ - case DNS_R_NXRRSET: \ - _what = "unsuccessful"; \ - } \ - if (isc_log_wouldlog(dns_lctx, LOGLEVEL_PROTOCOL)) { \ - char _nbuf[DNS_NAME_FORMATSIZE]; \ - char _tbuf[DNS_RDATATYPE_FORMATSIZE]; \ - dns_name_format(name, _nbuf, sizeof(_nbuf)); \ - dns_rdatatype_format(type, _tbuf, sizeof(_tbuf)); \ - update_log(log, zone, LOGLEVEL_PROTOCOL, \ - "update %s: %s/%s: %s (%s)", _what, _nbuf, \ - _tbuf, msg, isc_result_totext(result)); \ - } \ - if (result != ISC_R_SUCCESS) \ - goto failure; \ - } while (0) - -/*% - * Fail unconditionally and log as a server error. - * The test against ISC_R_SUCCESS is there to keep the Solaris compiler - * from complaining about "end-of-loop code not reached". - */ -#define FAILS(code, msg) \ - do { \ - result = (code); \ - update_log(log, zone, LOGLEVEL_PROTOCOL, "error: %s: %s", msg, \ - isc_result_totext(result)); \ - if (result != ISC_R_SUCCESS) \ - goto failure; \ - } while (0) - /**************************************************************************/ typedef struct rr rr_t; @@ -735,7 +625,7 @@ CHECK(dns_difftuple_create(list->mctx, DNS_DIFFOP_EXISTS, name, 0, &dummy_rdata, &tuple)); dns_diff_append(list, &tuple); -failure: +cleanup: return result; } @@ -765,7 +655,7 @@ if (result == ISC_R_NOMORE) { result = ISC_R_SUCCESS; } -failure: +cleanup: if (dbit != NULL) { dns_dbiterator_destroy(&dbit); } @@ -836,7 +726,7 @@ } while (1); p = ISC_LIST_NEXT(p, link); } -failure: +cleanup: return result; } @@ -938,8 +828,7 @@ if (wraps == 2) { update_log(log, zone, ISC_LOG_ERROR, "secure zone with no NSECs"); - result = DNS_R_BADZONE; - goto failure; + CHECK(DNS_R_BADZONE); } } CHECK(dns_dbiterator_current(dbit, &node, newname)); @@ -975,7 +864,7 @@ } } } while (!has_nsec); -failure: +cleanup: if (dbit != NULL) { dns_dbiterator_destroy(&dbit); } @@ -1027,7 +916,7 @@ CHECK(do_one_tuple(&tuple, db, ver, diff)); INSIST(tuple == NULL); -failure: +cleanup: if (node != NULL) { dns_db_detachnode(db, &node); } @@ -1052,7 +941,7 @@ CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name, 0, &rdata, &tuple)); CHECK(do_one_tuple(&tuple, db, ver, diff)); -failure: +cleanup: return result; } @@ -1074,7 +963,7 @@ nkeys); dns_zone_unlock_keyfiles(zone); -failure: +cleanup: if (node != NULL) { dns_db_detachnode(db, &node); } @@ -1275,7 +1164,7 @@ result = ISC_R_NOTFOUND; } -failure: +cleanup: if (dns_rdataset_isassociated(&rdataset)) { dns_rdataset_disassociate(&rdataset); } @@ -1306,9 +1195,8 @@ if (result == ISC_R_NOTFOUND) { return ISC_R_SUCCESS; } - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(result); + result = dns_db_findrdataset(db, node, ver, dns_rdatatype_rrsig, dns_rdatatype_dnskey, (isc_stdtime_t)0, &rdataset, NULL); @@ -1317,9 +1205,7 @@ if (result == ISC_R_NOTFOUND) { return ISC_R_SUCCESS; } - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(result); for (result = dns_rdataset_first(&rdataset); result == ISC_R_SUCCESS; result = dns_rdataset_next(&rdataset)) @@ -1363,7 +1249,8 @@ if (result == ISC_R_NOMORE) { result = ISC_R_SUCCESS; } -failure: + +cleanup: if (node != NULL) { dns_db_detachnode(db, &node); } @@ -1568,7 +1455,7 @@ update_log(log, zone, ISC_LOG_ERROR, "could not get zone keys for secure " "dynamic update"); - goto failure; + goto cleanup; } isc_stdtime_get(&state->now); @@ -2012,7 +1899,7 @@ if (!state->build_nsec3) { update_log(log, zone, ISC_LOG_DEBUG(3), "no NSEC3 chains to rebuild"); - goto failure; + goto cleanup; } update_log(log, zone, ISC_LOG_DEBUG(3), @@ -2186,7 +2073,7 @@ UNREACHABLE(); } -failure: +cleanup: if (node != NULL) { dns_db_detachnode(db, &node); } diff -Nru bind9-9.18.41/lib/dns/validator.c bind9-9.18.44/lib/dns/validator.c --- bind9-9.18.41/lib/dns/validator.c 2025-10-18 10:21:03.168261843 +0000 +++ bind9-9.18.44/lib/dns/validator.c 2026-01-09 13:44:04.850039418 +0000 @@ -1176,7 +1176,13 @@ goto done; } dst_key_free(&val->key); - } else { + } else if (result != DST_R_UNSUPPORTEDALG) { + /* + * We can encounter unsupported algorithm when the zone + * is signed with both supported and unsupported + * algorithm at the same time. Stop looking in all + * other failure cases. + */ break; } dns_rdata_reset(&rdata); diff -Nru bind9-9.18.41/lib/dns/view.c bind9-9.18.44/lib/dns/view.c --- bind9-9.18.41/lib/dns/view.c 2025-10-18 10:21:03.168261843 +0000 +++ bind9-9.18.44/lib/dns/view.c 2026-01-09 13:44:04.850039418 +0000 @@ -64,13 +64,6 @@ #include #include -#define CHECK(op) \ - do { \ - result = (op); \ - if (result != ISC_R_SUCCESS) \ - goto cleanup; \ - } while (0) - #define RESSHUTDOWN(v) \ ((atomic_load(&(v)->attributes) & DNS_VIEWATTR_RESSHUTDOWN) != 0) #define ADBSHUTDOWN(v) \ diff -Nru bind9-9.18.41/lib/dns/xfrin.c bind9-9.18.44/lib/dns/xfrin.c --- bind9-9.18.41/lib/dns/xfrin.c 2025-10-18 10:21:03.168261843 +0000 +++ bind9-9.18.44/lib/dns/xfrin.c 2026-01-09 13:44:04.850039418 +0000 @@ -52,25 +52,6 @@ */ /*% - * It would be non-sensical (or at least obtuse) to use FAIL() with an - * ISC_R_SUCCESS code, but the test is there to keep the Solaris compiler - * from complaining about "end-of-loop code not reached". - */ -#define FAIL(code) \ - do { \ - result = (code); \ - if (result != ISC_R_SUCCESS) \ - goto failure; \ - } while (0) - -#define CHECK(op) \ - do { \ - result = (op); \ - if (result != ISC_R_SUCCESS) \ - goto failure; \ - } while (0) - -/*% * The states of the *XFR state machine. We handle both IXFR and AXFR * with a single integrated state machine because they cannot be distinguished * immediately - an AXFR response to an IXFR request can only be detected @@ -294,7 +275,7 @@ dns_rdatacallbacks_init(&xfr->axfr); CHECK(dns_db_beginload(xfr->db, &xfr->axfr)); result = ISC_R_SUCCESS; -failure: +cleanup: return result; } @@ -317,7 +298,7 @@ CHECK(axfr_apply(xfr)); } result = ISC_R_SUCCESS; -failure: +cleanup: return result; } @@ -335,12 +316,11 @@ if (xfr->maxrecords != 0U) { result = dns_db_getsize(xfr->db, xfr->ver, &records, NULL); if (result == ISC_R_SUCCESS && records > xfr->maxrecords) { - result = DNS_R_TOOMANYRECORDS; - goto failure; + CHECK(DNS_R_TOOMANYRECORDS); } } result = ISC_R_SUCCESS; -failure: +cleanup: return result; } @@ -353,7 +333,7 @@ CHECK(dns_zone_verifydb(xfr->zone, xfr->db, NULL)); result = ISC_R_SUCCESS; -failure: +cleanup: return result; } @@ -364,7 +344,7 @@ CHECK(dns_zone_replacedb(xfr->zone, xfr->db, true)); result = ISC_R_SUCCESS; -failure: +cleanup: return result; } @@ -395,7 +375,7 @@ } result = ISC_R_SUCCESS; -failure: +cleanup: return result; } @@ -419,7 +399,7 @@ CHECK(ixfr_apply(xfr)); } result = ISC_R_SUCCESS; -failure: +cleanup: return result; } @@ -441,20 +421,16 @@ if (xfr->maxrecords != 0U) { result = dns_db_getsize(xfr->db, xfr->ver, &records, NULL); if (result == ISC_R_SUCCESS && records > xfr->maxrecords) { - result = DNS_R_TOOMANYRECORDS; - goto failure; + CHECK(DNS_R_TOOMANYRECORDS); } } if (xfr->ixfr.journal != NULL) { - result = dns_journal_writediff(xfr->ixfr.journal, &xfr->diff); - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(dns_journal_writediff(xfr->ixfr.journal, &xfr->diff)); } dns_diff_clear(&xfr->diff); xfr->difflen = 0; result = ISC_R_SUCCESS; -failure: +cleanup: return result; } @@ -473,7 +449,7 @@ dns_zone_markdirty(xfr->zone); } result = ISC_R_SUCCESS; -failure: +cleanup: return result; } @@ -500,7 +476,7 @@ dns_rdatatype_format(rdata->type, buf, sizeof(buf)); xfrin_log(xfr, ISC_LOG_NOTICE, "Unexpected %s record in zone transfer", buf); - FAIL(DNS_R_FORMERR); + CHECK(DNS_R_FORMERR); } /* @@ -515,7 +491,7 @@ dns_name_format(name, namebuf, sizeof(namebuf)); xfrin_log(xfr, ISC_LOG_DEBUG(3), "SOA name mismatch: '%s'", namebuf); - FAIL(DNS_R_NOTZONETOP); + CHECK(DNS_R_NOTZONETOP); } redo: @@ -524,7 +500,7 @@ if (rdata->type != dns_rdatatype_soa) { xfrin_log(xfr, ISC_LOG_NOTICE, "non-SOA response to SOA query"); - FAIL(DNS_R_FORMERR); + CHECK(DNS_R_FORMERR); } xfr->end_serial = dns_soa_getserial(rdata); if (!DNS_SERIAL_GT(xfr->end_serial, xfr->ixfr.request_serial) && @@ -534,7 +510,7 @@ "requested serial %u, " "primary has %u, not updating", xfr->ixfr.request_serial, xfr->end_serial); - FAIL(DNS_R_UPTODATE); + CHECK(DNS_R_UPTODATE); } xfr->state = XFRST_GOTSOA; break; @@ -549,7 +525,7 @@ if (rdata->type != dns_rdatatype_soa) { xfrin_log(xfr, ISC_LOG_NOTICE, "first RR in zone transfer must be SOA"); - FAIL(DNS_R_FORMERR); + CHECK(DNS_R_FORMERR); } /* * Remember the serial number in the initial SOA. @@ -569,7 +545,7 @@ "requested serial %u, " "primary has %u, not updating", xfr->ixfr.request_serial, xfr->end_serial); - FAIL(DNS_R_UPTODATE); + CHECK(DNS_R_UPTODATE); } xfr->firstsoa = *rdata; if (xfr->firstsoa_data != NULL) { @@ -636,7 +612,7 @@ "IXFR out of sync: " "expected serial %u, got %u", xfr->ixfr.current_serial, soa_serial); - FAIL(DNS_R_FORMERR); + CHECK(DNS_R_FORMERR); } else { CHECK(ixfr_commit(xfr)); xfr->state = XFRST_IXFR_DELSOA; @@ -646,7 +622,7 @@ if (rdata->type == dns_rdatatype_ns && dns_name_iswildcard(name)) { - FAIL(DNS_R_INVALIDNS); + CHECK(DNS_R_INVALIDNS); } CHECK(ixfr_putdata(xfr, DNS_DIFFOP_ADD, name, ttl, rdata)); break; @@ -671,7 +647,7 @@ xfrin_log(xfr, ISC_LOG_NOTICE, "start and ending SOA records " "mismatch"); - FAIL(DNS_R_FORMERR); + CHECK(DNS_R_FORMERR); } CHECK(axfr_commit(xfr)); xfr->state = XFRST_AXFR_END; @@ -680,13 +656,13 @@ break; case XFRST_AXFR_END: case XFRST_IXFR_END: - FAIL(DNS_R_EXTRADATA); + CHECK(DNS_R_EXTRADATA); FALLTHROUGH; default: UNREACHABLE(); } result = ISC_R_SUCCESS; -failure: +cleanup: return result; } @@ -1010,10 +986,7 @@ * parameters from the configuration file and try to * store it for further reuse. */ - result = isc_tlsctx_createclient(&tlsctx); - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(isc_tlsctx_createclient(&tlsctx)); tls_versions = dns_transport_get_tls_versions(xfr->transport); if (tls_versions != 0) { isc_tlsctx_set_protocols(tlsctx, tls_versions); @@ -1045,12 +1018,8 @@ * CA certificates will be created, just * as planned. */ - result = isc_tls_cert_store_create(ca_file, - &store); - - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(isc_tls_cert_store_create(ca_file, + &store)); } else { store = found_store; } @@ -1077,12 +1046,9 @@ * Only SubjectAltName must be checked. */ hostname_ignore_subject = true; - result = isc_tlsctx_enable_peer_verification( + CHECK(isc_tlsctx_enable_peer_verification( tlsctx, false, store, hostname, - hostname_ignore_subject); - if (result != ISC_R_SUCCESS) { - goto failure; - } + hostname_ignore_subject)); /* * Let's load client certificate and enable @@ -1093,11 +1059,8 @@ if (cert_file != NULL) { INSIST(key_file != NULL); - result = isc_tlsctx_load_certificate( - tlsctx, key_file, cert_file); - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(isc_tlsctx_load_certificate( + tlsctx, key_file, cert_file)); } } @@ -1171,7 +1134,7 @@ return ISC_R_SUCCESS; -failure: +cleanup: if (tlsctx != NULL) { isc_tlsctx_free(&tlsctx); } @@ -1232,10 +1195,7 @@ connect_xfr, 30000, 0); break; case DNS_TRANSPORT_TLS: { - result = get_create_tlsctx(xfr, &tlsctx, &sess_cache); - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(get_create_tlsctx(xfr, &tlsctx, &sess_cache)); INSIST(tlsctx != NULL); isc_nm_tlsdnsconnect(xfr->netmgr, &xfr->sourceaddr, &xfr->primaryaddr, xfrin_connect_done, @@ -1247,7 +1207,7 @@ return ISC_R_SUCCESS; -failure: +cleanup: isc_refcount_decrement0(&xfr->connects); dns_xfrin_detach(&connect_xfr); return result; @@ -1270,7 +1230,7 @@ CHECK(dns_message_rendersection(msg, DNS_SECTION_ADDITIONAL, 0)); CHECK(dns_message_renderend(msg)); result = ISC_R_SUCCESS; -failure: +cleanup: if (cleanup_cctx) { dns_compress_invalidate(&cctx); } @@ -1299,13 +1259,13 @@ if (result != ISC_R_SUCCESS) { xfrin_fail(xfr, result, "failed to connect"); - goto failure; + goto cleanup; } result = isc_nm_xfr_checkperm(handle); if (result != ISC_R_SUCCESS) { xfrin_fail(xfr, result, "connected but unable to transfer"); - goto failure; + goto cleanup; } zmgr = dns_zone_getmgr(xfr->zone); @@ -1333,7 +1293,7 @@ xfrin_fail(xfr, result, "connected but unable to send"); } -failure: +cleanup: switch (result) { case ISC_R_SUCCESS: break; @@ -1401,8 +1361,7 @@ *target = name; return ISC_R_SUCCESS; -failure: - +cleanup: if (rds != NULL) { dns_rdataset_disassociate(rds); dns_message_puttemprdataset(msg, &rds); @@ -1501,7 +1460,7 @@ isc_refcount_increment0(&send_xfr->sends); isc_nm_send(xfr->handle, ®ion, xfrin_send_done, send_xfr); -failure: +cleanup: if (qname != NULL) { dns_message_puttempname(msg, &qname); } @@ -1542,7 +1501,7 @@ isc_refcount_increment0(&recv_xfr->recvs); isc_nm_read(recv_xfr->handle, xfrin_recv_done, recv_xfr); -failure: +cleanup: if (result != ISC_R_SUCCESS) { xfrin_fail(xfr, result, "failed sending request data"); } @@ -1629,7 +1588,7 @@ if (xfr->reqtype == dns_rdatatype_axfr || xfr->reqtype == dns_rdatatype_soa) { - goto failure; + goto cleanup; } xfrin_log(xfr, ISC_LOG_DEBUG(3), "got %s, retrying with AXFR", @@ -1658,16 +1617,14 @@ if (msg->counts[DNS_SECTION_QUESTION] > 1) { xfrin_log(xfr, ISC_LOG_NOTICE, "too many questions (%u)", msg->counts[DNS_SECTION_QUESTION]); - result = DNS_R_FORMERR; - goto failure; + CHECK(DNS_R_FORMERR); } if ((xfr->state == XFRST_SOAQUERY || xfr->state == XFRST_INITIALSOA) && msg->counts[DNS_SECTION_QUESTION] != 1) { xfrin_log(xfr, ISC_LOG_NOTICE, "missing question section"); - result = DNS_R_FORMERR; - goto failure; + CHECK(DNS_R_FORMERR); } for (result = dns_message_firstname(msg, DNS_SECTION_QUESTION); @@ -1679,28 +1636,25 @@ name = NULL; dns_message_currentname(msg, DNS_SECTION_QUESTION, &name); if (!dns_name_equal(name, &xfr->name)) { - result = DNS_R_FORMERR; xfrin_log(xfr, ISC_LOG_NOTICE, "question name mismatch"); - goto failure; + CHECK(DNS_R_FORMERR); } rds = ISC_LIST_HEAD(name->list); INSIST(rds != NULL); if (rds->type != xfr->reqtype) { - result = DNS_R_FORMERR; xfrin_log(xfr, ISC_LOG_NOTICE, "question type mismatch"); - goto failure; + CHECK(DNS_R_FORMERR); } if (rds->rdclass != xfr->rdclass) { - result = DNS_R_FORMERR; xfrin_log(xfr, ISC_LOG_NOTICE, "question class mismatch"); - goto failure; + CHECK(DNS_R_FORMERR); } } if (result != ISC_R_NOMORE) { - goto failure; + goto cleanup; } /* @@ -1721,14 +1675,14 @@ if (xfr->reqtype == dns_rdatatype_soa && (msg->flags & DNS_MESSAGEFLAG_AA) == 0) { - FAIL(DNS_R_NOTAUTHORITATIVE); + CHECK(DNS_R_NOTAUTHORITATIVE); } result = dns_message_checksig(msg, dns_zone_getview(xfr->zone)); if (result != ISC_R_SUCCESS) { xfrin_log(xfr, ISC_LOG_DEBUG(3), "TSIG check failed: %s", isc_result_totext(result)); - goto failure; + goto cleanup; } for (result = dns_message_firstname(msg, DNS_SECTION_ANSWER); @@ -1753,7 +1707,7 @@ } } if (result != ISC_R_NOMORE) { - goto failure; + goto cleanup; } if (dns_message_gettsig(msg, &tsigowner) != NULL) { @@ -1779,8 +1733,7 @@ xfr->state == XFRST_AXFR_END || xfr->state == XFRST_IXFR_END) { - result = DNS_R_EXPECTEDTSIG; - goto failure; + CHECK(DNS_R_EXPECTEDTSIG); } } @@ -1850,7 +1803,7 @@ return; } -failure: +cleanup: if (result != ISC_R_SUCCESS) { xfrin_fail(xfr, result, "failed while receiving responses"); } diff -Nru bind9-9.18.41/lib/dns/zone.c bind9-9.18.44/lib/dns/zone.c --- bind9-9.18.41/lib/dns/zone.c 2025-10-18 10:21:03.171261923 +0000 +++ bind9-9.18.44/lib/dns/zone.c 2026-01-09 13:44:04.853039467 +0000 @@ -593,13 +593,6 @@ #define UNREACH_CACHE_SIZE 10U #define UNREACH_HOLD_TIME 600 /* 10 minutes */ -#define CHECK(op) \ - do { \ - result = (op); \ - if (result != ISC_R_SUCCESS) \ - goto failure; \ - } while (0) - struct dns_unreachable { isc_sockaddr_t remote; isc_sockaddr_t local; @@ -4350,7 +4343,7 @@ set_refreshkeytimer(zone, &kd, now, true); return ISC_R_SUCCESS; -failure: +cleanup: return result; } @@ -4451,7 +4444,7 @@ dns_keytable_detach(&sr); -failure: +cleanup: if (sr != NULL) { dns_keytable_detach(&sr); } @@ -4624,7 +4617,7 @@ CHECK(do_one_tuple(&addtuple, db, ver, diff)); result = ISC_R_SUCCESS; -failure: +cleanup: if (addtuple != NULL) { dns_difftuple_free(&addtuple); } @@ -4693,7 +4686,7 @@ dns_zone_log(zone, ISC_LOG_ERROR, "add_soa:dns_db_newversion -> %s", isc_result_totext(result)); - goto failure; + goto cleanup; } /* Build SOA record */ @@ -4703,13 +4696,13 @@ dns_zone_log(zone, ISC_LOG_ERROR, "add_soa:dns_soa_buildrdata -> %s", isc_result_totext(result)); - goto failure; + goto cleanup; } result = update_one_rr(db, ver, &diff, DNS_DIFFOP_ADD, &zone->origin, 0, &rdata); -failure: +cleanup: dns_diff_clear(&diff); if (ver != NULL) { dns_db_closeversion(db, &ver, result == ISC_R_SUCCESS); @@ -4813,7 +4806,7 @@ dnssec_log(zone, ISC_LOG_ERROR, "sync_keyzone:dns_db_newversion -> %s", isc_result_totext(result)); - goto failure; + goto cleanup; } /* @@ -4840,7 +4833,7 @@ dns_rriterator_current(&rrit, &rrname, &ttl, &rdataset, NULL); if (!dns_rdataset_isassociated(rdataset)) { dns_rriterator_destroy(&rrit); - goto failure; + goto cleanup; } if (rdataset->type != dns_rdatatype_keydata) { @@ -4915,7 +4908,7 @@ commit = true; } -failure: +cleanup: if (result != ISC_R_SUCCESS) { dnssec_log(zone, ISC_LOG_ERROR, "unable to synchronize managed keys: %s", @@ -4953,7 +4946,7 @@ result = sync_keyzone(zone, db); UNLOCK_ZONE(zone); -failure: +cleanup: if (db != NULL) { dns_db_detach(&db); } @@ -6680,7 +6673,7 @@ result = ISC_R_SUCCESS; } -failure: +cleanup: if (node != NULL) { dns_db_detachnode(db, &node); @@ -6719,8 +6712,8 @@ dns_zone_getmctx(zone), keys); dns_zone_unlock_keyfiles(zone); - if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) { - goto failure; + if (result != ISC_R_NOTFOUND) { + CHECK(result); } /* Get public keys (dnskeys). */ @@ -6756,7 +6749,7 @@ } } -failure: +cleanup: if (dns_rdataset_isassociated(&keyset)) { dns_rdataset_disassociate(&keyset); } @@ -6942,9 +6935,8 @@ if (result == ISC_R_NOTFOUND) { return ISC_R_SUCCESS; } - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(result); + result = dns_db_findrdataset(db, node, ver, dns_rdatatype_rrsig, type, (isc_stdtime_t)0, &rdataset, NULL); dns_db_detachnode(db, &node); @@ -6955,7 +6947,7 @@ } if (result != ISC_R_SUCCESS) { INSIST(!dns_rdataset_isassociated(&rdataset)); - goto failure; + goto cleanup; } for (result = dns_rdataset_first(&rdataset); result == ISC_R_SUCCESS; @@ -7105,7 +7097,7 @@ "key expiry warning time out of range"); } } -failure: +cleanup: if (node != NULL) { dns_db_detachnode(db, &node); } @@ -7145,9 +7137,8 @@ if (result == ISC_R_NOTFOUND) { return ISC_R_SUCCESS; } - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(result); + result = dns_db_findrdataset(db, node, ver, type, 0, (isc_stdtime_t)0, &rdataset, NULL); dns_db_detachnode(db, &node); @@ -7157,7 +7148,7 @@ } if (result != ISC_R_SUCCESS) { INSIST(!dns_rdataset_isassociated(&rdataset)); - goto failure; + goto cleanup; } for (i = 0; i < nkeys; i++) { @@ -7364,7 +7355,7 @@ } } -failure: +cleanup: if (dns_rdataset_isassociated(&rdataset)) { dns_rdataset_disassociate(&rdataset); } @@ -7460,8 +7451,7 @@ if (zone->update_disabled || DNS_ZONEKEY_OPTION(zone, DNS_ZONEKEY_NORESIGN)) { - result = ISC_R_FAILURE; - goto failure; + CHECK(ISC_R_FAILURE); } ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read); @@ -7470,8 +7460,7 @@ } ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); if (db == NULL) { - result = ISC_R_FAILURE; - goto failure; + CHECK(ISC_R_FAILURE); } result = dns_db_newversion(db, &version); @@ -7479,7 +7468,7 @@ dns_zone_log(zone, ISC_LOG_ERROR, "zone_resigninc:dns_db_newversion -> %s", isc_result_totext(result)); - goto failure; + goto cleanup; } isc_stdtime_get(&now); @@ -7490,7 +7479,7 @@ dns_zone_log(zone, ISC_LOG_ERROR, "zone_resigninc:dns__zone_findkeys -> %s", isc_result_totext(result)); - goto failure; + goto cleanup; } calculate_rrsig_validity(zone, now, &inception, &soaexpire, &expire, @@ -7568,8 +7557,8 @@ } } - if (result != ISC_R_NOMORE && result != ISC_R_SUCCESS) { - goto failure; + if (result != ISC_R_NOMORE) { + CHECK(result); } result = del_sigs(zone, db, version, &zone->origin, dns_rdatatype_soa, @@ -7578,7 +7567,7 @@ dns_zone_log(zone, ISC_LOG_ERROR, "zone_resigninc:del_sigs -> %s", isc_result_totext(result)); - goto failure; + goto cleanup; } /* @@ -7591,7 +7580,7 @@ if (zonediff.offline) { dns_db_closeversion(db, &version, true); } - goto failure; + goto cleanup; } /* Increment SOA serial if we have made changes */ @@ -7601,7 +7590,7 @@ dns_zone_log(zone, ISC_LOG_ERROR, "zone_resigninc:update_soa_serial -> %s", isc_result_totext(result)); - goto failure; + goto cleanup; } /* @@ -7615,7 +7604,7 @@ dns_zone_log(zone, ISC_LOG_ERROR, "zone_resigninc:add_sigs -> %s", isc_result_totext(result)); - goto failure; + goto cleanup; } /* Write changes to journal file. */ @@ -7624,7 +7613,7 @@ /* Everything has succeeded. Commit the changes. */ dns_db_closeversion(db, &version, true); -failure: +cleanup: dns_diff_clear(&_sig_diff); for (i = 0; i < nkeys; i++) { dst_key_free(&zone_keys[i]); @@ -7687,7 +7676,7 @@ break; } } while (1); -failure: +cleanup: if (node != NULL) { dns_db_detachnode(db, &node); } @@ -7786,7 +7775,7 @@ CHECK(dns_nsec_buildrdata(db, version, node, next, nsecbuffer, &rdata)); CHECK(update_one_rr(db, version, diff, DNS_DIFFOP_ADD, name, ttl, &rdata)); -failure: +cleanup: return result; } @@ -7827,19 +7816,62 @@ dns_rdataset_disassociate(&rdataset); } if (result != ISC_R_NOMORE) { - goto failure; + goto cleanup; } if ((seen_ns && !seen_soa) || seen_dname) { *is_bottom_of_zone = true; } result = ISC_R_SUCCESS; -failure: +cleanup: dns_rdatasetiter_destroy(&iterator); return result; } +typedef struct seen { + bool rr; + bool soa; + bool ns; + bool nsec; + bool nsec3; + bool ds; + bool dname; +} seen_t; + +static isc_result_t +allrdatasets(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version, + dns_rdatasetiter_t **iterp, seen_t *seen) { + isc_result_t result; + dns_rdataset_t rdataset = DNS_RDATASET_INIT; + *seen = (seen_t){}; + RETERR(dns_db_allrdatasets(db, node, version, 0, 0, iterp)); + for (result = dns_rdatasetiter_first(*iterp); result == ISC_R_SUCCESS; + result = dns_rdatasetiter_next(*iterp)) + { + dns_rdatasetiter_current(*iterp, &rdataset); + if (rdataset.type == dns_rdatatype_rrsig) { + dns_rdataset_disassociate(&rdataset); + continue; + } + (*seen).rr = true; + if (rdataset.type == dns_rdatatype_soa) { + (*seen).soa = true; + } else if (rdataset.type == dns_rdatatype_ns) { + (*seen).ns = true; + } else if (rdataset.type == dns_rdatatype_ds) { + (*seen).ds = true; + } else if (rdataset.type == dns_rdatatype_dname) { + (*seen).dname = true; + } else if (rdataset.type == dns_rdatatype_nsec) { + (*seen).nsec = true; + } else if (rdataset.type == dns_rdatatype_nsec3) { + (*seen).nsec3 = true; + } + dns_rdataset_disassociate(&rdataset); + } + return ISC_R_SUCCESS; +} static isc_result_t sign_a_node(dns_db_t *db, dns_zone_t *zone, dns_name_t *name, dns_dbnode_t *node, dns_dbversion_t *version, bool build_nsec3, @@ -7856,9 +7888,9 @@ isc_buffer_t buffer; unsigned char data[1024]; - bool seen_soa, seen_ns, seen_rr, seen_nsec, seen_nsec3, seen_ds; + seen_t seen; - result = dns_db_allrdatasets(db, node, version, 0, 0, &iterator); + result = allrdatasets(db, node, version, &iterator, &seen); if (result != ISC_R_SUCCESS) { if (result == ISC_R_NOTFOUND) { result = ISC_R_SUCCESS; @@ -7868,36 +7900,13 @@ dns_rdataset_init(&rdataset); isc_buffer_init(&buffer, data, sizeof(data)); - seen_rr = seen_soa = seen_ns = seen_nsec = seen_nsec3 = seen_ds = false; - for (result = dns_rdatasetiter_first(iterator); result == ISC_R_SUCCESS; - result = dns_rdatasetiter_next(iterator)) - { - dns_rdatasetiter_current(iterator, &rdataset); - if (rdataset.type == dns_rdatatype_soa) { - seen_soa = true; - } else if (rdataset.type == dns_rdatatype_ns) { - seen_ns = true; - } else if (rdataset.type == dns_rdatatype_ds) { - seen_ds = true; - } else if (rdataset.type == dns_rdatatype_nsec) { - seen_nsec = true; - } else if (rdataset.type == dns_rdatatype_nsec3) { - seen_nsec3 = true; - } - if (rdataset.type != dns_rdatatype_rrsig) { - seen_rr = true; - } - dns_rdataset_disassociate(&rdataset); - } - if (result != ISC_R_NOMORE) { - goto failure; - } + /* * Going from insecure to NSEC3. * Don't generate NSEC3 records for NSEC3 records. */ - if (build_nsec3 && !seen_nsec3 && seen_rr) { - bool unsecure = !seen_ds && seen_ns && !seen_soa; + if (build_nsec3 && !seen.nsec3 && seen.rr) { + bool unsecure = !seen.ds && seen.ns && !seen.soa; CHECK(dns_nsec3_addnsec3s(db, version, name, nsecttl, unsecure, diff)); (*signatures)--; @@ -7906,7 +7915,7 @@ * Going from insecure to NSEC. * Don't generate NSEC records for NSEC3 records. */ - if (build_nsec && !seen_nsec3 && !seen_nsec && seen_rr) { + if (build_nsec && !seen.nsec3 && !seen.nsec && seen.rr) { /* * Build a NSEC record except at the origin. */ @@ -7948,7 +7957,7 @@ } } - if (seen_ns && !seen_soa && rdataset.type != dns_rdatatype_ds && + if (seen.ns && !seen.soa && rdataset.type != dns_rdatatype_ds && rdataset.type != dns_rdatatype_nsec) { goto next_rdataset; @@ -7990,7 +7999,8 @@ if (result == ISC_R_NOMORE) { result = ISC_R_SUCCESS; } -failure: + +cleanup: if (dns_rdataset_isassociated(&rdataset)) { dns_rdataset_disassociate(&rdataset); } @@ -8022,15 +8032,13 @@ if (result == ISC_R_NOTFOUND) { goto success; } - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(result); } CHECK(delete_nsec(db, version, node, name, diff)); CHECK(add_nsec(db, version, name, node, nsecttl, false, diff)); success: result = ISC_R_SUCCESS; -failure: +cleanup: if (node != NULL) { dns_db_detachnode(db, &node); } @@ -8050,10 +8058,7 @@ bool have_rr = false; dns_rdataset_init(&rdataset); - result = dns_db_getoriginnode(signing->db, &node); - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(dns_db_getoriginnode(signing->db, &node)); result = dns_db_findrdataset(signing->db, node, version, zone->privatetype, dns_rdatatype_none, 0, @@ -8061,11 +8066,11 @@ if (result == ISC_R_NOTFOUND) { INSIST(!dns_rdataset_isassociated(&rdataset)); result = ISC_R_SUCCESS; - goto failure; + goto cleanup; } if (result != ISC_R_SUCCESS) { INSIST(!dns_rdataset_isassociated(&rdataset)); - goto failure; + goto cleanup; } for (result = dns_rdataset_first(&rdataset); result == ISC_R_SUCCESS; result = dns_rdataset_next(&rdataset)) @@ -8137,7 +8142,7 @@ diff)); } -failure: +cleanup: if (dns_rdataset_isassociated(&rdataset)) { dns_rdataset_disassociate(&rdataset); } @@ -8195,7 +8200,7 @@ goto try_private; } if (result != ISC_R_SUCCESS) { - goto failure; + goto cleanup; } /* @@ -8228,7 +8233,7 @@ dns_rdata_reset(&rdata); } if (result != ISC_R_NOMORE) { - goto failure; + goto cleanup; } dns_rdataset_disassociate(&rdataset); @@ -8250,9 +8255,7 @@ if (result == ISC_R_NOTFOUND) { goto add; } - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(result); for (result = dns_rdataset_first(&rdataset); result == ISC_R_SUCCESS; result = dns_rdataset_next(&rdataset)) @@ -8285,13 +8288,13 @@ dns_rdata_reset(&rdata); } if (result != ISC_R_NOMORE) { - goto failure; + goto cleanup; } add: if ((chain->nsec3param.flags & DNS_NSEC3FLAG_REMOVE) != 0) { result = ISC_R_SUCCESS; - goto failure; + goto cleanup; } /* @@ -8308,7 +8311,7 @@ rdata.data[1] = 0; /* Clear flag bits. */ CHECK(update_one_rr(db, ver, diff, DNS_DIFFOP_ADD, name, ttl, &rdata)); -failure: +cleanup: dns_db_detachnode(db, &node); if (dns_rdataset_isassociated(&rdataset)) { dns_rdataset_disassociate(&rdataset); @@ -8344,7 +8347,8 @@ if (result == ISC_R_NOMORE) { result = ISC_R_SUCCESS; } -failure: + +cleanup: dns_rdataset_disassociate(&rdataset); return result; } @@ -8387,7 +8391,8 @@ if (result == ISC_R_NOMORE) { result = ISC_R_SUCCESS; } -failure: + +cleanup: dns_rdataset_disassociate(&rdataset); return result; } @@ -8464,7 +8469,7 @@ result = ISC_R_SUCCESS; } -failure: +cleanup: if (dns_rdataset_isassociated(&rdataset)) { dns_rdataset_disassociate(&rdataset); } @@ -8594,8 +8599,7 @@ unsigned int nkeys = 0; uint32_t nodes; bool unsecure = false; - bool seen_soa, seen_ns, seen_dname, seen_ds; - bool seen_nsec, seen_nsec3, seen_rr; + seen_t seen; dns_rdatasetiter_t *iterator = NULL; bool buildnsecchain; bool updatensec = false; @@ -8617,8 +8621,7 @@ * Updates are disabled. Pause for 5 minutes. */ if (zone->update_disabled) { - result = ISC_R_FAILURE; - goto failure; + CHECK(ISC_R_FAILURE); } ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read); @@ -8644,7 +8647,7 @@ dnssec_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:dns_db_newversion -> %s", isc_result_totext(result)); - goto failure; + goto cleanup; } isc_stdtime_get(&now); @@ -8655,7 +8658,7 @@ dnssec_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:dns__zone_findkeys -> %s", isc_result_totext(result)); - goto failure; + goto cleanup; } calculate_rrsig_validity(zone, now, &inception, &soaexpire, NULL, @@ -8767,47 +8770,27 @@ /* * Check to see if this is a bottom of zone node. */ - result = dns_db_allrdatasets(db, node, version, 0, 0, - &iterator); + result = allrdatasets(db, node, version, &iterator, &seen); if (result == ISC_R_NOTFOUND) { /* Empty node? */ goto next_addnode; } - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(result); + + INSIST(!seen.nsec3); - seen_soa = seen_ns = seen_dname = seen_ds = seen_nsec = false; - for (result = dns_rdatasetiter_first(iterator); - result == ISC_R_SUCCESS; - result = dns_rdatasetiter_next(iterator)) - { - dns_rdatasetiter_current(iterator, &rdataset); - INSIST(rdataset.type != dns_rdatatype_nsec3); - if (rdataset.type == dns_rdatatype_soa) { - seen_soa = true; - } else if (rdataset.type == dns_rdatatype_ns) { - seen_ns = true; - } else if (rdataset.type == dns_rdatatype_dname) { - seen_dname = true; - } else if (rdataset.type == dns_rdatatype_ds) { - seen_ds = true; - } else if (rdataset.type == dns_rdatatype_nsec) { - seen_nsec = true; - } - dns_rdataset_disassociate(&rdataset); - } dns_rdatasetiter_destroy(&iterator); /* * Is there a NSEC chain than needs to be cleaned up? */ - if (seen_nsec) { + if (seen.nsec) { nsec3chain->seen_nsec = true; } - if (seen_ns && !seen_soa && !seen_ds) { + + if (seen.ns && !seen.soa && !seen.ds) { unsecure = true; } - if ((seen_ns && !seen_soa) || seen_dname) { + if ((seen.ns && !seen.soa) || seen.dname) { delegation = true; } @@ -8823,7 +8806,7 @@ "zone_nsec3chain:" "dns_nsec3_addnsec3 -> %s", isc_result_totext(result)); - goto failure; + goto cleanup; } /* @@ -8880,7 +8863,7 @@ "zone_nsec3chain:" "dns_dbiterator_next -> %s", isc_result_totext(result)); - goto failure; + goto cleanup; } else if (delegation) { dns_dbiterator_current(nsec3chain->dbiterator, &node, nextname); @@ -8960,13 +8943,13 @@ "zone_nsec3chain:" "need_nsec_chain -> %s", isc_result_totext(result)); - goto failure; + goto cleanup; } } if (first) { dnssec_log(zone, ISC_LOG_DEBUG(3), - "zone_nsec3chain:buildnsecchain = %u\n", + "zone_nsec3chain:buildnsecchain = %u", buildnsecchain); } @@ -8987,7 +8970,7 @@ "zone_nsec3chain:" "fixup_nsec3param -> %s", isc_result_totext(result)); - goto failure; + goto cleanup; } } @@ -9002,7 +8985,7 @@ "zone_nsec3chain:" "deletematchingnsec3 -> %s", isc_result_totext(result)); - goto failure; + goto cleanup; } goto next_removenode; } @@ -9031,44 +9014,19 @@ /* * Check to see if this is a bottom of zone node. */ - result = dns_db_allrdatasets(db, node, version, 0, 0, - &iterator); + result = allrdatasets(db, node, version, &iterator, &seen); if (result == ISC_R_NOTFOUND) { /* Empty node? */ goto next_removenode; } - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(result); - seen_soa = seen_ns = seen_dname = seen_nsec3 = seen_nsec = - seen_rr = false; - for (result = dns_rdatasetiter_first(iterator); - result == ISC_R_SUCCESS; - result = dns_rdatasetiter_next(iterator)) - { - dns_rdatasetiter_current(iterator, &rdataset); - if (rdataset.type == dns_rdatatype_soa) { - seen_soa = true; - } else if (rdataset.type == dns_rdatatype_ns) { - seen_ns = true; - } else if (rdataset.type == dns_rdatatype_dname) { - seen_dname = true; - } else if (rdataset.type == dns_rdatatype_nsec) { - seen_nsec = true; - } else if (rdataset.type == dns_rdatatype_nsec3) { - seen_nsec3 = true; - } else if (rdataset.type != dns_rdatatype_rrsig) { - seen_rr = true; - } - dns_rdataset_disassociate(&rdataset); - } dns_rdatasetiter_destroy(&iterator); - if (!seen_rr || seen_nsec3 || seen_nsec) { + if (!seen.rr || seen.nsec3 || seen.nsec) { goto next_removenode; } - if ((seen_ns && !seen_soa) || seen_dname) { + if ((seen.ns && !seen.soa) || seen.dname) { delegation = true; } @@ -9111,7 +9069,7 @@ "zone_nsec3chain:" "fixup_nsec3param -> %s", isc_result_totext(result)); - goto failure; + goto cleanup; } goto next_removechain; } else if (result != ISC_R_SUCCESS) { @@ -9119,7 +9077,7 @@ "zone_nsec3chain:" "dns_dbiterator_next -> %s", isc_result_totext(result)); - goto failure; + goto cleanup; } else if (delegation) { dns_dbiterator_current(nsec3chain->dbiterator, &node, nextname); @@ -9159,7 +9117,7 @@ dnssec_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:dns_db_allrdatasets -> %s", isc_result_totext(result)); - goto failure; + goto cleanup; } for (result = dns_rdatasetiter_first(iterator); result == ISC_R_SUCCESS; @@ -9188,7 +9146,7 @@ dnssec_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:updatesecure -> %s", isc_result_totext(result)); - goto failure; + goto cleanup; } } @@ -9205,7 +9163,7 @@ "zone_nsec3chain:" "dns_nsec3_addnsec3s -> %s", isc_result_totext(result)); - goto failure; + goto cleanup; } } } @@ -9223,7 +9181,7 @@ dnssec_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:dns__zone_updatesigs -> %s", isc_result_totext(result)); - goto failure; + goto cleanup; } /* @@ -9237,7 +9195,7 @@ dnssec_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:dns__zone_updatesigs -> %s", isc_result_totext(result)); - goto failure; + goto cleanup; } if (updatensec) { @@ -9247,7 +9205,7 @@ dnssec_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:updatesecure -> %s", isc_result_totext(result)); - goto failure; + goto cleanup; } } @@ -9258,7 +9216,7 @@ dnssec_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:dns__zone_updatesigs -> %s", isc_result_totext(result)); - goto failure; + goto cleanup; } /* @@ -9270,7 +9228,7 @@ * No need to call dns_db_closeversion() here as it is * called with commit = true below. */ - goto done; + goto closeversion; } result = del_sigs(zone, db, version, &zone->origin, dns_rdatatype_soa, @@ -9279,7 +9237,7 @@ dnssec_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:del_sigs -> %s", isc_result_totext(result)); - goto failure; + goto cleanup; } result = update_soa_serial(zone, db, version, zonediff.diff, zone->mctx, @@ -9288,7 +9246,7 @@ dnssec_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:update_soa_serial -> %s", isc_result_totext(result)); - goto failure; + goto cleanup; } result = add_sigs(db, version, &zone->origin, zone, dns_rdatatype_soa, @@ -9298,7 +9256,7 @@ dnssec_log(zone, ISC_LOG_ERROR, "zone_nsec3chain:add_sigs -> %s", isc_result_totext(result)); - goto failure; + goto cleanup; } /* Write changes to journal file. */ @@ -9309,7 +9267,7 @@ DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NEEDNOTIFY); UNLOCK_ZONE(zone); -done: +closeversion: /* * Pause all iterators so that dns_db_closeversion() can succeed. */ @@ -9343,7 +9301,7 @@ set_resigntime(zone); UNLOCK_ZONE(zone); -failure: +cleanup: if (result != ISC_R_SUCCESS) { dnssec_log(zone, ISC_LOG_ERROR, "zone_nsec3chain: %s", isc_result_totext(result)); @@ -9481,7 +9439,7 @@ rdataset.ttl, &rdata)); } if (result != ISC_R_NOMORE) { - goto failure; + goto cleanup; } dns_rdataset_disassociate(&rdataset); continue; @@ -9533,7 +9491,7 @@ * i.e., found in at least one, and not missing from any. */ *has_algp = (alg_found && !alg_missed); -failure: +cleanup: if (dns_rdataset_isassociated(&rdataset)) { dns_rdataset_disassociate(&rdataset); } @@ -9631,12 +9589,12 @@ /* Refuse to allow NSEC3 with NSEC-only keys */ if (nseconly && nsec3) { - goto failure; + goto cleanup; } return true; -failure: +cleanup: return false; } @@ -9691,7 +9649,7 @@ */ if (zone->update_disabled) { result = ISC_R_FAILURE; - goto cleanup; + goto done; } ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read); @@ -9701,7 +9659,7 @@ ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); if (db == NULL) { result = ISC_R_FAILURE; - goto cleanup; + goto done; } result = dns_db_newversion(db, &version); @@ -9709,7 +9667,7 @@ dnssec_log(zone, ISC_LOG_ERROR, "zone_sign:dns_db_newversion -> %s", isc_result_totext(result)); - goto cleanup; + goto done; } isc_stdtime_get(&now); @@ -9720,7 +9678,7 @@ dnssec_log(zone, ISC_LOG_ERROR, "zone_sign:dns__zone_findkeys -> %s", isc_result_totext(result)); - goto cleanup; + goto done; } calculate_rrsig_validity(zone, now, &inception, &soaexpire, NULL, @@ -10037,7 +9995,7 @@ "updatesecure -> %s", isc_result_totext( result)); - goto cleanup; + goto done; } } result = updatesignwithkey( @@ -10047,7 +10005,7 @@ dnssec_log(zone, ISC_LOG_ERROR, "updatesignwithkey -> %s", isc_result_totext(result)); - goto cleanup; + goto done; } build_nsec = false; goto next_signing; @@ -10056,7 +10014,7 @@ "zone_sign:" "dns_dbiterator_next -> %s", isc_result_totext(result)); - goto cleanup; + goto done; } else if (is_bottom_of_zone) { dns_dbiterator_current(signing->dbiterator, &node, nextname); @@ -10085,7 +10043,7 @@ dnssec_log(zone, ISC_LOG_ERROR, "zone_sign:dns__zone_updatesigs -> %s", isc_result_totext(result)); - goto cleanup; + goto done; } } @@ -10107,7 +10065,7 @@ if (result != ISC_R_SUCCESS) { dnssec_log(zone, ISC_LOG_ERROR, "zone_sign:del_sigs -> %s", isc_result_totext(result)); - goto cleanup; + goto done; } result = update_soa_serial(zone, db, version, zonediff.diff, zone->mctx, @@ -10116,7 +10074,7 @@ dnssec_log(zone, ISC_LOG_ERROR, "zone_sign:update_soa_serial -> %s", isc_result_totext(result)); - goto cleanup; + goto done; } /* @@ -10129,7 +10087,7 @@ if (result != ISC_R_SUCCESS) { dnssec_log(zone, ISC_LOG_ERROR, "zone_sign:add_sigs -> %s", isc_result_totext(result)); - goto cleanup; + goto done; } /* @@ -10178,13 +10136,13 @@ } UNLOCK_ZONE(zone); -failure: +cleanup: if (result != ISC_R_SUCCESS) { dnssec_log(zone, ISC_LOG_ERROR, "zone_sign: failed: %s", isc_result_totext(result)); } -cleanup: +done: /* * Pause all dbiterators. */ @@ -10430,9 +10388,8 @@ if (result == ISC_R_UNEXPECTEDEND) { continue; } - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(result); + keydata.refresh = refresh_time(kfetch, true); set_refreshkeytimer(zone, &keydata, now, false); @@ -10447,7 +10404,7 @@ 0, &rdata)); } result = ISC_R_SUCCESS; -failure: +cleanup: return result; } @@ -10583,7 +10540,7 @@ LOCK_ZONE(zone); if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_EXITING) || zone->view == NULL) { - goto cleanup; + goto out; } isc_stdtime_get(&now); @@ -11142,7 +11099,7 @@ result = ISC_R_SUCCESS; } -failure: +cleanup: if (result != ISC_R_SUCCESS) { dnssec_log(zone, ISC_LOG_ERROR, "error during managed-keys processing (%s): " @@ -11154,7 +11111,7 @@ dns_db_closeversion(kfetch->db, &ver, commit); } -cleanup: +out: dns_db_detach(&kfetch->db); /* The zone must be managed */ @@ -11418,7 +11375,7 @@ zone_needdump(zone, 30); } -failure: +cleanup: if (!timerset) { isc_time_settoepoch(&zone->refreshkeytime); } @@ -11762,7 +11719,7 @@ "policies unloaded"); } -failure: +cleanup: if (db != NULL) { dns_db_detach(&db); } @@ -16794,7 +16751,7 @@ result = ISC_R_SUCCESS; } -failure: +cleanup: return result; } @@ -17182,8 +17139,8 @@ result = dns_journal_open(zone->mctx, zone->journal, DNS_JOURNAL_READ, &sjournal); - if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) { - goto failure; + if (result != ISC_R_NOTFOUND) { + CHECK(result); } if (!dns_journal_get_sourceserial(rjournal, &start)) { @@ -17217,7 +17174,7 @@ start, end, &soatuple, &zone->rss_diff); if (result == DNS_R_UNCHANGED) { - goto failure; + goto cleanup; } else if (result != ISC_R_SUCCESS) { CHECK(sync_secure_db(zone, zone->rss_raw, zone->rss_db, zone->rss_oldver, &soatuple, @@ -17273,7 +17230,7 @@ * that contents of the raw zone and the secure zone are kept in sync. */ if (result != ISC_R_SUCCESS && dns_db_issecure(zone->rss_db)) { - goto failure; + goto cleanup; } if (rjournal == NULL) { @@ -17311,7 +17268,7 @@ newserial, desired); } -failure: +cleanup: isc_event_free(&zone->rss_event); event = ISC_LIST_HEAD(zone->rss_events); @@ -17589,7 +17546,7 @@ result = ISC_R_SUCCESS; } -failure: +cleanup: if (node != NULL) { dns_db_detachnode(db, &node); } @@ -17744,8 +17701,7 @@ LOCK_ZONE(zone); if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_EXITING) || !inline_secure(zone)) { - result = ISC_R_SHUTTINGDOWN; - goto failure; + CHECK(ISC_R_SHUTTINGDOWN); } TIME_NOW(&loadtime); @@ -17763,45 +17719,32 @@ result = save_nsec3param(zone, &nsec3list); if (result != ISC_R_SUCCESS) { ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); - goto failure; + goto cleanup; } } ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); - result = dns_db_create(zone->mctx, zone->db_argv[0], &zone->origin, - dns_dbtype_zone, zone->rdclass, - zone->db_argc - 1, zone->db_argv + 1, &db); - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(dns_db_create(zone->mctx, zone->db_argv[0], &zone->origin, + dns_dbtype_zone, zone->rdclass, zone->db_argc - 1, + zone->db_argv + 1, &db)); result = dns_db_setgluecachestats(db, zone->gluecachestats); - if (result != ISC_R_SUCCESS && result != ISC_R_NOTIMPLEMENTED) { - goto failure; + if (result != ISC_R_NOTIMPLEMENTED) { + CHECK(result); } - result = dns_db_newversion(db, &version); - if (result != ISC_R_SUCCESS) { - goto failure; - } - - result = dns_db_createiterator(rawdb, 0, &dbiterator); - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(dns_db_newversion(db, &version)); + CHECK(dns_db_createiterator(rawdb, 0, &dbiterator)); for (result = dns_dbiterator_first(dbiterator); result == ISC_R_SUCCESS; result = dns_dbiterator_next(dbiterator)) { - result = copy_non_dnssec_records(db, version, rawdb, dbiterator, - oldserialp); - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(copy_non_dnssec_records(db, version, rawdb, dbiterator, + oldserialp)); } dns_dbiterator_destroy(&dbiterator); if (result != ISC_R_NOMORE) { - goto failure; + goto cleanup; } /* @@ -17809,10 +17752,7 @@ * the old nsec3 parameters and insert them into db */ if (!ISC_LIST_EMPTY(nsec3list)) { - result = restore_nsec3param(zone, db, version, &nsec3list); - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(restore_nsec3param(zone, db, version, &nsec3list)); } dns_db_closeversion(db, &version, true); @@ -17839,7 +17779,7 @@ isc_task_send(zone->task, &setnsec3param_event); } -failure: +cleanup: UNLOCK_ZONE(zone); if (dbiterator != NULL) { dns_dbiterator_destroy(&dbiterator); @@ -20939,7 +20879,7 @@ if (result == ISC_R_NOTFOUND) { *flag = false; result = ISC_R_SUCCESS; - goto failure; + goto cleanup; } for (result = dns_rdataset_first(&rdataset); result == ISC_R_SUCCESS; @@ -20959,7 +20899,7 @@ result = ISC_R_SUCCESS; } -failure: +cleanup: if (node != NULL) { dns_db_detachnode(db, &node); } @@ -21102,7 +21042,7 @@ } } -failure: +cleanup: /* * Put the DNSKEY changes we cared about back on diff->tuples. */ @@ -21198,37 +21138,26 @@ * signature and if not cause them to sign so that newly activated * keys are used. */ - result = tickle_apex_rrset(dns_rdatatype_dnskey, zone, db, ver, now, - diff, zonediff, zone_keys, nkeys, inception, - keyexpire, check_ksk, keyset_kskonly); - if (result != ISC_R_SUCCESS) { - goto failure; - } - result = tickle_apex_rrset(dns_rdatatype_cds, zone, db, ver, now, diff, - zonediff, zone_keys, nkeys, inception, - keyexpire, check_ksk, keyset_kskonly); - if (result != ISC_R_SUCCESS) { - goto failure; - } - result = tickle_apex_rrset(dns_rdatatype_cdnskey, zone, db, ver, now, - diff, zonediff, zone_keys, nkeys, inception, - keyexpire, check_ksk, keyset_kskonly); - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(tickle_apex_rrset(dns_rdatatype_dnskey, zone, db, ver, now, diff, + zonediff, zone_keys, nkeys, inception, + keyexpire, check_ksk, keyset_kskonly)); + CHECK(tickle_apex_rrset(dns_rdatatype_cds, zone, db, ver, now, diff, + zonediff, zone_keys, nkeys, inception, + keyexpire, check_ksk, keyset_kskonly)); + CHECK(tickle_apex_rrset(dns_rdatatype_cdnskey, zone, db, ver, now, diff, + zonediff, zone_keys, nkeys, inception, + keyexpire, check_ksk, keyset_kskonly)); result = dns__zone_updatesigs(diff, db, ver, zone_keys, nkeys, zone, inception, soaexpire, keyexpire, now, check_ksk, keyset_kskonly, zonediff); - if (result != ISC_R_SUCCESS) { dnssec_log(zone, ISC_LOG_ERROR, "sign_apex:dns__zone_updatesigs -> %s", isc_result_totext(result)); - goto failure; } -failure: +cleanup: for (i = 0; i < nkeys; i++) { dst_key_free(&zone_keys[i]); } @@ -21251,12 +21180,12 @@ dns_rdataset_disassociate(&rdataset); } if (result != ISC_R_NOTFOUND) { - goto failure; + goto cleanup; } result = dns_nsec3param_deletechains(db, ver, zone, true, diff); -failure: +cleanup: if (node != NULL) { dns_db_detachnode(db, &node); } @@ -21309,7 +21238,7 @@ } CHECK(updatesecure(db, ver, origin, zone_nsecttl(zone), true, diff)); -failure: +cleanup: return result; } @@ -21516,7 +21445,7 @@ dns_zone_log(zone, ISC_LOG_NOTICE, "checkds: bad DS response from %s: %.*s", addrbuf, (int)buf.used, rcode); - goto failure; + goto cleanup; } /* Make sure that either AA or RA bit is set. */ @@ -21527,7 +21456,7 @@ "checkds: bad DS response from %s: expected AA or " "RA bit set", addrbuf); - goto failure; + goto cleanup; } /* Lookup DS RRset. */ @@ -21672,7 +21601,7 @@ dns_zone_rekey(zone, false); } -failure: +cleanup: if (result != ISC_R_SUCCESS) { dns_zone_log(zone, ISC_LOG_DEBUG(3), "checkds: DS request failed: %s", @@ -22293,7 +22222,7 @@ "failed: %s", keyset.ttl, ttl, isc_result_totext(result)); - goto failure; + goto cleanup; } dnssec_log(zone, ISC_LOG_INFO, "Updating DNSKEY TTL from %u to %u", @@ -22309,11 +22238,9 @@ dns_zone_unlock_keyfiles(zone); - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(result); } else if (result != ISC_R_NOTFOUND) { - goto failure; + goto cleanup; } /* Get the CDS rdataset */ @@ -22328,7 +22255,7 @@ dnssec_log(zone, ISC_LOG_ERROR, "Updating CDS TTL from %u to %u failed: %s", cdsset.ttl, ttl, isc_result_totext(result)); - goto failure; + goto cleanup; } dnssec_log(zone, ISC_LOG_INFO, "Updating CDS TTL from %u to %u", cdsset.ttl, ttl); @@ -22349,7 +22276,7 @@ zone, ISC_LOG_ERROR, "Updating CDNSKEY TTL from %u to %u failed: %s", cdnskeyset.ttl, ttl, isc_result_totext(result)); - goto failure; + goto cleanup; } dnssec_log(zone, ISC_LOG_INFO, "Updating CDNSKEY TTL from %u to %u", cdnskeyset.ttl, @@ -22385,7 +22312,7 @@ "zone_rekey:zone_verifykeys failed: " "some key files are missing"); KASP_UNLOCK(kasp); - goto failure; + goto cleanup; } /* @@ -22424,7 +22351,7 @@ "failed: %s", isc_result_totext(result)); KASP_UNLOCK(kasp); - goto failure; + goto cleanup; } } } @@ -22515,7 +22442,7 @@ dnssec_log(zone, ISC_LOG_ERROR, "zone_rekey:couldn't update zone keys: %s", isc_result_totext(result)); - goto failure; + goto cleanup; } /* @@ -22528,7 +22455,7 @@ dnssec_log(zone, ISC_LOG_ERROR, "zone_rekey:couldn't update CDS/CDNSKEY: %s", isc_result_totext(result)); - goto failure; + goto cleanup; } if (cdsdel || cdnskeydel) { @@ -22567,7 +22494,7 @@ "zone_rekey:couldn't update CDS/CDNSKEY " "DELETE records: %s", isc_result_totext(result)); - goto failure; + goto cleanup; } /* @@ -22875,7 +22802,7 @@ result = ISC_R_SUCCESS; -failure: +cleanup: LOCK_ZONE(zone); if (result != ISC_R_SUCCESS) { /* @@ -22992,29 +22919,29 @@ result = dns_db_findrdataset(db, node, version, dns_rdatatype_cds, dns_rdatatype_none, 0, &cds, NULL); - if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) { - goto failure; + if (result != ISC_R_NOTFOUND) { + CHECK(result); } result = dns_db_findrdataset(db, node, version, dns_rdatatype_cdnskey, dns_rdatatype_none, 0, &cdnskey, NULL); - if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) { - goto failure; + if (result != ISC_R_NOTFOUND) { + CHECK(result); } if (!dns_rdataset_isassociated(&cds) && !dns_rdataset_isassociated(&cdnskey)) { result = ISC_R_SUCCESS; - goto failure; + goto cleanup; } result = dns_db_findrdataset(db, node, version, dns_rdatatype_dnskey, dns_rdatatype_none, 0, &dnskey, NULL); if (result == ISC_R_NOTFOUND) { empty = true; - } else if (result != ISC_R_SUCCESS) { - goto failure; + } else { + CHECK(result); } /* @@ -23046,8 +22973,7 @@ } if (empty) { - result = DNS_R_BADCDS; - goto failure; + CHECK(DNS_R_BADCDS); } CHECK(dns_rdata_tostruct(&crdata, &structcds, NULL)); @@ -23096,18 +23022,16 @@ } } if (result != ISC_R_NOMORE) { - goto failure; + goto cleanup; } } for (i = 0; i < sizeof(algorithms); i++) { if (delete) { if (algorithms[i] != notexpected) { - result = DNS_R_BADCDS; - goto failure; + CHECK(DNS_R_BADCDS); } } else if (algorithms[i] == expected) { - result = DNS_R_BADCDS; - goto failure; + CHECK(DNS_R_BADCDS); } } } @@ -23142,8 +23066,7 @@ } if (empty) { - result = DNS_R_BADCDNSKEY; - goto failure; + CHECK(DNS_R_BADCDNSKEY); } CHECK(dns_rdata_tostruct(&crdata, &structcdnskey, @@ -23170,24 +23093,22 @@ } } if (result != ISC_R_NOMORE) { - goto failure; + goto cleanup; } } for (i = 0; i < sizeof(algorithms); i++) { if (delete) { if (algorithms[i] != notexpected) { - result = DNS_R_BADCDNSKEY; - goto failure; + CHECK(DNS_R_BADCDNSKEY); } } else if (algorithms[i] == expected) { - result = DNS_R_BADCDNSKEY; - goto failure; + CHECK(DNS_R_BADCDNSKEY); } } } result = ISC_R_SUCCESS; -failure: +cleanup: if (dns_rdataset_isassociated(&cds)) { dns_rdataset_disassociate(&cds); } @@ -23446,7 +23367,7 @@ } ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); if (db == NULL) { - goto failure; + goto cleanup; } dns_db_currentversion(db, &oldver); @@ -23455,23 +23376,16 @@ dnssec_log(zone, ISC_LOG_ERROR, "keydone:dns_db_newversion -> %s", isc_result_totext(result)); - goto failure; + goto cleanup; } - result = dns_db_getoriginnode(db, &node); - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(dns_db_getoriginnode(db, &node)); result = dns_db_findrdataset(db, node, newver, zone->privatetype, dns_rdatatype_none, 0, &rdataset, NULL); - if (result == ISC_R_NOTFOUND) { - INSIST(!dns_rdataset_isassociated(&rdataset)); - goto failure; - } if (result != ISC_R_SUCCESS) { INSIST(!dns_rdataset_isassociated(&rdataset)); - goto failure; + goto cleanup; } for (result = dns_rdataset_first(&rdataset); result == ISC_R_SUCCESS; @@ -23528,7 +23442,7 @@ UNLOCK_ZONE(zone); } -failure: +cleanup: if (dns_rdataset_isassociated(&rdataset)) { dns_rdataset_disassociate(&rdataset); } @@ -23610,7 +23524,7 @@ zone_iattach(zone, &dummy); isc_task_send(zone->task, &e); -failure: +cleanup: if (e != NULL) { isc_event_free(&e); } @@ -23725,7 +23639,7 @@ } ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); if (db == NULL) { - goto failure; + goto cleanup; } dns_db_currentversion(db, &oldver); @@ -23734,7 +23648,7 @@ dnssec_log(zone, ISC_LOG_ERROR, "setnsec3param:dns_db_newversion -> %s", isc_result_totext(result)); - goto failure; + goto cleanup; } CHECK(dns_db_getoriginnode(db, &node)); @@ -23756,15 +23670,15 @@ if (result == ISC_R_SUCCESS) { /* * Success because the NSEC3PARAM already exists, but - * function returns void, so goto failure to clean up. + * function returns void, so goto cleanup. */ - goto failure; + goto cleanup; } if (result != DNS_R_NSEC3RESALT && result != ISC_R_NOTFOUND) { dnssec_log(zone, ISC_LOG_DEBUG(3), "setnsec3param:lookup nsec3param -> %s", isc_result_totext(result)); - goto failure; + goto cleanup; } INSIST(param.salt != NULL); @@ -23808,7 +23722,7 @@ } } else if (result != ISC_R_NOTFOUND) { INSIST(!dns_rdataset_isassociated(&prdataset)); - goto failure; + goto cleanup; } /* @@ -23834,7 +23748,7 @@ } } else if (result != ISC_R_NOTFOUND) { INSIST(!dns_rdataset_isassociated(&nrdataset)); - goto failure; + goto cleanup; } /* @@ -23898,7 +23812,7 @@ UNLOCK_ZONE(zone); } -failure: +cleanup: if (dns_rdataset_isassociated(&prdataset)) { dns_rdataset_disassociate(&prdataset); } @@ -24038,8 +23952,8 @@ param->salt = lookup->salt; } - if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS) { - goto failure; + if (result != ISC_R_NOTFOUND) { + CHECK(result); } if (param->salt_length == 0) { @@ -24074,7 +23988,7 @@ INSIST(result != ISC_R_SUCCESS); } -failure: +cleanup: if (dns_rdataset_isassociated(&rdataset)) { dns_rdataset_disassociate(&rdataset); } @@ -24224,7 +24138,7 @@ result = ISC_R_SUCCESS; -failure: +cleanup: if (e != NULL) { isc_event_free(&e); } @@ -24354,7 +24268,7 @@ } ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read); if (db == NULL) { - goto failure; + goto cleanup; } dns_db_currentversion(db, &oldver); @@ -24363,7 +24277,7 @@ dns_zone_log(zone, ISC_LOG_ERROR, "setserial:dns_db_newversion -> %s", isc_result_totext(result)); - goto failure; + goto cleanup; } CHECK(dns_db_createsoatuple(db, oldver, diff.mctx, DNS_DIFFOP_DEL, @@ -24383,7 +24297,7 @@ desired, oldserial + 1, oldserial + 0x7fffffff); } - goto failure; + goto cleanup; } dns_soa_setserial(desired, &newtuple->rdata); @@ -24403,7 +24317,7 @@ zone_needdump(zone, 30); UNLOCK_ZONE(zone); -failure: +cleanup: if (oldtuple != NULL) { dns_difftuple_free(&oldtuple); } @@ -24442,14 +24356,12 @@ if (!inline_secure(zone)) { if (!dns_zone_isdynamic(zone, true)) { - result = DNS_R_NOTDYNAMIC; - goto failure; + CHECK(DNS_R_NOTDYNAMIC); } } if (zone->update_disabled) { - result = DNS_R_FROZEN; - goto failure; + CHECK(DNS_R_FROZEN); } e = isc_event_allocate(zone->mctx, zone, DNS_EVENT_SETSERIAL, setserial, @@ -24461,7 +24373,7 @@ zone_iattach(zone, &dummy); isc_task_send(zone->task, &e); -failure: +cleanup: if (e != NULL) { isc_event_free(&e); } @@ -24509,16 +24421,14 @@ if (zone->view != NULL) { result = dns_view_getsecroots(zone->view, &secroots); - if (result != ISC_R_SUCCESS) { - goto done; - } + CHECK(result); } origin = dns_db_origin(db); result = dns_zoneverify_dnssec(zone, db, version, origin, secroots, zone->mctx, true, false, dnssec_report); -done: +cleanup: if (secroots != NULL) { dns_keytable_detach(&secroots); } diff -Nru bind9-9.18.41/lib/irs/resconf.c bind9-9.18.44/lib/irs/resconf.c --- bind9-9.18.41/lib/irs/resconf.c 2025-10-18 10:21:03.172261950 +0000 +++ bind9-9.18.44/lib/irs/resconf.c 2026-01-09 13:44:04.854039484 +0000 @@ -76,13 +76,6 @@ #define RESCONFMAXLINELEN 256U /*%< max size of a line */ #define RESCONFMAXSORTLIST 10U /*%< max 10 */ -#define CHECK(op) \ - do { \ - result = (op); \ - if (result != ISC_R_SUCCESS) \ - goto cleanup; \ - } while (0) - /*! * configuration data structure */ diff -Nru bind9-9.18.41/lib/isc/base32.c bind9-9.18.44/lib/isc/base32.c --- bind9-9.18.41/lib/isc/base32.c 2025-10-18 10:21:03.172261950 +0000 +++ bind9-9.18.44/lib/isc/base32.c 2026-01-09 13:44:04.854039484 +0000 @@ -22,13 +22,6 @@ #include #include -#define RETERR(x) \ - do { \ - isc_result_t _r = (x); \ - if (_r != ISC_R_SUCCESS) \ - return ((_r)); \ - } while (0) - /*@{*/ /*! * These static functions are also present in lib/dns/rdata.c. I'm not diff -Nru bind9-9.18.41/lib/isc/base64.c bind9-9.18.44/lib/isc/base64.c --- bind9-9.18.41/lib/isc/base64.c 2025-10-18 10:21:03.173261976 +0000 +++ bind9-9.18.44/lib/isc/base64.c 2026-01-09 13:44:04.854039484 +0000 @@ -21,13 +21,6 @@ #include #include -#define RETERR(x) \ - do { \ - isc_result_t _r = (x); \ - if (_r != ISC_R_SUCCESS) \ - return ((_r)); \ - } while (0) - /*@{*/ /*! * These static functions are also present in lib/dns/rdata.c. I'm not diff -Nru bind9-9.18.41/lib/isc/hex.c bind9-9.18.44/lib/isc/hex.c --- bind9-9.18.41/lib/isc/hex.c 2025-10-18 10:21:03.174262003 +0000 +++ bind9-9.18.44/lib/isc/hex.c 2026-01-09 13:44:04.856039517 +0000 @@ -22,13 +22,6 @@ #include #include -#define RETERR(x) \ - do { \ - isc_result_t _r = (x); \ - if (_r != ISC_R_SUCCESS) \ - return ((_r)); \ - } while (0) - /* * BEW: These static functions are copied from lib/dns/rdata.c. */ diff -Nru bind9-9.18.41/lib/isc/httpd.c bind9-9.18.44/lib/isc/httpd.c --- bind9-9.18.41/lib/isc/httpd.c 2025-10-18 10:21:03.175262030 +0000 +++ bind9-9.18.44/lib/isc/httpd.c 2026-01-09 13:44:04.856039517 +0000 @@ -37,14 +37,6 @@ #include #endif /* ifdef HAVE_ZLIB */ -#define CHECK(m) \ - do { \ - result = (m); \ - if (result != ISC_R_SUCCESS) { \ - goto cleanup; \ - } \ - } while (0) - /* * Size the recv buffer to hold at maximum two full buffers from isc_nm_read(), * so we don't have to handle the truncation. diff -Nru bind9-9.18.41/lib/isc/include/isc/log.h bind9-9.18.44/lib/isc/include/isc/log.h --- bind9-9.18.41/lib/isc/include/isc/log.h 2025-10-18 10:21:03.178262110 +0000 +++ bind9-9.18.44/lib/isc/include/isc/log.h 2026-01-09 13:44:04.860039583 +0000 @@ -19,6 +19,7 @@ #include #include #include /* XXXDCL NT */ +#include #include #include diff -Nru bind9-9.18.41/lib/isc/include/isc/util.h bind9-9.18.44/lib/isc/include/isc/util.h --- bind9-9.18.41/lib/isc/include/isc/util.h 2025-10-18 10:21:03.183262244 +0000 +++ bind9-9.18.44/lib/isc/include/isc/util.h 2026-01-09 13:44:04.865039665 +0000 @@ -345,6 +345,29 @@ #endif /* UNIT_TESTING */ +/* + * Check for ISC_R_SUCCESS. On any other result, jump to a cleanup + * label. (This macro requires the function to define `result` + * and `cleanup:`.) + */ +#define CHECK(r) \ + do { \ + result = (r); \ + if (result != ISC_R_SUCCESS) \ + goto cleanup; \ + } while (0) + +/* + * Check for ISC_R_SUCCESS and continue if found. For any other + * result, return the result. + */ +#define RETERR(x) \ + do { \ + isc_result_t _r = (x); \ + if (_r != ISC_R_SUCCESS) \ + return ((_r)); \ + } while (0) + /*% * Time */ diff -Nru bind9-9.18.41/lib/isc/random.c bind9-9.18.44/lib/isc/random.c --- bind9-9.18.41/lib/isc/random.c 2025-10-18 10:21:03.189262404 +0000 +++ bind9-9.18.44/lib/isc/random.c 2026-01-09 13:44:04.872039781 +0000 @@ -50,8 +50,7 @@ #if FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION /* * A fixed stream of numbers helps with problem reproduction when - * fuzzing. The first result needs to be non-zero as expected by - * random_test.c (it starts with ISC_RANDOM_BUFSIZE, see above). + * fuzzing. */ return (uint32_t)(isc__random_pos++); #endif /* if FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */ diff -Nru bind9-9.18.41/lib/isccfg/namedconf.c bind9-9.18.44/lib/isccfg/namedconf.c --- bind9-9.18.41/lib/isccfg/namedconf.c 2025-10-18 10:21:03.195262564 +0000 +++ bind9-9.18.44/lib/isccfg/namedconf.c 2026-01-09 13:44:04.879039896 +0000 @@ -34,14 +34,6 @@ #define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base) -/*% Check a return value. */ -#define CHECK(op) \ - do { \ - result = (op); \ - if (result != ISC_R_SUCCESS) \ - goto cleanup; \ - } while (0) - /*% Clean up a configuration object if non-NULL. */ #define CLEANUP_OBJ(obj) \ do { \ diff -Nru bind9-9.18.41/lib/isccfg/parser.c bind9-9.18.44/lib/isccfg/parser.c --- bind9-9.18.41/lib/isccfg/parser.c 2025-10-18 10:21:03.196262591 +0000 +++ bind9-9.18.44/lib/isccfg/parser.c 2026-01-09 13:44:04.879039896 +0000 @@ -78,14 +78,6 @@ #define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base) -/* Check a return value. */ -#define CHECK(op) \ - do { \ - result = (op); \ - if (result != ISC_R_SUCCESS) \ - goto cleanup; \ - } while (0) - /* Clean up a configuration object if non-NULL. */ #define CLEANUP_OBJ(obj) \ do { \ diff -Nru bind9-9.18.41/lib/ns/hooks.c bind9-9.18.44/lib/ns/hooks.c --- bind9-9.18.41/lib/ns/hooks.c 2025-10-18 10:21:03.196262591 +0000 +++ bind9-9.18.44/lib/ns/hooks.c 2026-01-09 13:44:04.880039913 +0000 @@ -34,14 +34,6 @@ #include #include -#define CHECK(op) \ - do { \ - result = (op); \ - if (result != ISC_R_SUCCESS) { \ - goto cleanup; \ - } \ - } while (0) - struct ns_plugin { isc_mem_t *mctx; uv_lib_t handle; diff -Nru bind9-9.18.41/lib/ns/query.c bind9-9.18.44/lib/ns/query.c --- bind9-9.18.41/lib/ns/query.c 2025-10-18 10:21:03.200262698 +0000 +++ bind9-9.18.44/lib/ns/query.c 2026-01-09 13:44:04.883039962 +0000 @@ -2281,7 +2281,10 @@ */ query_addtoname(mname, rdataset); query_setorder(qctx, mname, rdataset); - if (qctx->qtype != dns_rdatatype_any) { + if (qctx->qtype != dns_rdatatype_any || + (!qctx->authoritative && section == DNS_SECTION_AUTHORITY && + rdataset->type == dns_rdatatype_ns)) + { query_additional(qctx, mname, rdataset); } diff -Nru bind9-9.18.41/lib/ns/update.c bind9-9.18.44/lib/ns/update.c --- bind9-9.18.41/lib/ns/update.c 2025-10-18 10:21:03.201262725 +0000 +++ bind9-9.18.44/lib/ns/update.c 2026-01-09 13:44:04.884039979 +0000 @@ -78,34 +78,6 @@ #define LOGLEVEL_DEBUG ISC_LOG_DEBUG(8) /*% - * Check an operation for failure. These macros all assume that - * the function using them has a 'result' variable and a 'failure' - * label. - */ -#define CHECK(op) \ - do { \ - result = (op); \ - if (result != ISC_R_SUCCESS) \ - goto failure; \ - } while (0) - -/*% - * Fail unconditionally with result 'code', which must not - * be ISC_R_SUCCESS. The reason for failure presumably has - * been logged already. - * - * The test against ISC_R_SUCCESS is there to keep the Solaris compiler - * from complaining about "end-of-loop code not reached". - */ - -#define FAIL(code) \ - do { \ - result = (code); \ - if (result != ISC_R_SUCCESS) \ - goto failure; \ - } while (0) - -/*% * Fail unconditionally and log as a client error. * The test against ISC_R_SUCCESS is there to keep the Solaris compiler * from complaining about "end-of-loop code not reached". @@ -127,7 +99,7 @@ "update %s: %s (%s)", _what, msg, \ isc_result_totext(result)); \ if (result != ISC_R_SUCCESS) \ - goto failure; \ + goto cleanup; \ } while (0) #define PREREQFAILC(code, msg) \ do { \ @@ -156,7 +128,7 @@ msg, isc_result_totext(result)); \ } \ if (result != ISC_R_SUCCESS) \ - goto failure; \ + goto cleanup; \ } while (0) #define PREREQFAILN(code, name, msg) \ do { \ @@ -187,7 +159,7 @@ _tbuf, msg, isc_result_totext(result)); \ } \ if (result != ISC_R_SUCCESS) \ - goto failure; \ + goto cleanup; \ } while (0) #define PREREQFAILNT(code, name, type, msg) \ do { \ @@ -206,7 +178,7 @@ update_log(client, zone, LOGLEVEL_PROTOCOL, "error: %s: %s", \ msg, isc_result_totext(result)); \ if (result != ISC_R_SUCCESS) \ - goto failure; \ + goto cleanup; \ } while (0) /* @@ -493,7 +465,7 @@ } return ISC_R_SUCCESS; -failure: +cleanup: dns_diff_clear(diff); return result; } @@ -1063,7 +1035,7 @@ CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_EXISTS, name, 0, rdata, &tuple)); ISC_LIST_APPEND(diff->tuples, tuple, link); -failure: +cleanup: return result; } @@ -1208,18 +1180,12 @@ { dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdataset_current(&rdataset, &rdata); - result = temp_append(&d_rrs, name, &rdata); - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(temp_append(&d_rrs, name, &rdata)); } if (result != ISC_R_NOMORE) { - goto failure; - } - result = dns_diff_sort(&d_rrs, temp_order); - if (result != ISC_R_SUCCESS) { - goto failure; + goto cleanup; } + CHECK(dns_diff_sort(&d_rrs, temp_order)); /* * Collect all update RRs for this name and type @@ -1236,11 +1202,8 @@ } /* Compare the two sorted lists. */ - result = temp_check_rrset(ISC_LIST_HEAD(u_rrs.tuples), - ISC_LIST_HEAD(d_rrs.tuples)); - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(temp_check_rrset(ISC_LIST_HEAD(u_rrs.tuples), + ISC_LIST_HEAD(d_rrs.tuples))); /* * We are done with the tuples, but we can't free @@ -1253,7 +1216,7 @@ continue; - failure: + cleanup: dns_diff_clear(&d_rrs); dns_diff_clear(&u_rrs); dns_diff_clear(&trash); @@ -1516,7 +1479,7 @@ dns_diff_append(&ctx->add_diff, &tuple); } } -failure: +cleanup: return result; } @@ -1584,7 +1547,7 @@ CHECK(do_one_tuple(&addtuple, db, ver, diff)); result = ISC_R_SUCCESS; -failure: +cleanup: if (addtuple != NULL) { dns_difftuple_free(&addtuple); } @@ -1733,7 +1696,7 @@ } result = dns_zone_checknames(zone, name, &rdata); if (result != ISC_R_SUCCESS) { - FAIL(DNS_R_REFUSED); + CHECK(DNS_R_REFUSED); } } else if (update_class == dns_rdataclass_any) { if (ttl != 0 || rdata.length != 0 || @@ -1750,7 +1713,7 @@ update_log(client, zone, ISC_LOG_WARNING, "update RR has incorrect class %d", update_class); - FAIL(DNS_R_FORMERR); + CHECK(DNS_R_FORMERR); } /* @@ -1866,7 +1829,7 @@ } } if (result != ISC_R_NOMORE) { - FAIL(result); + CHECK(result); } update_log(client, zone, LOGLEVEL_DEBUG, "update section prescan OK"); @@ -1899,7 +1862,7 @@ dns_zone_gettask(zone, &zonetask); isc_task_send(zonetask, ISC_EVENT_PTR(&event)); -failure: +cleanup: if (db != NULL) { dns_db_closeversion(db, &ver, false); dns_db_detach(&db); @@ -2012,9 +1975,7 @@ * We can now fail due to a bad signature as we now know * that we are the primary. */ - if (sigresult != ISC_R_SUCCESS) { - FAIL(sigresult); - } + CHECK(sigresult); dns_message_clonebuffer(client->message); CHECK(send_update_event(client, zone)); break; @@ -2028,7 +1989,7 @@ } return; -failure: +cleanup: if (result == DNS_R_REFUSED) { inc_stats(client, zone, ns_statscounter_updaterej); } @@ -2087,7 +2048,7 @@ } result = ISC_R_SUCCESS; -failure: +cleanup: for (tuple = ISC_LIST_HEAD(temp_diff.tuples); tuple != NULL; tuple = ISC_LIST_HEAD(temp_diff.tuples)) { @@ -2225,7 +2186,7 @@ if (result == ISC_R_NOTFOUND) { *flag = false; result = ISC_R_SUCCESS; - goto failure; + goto cleanup; } else { CHECK(result); } @@ -2234,7 +2195,7 @@ if (result == ISC_R_NOTFOUND) { *flag = false; result = ISC_R_SUCCESS; - goto failure; + goto cleanup; } for (result = dns_rdataset_first(&rdataset); result == ISC_R_SUCCESS; @@ -2254,7 +2215,7 @@ result = ISC_R_SUCCESS; } -failure: +cleanup: if (node != NULL) { dns_db_detachnode(db, &node); } @@ -2281,9 +2242,7 @@ if (result == ISC_R_NOTFOUND) { goto try_private; } - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(result); for (result = dns_rdataset_first(&rdataset); result == ISC_R_SUCCESS; result = dns_rdataset_next(&rdataset)) @@ -2299,7 +2258,7 @@ } } if (result != ISC_R_NOMORE) { - goto failure; + goto cleanup; } dns_rdataset_disassociate(&rdataset); @@ -2314,9 +2273,7 @@ if (result == ISC_R_NOTFOUND) { goto success; } - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(result); for (result = dns_rdataset_first(&rdataset); result == ISC_R_SUCCESS; result = dns_rdataset_next(&rdataset)) @@ -2340,14 +2297,14 @@ } } if (result != ISC_R_NOMORE) { - goto failure; + goto cleanup; } success: *iterationsp = iterations; result = ISC_R_SUCCESS; -failure: +cleanup: if (node != NULL) { dns_db_detachnode(db, &node); } @@ -2372,8 +2329,7 @@ if (!dns_zone_check_dnskey_nsec3(zone, db, ver, diff, NULL, 0)) { update_log(client, zone, ISC_LOG_ERROR, "NSEC only DNSKEYs and NSEC3 chains not allowed"); - result = DNS_R_REFUSED; - goto failure; + CHECK(DNS_R_REFUSED); } /* Verify NSEC3 params */ @@ -2381,11 +2337,10 @@ if (iterations > dns_nsec3_maxiterations()) { update_log(client, zone, ISC_LOG_ERROR, "too many NSEC3 iterations (%u)", iterations); - result = DNS_R_REFUSED; - goto failure; + CHECK(DNS_R_REFUSED); } -failure: +cleanup: return result; } @@ -2660,7 +2615,7 @@ } result = ISC_R_SUCCESS; -failure: +cleanup: dns_diff_clear(&temp_diff); return result; } @@ -2719,7 +2674,7 @@ } result = ISC_R_SUCCESS; -failure: +cleanup: dns_diff_clear(&temp_diff); return result; } @@ -2860,7 +2815,7 @@ } } -failure: +cleanup: dns_diff_clear(&temp_diff); return result; } @@ -3013,14 +2968,14 @@ UNEXPECTED_ERROR( "temp entry creation failed: %s", isc_result_totext(result)); - FAIL(ISC_R_UNEXPECTED); + CHECK(ISC_R_UNEXPECTED); } } else { PREREQFAILC(DNS_R_FORMERR, "malformed prerequisite"); } } if (result != ISC_R_NOMORE) { - FAIL(result); + CHECK(result); } /* @@ -3287,7 +3242,7 @@ if (result != ISC_R_SUCCESS) { dns_diff_clear(&ctx.del_diff); dns_diff_clear(&ctx.add_diff); - goto failure; + goto cleanup; } result = update_one_rr( db, ver, &diff, DNS_DIFFOP_ADD, @@ -3299,7 +3254,7 @@ "failed: %s", isc_result_totext( result)); - goto failure; + goto cleanup; } } } @@ -3390,13 +3345,9 @@ * that are in use (under our control). */ if (dns_rdatatype_iskeymaterial(rdata.type)) { - isc_result_t r; bool inuse = false; - r = dns_zone_dnskey_inuse(zone, &rdata, - &inuse); - if (r != ISC_R_SUCCESS) { - FAIL(r); - } + CHECK(dns_zone_dnskey_inuse( + zone, &rdata, &inuse)); if (inuse) { char typebuf [DNS_RDATATYPE_FORMATSIZE]; @@ -3423,7 +3374,7 @@ } } if (result != ISC_R_NOMORE) { - FAIL(result); + CHECK(result); } /* @@ -3442,8 +3393,7 @@ update_log(client, zone, LOGLEVEL_PROTOCOL, "update rejected: post update name server " "sanity check failed"); - result = DNS_R_REFUSED; - goto failure; + CHECK(DNS_R_REFUSED); } } if (!ISC_LIST_EMPTY(diff.tuples)) { @@ -3452,12 +3402,9 @@ update_log(client, zone, LOGLEVEL_PROTOCOL, "update rejected: bad %s RRset", result == DNS_R_BADCDS ? "CDS" : "CDNSKEY"); - result = DNS_R_REFUSED; - goto failure; - } - if (result != ISC_R_SUCCESS) { - goto failure; + CHECK(DNS_R_REFUSED); } + CHECK(result); } /* @@ -3499,8 +3446,7 @@ "records removed and " "'dnssec-secure-to-insecure' " "not set"); - result = DNS_R_REFUSED; - goto failure; + CHECK(DNS_R_REFUSED); } } @@ -3533,7 +3479,7 @@ update_log(client, zone, ISC_LOG_ERROR, "RRSIG/NSEC/NSEC3 update failed: %s", isc_result_totext(result)); - goto failure; + goto cleanup; } } @@ -3545,8 +3491,7 @@ "records in zone (%" PRIu64 ") exceeds max-records (%u)", records, maxrecords); - result = DNS_R_TOOMANYRECORDS; - goto failure; + CHECK(DNS_R_TOOMANYRECORDS); } } @@ -3674,7 +3619,7 @@ result = ISC_R_SUCCESS; goto common; -failure: +cleanup: /* * The reason for failure should have been logged at this point. */ diff -Nru bind9-9.18.41/lib/ns/xfrout.c bind9-9.18.44/lib/ns/xfrout.c --- bind9-9.18.41/lib/ns/xfrout.c 2025-10-18 10:21:03.201262725 +0000 +++ bind9-9.18.44/lib/ns/xfrout.c 2026-01-09 13:44:04.884039979 +0000 @@ -81,7 +81,7 @@ "bad zone transfer request: %s (%s)", msg, \ isc_result_totext(code)); \ if (result != ISC_R_SUCCESS) \ - goto failure; \ + goto cleanup; \ } while (0) #define FAILQ(code, msg, question, rdclass) \ @@ -96,14 +96,7 @@ "bad zone transfer request: '%s/%s': %s (%s)", \ _buf1, _buf2, msg, isc_result_totext(code)); \ if (result != ISC_R_SUCCESS) \ - goto failure; \ - } while (0) - -#define CHECK(op) \ - do { \ - result = (op); \ - if (result != ISC_R_SUCCESS) \ - goto failure; \ + goto cleanup; \ } while (0) /**************************************************************************/ @@ -250,7 +243,7 @@ *sp = (rrstream_t *)s; return ISC_R_SUCCESS; -failure: +cleanup: ixfr_rrstream_destroy((rrstream_t **)(void *)&s); return result; } @@ -331,7 +324,7 @@ *sp = (rrstream_t *)s; return ISC_R_SUCCESS; -failure: +cleanup: axfr_rrstream_destroy((rrstream_t **)(void *)&s); return result; } @@ -451,7 +444,7 @@ *sp = (rrstream_t *)s; return ISC_R_SUCCESS; -failure: +cleanup: soa_rrstream_destroy((rrstream_t **)(void *)&s); return result; } @@ -772,7 +765,7 @@ isc_log_write(XFROUT_COMMON_LOGARGS, ISC_LOG_WARNING, "%s request denied: %s", mnemonic, isc_result_totext(result)); - goto failure; + goto cleanup; } /* @@ -829,7 +822,7 @@ ISC_LOG_ERROR, "zone transfer '%s/%s' denied", _buf1, _buf2); - goto failure; + goto cleanup; } if (result != ISC_R_SUCCESS) { FAILQ(DNS_R_NOTAUTH, "non-authoritative zone", @@ -1171,7 +1164,7 @@ result = ISC_R_SUCCESS; -failure: +cleanup: if (result == DNS_R_REFUSED) { inc_stats(client, zone, ns_statscounter_xfrrej); } @@ -1279,7 +1272,7 @@ xfr->txmemlen = len; /* - * These MUST be after the last "goto failure;" / CHECK to + * These MUST be after the last "goto cleanup;" / CHECK to * prevent a double free by the caller. */ xfr->quota = quota; @@ -1387,18 +1380,12 @@ isc_buffer_add(&xfr->buf, 12 + 4); qrdataset = NULL; - result = dns_message_gettemprdataset(msg, &qrdataset); - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(dns_message_gettemprdataset(msg, &qrdataset)); dns_rdataset_makequestion(qrdataset, xfr->client->message->rdclass, xfr->qtype); - result = dns_message_gettempname(msg, &qname); - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(dns_message_gettempname(msg, &qname)); isc_buffer_availableregion(&xfr->buf, &r); INSIST(r.length >= xfr->qname->length); r.length = xfr->qname->length; @@ -1458,8 +1445,7 @@ "(%d bytes)", size); /* XXX DNS_R_RRTOOLARGE? */ - result = ISC_R_NOSPACE; - goto failure; + CHECK(ISC_R_NOSPACE); } break; } @@ -1468,10 +1454,7 @@ log_rr(name, rdata, ttl); /* XXX */ } - result = dns_message_gettempname(msg, &msgname); - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(dns_message_gettempname(msg, &msgname)); isc_buffer_availableregion(&xfr->buf, &r); INSIST(r.length >= name->length); r.length = name->length; @@ -1481,20 +1464,14 @@ /* Reserve space for RR header. */ isc_buffer_add(&xfr->buf, 10); - result = dns_message_gettemprdata(msg, &msgrdata); - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(dns_message_gettemprdata(msg, &msgrdata)); isc_buffer_availableregion(&xfr->buf, &r); r.length = rdata->length; isc_buffer_putmem(&xfr->buf, rdata->data, rdata->length); dns_rdata_init(msgrdata); dns_rdata_fromregion(msgrdata, rdata->rdclass, rdata->type, &r); - result = dns_message_gettemprdatalist(msg, &msgrdl); - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(dns_message_gettemprdatalist(msg, &msgrdl)); msgrdl->type = rdata->type; msgrdl->rdclass = rdata->rdclass; msgrdl->ttl = ttl; @@ -1507,10 +1484,7 @@ } ISC_LIST_APPEND(msgrdl->rdata, msgrdata, link); - result = dns_message_gettemprdataset(msg, &msgrds); - if (result != ISC_R_SUCCESS) { - goto failure; - } + CHECK(dns_message_gettemprdataset(msg, &msgrds)); result = dns_rdatalist_tordataset(msgrdl, msgrds); INSIST(result == ISC_R_SUCCESS); @@ -1616,7 +1590,7 @@ /* Advance lasttsig to be the last TSIG generated */ CHECK(dns_message_getquerytsig(msg, xfr->mctx, &xfr->lasttsig)); -failure: +cleanup: if (msgname != NULL) { if (msgrds != NULL) { if (dns_rdataset_isassociated(msgrds)) { diff -Nru bind9-9.18.41/srcid bind9-9.18.44/srcid --- bind9-9.18.41/srcid 2025-10-18 10:22:48.202997199 +0000 +++ bind9-9.18.44/srcid 2026-01-09 13:46:21.762592221 +0000 @@ -1 +1 @@ -e8adafa +2e74eea diff -Nru bind9-9.18.41/tests/dns/db_test.c bind9-9.18.44/tests/dns/db_test.c --- bind9-9.18.41/tests/dns/db_test.c 2025-10-18 10:21:03.205262832 +0000 +++ bind9-9.18.44/tests/dns/db_test.c 2026-01-09 13:44:04.888040045 +0000 @@ -177,7 +177,7 @@ count = 0; do { count++; - assert_in_range(count, 1, 21); /* loop sanity */ + assert_int_in_range(count, 1, 21); /* loop sanity */ assert_int_equal(rdataset.attributes & DNS_RDATASETATTR_STALE, 0); @@ -212,7 +212,8 @@ count = 0; do { count++; - assert_in_range(count, 0, 49); /* loop sanity */ + assert_int_in_range(count, 0, 49); /* loop + sanity */ assert_int_equal(result, ISC_R_SUCCESS); assert_int_equal(rdataset.attributes & DNS_RDATASETATTR_STALE, @@ -231,7 +232,7 @@ * usleep(100000) can be slightly less than 10ms so * allow the count to reach 11. */ - assert_in_range(count, 1, 11); + assert_int_in_range(count, 1, 11); assert_int_equal(result, ISC_R_NOTFOUND); break; case 2: diff -Nru bind9-9.18.41/tests/dns/dispatch_test.c bind9-9.18.44/tests/dns/dispatch_test.c --- bind9-9.18.41/tests/dns/dispatch_test.c 2025-10-18 10:21:03.205262832 +0000 +++ bind9-9.18.44/tests/dns/dispatch_test.c 2026-01-09 13:44:04.889040061 +0000 @@ -558,7 +558,7 @@ uv_sem_wait(&sem); - assert_in_range(atomic_load_acquire(&testdata.responses), 1, 2); + assert_uint_in_range(atomic_load_acquire(&testdata.responses), 1, 2); assert_int_equal(atomic_load_acquire(&testdata.result), ISC_R_SUCCESS); /* Cleanup */ diff -Nru bind9-9.18.41/tests/dns/rbtdb_test.c bind9-9.18.44/tests/dns/rbtdb_test.c --- bind9-9.18.41/tests/dns/rbtdb_test.c 2025-10-18 10:21:03.207262885 +0000 +++ bind9-9.18.44/tests/dns/rbtdb_test.c 2026-01-09 13:44:04.890040078 +0000 @@ -35,11 +35,9 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wshadow" -#undef CHECK #include "rbtdb.c" #pragma GCC diagnostic pop -#undef CHECK #include const char *ownercase_vectors[12][2] = { diff -Nru bind9-9.18.41/tests/dns/rdata_test.c bind9-9.18.44/tests/dns/rdata_test.c --- bind9-9.18.41/tests/dns/rdata_test.c 2025-10-18 10:21:03.207262885 +0000 +++ bind9-9.18.44/tests/dns/rdata_test.c 2026-01-09 13:44:04.891040094 +0000 @@ -1026,11 +1026,13 @@ ISC_RUN_TEST_IMPL(amtrelay) { text_ok_t text_ok[] = { TEXT_INVALID(""), TEXT_INVALID("0"), TEXT_INVALID("0 0"), + TEXT_INVALID("0 0 0"), /* gateway type 0 */ - TEXT_VALID("0 0 0"), TEXT_VALID("0 1 0"), - TEXT_INVALID("0 2 0"), /* discovery out of range */ - TEXT_VALID("255 1 0"), /* max precedence */ - TEXT_INVALID("256 1 0"), /* precedence out of range */ + TEXT_INVALID("0 0 0 x"), /* bad placeholder */ + TEXT_VALID("0 0 0 ."), TEXT_VALID("0 1 0 ."), + TEXT_INVALID("0 2 0 ."), /* discovery out of range */ + TEXT_VALID("255 1 0 ."), /* max precedence */ + TEXT_INVALID("256 1 0 ."), /* precedence out of range */ /* IPv4 gateway */ TEXT_INVALID("0 0 1"), /* no address */ diff -Nru bind9-9.18.41/tests/dns/resolver_test.c bind9-9.18.44/tests/dns/resolver_test.c --- bind9-9.18.41/tests/dns/resolver_test.c 2025-10-18 10:21:03.208262912 +0000 +++ bind9-9.18.44/tests/dns/resolver_test.c 2026-01-09 13:44:04.891040094 +0000 @@ -180,7 +180,7 @@ dns_resolver_settimeout(resolver, 4000000); timeout = dns_resolver_gettimeout(resolver); - assert_in_range(timeout, 0, 3999999); + assert_uint_in_range(timeout, 0, 3999999); destroy_resolver(&resolver); } diff -Nru bind9-9.18.41/tests/dns/update_test.c bind9-9.18.44/tests/dns/update_test.c --- bind9-9.18.41/tests/dns/update_test.c 2025-10-18 10:21:03.213263045 +0000 +++ bind9-9.18.44/tests/dns/update_test.c 2026-01-09 13:44:04.897040193 +0000 @@ -38,11 +38,9 @@ */ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wshadow" -#undef CHECK #include "update.c" #pragma GCC diagnostic pop -#undef CHECK #include static int diff -Nru bind9-9.18.41/tests/include/tests/isc.h bind9-9.18.44/tests/include/tests/isc.h --- bind9-9.18.41/tests/include/tests/isc.h 2025-10-18 10:21:03.214263072 +0000 +++ bind9-9.18.44/tests/include/tests/isc.h 2026-01-09 13:44:04.898040210 +0000 @@ -62,6 +62,16 @@ #define TESTS_DIR "./" #endif +/* cmocka<2.0.0 compatibility */ +#ifndef assert_int_in_range +#define assert_int_in_range(value, min, max) \ + assert_in_range((value), (min), (max)) +#endif +#ifndef assert_uint_in_range +#define assert_uint_in_range(value, min, max) \ + assert_in_range((value), (min), (max)) +#endif + /* clang-format off */ /* Copied from cmocka */ #define ISC_TEST_ENTRY(name) \ @@ -117,8 +127,8 @@ isc_mem_debugging |= ISC_MEM_DEBUGRECORD; \ isc_mem_create(&mctx); \ \ - while ((c = isc_commandline_parse(argc, argv, "dlt:")) != -1) \ - { \ + while ((c = isc_commandline_parse(argc, argv, "dlt:")) != \ + -1) { \ switch (c) { \ case 'd': \ debug = true; \ diff -Nru bind9-9.18.41/tests/isc/Makefile.am bind9-9.18.44/tests/isc/Makefile.am --- bind9-9.18.41/tests/isc/Makefile.am 2025-10-18 10:21:03.216263126 +0000 +++ bind9-9.18.44/tests/isc/Makefile.am 2026-01-09 13:44:04.899040226 +0000 @@ -29,7 +29,6 @@ pool_test \ quota_test \ radix_test \ - random_test \ regex_test \ result_test \ safe_test \ @@ -89,10 +88,6 @@ netmgr_test.c \ uv_wrap.h -random_test_LDADD = \ - $(LDADD) \ - -lm - task_test_CPPFLAGS = \ $(AM_CPPFLAGS) diff -Nru bind9-9.18.41/tests/isc/Makefile.in bind9-9.18.44/tests/isc/Makefile.in --- bind9-9.18.41/tests/isc/Makefile.in 2025-10-18 10:21:44.009326884 +0000 +++ bind9-9.18.44/tests/isc/Makefile.in 2026-01-09 13:45:08.548189563 +0000 @@ -105,12 +105,11 @@ hmac_test$(EXEEXT) ht_test$(EXEEXT) lex_test$(EXEEXT) \ md_test$(EXEEXT) mem_test$(EXEEXT) netaddr_test$(EXEEXT) \ netmgr_test$(EXEEXT) parse_test$(EXEEXT) pool_test$(EXEEXT) \ - quota_test$(EXEEXT) radix_test$(EXEEXT) random_test$(EXEEXT) \ - regex_test$(EXEEXT) result_test$(EXEEXT) safe_test$(EXEEXT) \ - siphash_test$(EXEEXT) sockaddr_test$(EXEEXT) \ - stats_test$(EXEEXT) symtab_test$(EXEEXT) task_test$(EXEEXT) \ - taskpool_test$(EXEEXT) time_test$(EXEEXT) timer_test$(EXEEXT) \ - $(am__EXEEXT_1) + quota_test$(EXEEXT) radix_test$(EXEEXT) regex_test$(EXEEXT) \ + result_test$(EXEEXT) safe_test$(EXEEXT) siphash_test$(EXEEXT) \ + sockaddr_test$(EXEEXT) stats_test$(EXEEXT) \ + symtab_test$(EXEEXT) task_test$(EXEEXT) taskpool_test$(EXEEXT) \ + time_test$(EXEEXT) timer_test$(EXEEXT) $(am__EXEEXT_1) @HAVE_LIBNGHTTP2_TRUE@am__append_2 = \ @HAVE_LIBNGHTTP2_TRUE@ doh_test @@ -268,9 +267,6 @@ radix_test_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \ $(top_builddir)/tests/libtest/libtest.la $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_3) -random_test_SOURCES = random_test.c -random_test_OBJECTS = random_test.$(OBJEXT) -random_test_DEPENDENCIES = $(am__DEPENDENCIES_4) regex_test_SOURCES = regex_test.c regex_test_OBJECTS = regex_test.$(OBJEXT) regex_test_LDADD = $(LDADD) @@ -363,11 +359,10 @@ ./$(DEPDIR)/netmgr_test-netmgr_test.Po \ ./$(DEPDIR)/parse_test.Po ./$(DEPDIR)/pool_test.Po \ ./$(DEPDIR)/quota_test.Po ./$(DEPDIR)/radix_test.Po \ - ./$(DEPDIR)/random_test.Po ./$(DEPDIR)/regex_test.Po \ - ./$(DEPDIR)/result_test.Po ./$(DEPDIR)/safe_test.Po \ - ./$(DEPDIR)/siphash_test.Po ./$(DEPDIR)/sockaddr_test.Po \ - ./$(DEPDIR)/stats_test.Po ./$(DEPDIR)/symtab_test.Po \ - ./$(DEPDIR)/task_test-task_test.Po \ + ./$(DEPDIR)/regex_test.Po ./$(DEPDIR)/result_test.Po \ + ./$(DEPDIR)/safe_test.Po ./$(DEPDIR)/siphash_test.Po \ + ./$(DEPDIR)/sockaddr_test.Po ./$(DEPDIR)/stats_test.Po \ + ./$(DEPDIR)/symtab_test.Po ./$(DEPDIR)/task_test-task_test.Po \ ./$(DEPDIR)/taskpool_test.Po ./$(DEPDIR)/time_test.Po \ ./$(DEPDIR)/timer_test.Po am__mv = mv -f @@ -393,18 +388,18 @@ $(doh_test_SOURCES) errno_test.c file_test.c hash_test.c \ heap_test.c hmac_test.c ht_test.c lex_test.c md_test.c \ mem_test.c netaddr_test.c $(netmgr_test_SOURCES) parse_test.c \ - pool_test.c quota_test.c radix_test.c random_test.c \ - regex_test.c result_test.c safe_test.c siphash_test.c \ - sockaddr_test.c stats_test.c symtab_test.c task_test.c \ - taskpool_test.c time_test.c timer_test.c + pool_test.c quota_test.c radix_test.c regex_test.c \ + result_test.c safe_test.c siphash_test.c sockaddr_test.c \ + stats_test.c symtab_test.c task_test.c taskpool_test.c \ + time_test.c timer_test.c DIST_SOURCES = aes_test.c buffer_test.c counter_test.c crc64_test.c \ $(am__doh_test_SOURCES_DIST) errno_test.c file_test.c \ hash_test.c heap_test.c hmac_test.c ht_test.c lex_test.c \ md_test.c mem_test.c netaddr_test.c $(netmgr_test_SOURCES) \ parse_test.c pool_test.c quota_test.c radix_test.c \ - random_test.c regex_test.c result_test.c safe_test.c \ - siphash_test.c sockaddr_test.c stats_test.c symtab_test.c \ - task_test.c taskpool_test.c time_test.c timer_test.c + regex_test.c result_test.c safe_test.c siphash_test.c \ + sockaddr_test.c stats_test.c symtab_test.c task_test.c \ + taskpool_test.c time_test.c timer_test.c am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ @@ -948,10 +943,6 @@ netmgr_test.c \ uv_wrap.h -random_test_LDADD = \ - $(LDADD) \ - -lm - task_test_CPPFLAGS = $(AM_CPPFLAGS) $(am__append_3) $(am__append_5) task_test_LDADD = $(LDADD) $(am__append_4) $(am__append_6) EXTRA_DIST = testdata @@ -1076,10 +1067,6 @@ @rm -f radix_test$(EXEEXT) $(AM_V_CCLD)$(LINK) $(radix_test_OBJECTS) $(radix_test_LDADD) $(LIBS) -random_test$(EXEEXT): $(random_test_OBJECTS) $(random_test_DEPENDENCIES) $(EXTRA_random_test_DEPENDENCIES) - @rm -f random_test$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(random_test_OBJECTS) $(random_test_LDADD) $(LIBS) - regex_test$(EXEEXT): $(regex_test_OBJECTS) $(regex_test_DEPENDENCIES) $(EXTRA_regex_test_DEPENDENCIES) @rm -f regex_test$(EXEEXT) $(AM_V_CCLD)$(LINK) $(regex_test_OBJECTS) $(regex_test_LDADD) $(LIBS) @@ -1150,7 +1137,6 @@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pool_test.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quota_test.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/radix_test.Po@am__quote@ # am--include-marker -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/random_test.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/regex_test.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/result_test.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/safe_test.Po@am__quote@ # am--include-marker @@ -1623,13 +1609,6 @@ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) -random_test.log: random_test$(EXEEXT) - @p='random_test$(EXEEXT)'; \ - b='random_test'; \ - $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ - --log-file $$b.log --trs-file $$b.trs \ - $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ - "$$tst" $(AM_TESTS_FD_REDIRECT) regex_test.log: regex_test$(EXEEXT) @p='regex_test$(EXEEXT)'; \ b='regex_test'; \ @@ -1826,7 +1805,6 @@ -rm -f ./$(DEPDIR)/pool_test.Po -rm -f ./$(DEPDIR)/quota_test.Po -rm -f ./$(DEPDIR)/radix_test.Po - -rm -f ./$(DEPDIR)/random_test.Po -rm -f ./$(DEPDIR)/regex_test.Po -rm -f ./$(DEPDIR)/result_test.Po -rm -f ./$(DEPDIR)/safe_test.Po @@ -1907,7 +1885,6 @@ -rm -f ./$(DEPDIR)/pool_test.Po -rm -f ./$(DEPDIR)/quota_test.Po -rm -f ./$(DEPDIR)/radix_test.Po - -rm -f ./$(DEPDIR)/random_test.Po -rm -f ./$(DEPDIR)/regex_test.Po -rm -f ./$(DEPDIR)/result_test.Po -rm -f ./$(DEPDIR)/safe_test.Po diff -Nru bind9-9.18.41/tests/isc/mem_test.c bind9-9.18.44/tests/isc/mem_test.c --- bind9-9.18.41/tests/isc/mem_test.c 2025-10-18 10:21:03.217263152 +0000 +++ bind9-9.18.44/tests/isc/mem_test.c 2026-01-09 13:44:04.901040259 +0000 @@ -326,7 +326,7 @@ p = strchr(buf, '\n'); assert_non_null(p); - assert_in_range(p, 0, buf + sizeof(buf) - 3); + assert_uint_in_range(p, 0, buf + sizeof(buf) - 3); p += 2; q = strchr(p, '\n'); assert_non_null(q); @@ -369,7 +369,7 @@ p = strchr(buf, '\n'); assert_non_null(p); - assert_in_range(p, 0, buf + sizeof(buf) - 3); + assert_uint_in_range(p, 0, buf + sizeof(buf) - 3); assert_memory_equal(p + 2, "ptr ", 4); p = strchr(p + 1, '\n'); assert_non_null(p); @@ -417,7 +417,7 @@ assert_non_null(p); p = strchr(p + 1, '\n'); assert_non_null(p); - assert_in_range(p, 0, buf + sizeof(buf) - 3); + assert_uint_in_range(p, 0, buf + sizeof(buf) - 3); assert_memory_equal(p + 2, "ptr ", 4); p = strchr(p + 1, '\n'); assert_non_null(p); diff -Nru bind9-9.18.41/tests/isc/random_test.c bind9-9.18.44/tests/isc/random_test.c --- bind9-9.18.41/tests/isc/random_test.c 2025-10-18 10:21:03.218263179 +0000 +++ bind9-9.18.44/tests/isc/random_test.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,789 +0,0 @@ -/* - * 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. - */ - -/* - * IMPORTANT NOTE: - * These tests work by generating a large number of pseudo-random numbers - * and then statistically analyzing them to determine whether they seem - * random. The test is expected to fail on occasion by random happenstance. - */ - -#include -#include -#include /* IWYU pragma: keep */ -#include -#include -#include -#include -#include - -#define UNIT_TESTING -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -#define REPS 25000 - -typedef double(pvalue_func_t)(uint16_t *values, size_t length); - -/* igamc(), igam(), etc. were adapted (and cleaned up) from the Cephes - * math library: - * - * Cephes Math Library Release 2.8: June, 2000 - * Copyright 1985, 1987, 2000 by Stephen L. Moshier - * - * The Cephes math library was released into the public domain as part - * of netlib. - */ - -static double MACHEP = 1.11022302462515654042E-16; -static double MAXLOG = 7.09782712893383996843E2; -static double big = 4.503599627370496e15; -static double biginv = 2.22044604925031308085e-16; - -static double -igamc(double a, double x); -static double -igam(double a, double x); - -typedef enum { - ISC_RANDOM8, - ISC_RANDOM16, - ISC_RANDOM32, - ISC_RANDOM_BYTES, - ISC_RANDOM_UNIFORM, - ISC_NONCE_BYTES -} isc_random_func; - -static double -igamc(double a, double x) { - double ans, ax, c, r, t, y, z; - double pkm1, pkm2, qkm1, qkm2; - - if ((x <= 0) || (a <= 0)) { - return 1.0; - } - - if ((x < 1.0) || (x < a)) { - return 1.0 - igam(a, x); - } - - ax = a * log(x) - x - lgamma(a); - if (ax < -MAXLOG) { - print_error("# igamc: UNDERFLOW, ax=%f\n", ax); - return 0.0; - } - ax = exp(ax); - - /* continued fraction */ - y = 1.0 - a; - z = x + y + 1.0; - c = 0.0; - pkm2 = 1.0; - qkm2 = x; - pkm1 = x + 1.0; - qkm1 = z * x; - ans = pkm1 / qkm1; - - do { - double yc, pk, qk; - c += 1.0; - y += 1.0; - z += 2.0; - yc = y * c; - pk = pkm1 * z - pkm2 * yc; - qk = qkm1 * z - qkm2 * yc; - if (qk != 0) { - r = pk / qk; - t = fabs((ans - r) / r); - ans = r; - } else { - t = 1.0; - } - - pkm2 = pkm1; - pkm1 = pk; - qkm2 = qkm1; - qkm1 = qk; - - if (fabs(pk) > big) { - pkm2 *= biginv; - pkm1 *= biginv; - qkm2 *= biginv; - qkm1 *= biginv; - } - } while (t > MACHEP); - - return ans * ax; -} - -static double -igam(double a, double x) { - double ans, ax, c, r; - - if ((x <= 0) || (a <= 0)) { - return 0.0; - } - - if ((x > 1.0) && (x > a)) { - return 1.0 - igamc(a, x); - } - - /* Compute x**a * exp(-x) / md_gamma(a) */ - ax = a * log(x) - x - lgamma(a); - if (ax < -MAXLOG) { - print_error("# igam: UNDERFLOW, ax=%f\n", ax); - return 0.0; - } - ax = exp(ax); - - /* power series */ - r = a; - c = 1.0; - ans = 1.0; - - do { - r += 1.0; - c *= x / r; - ans += c; - } while (c / ans > MACHEP); - - return ans * ax / a; -} - -static int8_t scounts_table[65536]; -static uint8_t bitcounts_table[65536]; - -static int8_t -scount_calculate(uint16_t n) { - int i; - int8_t sc; - - sc = 0; - for (i = 0; i < 16; i++) { - uint16_t lsb; - - lsb = n & 1; - if (lsb != 0) { - sc += 1; - } else { - sc -= 1; - } - - n >>= 1; - } - - return sc; -} - -static uint8_t -bitcount_calculate(uint16_t n) { - int i; - uint8_t bc; - - bc = 0; - for (i = 0; i < 16; i++) { - uint16_t lsb; - - lsb = n & 1; - if (lsb != 0) { - bc += 1; - } - - n >>= 1; - } - - return bc; -} - -static void -tables_init(void) { - uint32_t i; - - for (i = 0; i < 65536; i++) { - scounts_table[i] = scount_calculate(i); - bitcounts_table[i] = bitcount_calculate(i); - } -} - -/* - * The following code for computing Marsaglia's rank is based on the - * implementation in cdbinrnk.c from the diehard tests by George - * Marsaglia. - * - * This function destroys (modifies) the data passed in bits. - */ -static uint32_t -matrix_binaryrank(uint32_t *bits, size_t rows, size_t cols) { - unsigned int rt = 0; - uint32_t rank = 0; - uint32_t tmp; - - for (size_t k = 0; k < rows; k++) { - size_t i = k; - - while (rt >= cols || ((bits[i] >> rt) & 1) == 0) { - i++; - - if (i < rows) { - continue; - } else { - rt++; - if (rt < cols) { - i = k; - continue; - } - } - - return rank; - } - - rank++; - if (i != k) { - tmp = bits[i]; - bits[i] = bits[k]; - bits[k] = tmp; - } - - for (size_t j = i + 1; j < rows; j++) { - if (((bits[j] >> rt) & 1) == 0) { - continue; - } else { - bits[j] ^= bits[k]; - } - } - - rt++; - } - - return rank; -} - -static void -random_test(pvalue_func_t *func, isc_random_func test_func) { - uint32_t m; - uint32_t j; - uint32_t histogram[11] = { 0 }; - uint32_t passed; - double proportion; - double p_hat; - double lower_confidence, higher_confidence; - double chi_square; - double p_value_t; - double alpha; - - tables_init(); - - m = 1000; - passed = 0; - - for (j = 0; j < m; j++) { - uint32_t i; - uint32_t values[REPS]; - uint16_t *uniform_values; - double p_value; - - switch (test_func) { - case ISC_RANDOM8: - for (i = 0; i < (sizeof(values) / sizeof(*values)); i++) - { - values[i] = isc_random8(); - } - break; - case ISC_RANDOM16: - for (i = 0; i < (sizeof(values) / sizeof(*values)); i++) - { - values[i] = isc_random16(); - } - break; - case ISC_RANDOM32: - for (i = 0; i < (sizeof(values) / sizeof(*values)); i++) - { - values[i] = isc_random32(); - } - break; - case ISC_RANDOM_BYTES: - for (i = 0; i < ARRAY_SIZE(values); i++) { - values[i] = isc_random32(); - } - break; - case ISC_RANDOM_UNIFORM: - uniform_values = (uint16_t *)values; - for (i = 0; - i < (sizeof(values) / (sizeof(*uniform_values))); - i++) - { - uniform_values[i] = - isc_random_uniform(UINT16_MAX); - } - break; - case ISC_NONCE_BYTES: - isc_nonce_buf(values, sizeof(values)); - break; - } - - p_value = (*func)((uint16_t *)values, REPS * 2); - if (p_value >= 0.01) { - passed++; - } - - assert_in_range(p_value, 0.0, 1.0); - - i = (int)floor(p_value * 10); - histogram[i]++; - } - - /* - * Check proportion of sequences passing a test (see section - * 4.2.1 in NIST SP 800-22). - */ - alpha = 0.01; /* the significance level */ - proportion = (double)passed / (double)m; - p_hat = 1.0 - alpha; - lower_confidence = p_hat - (3.0 * sqrt((p_hat * (1.0 - p_hat)) / m)); - higher_confidence = p_hat + (3.0 * sqrt((p_hat * (1.0 - p_hat)) / m)); - - assert_in_range(proportion, lower_confidence, higher_confidence); - - /* - * Check uniform distribution of p-values (see section 4.2.2 in - * NIST SP 800-22). - */ - - /* Fold histogram[10] (p_value = 1.0) into histogram[9] for - * interval [0.9, 1.0] - */ - histogram[9] += histogram[10]; - histogram[10] = 0; - - /* Pre-requisite that at least 55 sequences are processed. */ - assert_true(m >= 55); - - chi_square = 0.0; - for (j = 0; j < 10; j++) { - double numer; - double denom; - - numer = (histogram[j] - (m / 10.0)) * - (histogram[j] - (m / 10.0)); - denom = m / 10.0; - chi_square += numer / denom; - } - - p_value_t = igamc(9 / 2.0, chi_square / 2.0); - - assert_true(p_value_t >= 0.0001); -} - -/* - * This is a frequency (monobits) test taken from the NIST SP 800-22 - * RANDOM test suite. - */ -static double -monobit(uint16_t *values, size_t length) { - size_t i; - int32_t scount; - uint32_t numbits; - double s_obs; - double p_value; - - UNUSED(mctx); - - numbits = length * sizeof(*values) * 8; - scount = 0; - - for (i = 0; i < length; i++) { - scount += scounts_table[values[i]]; - } - - /* Preconditions (section 2.1.7 in NIST SP 800-22) */ - assert_true(numbits >= 100); - - s_obs = abs(scount) / sqrt(numbits); - p_value = erfc(s_obs / sqrt(2.0)); - - return p_value; -} - -/* - * This is the runs test taken from the NIST SP 800-22 RNG test suite. - */ -static double -runs(uint16_t *values, size_t length) { - size_t i; - uint32_t bcount; - uint32_t numbits; - double pi; - double tau; - uint32_t j; - uint32_t b; - uint8_t bit_prev; - uint32_t v_obs; - double numer; - double denom; - double p_value; - - UNUSED(mctx); - - numbits = length * sizeof(*values) * 8; - bcount = 0; - - for (i = 0; i < length; i++) { - bcount += bitcounts_table[values[i]]; - } - - pi = (double)bcount / (double)numbits; - tau = 2.0 / sqrt(numbits); - - /* Preconditions (section 2.3.7 in NIST SP 800-22) */ - assert_true(numbits >= 100); - - /* - * Pre-condition implied from the monobit test. This can fail - * for some sequences, and the p-value is taken as 0 in these - * cases. - */ - if (fabs(pi - 0.5) >= tau) { - return 0.0; - } - - /* Compute v_obs */ - j = 0; - b = 14; - bit_prev = (values[j] & (1U << 15)) == 0 ? 0 : 1; - - v_obs = 0; - - for (i = 1; i < numbits; i++) { - uint8_t bit_this = (values[j] & (1U << b)) == 0 ? 0 : 1; - if (b == 0) { - b = 15; - j++; - } else { - b--; - } - - v_obs += bit_this ^ bit_prev; - - bit_prev = bit_this; - } - - v_obs += 1; - - numer = fabs(v_obs - (2.0 * numbits * pi * (1.0 - pi))); - denom = 2.0 * sqrt(2.0 * numbits) * pi * (1.0 - pi); - - p_value = erfc(numer / denom); - - return p_value; -} - -/* - * This is the block frequency test taken from the NIST SP 800-22 RNG - * test suite. - */ -static double -blockfrequency(uint16_t *values, size_t length) { - uint32_t i; - uint32_t numbits; - uint32_t mbits; - uint32_t mwords; - uint32_t numblocks; - double *pi; - double chi_square; - double p_value; - - numbits = length * sizeof(*values) * 8; - mbits = 32000; - mwords = mbits / 16; - numblocks = numbits / mbits; - - /* Preconditions (section 2.2.7 in NIST SP 800-22) */ - assert_true(numbits >= 100); - assert_true(mbits >= 20); - assert_true((double)mbits > (0.01 * numbits)); - assert_true(numblocks < 100); - assert_true(numbits >= (mbits * numblocks)); - - pi = isc_mem_get(mctx, numblocks * sizeof(double)); - assert_non_null(pi); - - for (i = 0; i < numblocks; i++) { - uint32_t j; - pi[i] = 0.0; - for (j = 0; j < mwords; j++) { - uint32_t idx; - - idx = i * mwords + j; - pi[i] += bitcounts_table[values[idx]]; - } - pi[i] /= mbits; - } - - /* Compute chi_square */ - chi_square = 0.0; - for (i = 0; i < numblocks; i++) { - chi_square += (pi[i] - 0.5) * (pi[i] - 0.5); - } - - chi_square *= 4 * mbits; - - isc_mem_put(mctx, pi, numblocks * sizeof(double)); - - p_value = igamc(numblocks * 0.5, chi_square * 0.5); - - return p_value; -} - -/* - * This is the binary matrix rank test taken from the NIST SP 800-22 RNG - * test suite. - */ -static double -binarymatrixrank(uint16_t *values, size_t length) { - uint32_t i; - size_t matrix_m; - size_t matrix_q; - uint32_t num_matrices; - size_t numbits; - uint32_t fm_0; - uint32_t fm_1; - uint32_t fm_rest; - double term1; - double term2; - double term3; - double chi_square; - double p_value; - - UNUSED(mctx); - - matrix_m = 32; - matrix_q = 32; - num_matrices = length / ((matrix_m * matrix_q) / 16); - numbits = num_matrices * matrix_m * matrix_q; - - /* Preconditions (section 2.5.7 in NIST SP 800-22) */ - assert_int_equal(matrix_m, 32); - assert_int_equal(matrix_q, 32); - assert_true(numbits >= (38 * matrix_m * matrix_q)); - - fm_0 = 0; - fm_1 = 0; - fm_rest = 0; - for (i = 0; i < num_matrices; i++) { - /* - * Each uint32_t supplies 32 bits, so a 32x32 bit matrix - * takes up uint32_t array of size 32. - */ - uint32_t bits[32]; - int j; - uint32_t rank; - - for (j = 0; j < 32; j++) { - size_t idx; - uint32_t r1; - uint32_t r2; - - idx = i * ((matrix_m * matrix_q) / 16); - idx += j * 2; - - r1 = values[idx]; - r2 = values[idx + 1]; - bits[j] = (r1 << 16) | r2; - } - - rank = matrix_binaryrank(bits, matrix_m, matrix_q); - - if (rank == matrix_m) { - fm_0++; - } else if (rank == (matrix_m - 1)) { - fm_1++; - } else { - fm_rest++; - } - } - - /* Compute chi_square */ - term1 = ((fm_0 - (0.2888 * num_matrices)) * - (fm_0 - (0.2888 * num_matrices))) / - (0.2888 * num_matrices); - term2 = ((fm_1 - (0.5776 * num_matrices)) * - (fm_1 - (0.5776 * num_matrices))) / - (0.5776 * num_matrices); - term3 = ((fm_rest - (0.1336 * num_matrices)) * - (fm_rest - (0.1336 * num_matrices))) / - (0.1336 * num_matrices); - - chi_square = term1 + term2 + term3; - - p_value = exp(-chi_square * 0.5); - - return p_value; -} - -/*** - *** Tests for isc_random32() function - ***/ - -/* Monobit test for the RANDOM */ -ISC_RUN_TEST_IMPL(isc_random32_monobit) { - UNUSED(state); - - random_test(monobit, ISC_RANDOM32); -} - -/* Runs test for the RANDOM */ -ISC_RUN_TEST_IMPL(isc_random32_runs) { - UNUSED(state); - - random_test(runs, ISC_RANDOM32); -} - -/* Block frequency test for the RANDOM */ -ISC_RUN_TEST_IMPL(isc_random32_blockfrequency) { - UNUSED(state); - - random_test(blockfrequency, ISC_RANDOM32); -} - -/* Binary matrix rank test for the RANDOM */ -ISC_RUN_TEST_IMPL(isc_random32_binarymatrixrank) { - UNUSED(state); - - random_test(binarymatrixrank, ISC_RANDOM32); -} - -/*** - *** Tests for isc_random_bytes() function - ***/ - -/* Monobit test for the RANDOM */ -ISC_RUN_TEST_IMPL(isc_random_bytes_monobit) { - UNUSED(state); - - random_test(monobit, ISC_RANDOM_BYTES); -} - -/* Runs test for the RANDOM */ -ISC_RUN_TEST_IMPL(isc_random_bytes_runs) { - UNUSED(state); - - random_test(runs, ISC_RANDOM_BYTES); -} - -/* Block frequency test for the RANDOM */ -ISC_RUN_TEST_IMPL(isc_random_bytes_blockfrequency) { - UNUSED(state); - - random_test(blockfrequency, ISC_RANDOM_BYTES); -} - -/* Binary matrix rank test for the RANDOM */ -ISC_RUN_TEST_IMPL(isc_random_bytes_binarymatrixrank) { - UNUSED(state); - - random_test(binarymatrixrank, ISC_RANDOM_BYTES); -} - -/*** - *** Tests for isc_random_uniform() function: - ***/ - -/* Monobit test for the RANDOM */ -ISC_RUN_TEST_IMPL(isc_random_uniform_monobit) { - UNUSED(state); - - random_test(monobit, ISC_RANDOM_UNIFORM); -} - -/* Runs test for the RANDOM */ -ISC_RUN_TEST_IMPL(isc_random_uniform_runs) { - UNUSED(state); - - random_test(runs, ISC_RANDOM_UNIFORM); -} - -/* Block frequency test for the RANDOM */ -ISC_RUN_TEST_IMPL(isc_random_uniform_blockfrequency) { - UNUSED(state); - - random_test(blockfrequency, ISC_RANDOM_UNIFORM); -} - -/* Binary matrix rank test for the RANDOM */ -ISC_RUN_TEST_IMPL(isc_random_uniform_binarymatrixrank) { - UNUSED(state); - - random_test(binarymatrixrank, ISC_RANDOM_UNIFORM); -} - -/* Tests for isc_nonce_bytes() function */ - -/* Monobit test for the RANDOM */ -ISC_RUN_TEST_IMPL(isc_nonce_bytes_monobit) { - UNUSED(state); - - random_test(monobit, ISC_NONCE_BYTES); -} - -/* Runs test for the RANDOM */ -ISC_RUN_TEST_IMPL(isc_nonce_bytes_runs) { - UNUSED(state); - - random_test(runs, ISC_NONCE_BYTES); -} - -/* Block frequency test for the RANDOM */ -ISC_RUN_TEST_IMPL(isc_nonce_bytes_blockfrequency) { - UNUSED(state); - - random_test(blockfrequency, ISC_NONCE_BYTES); -} - -/* Binary matrix rank test for the RANDOM */ -ISC_RUN_TEST_IMPL(isc_nonce_bytes_binarymatrixrank) { - UNUSED(state); - - random_test(binarymatrixrank, ISC_NONCE_BYTES); -} - -ISC_TEST_LIST_START - -ISC_TEST_ENTRY(isc_random32_monobit) -ISC_TEST_ENTRY(isc_random32_runs) -ISC_TEST_ENTRY(isc_random32_blockfrequency) -ISC_TEST_ENTRY(isc_random32_binarymatrixrank) -ISC_TEST_ENTRY(isc_random_bytes_monobit) -ISC_TEST_ENTRY(isc_random_bytes_runs) -ISC_TEST_ENTRY(isc_random_bytes_blockfrequency) -ISC_TEST_ENTRY(isc_random_bytes_binarymatrixrank) -ISC_TEST_ENTRY(isc_random_uniform_monobit) -ISC_TEST_ENTRY(isc_random_uniform_runs) -ISC_TEST_ENTRY(isc_random_uniform_blockfrequency) -ISC_TEST_ENTRY(isc_random_uniform_binarymatrixrank) -ISC_TEST_ENTRY(isc_nonce_bytes_monobit) -ISC_TEST_ENTRY(isc_nonce_bytes_runs) -ISC_TEST_ENTRY(isc_nonce_bytes_blockfrequency) -ISC_TEST_ENTRY(isc_nonce_bytes_binarymatrixrank) - -ISC_TEST_LIST_END - -ISC_TEST_MAIN