Extend the ZoneKeyFile to read the file-backed private key and return it
in a format suitable for use with dnspython. Add ZoneKey.private_key
property to unify the interface.
The get_dnsalg() was just a compatibility layer for 9.18 which lacked
the Algorithm support - remove it in favor of using the .algorithm
property.
In order to properly support private OID algorithms, use the DST value
which is unique across all algorithms.
Also fix private_type_record(): the choice between the 5- and 7-byte
signing record depends on the DST value (256/257 for the private-OID
algorithms), not the on-wire number, which never reaches 256.
Assisted-by: Claude:claude-opus-4-8
Make zone.FileZoneKey the single representation of a file-backed key
(typically generated by dnssec-keygen). Move the common key-related
functionality into zone.FileZoneKey, and extend that functionality in
kasp.Key to also add state and timing related operations on top. Remove
duplicate into_ta() function.
Note that is_ksk() is implemented differently for kasp.Key: with the
metadata file available, the KSK status is loaded from that file, as it
indicates the authoritative policy decision which makes the key a KSK.
In zone.FileZoneKey which doesn't work with the metadata file, the KSK
status if inferred from the DNSKEY SEP flag - the best information
available for that class.
Assisted-by: Claude:claude-opus-4-8
The qpcache_rrsig_any test enumerated only meta-types as the covered
type, so an RRSIG covering RRSIG -- a non-meta signature type -- slipped
through the earlier meta-type hardening. Probe that case too.
Assisted-by: Claude:claude-opus-4-8
Cache a DNSSEC-validated A record, then make a CD=1 query elicit an
unvalidated NXDOMAIN for the name: the secure RRset must survive, and an
uncached-type query must not get the wrong RRset back.
Assisted-by: Claude:claude-opus-4-8
Add a system test which covers the behavior of the `min-delegation-ttl`
and `max-delegation-ttl` options (including default, disabling, and
enforcing that min- must be strictly less than max-).
Delegations are now stored in delegdb with a TTL of at least 60 seconds
by default. A new configuration option `min-delegation-ttl` allows
overriding this value or disabling entirely it with `0`.
This hardens the resolver against misconfigured glue or NS records
with very low TTLs, which would otherwise trigger delegation refetches
too often.
A new option `max-delegation-ttl` (which default to `0`) is also added,
enabling an operator to enforce a maximum TTL check for delegations.
isc_radix_insert can no longer fail: node allocation uses
isc_mem_get which aborts on OOM, and prefix copying was eliminated
by inlining. Propagate the void return through dns_iptable_addprefix,
dns_iptable_merge, dns_acl_any, dns_acl_none, and all their callers.
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.
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.
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
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.
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
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
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.
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.
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().
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.
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.
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.
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.
expand on the isctest.check.empty_answer() function, adding
empty_authority(), empty_additional(), has_answer(), has_authority(),
and has_additional().
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.
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.
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.
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.
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.
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.
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).
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.
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.
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