Only class IN is allowed for user-defined views; the internally
generated `_bind` view stays in the CH class. Both `named` and the
shared checker in `lib/isccfg/check.c` now reject non-IN views, so a
config can no longer pass `named-checkconf` yet fail to start in
`named`.
Tests, configs, and catalog zones using CH or arbitrary classes
(e.g. `class10`) are removed accordingly.
The delegation database kept one SIEVE LRU list per loop so that node
eviction could run lock-free on each node's owning loop; this required
every node to hold a loop reference and to defer its own destruction to
that loop via isc_async_run(). Move the SIEVE unlink into the QP write
transaction, taking the evicted node directly from dns_qp_deletename(),
which serialises every list mutation under the qpmulti writer lock and
lets a single shared list replace the per-loop arrays. Node and database
teardown are now synchronous.
The QP trie and the SIEVE list are wrapped in a reference-counted holder.
Each node keeps a reference to the holder so it (and its memory context)
stays valid until the node is destroyed, while shutdown drains the SIEVE
and destroys the trie from an RCU callback and frees the holder once the
last node drops its reference. Reuse across a reconfiguration now moves
ownership of the holder to the new view instead of sharing it through a
separate owners counter, so dns_delegdb_reuse() is removed.
named_server_sync() logged isc_result_totext(result) but returns
tresult. The loop accumulates errors into tresult, so result only
holds the last iteration's value. If the last view succeeded but an
earlier one failed, the log would incorrectly say "success".
Assisted-by: Claude:claude-opus-4-7
The `rndc flush` command flushes the delegdb by deleting the
existing database and creating a new one. In the process, the
delegdb was losing its configured size limit; as a result, once
flushed, the delegdb size became unbounded.
This is now fixed by using `dns_delegdb_getconfig()` to back up the
current configuration before instantiating a new delegdb, then
restoring it with `dns_delegdb_setconfig()`.
Instead of having independent APIs to configure various aspects of the
delegdb (i.e. cache size, other settings that may come up later), a
single configuration struct is passed to `dns_delegdb_setconfig()`, which
internally does all the plumbing. To avoid relying on
atomics/synchronization, `dns_delegdb_setconfig()` must be called from
exclusive mode (for now).
The configuration can be retrieved at any time (not necessarily from
exclusive mode) using `dns_delegdb_getconfig()`. This is useful, for
instance, to flush the delegdb without losing its parameters.
Until now, the dispatcher silently dropped UDP responses from the
expected peer that carried the wrong DNS message id and kept listening
for the correct id to arrive within the read timeout. An off-path
attacker who knows the destination address and source port of an
outgoing fetch could exploit that quiet retry window to flood the
resolver with guessed responses; with a gigabit link the per-query
success probability grows linearly with the number of guesses that
arrive before the legitimate answer or the timeout.
Treat any such mismatch as a possible spoofing attempt and let the
resolver immediately retry the same query over TCP, the same control
path the truncation handler already uses.
Add a resolver statistics counter - exposed as 'queries retried over TCP
after a response with mismatched query id' in rndc stats and
'MismatchTCP' in the statistics channel
Assisted-by: Claude:claude-opus-4-7
The statistics charts where not displaying on some browsers (e.g. Chrome)
due to '>' being escaped as '>'. Use disable-output-escaping="yes" to
turn this off.
Send various UPDATE requests that are known to have caused
crashes previously with deliberately misconfigured non-IN
zones; confirm that UPDATE is not processed.
Return NOTIMP for UPDATE and NOTIFY requests received for views with a
class other than IN. Only QUERY is now supported for non-IN views such
as CHAOS.
When running dns dns_rdata_tostruct() with types that are only defined
for class IN, ensure that the class is correct before proceeding.
Add an assertion that any zone being updated is of class IN. (Note
that previously, a DLZ zone could have its class value set incorrectly
to NONE; this has been fixed.)
This addresses YWH-PGM40640-70 and YWH-PGM40640-73 (as well as any
similar problems that might have occurred in the future) by minimizing
the code paths that can be reached by rdata classes other than IN, so it
is safe for the implementation to assume that rdatatypes that are only
defined for class IN, such as SVCB or WKS, have been parsed and
validated, and not accepted as unknown/opaque data.
Fixes: isc-projects/bind9#5777Fixes: isc-projects/bind9#5779
Force recursion off, and set allow-recursion/allow-recursion-on ACLs
to none, for views with a class other than IN. Log a configuration
warning if recursion is explicitly enabled for a non-IN view.
This addresses YWH-PGM40640-74 and YWH-PGM40640-75 by preventing any
attempt at recursive processing in a class-CHAOS view, ensuring that
server addresses used for recursive queries and received in recursive
responses are of the expected format.
Fixes: isc-projects/bind9#5780Fixes: isc-projects/bind9#5781
The bare return left conn->secret, conn->response, conn->request, and
conn->text pinned until the connection itself was torn down — every
other error in the function reaches conn_cleanup via goto, and the
success path falls into the same label, so the towire-failure return
was the lone outlier. Send it through the existing cleanup path.
Assisted-by: Claude:claude-opus-4-7
testgen existed solely to let the rndc system test exercise large
response payloads — it has no operator value, accepts an unbounded
count, and could be invoked by any read-only rndc client to drive
named into memory exhaustion. Drop the command, the gencheck helper
that validated its output, and the buffer-size loop in the rndc
system test; the remaining rndc subcommands already produce
non-trivial responses, so the framing path stays exercised.
Assisted-by: Claude:claude-opus-4-7
When named is running as root, nzd_env_close() chowns the per-view
NZD database file to the unprivileged user that named will drop to.
The call used chown(), which follows symlinks, so a symlink at the
NZD path would silently transfer ownership of whatever the link
pointed at instead of the database file itself.
Switch to lstat() + S_ISREG() + lchown() so the chown only fires when
the path is a regular file and never traverses a symlink even if one
is planted between the lstat and the lchown.
Assisted-by: Claude:claude-opus-4-7
When named_os_gethostname() was replaced with raw gethostname(), the
success/failure polarity was flipped: the fallback to "localhost" now
runs on success and the hostname buffer is left uninitialized on
failure. In the failure path, snprintf() then reads the uninitialized
stack buffer, disclosing stack contents via the rndc status reply.
With the parent-centric resolver, dns_view_bestzonecut() consults the
delegation DB (view->deleg) rather than the main cache for the closest
zonecut. Root is never the target of a referral, so it never lands in
delegdb; bestzonecut therefore falls through to the hints lookup on
every query whose closest ancestor is root. prime_done() only called
dns_root_checkhints(), which logs discrepancies but does not update
any store bestzonecut looks at, so the fresh root NS records obtained
by priming were never used and priming kept re-firing.
Rename view->hints to view->rootdb and refresh it when a priming
fetch completes: the '.' NS rdataset is replaced with the fetched
one, and for each listed nameserver the matching A/AAAA glue is
copied from the response's ADDITIONAL section. Only glue for names
that actually appear as NS targets is accepted, so a hostile response
cannot inject unrelated records. Glue the response did not carry is
left untouched, so the hints-file records loaded at startup remain as
a fallback.
Each view gets its own rootdb: the previous shared
named_g_server->in_roothints is gone, and configure_view() calls
dns_rootns_create() per view when the class-IN defaults are needed.
That keeps the priming writer one-per-DB, so concurrent priming in
different views cannot race on the same zone-DB version.
The rootdb refresh runs synchronously from the resolver response path,
so records go straight from the wire into rootdb with no cache round
trip and no dependency on DNSSEC validation state. A new
DNS_FETCHOPT_PRIMING option marks the priming fetch; prime_done()
itself is now pure cleanup.
Track the rootdb freshness window in view->rootdb_expires and trigger
re-priming lazily from dns_view_find() and bestzonecut_rootdb() only
when the window has elapsed. Stale records are still served while the
fresh priming fetch is in flight.
Drop dns_root_checkhints() and its helpers; the rootdb is now the
authoritative source the resolver consults.
Cap the number of in-flight queries on a single shared TCP dispatch.
When the limit is reached, the dispatch is removed from the hash
table so subsequent queries get a fresh connection. The existing
dispatch continues serving its queries until they complete.
This bounds the blast radius of a connection drop: at most N queries
fail simultaneously instead of all queries to that server.
The default limit is 256. It can be overridden for testing via
'named -T tcppipelining=N'.
The cleanup path always unlocks the 'view->newzone.lock' lock, but
there are 'goto cleanup;' operations even before the lock is locked,
which causes an assertion failure.
Don't use the cleanup path before the lock is locked.
In order to make zone.c more readable, we are splitting it up in
separate source files. This moves the zonemgr to its own file
("zonemgr.c").
Since this code accesses the zone structure directly, move the
'struct dns_zonemgr' and its prerequisites to "zone_p.h".
The helper functions 'forward_cancel()', 'zone_xfrdone()',
'zmgr_start_xfrin_ifquota()', and 'zmgr_resume_xfrs() need to be
internally accessible to both source files.
Note: This commit does not compile.
Replace the separate pointer+length field pairs in dns_rdata_nsec3_t
(salt/salt_length, next/next_length, typebits/len) and
dns_rdata_nsec3param_t (salt/salt_length) with isc_region_t. This
makes the structs self-describing and eliminates a class of
length-mismatch bugs.
The dns_zone_setnsec3param() signature is updated to take
isc_region_t *salt instead of separate saltlen and salt arguments.
Function signatures for dns_nsec3_addnsec3, dns_db_getnsec3parameters,
and related internal functions still use separate pointer+length pairs
and should be updated in a follow-up.
dns_zone_getloadtime(), dns_zone_getexpiretime(),
dns_zone_getrefreshtime(), and dns_zone_getrefreshkeytime()
cannot fail, so return void instead of ISC_R_SUCCESS.
In order to make zone.c more readable, we are splitting it up in
separate source files. This moves the set and get functions to its
own file ("zoneproperties.c").
Since this code accesses the zone structure directly, move the
'struct dns_zone' and its prerequisites to "zone_p.h".
The helper functions 'inline_raw()', 'inline_secure()',
'dns_zone_setview_helper()', 'zone_settimer(), 'set_resigntime()', and
'zone_freedbargs()' need to be internally accessible to both source
files.
A few set/get functions remain in zone.c for now:
- dns_zone_getserial
- dns_zone_getversion
- dns_zone_setviewcommit
- dns_zone_setviewrevert
- dns_zone_get_rpz_num
- dns_zone_set_parentcatz
- dns_zone_get_parentcatz
- dns_zone_setrawdata
- dns_zone_setskr
- dns_zone_getskrbundle
- dns_zone_setnsec3param
- dns_zone_setoption
- dns_zone_getoptions
- dns_zone_getrequesttransporttype
- dns_zone_getredirecttype
- dns__zone_getnotifyctx
- dns_zone_getgluecachestats
- dns_zone_setplugins
- dns_zone_setserial
- dns_zone_getxfr
- dns_zone_getkeystores
In the rare case where a catalog zone member is being modified with
'rndc modzone', also mark the zone as modded, so when the zone is
deleted again with 'rndc delzone', the configuration is also removed
from the NZD.
When a zone that is configured in named.conf is modified with
'rndc modzone', the new zone configuration is now also stored in the
NZD.
This must be removed when the zone is deleted with 'rndc delzone',
otherwise a restart will fail.
When a zone that is configured in named.conf is modified with
'rndc modzone', the zone configuration is deleted from the effective
config. Store the new configuration in the NZD. Mark the zone
as 'modified by rndc modzone'. Otherwise, subsequent calls to
'rndc modzone' would fail because the zone configuration cannot be
found.
Lower the hard floor for max-cache-size from 2 MB to 8 MB to support
resource-constrained environments (e.g. CPE devices) while remaining
safe for LRU-only eviction.
Extract the inline max-cache-size logic from configure_view() into
reusable helpers: configure_max_cache_size(), default_max_cache_size(),
max_cache_size_as_percent(), and sanitized_max_cache_size().
Move DNS_CACHE_MINSIZE and DNS_ADB_MINADBSIZE to public headers and
remove the SIZE_AS_PERCENT sentinel.
Since TTL-based cache cleaning has been removed, an unlimited
max-cache-size would eventually exhaust system memory.
Both 'max-cache-size unlimited;' and 'max-cache-size 0;' now fall
back to the default value (90% of physical memory for recursive
views).
The delegation DB now uses the same amount of memory than ADB, which is
1/8 of the `max-cache-size`.
The main cache database, instead of using `max-cache-size`, now use the
"remaining" part of it, after the delegation DB and ADB took their part,
so 6/8.
This avoid blowing up the host memory, typically when specifying
`max-cache-size 95%`, as the global cache usage would go way ahead 100%.
When dumping the cache, include the contents of the delegation
database. Add a new 'rndc dumpdb -deleg' option, which dumps
the delegation database exclusively.
While the delegation database dumping format mimic the zone file format,
the API does not use the `dns_master_style_t` configuration (i.e. to
specify how many spaces/tab are used between each RR fields), because
the generic API handling this relies on databse using `dns_rdataset_t`
as internal storage format. This can be improved later.
Function `dns_view_bestzonecut()` now uses the delegation DB instead of
the main cache when looking up at the cache.
As a result, replace `dns_rdataset_t` (representing an NS RRset) with
`dns_delegset_t` in `dns_view_bestzonecut()` and
`dns_resolver_createfetch()` APIs. The resolver and query processing now
use the delegation DB instead of the cache for zonecut lookups.
In the case of the delegation lives in the local database, the locally
found `rdataset` is internally converted into a `dns_delegset_t` object.
From caller POV, it doesn't change anything: a delegation set is a
read-only object which can be used as long as needed and must be
detached one it's done with it.
The resolver now caches NS records and their A/AAAA glues from referral
answers into the delegation database.
A new `cache_delegns()` function extracts NS names and associated glue
addresses from the authority/additional sections of a referral answer
and use those informations to build a delegation set, which is then
inserted into the delegation database.
The created delegation set contains a delegation per NS RR. If the NS RR
has matching A/AAAA RR, the delegation only store the addresses and not
the name. (Note this is technically possible to group all NS RR which
doesn't have glues into a single delegation, and the implementation can
be changed in that way in the future).
Each view has its own instance of the delegation database (they are
never shared between views), but a server restart/reload preserve the
delegation database state.
Since it is impossible to increase an isc_statsmulti counter and
retrieve the new counter atomically, and we need the output of
recursclients in order to compute ns_highwater_recursive, we change the
recursclients counter to an isc_stats one.
In the current statistics counter implementation, the statistics are
backed by an array of counters, which are updated via atomic operations.
This leads to contention, especially on high core count
machines.
This commit introduces a new isc_statsmulti_t counter that keeps a
separate array per thread. These counters are then aggregated only when
statistics are queried, shifting work off the critical path.
These changes lead to a ~2% improvement in perflab.
Unlike new transports with a new dependency DNS-over-QUIC support will
be added incrementally due to the non-trivial amound of plumbing
required by libngtcp2. This will require non-functional QUIC code in the
main branch that won't be exposed for non-development builds.
Therefore, libngtcp2 is linked as an optional dependency only on
explicitly enabled development builds and cannot be required. This will
be changed with a `doq` meson build option once the server-side
functionality is complete for consumption.
Move all LMDB-based new zone database functions from server.c into
nzd.c to reduce the size of server.c and isolate the NZD/LMDB
interface. Rename load_nzf() to nzd_load_nzf() to match the nzd_
namespace.
Now that NZF write support is gone, remove the unused nzfwriter_t
typedef and nzfwriter parameter from delete_zoneconf(). Remove the
bool locked parameter and simplify the locking in do_modzone() and
rmzone() to unconditional lock/unlock pairs.
Drop the NZF (New Zone File) fallback for persisting runtime zone
configurations, making LMDB (NZD) the only storage backend. This
removes all #ifdef HAVE_LMDB conditionals, the meson 'lmdb' option,
and the NZF-related functions. LMDB is now a mandatory build
dependency.
The named-nzd2nzf tool is now always built.
If we are modifiying the zone, the zone must have been added before.
Don't overwrite this value on modifications.
Also it feels cleaner to pass added=false to configure_zone() in
do_modzone().
Some code paths try to lock an already locked view->newzone.lock.
For example, do_modzone() aqcuires the lock and then calls
delete_zoneconf(), that wants to acquire the same lock.
Add a parameter to delete_zoneconf() that informs the function if the
lock has already been acquired.
A few port validation checks use >= UINT16_MAX instead of > UINT16_MAX,
incorrectly rejecting port 65535 as out of range. Port 65535 is a valid
TCP/UDP port number. Other port checks in the same file already use the
correct > comparison.