Commit graph

13521 commits

Author SHA1 Message Date
Michal Nowak
b953ca2c4a Drop duplicate dnstap-read.rst entry
It was added both unconditionally and gated on HAVE_DNSTAP; keep
only the gated entry.

Assisted-by: Claude:claude-opus-4-8
2026-06-30 17:03:25 +02:00
Alessio Podda
0001066fb6 Rename inline sync related methods
The inline sync related methods and structs retained the old
receive_secure_ prefix. Since there is no async tasks involved anymore
we change the naming scheme to a more descriptive one.
2026-06-30 12:36:46 +00:00
Alessio Podda
0bb6cd432c Add NSEC3-to-NSEC rollover regression test
Before this commit, the NSEC3-to-NSEC transition was only tested by
test_nsec_case[nsec3-to-rsasha1-ds.kasp], which is gated by
RSASHA1_SUPPORTED.

Add another test that does not depend on RSASHA1_SUPPORTED, so this
coverage also runs when RSASHA1 signing is unavailable, such as with
newer OpenSSL configurations.
2026-06-30 12:36:46 +00:00
Matthijs Mekking
77754dad29 Remove dead code warnings found by scan-build
Small cleanup, fixing the dead code issues found by scan-build.

Patch submitted by Tim Rühsen.
2026-06-29 10:00:17 +00:00
Nicki Křížek
a2b9dcff54 Add dnssec_py/tests_sibling_ds: reject DS for sibling zones in referrals
Add a system test that verifies the resolver rejects DS records whose
owner name does not match the delegation (NS) name in a referral
response.

A custom authoritative server (ans4) serves the parent zone sibling-ds.
from zone file with delegations for child and sibling subzones.  Its
DomainHandler injects a DS record for sibling.sibling-ds into referrals
for child.sibling-ds.  The resolver must detect the mismatch, log "DS
doesn't match referral (NS)", and return SERVFAIL.

Assisted-by: Claude:claude-opus-4-8
2026-06-25 15:21:00 +10:00
Nicki Křížek
95a268f119 Don't include ORIGIN for AsyncDnsServer zones
The zone template can't use $ORIGIN with AsyncDnsServer because
AsyncDnsServer._load_zone_file_with_origin() rejects it for non-root
zones.
2026-06-25 15:21:00 +10:00
Alessio Podda
4fb662a171 Reproducer for #5876 child apex self-loop NSEC
Added to "nsec_synthesis" system test.

Co-Authored-By: Evan Hunt <each@isc.org>
2026-06-24 21:03:45 +00:00
Colin Vidal
49cb99bb08 Fix RRL random failure
RRL test were randomly failing because `ns2` hint files uses

```
.	NS	ns1.
ns1.	A	10.53.0.1
```

Whereas `ns1` root zone didn't contains `ns1.` as NS (but only `ns.`).
This is a problem with the following scenario:

- A query starts before priming;
- It gets the root hints as zonecut (with `. NS ns1.`, and no glues, this
  is how parent-centric currently works);
- Priming starts and complete (so now rootdb contains the answer/glues
  from `ns1` root file);
- Then the query go to ADB to resolve `ns1.`.

Resolution of `ns1.` fails since it doesn't exists from the rootdb
anymore. This is a configuration issue (the resolver behavior is correct
and expected) whch is now fixed.
2026-06-24 21:56:16 +02:00
Michal Nowak
62670aa1b7 Fix a false positive compiler warning/error on Alpine 3.24
On Alpine Linux 3.24, GCC 15 with fortify-headers produces a compiler
warning when building bin/nsupdate/nsupdate.c:

    In function 'fgets',
        inlined from 'get_next_command' at ../bin/nsupdate/nsupdate.c:2414:13:
    /usr/include/fortify/stdio.h:48:16: error: 'cmdlinebuf' may be used uninitialized [-Werror=maybe-uninitialized]
       48 |         return __orig_fgets(__s, __n, __f);
          |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~
    /usr/include/fortify/stdio.h:42:1: note: in a call to '*fgets' declared with attribute 'access (read_write, 1, 2)' here
       42 | _FORTIFY_FN(fgets) char *fgets(char * _FORTIFY_POS0 __s, int __n, FILE *__f)
          | ^~~~~~~~~~~
    ../bin/nsupdate/nsupdate.c:2405:14: note: 'cmdlinebuf' declared here
     2405 |         char cmdlinebuf[MAXCMD];
          |              ^~~~~~~~~~

This is a false positive, because fgets() only writes into the buffer;
the fortify-headers wrapper annotates the buffer argument with
'access (read_write, ...)', which makes GCC treat passing an
uninitialized buffer as a read of uninitialized memory.

Initialize the 'cmdlinebuf' buffer anyway to avoid the build error.

Assisted-by: Claude:claude-fable-5
2026-06-24 17:44:42 +02:00
Michal Nowak
b9cf877277 Retry pipequeries on a transient EADDRINUSE in the pipelined test
On FreeBSD, the TCP connect() call can transiently fail with
EADDRINUSE under parallel CI load.  The netmgr already retries such
connects (see #3451), but it retries on the same socket, which is
already bound to the same ephemeral source port, so when the
four-tuple is genuinely busy (e.g. in TIME_WAIT) every retry fails
the same way.  pipequeries then exits with "request event result:
address in use", leaving raw.1 empty and failing the first check.

All eight requests share a single TCP dispatch, so the failed connect
means no query ever reached ns4 and its cache is still cold.  It is
therefore safe to run pipequeries again: a fresh process binds a new
ephemeral port, and the out-of-order check keeps its meaning.  Retry
for up to ten attempts, but only on this specific transient error.

Assisted-by: Claude:claude-fable-5
Assisted-by: Claude:claude-opus-4-8
2026-06-24 17:15:42 +02:00
Michal Nowak
b5ca9d3372 Print OS platform in "named -V"
The "running on" line emitted by `named -V` (as well as the startup
log and `rndc status`, which share the same source) now appends the
PRETTY_NAME value from /etc/os-release in parentheses after the uname
output, e.g.:

    running on Linux x86_64 6.19.14-... (Fedora Linux 42 (Workstation Edition))

This helps disambiguate environments where the kernel string is not a
reliable indicator of the userspace, such as RHEL clones and
containers whose kernel does not match the host OS.

When /etc/os-release is absent, /usr/lib/os-release is tried as a
fallback per the systemd os-release(5) specification. When neither is
available or no PRETTY_NAME is found, the output is unchanged.

Assisted-by: Claude:claude-opus-4-7
2026-06-24 17:05:35 +02:00
Nicki Křížek
fe2fea73a4 Add extra expired RRSIGs test to dnssec_py
The test verifies that a validating resolver enforces the
max-validations-per-fetch limit when encountering a record with multiple
expired RRSIGs followed by a valid one. One of the records is signed
three times: twice with expired timestamps (to produce two expired
RRSIGs for a.rrsigs-extra-expired/A) and once with valid timestamps,
after which the expired RRSIGs are injected back into the signed zone
file. max-validations-per-fetch is set to 2 via the template variable so
that the third (valid) RRSIG is never reached, causing SERVFAIL.

Assisted-by: Claude:claude-opus-4-8
2026-06-24 15:09:38 +02:00
Ondřej Surý
477130cf8e Add the tcp-reuse-timeout option
The idle timeout that bounds how long a reused outgoing TCP/TLS
connection is held open for reuse was only tunable through the 'named -T
tcpidletimeout' developer hook added earlier on this branch. Make it a
proper configuration option, tcp-reuse-timeout (options block, in units
of 100 milliseconds like the other tcp-*-timeout options), and drop the
-T hook.
2026-06-24 13:52:46 +02:00
Ondřej Surý
febeac215d Keep idle reused outgoing TCP connections under read
A reused TCP/TLS dispatch with no outstanding responses was left in the
reuse pool with no read pending, so a peer closing the idle connection
went unnoticed: the socket lingered in CLOSE-WAIT and the dead dispatch
was later handed to a new query, which failed and the fetch timed out.
Keep a read pending on an idle connected dispatch, bounded by an idle
timeout, so the close is seen promptly and the connection is dropped
from the pool instead of reused.

The idle read may only be (re)armed while the dispatch is still
connected; arming it on a dispatch that is already shutting down
re-reads a dying handle and double-schedules a netmgr job.

On shutdown, close the connection as soon as the dispatch reaches its
terminal state instead of waiting for the last reference to drop, so an
unexpected read (or a peer-side close) cannot leave the socket in
CLOSE-WAIT while a reference still lingers.
2026-06-24 13:52:46 +02:00
Ondřej Surý
2327277f90 Fail the fetch when a response fails the TSIG signature check
A response that failed the signature check with a missing or unexpected
TSIG used to set nextitem, so the resolver kept reading the dispatch for
another response.  When the response was truncated with the TSIG cut off
the end of the wire, no further response ever arrived and the fetch
stalled until resolver-query-timeout.

Treat an unauthenticated response like every other signature-check
failure and finish the fetch immediately.  A response carrying a missing
or bogus TSIG now yields SERVFAIL instead of being skipped in favour of
a later one; the cookie system test that fed a spoofed TSIG response is
updated to expect that.  The unauthenticated data is still never
returned.
2026-06-24 12:18:19 +02:00
Michal Nowak
a8368cdf17 Avoid writing through a const pointer in render_xsl()
render_xsl() cast away the const of the static xslmsg stylesheet and
passed it to isc_buffer_reinit(), which memmove()s into the base when
the buffer is non-empty -- a write to read-only memory that -fanalyzer
flags (-Wanalyzer-write-to-const).  It only stayed safe because the
body buffer is always empty here.

Use isc_buffer_constinit(), which never writes to the base, and assert
the empty-buffer contract with REQUIRE(isc_buffer_length(b) == 0).

Assisted-by: Claude:claude-opus-4-8
2026-06-24 10:56:34 +02:00
Aram Sargsyan
37f265f59a Add a new check in "inline" system test
This new check floods the server with DNS UPDATE messages for an
'inline-signing yes; sig-signing-signatures 1;' zone to see if
it manages to process the updates correctly.
2026-06-22 20:51:53 +00:00
Ondřej Surý
057042304d
Keep in-flight cache headers safe from LRU eviction during add
When the cache is over its size limit, qpcache_miss() runs LRU eviction
while add() is still in progress.  Eviction removes a header together
with its RRSIG/covered 'related' partner, so it could free a header the
add still needs -- the new header, the partner it was just paired with,
or the one about to be displaced for max-types-per-name.  With 'related'
now a counted reference, that became a use-after-free reachable under
sustained load.

Run the eviction last, after the new header is linked, bound and any
over-limit header removed, and skip the new header and its partner in
the eviction loop (the partner is marked visited so the SIEVE hand still
advances).  Per-header removal is factored into header_delete(),
expire_header() and flush_node().
2026-06-22 13:45:23 +02:00
Ondřej Surý
06abfa3264
Remove the ANCIENT slabheader attribute and statistics counter
Cache headers are now unlinked from their node as soon as they expire,
so a slabheader is never left in the "ancient, awaiting cleanup" state
that DNS_SLABHEADERATTR_ANCIENT tracked.  Drop the attribute, rename
mark_ancient() to header_delete() to reflect that it now removes the
header rather than flagging it (keying idempotency on list membership),
and remove the ancient RRset statistics counter that recorded the state,
which is now always zero; the rdataset statistics array shrinks to
match.

The rdataset-level 'ancient' flag and 'rndc dumpdb -expired' are
unaffected: expiry is derived from the entry's TTL when the rdataset is
bound, not from the slabheader attribute.
2026-06-22 13:45:18 +02:00
Ondřej Surý
76e21399dd
Remove cache headers and empty slabtops eagerly in mark_ancient
Now that a bound rdataset keeps references to both the slabheader and
the node, an ancient header (and the slabtop it leaves empty) can be
removed from the cache immediately in mark_ancient, instead of being
parked on a per-node dirty list and reclaimed only once the node becomes
unreferenced.  This drops the dirty list and the clean_cache_* machinery
entirely.

Because the cache structure can now change under a node that still has
external references, the all-rdatasets iterator no longer walks the live
slabtop list: allrdatasets() binds every matching rdataset up front and
the iterator works from that snapshot, cloning out each entry.
2026-06-22 13:45:08 +02:00
Matthijs Mekking
fdf74636e2 Don't rely on smart signing in cds system test
With dnssec-signzone smart-signing (-S), the CDS and CDSNKEY are
derived from the key timing metadata and the configuration options (-G).

The test has specific test cases that smart signing (with the fix)
interferes with. Therefor, disable smart-signing in the cds system test.
2026-06-19 08:34:54 +00:00
Matthijs Mekking
89ebe61ad3 Add system test for reconfiguring CDS/CDNSKEY
When on an 'rndc reconfig' the DNSSEC policy changes such that it
changes the expected CDNSKEY/CDS records in the zone, the RRset should
be updated accordingly.

Add a test case where we reconfigure a zone with a policy such that
these records should be removed, and on a second reconfigure add
them back again.

Note the test deliberately adds a different CDS digest on the
second reconfigure.
2026-06-19 08:34:54 +00:00
Evan Hunt
dc2640f87d update tests_validation.py test for new behavior
Some of the tests in in dnssec/tests_validation.py worked by iterating
through the response message looking for failure conditions, such as
excessively high TTL values. In some cases, previous changes caused
additional data not to be returned. Since there was nothing to
iterate, the tests still "passed".

Tests that don't make sense anymore have been removed. Other tests that
iterate through responses have been updated with checks to ensure that
the responses actually do contain data.
2026-06-18 18:39:42 +00:00
Evan Hunt
25e5a72ab7 add isctest.check functions for section empty or non-empty
expand on the isctest.check.empty_answer() function, adding
empty_authority(), empty_additional(), has_answer(), has_authority(),
and has_additional().
2026-06-18 18:39:42 +00:00
Alessio Podda
680e0d8532 Reproducer for #5971 NSEC3 from ancestor zone
Create a new nsec3_wrong_zone system test as a regression test.

Co-Authored By: Evan Hunt <each@isc.org>
2026-06-18 10:01:37 -07:00
Evan Hunt
45c9bd2603 Check wildcard signer and NOQNAME signer match
A positive wildcard answer, and the NSEC3 proof that the requested
name doesn't exist in the zone, must both be from the same zone.
Otherwise, an NSEC3 from an ancestor zone could be used to interfere
with validation.

We now retrieve the signer name from a wildcard response's signature.
An NSEC3 record cannot be used as a NOQNAME proof for the wildcard
unless it exactly matches the name one level above the NSEC3.

Fixes: isc-projects/bind9#5971
2026-06-18 10:01:29 -07:00
Alessio Podda
ee34bbd208 Add forward-first referral poisoning reproducer
Add a system test covering authority-section NS referrals returned by
configured forwarders under forward first.

The test verifies that a forwarder for fwd.hack cannot install the
parent hack zone cut and redirect resolution for the sibling zone
sibling.hack.
2026-06-18 12:13:32 +00:00
Matthijs Mekking
4c22fa0d56 Reproducer forwarder resend loop
Run malicious server: resend_loop/ans2/ans.py

Start BIND: ns1

Send single query to test.com

OBSERVED BEHAVIOR
The malicious server receives tens of thousands of resend packets
within seconds. CPU usage of the named worker thread remains elevated
(50–100% of one core) until the default fetch timeout (~10 seconds)
terminates the request. Instrumentation during testing confirmed that
isc_counter_used(fctx->qc) remains constant (value 1) throughout the
entire resend loop.
2026-06-18 08:49:23 +02:00
Colin Vidal
7a27fdf3be Cache glue only for enabled address families
When caching delegation NS data, only use A/AAAA glue records if the
resolver has the corresponding IPv4/IPv6 dispatcher configured. If IPv4
or IPv6 is disabled, ignore glue for that family and fall back to
caching the nameserver name if there is no glue from the other supported
family.

The new `cache_delegns` system test is covering delegation NS caching
with dual-stack resolver, IPv4-only, IPv6-only configurations. It also
set up an authoritative sever with zones with A-only, AAAA-only, and
dual-stack glue, which are all queried, and checks the delegation
database dump to confirm that the cached delegation data correspond to
the resolver configuration.
2026-06-18 08:48:20 +02:00
Colin Vidal
6645371566 remove GeoIP2 metro and metrocode
The `geoip metro` and `geoip metrocode` configuration options has been
removed as metro code are deprecated from MaxMind library.
2026-06-18 07:23:31 +02:00
Ondřej Surý
f39089576a Fix invalid free in statistics-channel JSON renderer
wrap_jsonfree() called json_object_put() on the response-body buffer
base, which is the JSON string returned by
json_object_to_json_string_ext(), not a struct json_object.  The root
object is already passed in as the callback argument; release only
that.
2026-06-18 07:10:01 +02:00
Ondřej Surý
9608158d73 Add regression test for statistics-channel JSON free bug
Hit every JSON statistics endpoint several times.  The current code
calls json_object_put() on the response-body string pointer, which
doesn't crash just by accident - the memory position contains large
value from static string.
2026-06-18 07:10:01 +02:00
Štěpán Balážik
62a3804a4c Simplify the packets in the formerr system test
Normalize the message ID to 0 and the TTL of records to 1 unless
required (OPT and UPDATE records require TTL=0).

Rename the questionclass test case to twoquestionclasses for
consistency.
2026-06-17 20:08:48 +02:00
Štěpán Balážik
151b2d6045 Reimplement the FORMERR system test in Python
Replace the shell and Perl based FORMERR system test with a Python
test that constructs the malformed DNS packets directly and checks
the responses.

Remove the legacy shell and Perl test script and the intermediate
packet files in hex, leaving the packet construction inline in
tests_formerr.py.

Preserve the same wire for all packets sent to the server, but
construct them in a more explicit and readable way.
2026-06-17 20:08:48 +02:00
Ondřej Surý
a5f13b3410 Replace the shared work pool with per-loop, per-lane worker threads
Offloaded work used two different mechanisms: a per-loop isc_helper
thread for CPU-bound crypto (DNSSEC validation, message signature
checks) and the process-global libuv thread pool for blocking I/O (zone
load and dump, inbound transfer apply). Neither could cancel a queued
task, and the two disagreed about exclusive mode — the helper paused
with its loop under isc_loopmgr_pause() but the libuv pool did not, so
blocking offloaded work kept running while a loop held the exclusive
lock.

Unify both behind isc_work: each loop gets its own worker thread per
lane — FAST for short, bounded tasks and SLOW for long, blocking ones —
fed by a private queue. Separate lanes keep a short crypto task off the
path of a multi-second zone dump once both run on per-loop workers;
every lane parks with isc_loopmgr_pause() so exclusive mode now quiesces
offloaded work too; and a still-queued task can be canceled before it
starts (isc_work_cancel). isc_helper is removed and its callers select a
lane.
2026-06-17 19:07:08 +02:00
Nicki Křížek (GitLab job 7567049)
46feca4e4a Merge tag 'v9.21.23' 2026-06-17 13:48:49 +00:00
Alessio Podda
e1cb775e95 Add test for query statistics
Ensure that no per-zone glue statistic is printed unless
zone-statistics is set to full.
2026-06-17 14:06:12 +02:00
Alessio Podda
f655b6af84 Only print per zone glue cache stats when zone-statistics is full
The code printing glue cache statistics was ignoring the
zone-statistics option. This has been fixed.
2026-06-17 14:06:12 +02:00
Colin Vidal
66af5b464d Use original query name when caching SERVFAIL
Instead of using `client->query.qname` when caching a SERVFAIL answer,
use `client->query.origqname` when available.

This avoids caching a SERVFAIL against a CNAME target when the failure
occurs while the resolver is following the CNAME chain. This is
problematic, for instance, when the SERVFAIL is triggered by the
`max-query-count` threshold being reached, which would incorrectly
prevent legitimate resolution of the CNAME target while in the SERVFAIL
cache.

Note that if the SERVFAIL genuinely originated from resolving the CNAME
target, that specific failure will no longer be cached, and a direct
query for the CNAME target will trigger a fresh (likely failing)
resolution attempt. However, this is still preferable to the previous
behaviour, which would wrongly prevent resolving the CNAME target if it
was cached for other reasons (like the example above).
2026-06-17 09:32:19 +02:00
Colin Vidal
d238b8eece
System test covering SERVFAIL cache and CNAME
Add a system test for the case where resolution SERVFAILs because the
fetch context reaches the `max-query-count` threshold while following a
CNAME.

Resolving the CNAME target independently should still work, because the
SERVFAIL cache stores the original query name rather than the target.
2026-06-17 06:42:16 +02:00
Nicki Křížek
72bda8335f Don't run vulture on ansX system test dirs
Exclude ansX directories from vulture, as splitting up the handlers into
multiple files gets flagged as unused code.
2026-06-16 19:07:06 -07:00
Evan Hunt
05691b53da Merge DNSSEC wildcard tests
Merge the tests for #5966 (F-043) and #5972 (F-045), previously called
dnssec_wildcard_additional and dnssec_replayed_parent_wildcard, into a
single directory with two modules.
2026-06-16 19:07:06 -07:00
Alessio Podda
99377e9fdd Reproducer for #5968 parent RRSIG in additional
Add a new "dnssec_parent_rrsig" system test.

Co-Authored-By: Matthijs Mekking <matthijs@isc.org>
2026-06-16 19:07:06 -07:00
Alessio Podda
4466bbcb4a Reproducer for #5972 DNS_R_FROMWILDCARD accepted
Add a new "dnssec_wildcard_additional" system test.

Co-Authored-By: Evan Hunt <each@isc.org>
2026-06-16 19:07:06 -07:00
Alessio Podda
1d4d3e09ad Reproducer for #5966 replay parent wildcard
Add a new "dnssec_replayed_parent_wildcard" system test.

Co-Authored-By: Matthijs Mekking <matthijs@isc.org>
2026-06-16 19:07:06 -07:00
Evan Hunt
6e44151466 add isctest.mark method for ecdsa_deterinistic
This checks support for ECDSA deterministic mode in the cryptography
library.
2026-06-16 19:07:06 -07:00
Matthijs Mekking
f678d03a67 Add isctest.check methods for AA flag
Similar to checking AD flag, add methods to see if the AA flag is
present or absent.
2026-06-16 14:45:49 -07:00
Ondřej Surý
153c9d3509 Poll for RPZ readiness in the servfail-until-ready test
RPZ is ready only once every policy zone has completed its first update,
and the zones do not finish in a fixed order, so whenever the updates
run serially (per-loop offload, or any single-CPU run): 'slow-rpz' zone
can finish before the others and the query still gets SERVFAIL.  Poll
the query until it returns NOERROR instead.

Assisted-by: Claude:claude-opus-4-8
2026-06-16 14:01:13 +02:00
Alessio Podda
c2ebd1f66e Add RRL cached NXDOMAIN system test
Add a Python system test that primes ns2's negative cache, queries the
cached NXDOMAIN through RRL, and verifies named remains responsive.
2026-06-16 11:47:09 +02:00
Nicki Křížek
630247e845 Use AnsInstance fixture in dispatch test
Replace manual ans.run parsing with the ans4 fixture.

Assisted-by: Claude:claude-fable-5
2026-06-16 09:34:25 +02:00