Mark Andrews [Mon, 3 Aug 2026 04:42:51 +0000 (14:42 +1000)]
Tighten EUI48 and EUI64 text parser
Check that leading zeros were present and that there wasn't
any garbage at the end of the token by generating the record
in canonical form and checking that the input matched.
Mark Andrews [Tue, 4 Aug 2026 05:44:20 +0000 (15:44 +1000)]
Fix checkzone 'cname and other data' tests
* Fix path name pattern for 'cname and other data' tests.
* Fix errors in test files for AFSDB, OPENGPG and SMIMEA.
* Add test files for DSYNC, HHIT and BRID.
* Use TYPE69 for unknown type now the DSYNC (66) has been allocated.
Nicki Křížek [Thu, 6 Aug 2026 11:50:25 +0000 (13:50 +0200)]
chg: test: Deduplicate and unify named.conf.j2 in system tests
named.conf.j2 templates were unified across the system test code base.
Changes with possible functional impact:
- use a common template for root.hints which use `a.root-server.nil. IN A 10.53.0.1`: this may change the TTL of the record - I don't think the affected tests rely on it
- add missing `*-source` options: unless the tests are intentionally about testing `*-source` options, I believe it's just a copy-paste omission
- add missing `listen-on-v6 { none; }`: not specifying those is misconfiguration. tests that want IPv6 must explicitly specify which IPv6 to use
- set the `port @PORT@` if missing: not setting the port is clearly not intended, as that would default to port 53, which shouldn't be used in tests (with the exception of a couple of live internet tests)
Changes without any functional impact:
- drop `directory .` default option
- drop `recursive yes` default option
- drop `notify yes` default option
- replace space indentation with tabs
- introduce jinja2 templates for shared options and root hints and use those
Closes #5398
Merge branch '5398-named-conf-boilterplate' into 'main'
Add meson lint check rejecting hand-indented template includes
An indented plain {% include %} pastes the included file at column zero
and a {% filter indent %} wrapper repeats the depth by hand;
{% include_indented %} replaces both. Reject them in CI so the system
test templates stay consistent.
Martin Basti [Wed, 29 Jul 2026 15:44:06 +0000 (15:44 +0000)]
Document the ns template variable in the cookbook
The explanation of the auto-injected ns variable was dropped when the
shared plumbing templates were documented, leaving @ns.ip@ and
@ns.ip6@ undocumented while the _common/options/ partials are built
on them. Restore it to the cookbook.
Use tabs for indentation in system test named configs
The system test named configs indented blocks with a mix of tabs,
spaces, and tab/space combinations. Reindent the space-indented lines
to one tab per block level to make the style uniform across all
tests.
Use the ns variable for instance addresses in config templates
Statements referring to the instance's own address were switched to use
the @ns.ip@ and @ns.ip6@ variable syntax. References to other nsX
servers are kept as plain addresses.
Use the root-hint template for indented hint configs
Replace the root hints boilerplate with a template in configs where the
config section should be indented, i.e. in view statements. While named
doesn't care about whitespace, use the ident filter to make the final
rendered config more readable.
Use partial options templates for non-standard named configs
The divergence from the standard plumbing is relevant for these tests
and needs to stay in place. Use partial templates for the common
statements which can be templated.
Use the dual-stack options template where instances listen on IPv6
Options blocks whose plumbing matches the standard except for
listening on the instance's own IPv6 address get the
_common/options-dual.conf.j2 template.
Use the shared options template for standard plumbing
Every options block carrying all seven standard plumbing statements
gets the _common/options.conf.j2 umbrella include at the top of the
block instead. Statement order inside options has no meaning to named,
so rendered configs are equivalent.
Add shared templates for the named.conf options plumbing
Almost every test instance's options block starts with the same seven
statements varying only in the instance address: the source-address
trio, port, pid-file, and the listen-on pair. Provide them as jinja2
partials in _common/options/ plus two umbrellas built on the
auto-injected per-instance `ns` variable: options.conf.j2 for the
v4-only case and options-dual.conf.j2 for dual-stack instances.
The partials carry no indentation of their own: consumers pull them in
with {% include_indented %} at the desired depth, so the templates do
not hardcode the block structure they are used in.
Semantic options (recursion, dnssec-validation, notify, allow-*) stay
in the individual config files by design.
Nicki Křížek [Mon, 3 Aug 2026 12:21:37 +0000 (12:21 +0000)]
Add an include_indented tag for indented template includes
jinja2's {% include %} inserts the rendered file verbatim, so includes
inside view statements have to be wrapped in a three-line
{% filter indent %} block with the depth repeated by hand. Add a small
jinja2 extension providing {% include_indented "..." %}, which detects
the tag's own leading whitespace at parse time and expands to the
builtin include wrapped in the builtin indent filter, keeping stock
jinja2 runtime semantics. Detection needs the tag alone on its line in
a loader-backed template; anything else fails at parse time instead of
misrendering.
Co-Authored-By: Martin Basti <mbasti@isc.org> Assisted-by: Claude:claude-fable-5
DNS NOTIFY is on by default, so an options-level `notify yes;` just
restates the default. The zone- and view-level statements and
non-default values are untouched.
Disable IPv6 listening explicitly on v4-only instances
Instances without a listen-on-v6 statement listened on every IPv6
address at the assigned port. No test relies on it, nor could it, as all
of the tests instances will be listening on all the IPv6 addresses,
making it unpredictable which packet would arrive to which instance.
The configuration makes no sense and is most likely a copy-paste error.
Explicitly disable IPv6 on IPv4-only test named instances.
Set the listening port through the options-level port statement
Some instances configured the assigned port only as a listen-on
qualifier. Where the file had no options-level port at all, that also
left named's default destination port for queries, notifies, and
transfers at 53 — on the test network every instance runs on the
assigned port, so this only worked for setups that never needed to
reach another instance. Set `port @PORT@;` at options level and use
plain listen-on statements; files with a deliberate non-standard port
statement (dns64's and notify/ns4's dual-port setups) are skipped.
Nicki Křížek [Wed, 5 Aug 2026 12:53:26 +0000 (12:53 +0000)]
Keep the live-internet test instances on the standard DNS port
The ns1 instances in mirror_root_zone and rfc5011 talk to the root
servers on the live internet: the options-level port must remain 53
for outgoing queries to reach the real servers, and the *-source
addresses must remain unset so that a routable source address is
picked. Exempt these two configs from the standard plumbing and
spell out the intent in a comment.
Nicki Křížek [Tue, 4 Aug 2026 16:01:12 +0000 (16:01 +0000)]
Add missing standard IPv6 source address statements
Instances that listen on IPv6 pin their query/notify/transfer source
addresses to their own fd92:7065:b8e:ffff::N, but in some configs the
*-source-v6 statements were left unset. Explicitly add the missing
statements to make the configuration of IPv6-capable instances uniform.
Most instances pin their query/notify/transfer source addresses to
their own 10.53.0.N. Explicitly add the missing source statements across
all named config templates to make the configuration uniform.
Michal Nowak [Thu, 6 Aug 2026 10:08:01 +0000 (12:08 +0200)]
chg: ci: Split ci/freebsd.ini into per-release machine files
FreeBSD 15 replaced base Heimdal with MIT Kerberos 5, so pointing
krb5-config at the MIT port is no longer necessary there; FreeBSD 14
still ships Heimdal and needs the override. A single shared machine
file cannot express that difference, so give each release its own.
Assisted-by: Claude:claude-opus-5[1m]
Merge branch 'mnowak/split-freebsd-ci-machine-files' into 'main'
Michal Nowak [Wed, 5 Aug 2026 16:35:16 +0000 (18:35 +0200)]
Split ci/freebsd.ini into per-release machine files
FreeBSD 15 replaced base Heimdal with MIT Kerberos 5, so pointing
krb5-config at the MIT port is no longer necessary there; FreeBSD 14
still ships Heimdal and needs the override. A single shared machine
file cannot express that difference, so give each release its own.
Michal Nowak [Thu, 6 Aug 2026 09:54:39 +0000 (11:54 +0200)]
chg: ci: Do not question obvious human Co-Authored-By trailers
Danger asks the reviewer to confirm that the person named in every
`Co-Authored-By` trailer is a human. A trailer which gives a name, a
surname and an @isc.org address leaves no room for doubt, so stop
emitting that message for those and keep it for everything else.
Assisted-by: Claude:claude-opus-5[1m]
Merge branch 'mnowak/danger-human-coauthored-by' into 'main'
Michal Nowak [Tue, 28 Jul 2026 14:57:47 +0000 (16:57 +0200)]
Do not question obvious human Co-Authored-By trailers
Danger asks the reviewer to confirm that the person named in every
`Co-Authored-By` trailer is a human. A trailer pointing at an
@isc.org address leaves no room for doubt, so stop emitting that
message for those and keep it for everything else.
Evan Hunt [Wed, 5 Aug 2026 18:10:23 +0000 (18:10 +0000)]
fix: usr: Check "asnum" validity in GeoIP ACLs
We now check the validity of autonomous system (AS) numbers when
parsing GeoIP ACLs that use `asnum` elements at configuration time.
`asnum` values start with an optional case-insensitive "AS" prefix, followed only by decimal digits, with no spaces or other extraneous characters. The value represented cannot exceed 2^32.
We now check the validity of autonomous system (AS) numbers when
parsing geoip ACLs that use "asnum" elements at configuration time.
"asnum" values start with an optional case-insensitive "AS" prefix,
followed only by decimal digits, with no spaces or other extraneous
characters. The value represented cannot exceed MAXUINT32.
Mark Andrews [Mon, 3 Aug 2026 05:48:59 +0000 (15:48 +1000)]
Grow arrays in signset if key->index is too big
The arrays in signset are sized to allow for one additional
key per RRSIG however the list of known keys is updated in
parallel which means that the index of the returned key can
be too big for the arrays. Grow the arrays if this happens.
Additionally ensure that the keylist and keycount are locked
when they are being read.
Štěpán Balážik [Mon, 3 Aug 2026 11:01:13 +0000 (13:01 +0200)]
Require IPv6 in the reclimit system test
The number of queries ns3 sends to the ans2 and ans4 servers depends
on whether it issues AAAA fetches, i.e. on IPv6 runtime support. The
test handled both cases by grepping ns3's log for AAAA fetches and
branching between two sets of expected query counts, which was
fragile.
Require IPv6 at runtime instead and drop the branching, making the
expected query counts unconditional. The test is skipped where IPv6
is unavailable.
Štěpán Balážik [Tue, 4 Aug 2026 11:11:57 +0000 (13:11 +0200)]
Reimplement 'reclimit/ans2' server using ControllableAsyncServer
Replace the Perl implementation with a Python one. Two behaviors of
the old server are dropped: the responses driving the NS explosion
are no longer truncated to 512 bytes, which only mattered for builds
without IPv6 support, and the AA flag is only cleared on referrals
rather than on every response with an empty answer section.
Reimplement 'reclimit/ans4' using ControllableAsyncServer
Replace the Perl implementation with a Python one. One behavior of
the old server is dropped: the AA flag is only cleared on referrals
rather than on every response with an empty answer section.
Nicki Křížek [Tue, 4 Aug 2026 08:09:37 +0000 (10:09 +0200)]
fix: test: Wait for the committed NSEC3 chain state in the nsec3 tests
Prior to this fix, the `nsec3` system test could fail due to a test race. The NSEC3 chain is updated asynchronously, and waiting for the keymgr to be done with the zone isn't sufficient to check that the NSEC3 chain has been updated. Wait for the NSEC3PARAM to be published to ensure the zone is in its final signed state.
Example failure: https://gitlab.isc.org/isc-projects/bind9/-/jobs/7819249
Merge branch 'nicki/nsec3-wait-for-committed-chain' into 'main'
Wait for the committed NSEC3 chain state in the nsec3 tests
The nsec3 tests synchronized on the "keymgr: <zone> done" log line
before checking NSEC3PARAM presence, but that line only covers key
management. NSEC3 chain changes are committed later, asynchronously,
by zone maintenance.
There are no log events to indicate it has finished. Instead, poll the
served NSEC3PARAM RRset until it matches the chain state the zone's
policy calls for. Check the salt length as well to detect a salt change
used by some of the zones.
Remove the obsolete log-check in tests_nsec3_change.py and use the
new wait_for_nsec3param() mechanism in all the places where zone is
expected to have finished signing. Move the keymgr/wait_for_nsec3param
checks into after_servers_start where applicable, rather than repeating
those checks in individual test functions.
Mark Andrews [Tue, 4 Aug 2026 01:31:21 +0000 (11:31 +1000)]
fix: dev: Fix recognition of DNSSEC keys using the PRIVATEDNS private algorithm
`dst_algorithm_fromprivatedns()` parses the algorithm name with `dns_name_fromwire()`,
which reads only the buffer's active region; the callers in frombuffer() and the
resolver never set it, so PRIVATEDNS keys were always rejected as unsupported.
Merge branch '5871-followup-fix-privatedns-algorithm-extraction' into 'main'
Mark Andrews [Tue, 30 Jun 2026 01:46:28 +0000 (11:46 +1000)]
Add minimal testing for the PRIVATEDNS algorithm mapping
Enable the example PRIVATEDNS mappings (rsasha256.example.org and
rsasha512.example.org) in developer builds via TEST_PRIVATEDNS and use
them to test dst_algorithm_fromdata().
The test algorithms take the DST algorithm numbers 258 and 259, so
DST_MAX_ALGS has to grow to cover them: arrays sized by it are indexed
with the numbers dst_algorithm_fromdata() returns, and a value past the
bound would overflow them. Also map the new numbers back to
DNS_KEYALG_PRIVATEDNS in dst_algorithm_tosecalg() so the round trip
stays consistent.
Mark Andrews [Mon, 29 Jun 2026 22:40:15 +0000 (08:40 +1000)]
Set the active region for the dst_algorithm_fromprivatedns() call
dst_algorithm_fromprivatedns() reads the name with dns_name_fromwire(),
which parses only the active region of the buffer. frombuffer() did not
set it, so the name could never be parsed and every PRIVATEDNS key was
rejected with DST_R_UNSUPPORTEDALG. Document the requirement in dst.h.
Mark Andrews [Tue, 30 Jun 2026 01:54:51 +0000 (11:54 +1000)]
Use dst_algorithm_fromdata() to extract DST algorithm numbers
dns_resolver_algorithm_supported() open-coded the extraction for
PRIVATEDNS and PRIVATEOID keys, and the PRIVATEDNS copy never set the
active region on the buffer, so dns_name_fromwire() failed and every
PRIVATEDNS key was treated as unsupported.
Replace both copies with dst_algorithm_fromdata(), which sets up the
buffer correctly and passes other algorithms through unchanged.
Mark Andrews [Tue, 28 Jul 2026 06:42:08 +0000 (16:42 +1000)]
Check that the message is non NULL in dns_dt_parse
It was possible to dereference a NULL pointer in dns_dt_parse
if it was reading a malformed DNSTAP file. This would could
cause dnstap-read to exit. This has been fixed.
Evan Hunt [Mon, 3 Aug 2026 20:28:01 +0000 (20:28 +0000)]
chg: dev: Move wire-test to bin/tools/named-wireformat
`wire-test`, a testing tool which parses wire-format DNS data and displays it in human-readable form, has been renamed to `named-wireformat` and moved from `bin/tests/system` to `bin/tools`.
Alessio Podda [Mon, 3 Aug 2026 13:04:20 +0000 (13:04 +0000)]
chg: usr: Batch qp transaction for RPZ updates
RPZ was built around fine-grained locking, but that forces the use of many small qp transactions. With this MR, we switch qp transaction to handle the
full update to the rpz summary structure.
While this serializes the RPZ updates, the reduced overhead from batching qp transactions more than compensates for it and results in improvements for big RPZ zones.
Before this commit, maint_lock jointly protected both the "summary
structure" dns_rpz_zones_t and the individual zones. In particular,
locking maint_lock would block timer callbacks and shutdown requests,
which is undesirable.
Now that the update state has been moved to the baton, there is no
longer a reason to have a lock shared among all zones. This commit
splits the old rpzs->maint_lock into two.
First, rpz->update_lock protects callbacks and timers for individual
RPZ zones. Second, rpzs->data_lock protects joint operations on the QP
and CIDR trees. The latter replaces the old maint_lock.
Instead of embedding the "in progress" database inside the RPZ zone
struct, we can attach it to the baton passed to libuv. This simplifies
lifetimes and will make refactoring the locking possible.
Alessio Podda [Mon, 9 Mar 2026 23:12:54 +0000 (00:12 +0100)]
Delete optimistically from rpz nodes hashtable
Instead of checking whether an element is present and then deleting it,
we can try to delete it optimistically and then rely on the result to
know if the element was present or not.
Alessio Podda [Thu, 5 Mar 2026 15:55:00 +0000 (16:55 +0100)]
Batch qp transaction
Qp write transactions have a setup cost, so going from many small
transactions to a single one that covers the whole rpz zone processing
is a performance win, especially with big rpz zones.
Move from many small transactions to a single big transaction.
Alessio Podda [Thu, 5 Mar 2026 14:09:08 +0000 (15:09 +0100)]
Increase lock scope in rpzs construction
RPZ was built around fine-grained locking, but that forces the use of
many small qp transactions. This commit increases the scope of
rpzs->maint_lock to encompass the full processing of an RPZ zone.
Done as a preparation for a later commit.
Previously, the packet.pl script was used to send the a series of frames
to named; this worked by accident as most of these were refused by the
kernel with EAGAIN. packet.pl prints a dot every 1000 packets, so this
slowed the script down and allowed some frames to get through.
Reimplement the test in Python: build the packet with dnspython, send
~6 MiB of data over TCP discarding all replies and then check if the
server is still alive.
The server may close the connection before we have written the whole
stream. This shows up notably on Alma Linux 8: under concurrent load
its small default TCP buffers let the flow control between us and named
wedge, named then hits its idle timeout and resets the connection
mid-write, and drain() raises ConnectionError.
That is fine -- the connection being torn down is acceptable and the
only thing this test asserts is that named survives the flood, which
the final query checks -- so tolerate the server closing the connection
early.
Add Python helpers for inspecting `rndc status`, opening probe
connections, and waiting for counter changes, then use them to port the
TCP and recursive high-water checks from the shell script.
Connections are now managed by the test script directly removing the
need for the ans6 server. This also removes the need for the send.pl
script and the respective shell test helper as they were used to control
said server.
Add a helper that runs `rndc stats` and reads the TCP request counter
from named.stats, then use it to port the resolver and forwarder checks
from the shell script to tests_tcp.py. Record named.stats as an extra
artifact so the generated statistics remain available after test runs.
Use isctest.query.create() and a shared round-trip helper in
bin/tests/system/tcp/tests_tcp.py, add type hints, and reorganize the
existing tests to follow current style.
Store negative cache entries under their natural type
Negative cache entries were exposed through dns_rdataset_t in an
inverted shape (type=none, covers=<qtype>) while the cache internally
stored them under (<qtype>, none) with a NEGATIVE flag, so every
consumer had to convert between the two representations, and several
places relied on type==0 as an implicit negativity test.
Expose negative entries in their natural shape instead: type holds the
RR type whose nonexistence is cached (dns_rdatatype_any for NXDOMAIN
and NODATA(QTYPE=ANY) proofs), covers stays none, and the negative
attribute marks the entry. The implicit type==0 tests become explicit
attribute checks, and the cache database now rejects meta-types other
than ANY, which remains valid as a lookup type and as a negative
entry.
chg: dev: Delete cache rdatasets directly instead of tombstoning them
Deleting an rdataset from the cache left a placeholder entry
behind that every lookup had to skip until it aged out. Deletion
now removes the entry outright, and the placeholder mechanism is gone.
Merge branch 'ondrej/qpcache-cleanup-delete-tombstone' into 'main'
Delete cache rdatasets directly instead of tombstoning them
The refactoring that cleans the previous headers immediately from the
cache allows additional cleanup. The cache now does not require the
NONEXISTENT tombstone and dns_db_deleterdataset() can directly remove
the header with associate type from the cache. This simplifies all the
other code working with headers as the cache now only contains header
that carry information, so the only check we need to do now is ACTIVE()
check whether the TTL is still ok (+ serve-stale hacks).
chg: ci: Use meson native files for CI build configure flags
Make it easier to reproduce failed CI builds locally by using meson native files to configure the jobs, rather than composing variables in `.gitlab-ci.yml`.
To reproduce a build locally, the following can be used now: (with `$EXTRA_SETUP_FILES` being set per-job in `.gitlab-ci.yml`, single variable, no composition)
The `*tarball*` CI jobs build directly from the unpacked tarball, but
the entire ci/ directory was export-ignored. Add the required ci/*.ini
scripts so these jobs can pass in CI.
Use meson native files for CI build configure flags
CI build jobs carried compiler flags and project options as YAML
variables and EXTRA_CONFIGURE strings composed through inheritance and
shell interpolation, which is awkward to reproduce locally. Move the
shared flags into meson native files under ci/ so a build can be
reproduced directly:
ci/common.ini exports cflags_common as a [constants] value so per-job
overlays can extend rather than replace it:
c_args = cflags_common + ['-DDEBUG']
The cross build file ci/amd64cross32.ini repeats the c_args from
ci/common.ini, because that file only configures the host and the
[constants] section is not visible for the cross build.
ci/scan-build.ini likewise repeats ci/common.ini instead of layering on
it, and notably declares no [binaries] section. scan-build substitutes
its analyzer wrapper by setting $CC via --use-cc, but a machine file's
[binaries] c takes precedence over $CC; inheriting the c = 'gcc' from
ci/common.ini would pin the compiler back to gcc and leave the analyzer
unused, undoing 23a722db57.
ci/reprotest.ini is standalone too. `meson reprotest` builds twice
and compares the results byte for byte, so it configures the smallest
build that still exercises the compiler rather than the full CI option
set.
The tumbleweed and tumbleed-libuv options were unified. The only
difference was gssapi=disabled, which seems like it should apply to all
tumbleweed builds as per 1b2c191bed4097e1095de3bc2f3854b6db894a8e.
Nicki Křížek [Tue, 30 Jun 2026 15:00:36 +0000 (15:00 +0000)]
Drop the redundant readline configuration check from CI
.check_readline_setup grepped build/config.h to confirm the requested
libedit/readline backend was actually selected. It dates to 2020
(bc1c0d2ef4), the autotools era, when several backends
(readline/editline/libedit) were selectable and autoconf's detection
could silently fall back to a different one or to none.
main builds only with meson now, where "line" is a strict feature
option: -Dline=enabled makes libedit a hard dependency (configure fails
if it is missing) and -Dline=disabled guarantees HAVE_LIBEDIT is never
set. Both assertions the check makes are therefore tautological -- the
silent-misdetection failure mode it guarded against can no longer occur.
Ignore comments when comparing built-in root hints to named.root
InterNIC periodically republishes named.root with only the "last
update" and "related version of root zone" comments changed, which
failed the live-internet check even though the actual records were
unchanged. Updating the built-in hints for comment-only churn is
pointless, so compare just the resource records.
chg: dev: Reduce the memory used by each record set in the cache
Each record set in the cache carried a 32-byte table for
restoring its owner name's letter case, unused since the
case handling was reworked. Removing it makes every cache
entry 32 bytes smaller.
Merge branch 'ondrej/remove-upper-from-slabheader' into 'main'
Since the database ownercase became modifiable only through
dns_db_addrdataset(), nothing ever sets the CASESET attribute on a
slabheader, so the upper[] case bitmap was never written and the slab
getownercase method could never restore anything. Remove both,
shrinking every cache slabheader by 32 bytes.
Martin Basti [Wed, 29 Jul 2026 12:31:57 +0000 (12:31 +0000)]
chg: test: Convert the digdelv system test from shell to pytest
The digdelv system test was a 2000-line tests.sh script run through the shell-test wrapper. It is now four native pytest modules — tests_dig.py, tests_delv.py, tests_mdig.py and tests_others.py (nslookup, host, nsupdate) — with shared helpers in common.py, repeated checks parametrized, and the yamlget.py helper script replaced by direct PyYAML parsing. PyYAML is now a required test dependency: the +yaml checks fail instead of being skipped when it is missing, and each module is skipped as a whole when its tool is not built.
Merge branch 'mbasti/digdelv-tests-to-py' into 'main'
Martin Basti [Mon, 27 Jul 2026 14:51:02 +0000 (14:51 +0000)]
Drop executable-availability skips from the digdelv tests
The digdelv modules skipped themselves when the tool under test was
not built, but dig, delv, mdig, nslookup, host and nsupdate are
always built on the main branch, so the guards could never trigger
and would only hide genuine breakage as a skip.