Commit graph

906 commits

Author SHA1 Message Date
Colin Vidal
d676ce8085 remove query_ctx_t detach_client property
Since the removal of NS_QUERY_QCTX_DESTROYED hook, there is no need for
the `qctx->detach_client` object anymore, as this was designed to tell
the plugin whether the client object is about to be, or is already,
freed from memory.  This is not needed anymore, as NS_QUERY_RESET is
called _always_ when the client object is about to be freed from memory.

Remove `detach_client` and tidy up the code a bit by including the
freeing of the qctx object (when allocated) inside the qctx_destroy
function instead of requiring extra calls.
2025-09-09 10:02:32 +02:00
Colin Vidal
95c71c2739 replace QCTX_INIT/_DESTROY hooks with QUERY_SETUP/_RESET
The hook NS_QUERY_QCTX_DESTROY is problematic with zone plugins because
it can be called in some contexts where `qctx->client` is invalid (the
pointer is dangling); which would lead to a use-after-free (spotted by
TSAN build) as `qctx->client` is used to get the zone hooktable, to find
out whether there is an authoritive zone which would have
NS_QUERY_QCTX_DESTROY registered.

This can't easily be fixed, because there is no easy way to know from
query.c code if `client` is still a valid object: `client->reqhandle`,
representing the request from a client, is refcounted, and the `client`
object is freed from memory once its refcounter gets to 0. While
`reqhandle` is attached from query.c code, it can be attached more than
once from asynchronous code and there is no clear path where detaching
it would lead to a client free. Hence, there is no way to know for sure
when to set `qctx->client = NULL` (this is why the pointer remains
dangling).

Back to the original problem; this is why the NS_QUERY_QCTX_DESTROY hook
is incompatible with zone plugins. `qctx->detach_client`, which is used
to tell a plugin that the `client` object is either free or about to be
free can't be use either, because in some cases the client is still
there, and should be used.

Code issue aside, the `qctx` object is really just an aggregate of
various data to pass easily in the various functions and callbacks,
initially stored on the stack, but allocated in some cases (for some
asynchronous flow, when recursion is needed), so the point it gets
created/"destroyed" is really just an implementation "detail", and
providing a higher level hook for the plugin would be beneficial. Hence,
NS_QUERY_RESET and NS_QUERY_INIT are removed, and instead, the existing
NS_QUERY_SETUP can be used as well as the newly introduced
NS_QUERY_RESET (which replaces NS_QUERY_QCTX_DESTROY). The advanage is
that NS_QUERY_RESET is called _only_ when the client object is _always_
about to be freed, which avoids usage of the extra `qctx->detach_client`
usage from the plugin.

The way NS_QUERY_RESET works is that when the `client` is freed, a
callback (from `query.c`) is called. This callback creates a transient
qctx object on the stack with a pointer to the view, and uses that
to call the hook.
2025-09-09 09:42:34 +02:00
Colin Vidal
260bbc24c9 add plugin_register param telling the source
The plugin `plugin_register` API has a new parameter `source` indicating
whether the plugin is loaded from a view or a zone.

This extra parameter enables the plugin to fail early during
initialization if a plugin written to be used in a zone exclusively
is loaded at a view level, or vice versa.
2025-09-09 09:42:34 +02:00
Colin Vidal
1566634fae add NS_QUERY_AUTHZONE_ATTACHED hook
Add a new query hook called `NS_QUERY_AUTHZONE_ATTACHED`. This hook is
called whenever an authoritative zone is found and attached during a
query answer.

From code level, this hook is called when `qctx->client->query->authzone`
is attached during a query.  This enables zone-specific plugins to
initialize specific states whenever a local zone is found that can
answer a query.
2025-09-09 09:42:34 +02:00
Colin Vidal
91cd7b865c update hook developer documentation
Attempt to add zone plugin specificities into the hook developer
documentation. In particular about the hook call order and hookpoint
which can't be called on a zone plugin.
2025-09-09 09:42:34 +02:00
Colin Vidal
5893770cd9 add zone-specific plugin instance
The zone object now has its own hooktable and plugins, which are
initialized during zone initialization.
2025-09-09 09:42:34 +02:00
Aram Sargsyan
1962857ac4 Log the servfail-until-ready message not faster than once per second
Since the log level has been raised, busy servers can "explode" from
the amount of log messages. Use the usual practice of logging "every
once in a while".
2025-09-03 13:23:12 +00:00
Aram Sargsyan
49356ce944 Change the "RPZ not ready yet" message and its log level
The "RPZ not ready yet" message is logged at debug 3 level. Use the
info level instead for better visibility.

After raising the log level, the rpz_log_fail_helper() function starts
appending " failed: " the the message. Change the log message so it
makes more sense.
2025-09-03 13:23:12 +00:00
Aram Sargsyan
d9b5f6c502 RPZ 'servfail-until-ready': skip updating SERVFAIL cache
In order to not pollute the SERVFAIL cache with the configured
SERVFAIL answers while RPZ is loading, set the NS_CLIENTATTR_NOSETFC
attribute for the client.
2025-09-03 13:23:12 +00:00
Alessio Podda
20a1583661 Lazily allocate fetch counter
The counter in ns_client_t is used to track the maximum number of
recursions in the resolver, but it is created unconditionally when
starting the client and deallocated when resetting it.

This commit defers the allocation of the counter till recursion needs to
actually happen, speeding up authoritative workloads in perflab by
1.5~2%.
2025-09-02 11:22:28 +02:00
Aram Sargsyan
5e718dd220 Implement '-T slowrpz' named testing option
When used, named processes RPZ zones slowly. Useful for system tests.
2025-08-22 16:31:17 +00:00
Aram Sargsyan
41387b8d30 Add a new 'servfail-until-ready' configuration option for RPZ
By default, when named is started it may start answering to
queries before the response policy zones are completely loaded
and processed. This new feature gives an option to the users to
tell named that incoming requests should result in SERVFAIL anwser
until all the response policy zones are procesed and ready.
2025-08-22 16:31:17 +00:00
Ondřej Surý
42496f3f4a
Use ControlStatementsExceptControlMacros for SpaceBeforeParens
> Put a space before opening parentheses only after control statement
> keywords (for/if/while...) except this option doesn’t apply to ForEach
> and If macros. This is useful in projects where ForEach/If macros are
> treated as function calls instead of control statements.
2025-08-19 07:58:33 +02:00
Ondřej Surý
3445362918
Add dns_rdatatype_isnsec() helper function
Replace the checks for both NSEC and NSEC3 with a single helper
function.
2025-08-15 07:22:52 +02:00
Ondřej Surý
59d1326175
Use dns_rdatatype_none more consistently
Use dns_rdatatype_none instead of plain '0' for dns_rdatatype_t and
dns_typepair_t manipulation.  While plain '0' is technically ok, it
doesn't carry the required semantic meaning, and using the named
dns_rdatatype_none constant makes the code more readable.
2025-08-15 07:22:52 +02:00
Alessio Podda
ae6a34cbda Decouple database and node lifetimes by adding node-specific vtables
All databases in the codebase follow the same structure: a database is
an associative container from DNS names to nodes, and each node is an
associative container from RR types to RR data.

Each database implementation (qpzone, qpcache, sdlz, builtin, dyndb) has
its own corresponding node type (qpznode, qpcnode, etc). However, some
code needs to work with nodes generically regardless of their specific
type - for example, to acquire locks, manage references, or
register/unregister slabs from the heap.

Currently, these generic node operations are implemented as methods in
the database vtable, which creates problematic coupling between database
and node lifetimes. If a node outlives its parent database, the node
destructor will destroy all RR data, and each RR data destructor will
try to unregister from heaps by calling a virtual function from the
database vtable. Since the database was already freed, this causes a
crash.

This commit breaks the coupling by standardizing the layout of all
database nodes, adding a dedicated vtable for node operations, and
moving node-specific methods from the database vtable to the node
vtable.
2025-08-07 11:39:38 -07:00
Matthijs Mekking
2f70a0ef12 Add ede for zone with rpz cname override policy
When the zone is configured with a CNAME override policy, also add the
configured EDE code.

When the zone is contains a wildcard CNAME, also add the configured
EDE code.
2025-08-05 08:35:51 +02:00
Colin Vidal
7747ac8aed plugin expand path automatically adds extension
If a plugin is configured without the extension,
`ns_plugin_expandpath()` automatically take cares of appending the
suffix to the path. The way it works is by checking if a file exists at
the expanded path. If it doesn't, it assumes the plugin path (or name)
doesn't have the extension and append the extension (which is
platform-specific) to the actual path.
2025-07-28 23:08:04 +02:00
Ondřej Surý
f6aed602f0
Refactor the network manager to be a singleton
There is only a single network manager running on top of the loop
manager (except for tests).  Refactor the network manager to be a
singleton (a single instance) and change the unit tests, so that the
shorter read timeouts apply only to a specific handle, not the whole
extra 'connect_nm' network manager instance.
2025-07-23 22:45:38 +02:00
Ondřej Surý
b8d00e2e18
Change the loopmgr to be singleton
All the applications built on top of the loop manager were required to
create just a single instance of the loop manager.  Refactor the loop
manager to not expose this instance to the callers and keep the loop
manager object internal to the isc_loop compilation unit.

This significantly simplifies a number of data structures and calls to
the isc_loop API.
2025-07-23 22:44:16 +02:00
Matthijs Mekking
7774f16ed5 Special case refresh stale ncache data
When refreshing stale ncache data, the qctx->rdataset is NULL and
requires special processing.
2025-07-23 07:18:48 +00:00
Matthijs Mekking
a66b04c8d4 Make serve-stale refresh behave as prefetch
A serve-stale refresh is similar to a prefetch, the only difference
is when it triggers. Where a prefetch is done when an RRset is about
to expire, a serve-stale refresh is done when the RRset is already
stale.

This means that the check for the stale-refresh window needs to
move into query_stale_refresh(). We need to clear the
DNS_DBFIND_STALEENABLED option at the same places as where we clear
DNS_DBFIND_STALETIMEOUT.

Now that serve-stale refresh acts the same as prefetch, there is no
worry that the same rdataset is added to the message twice. This makes
some code obsolete, specifically where we need to clear rdatasets from
the message.
2025-07-23 07:18:48 +00:00
Andoni Duarte Pintado
ffee986ae0 Merge tag 'v9.21.10' 2025-07-16 17:16:27 +02:00
Petr Špaček
750d8a61b6 Convert DNS_RDATASETATTR_ bitfield manipulation to struct of bools
RRset ordering is now an enum inside struct rdataset attributes. This
was done to keep size to of the structure to its original value before
this MR.

I expect zero performance impact but it should be easier to deal with
attributes in debuggers and language servers.
2025-07-10 11:17:19 +02:00
Aram Sargsyan
27e7961479 Log dropped or slipped responses in the query-errors category
As mentioned in the comments block before the changed code block,
the dropped or slipped responses should be logged in the query
category (or rather query-errors category as done in lib/ns/client.c),
so that requests are not silently lost.

Also fix a couple of errors/typos in the code comments.
2025-07-10 08:20:17 +00:00
Alessio Podda
e84704bd55 Improve efficiency of ns_client_t reset
The ns_client_t struct is reset and zero-ed out on every query,
but some fields (query, message, manager) are preserved.

We observe two things:
 - The sendbuf field is going to be overwritten anyway, there's
   no need to zero it out.
 - The fields are copied out when the struct is zero-ed out, and
   then copied back in. For the query field (which is 896 bytes)
   this is very inefficient.

This commit makes the reset more efficient avoiding to unnecessary
zero-ing and copy.
2025-07-10 07:19:47 +02:00
Aram Sargsyan
3d8bd8bbf1 Reset DNS_DBFIND_STALETIMEOUT in query_lookup()
If ns__query_start() is called because of a chained query (e.g.
after encountering a CNAME), a previously set DNS_DBFIND_STALETIMEOUT
flag on the query's 'dboptions' field can cause an assertion
failure if the new query's 'stalefirst' value is not true (e.g. if the
target qname is an authoritative zone for the server). Reset the
DNS_DBFIND_STALETIMEOUT flag in the query_lookup() function before
evaluating the 'stalefirst' value, and make sure to assign a fresh
value to the `stalefirst' flag instead of conditionally assigning it
only if the value is 'true'.
2025-07-03 11:03:34 +02:00
Ondřej Surý
1032681af0 Convert the isc/tid.h to use own signed integer isc_tid_t type
Change the internal type used for isc_tid unit to isc_tid_t to hide the
specific integer type being used for the 'tid'.  Internally, the signed
integer type is being used.  This allows us to have negatively indexed
arrays that works both for threads with assigned tid and the threads
with unassigned tid.  This should be used only in specific situations.
2025-06-28 13:32:12 +02:00
Mark Andrews
422b9118e8 Use clang-format-20 to update formatting 2025-06-25 12:44:22 +10:00
Mark Andrews
eb184b864c Support PRIVATEOID/PRIVATEDNS in the resolver
dns_resolver_algorithm_supported() has been extended so in addition to
an algorithm number, it can also take a pointer to an RRSIG signature
field in which key information is encoded.
2025-06-19 07:00:53 +10:00
Mark Andrews
71801ab123 Use DST algorithm values instead of dns_secalg where needed
DST algorithm and DNSSEC algorithm values are not necessarily the same
anymore: if the DNSSEC algorithm value is PRIVATEOID or PRIVATEDNS, then
the DST algorithm will be mapped to something else. The conversion is
now done correctly where necessary.
2025-06-19 07:00:53 +10:00
Evan Hunt
d586c29069 Remove zone keyopts field
The "keyopts" field of the dns_zone object was added to support
"auto-dnssec"; at that time the "options" field already had most of
its 32 bits in use by other flags, so it made sense to add a new
field.

Since then, "options" has been widened to 64 bits, and "auto-dnssec"
has been obsoleted and removed. Most of the DNS_ZONEKEY flags are no
longer needed. The one that still seems useful (_FULLSIGN) has been
moved into DNS_ZONEOPT and the rest have been removed, along with
"keyopts" and its setter/getter functions.
2025-06-12 18:29:29 -07:00
Aydın Mercan
5cd6c173ff
replace the build system with meson
Meson is a modern build system that has seen a rise in adoption and some
version of it is available in almost every platform supported.

Compared to automake, meson has the following advantages:

* Meson provides a significant boost to the build and configuration time
  by better exploiting parallelism.

* Meson is subjectively considered to be better in readability.

These merits alone justify experimenting with meson as a way of
improving development time and ergonomics. However, there are some
compromises to ensure the transition goes relatively smooth:

* The system tests currently rely on various files within the source
  directory. Changing this requirement is a non-trivial task that can't
  be currently justified. Currently the last compiled build directory
  writes into the source tree which is in turn used by pytest.

* The minimum version supported has been fixed at 0.61. Increasing this
  value will require choosing a baseline of distributions that can
  package with meson. On the contrary, there will likely be an attempt
  to decrease this value to ensure almost universal support for building
  BIND 9 with meson.
2025-06-11 10:30:12 +03:00
Mark Andrews
72cd6e8591 Silence tainted scalar in client.c
Coverity detected that 'optlen' was not being checked in 'process_opt'.
This is actually already done when the OPT record was initially
parsed.  Add an INSIST to silence Coverity as is done in message.c.
2025-05-29 06:26:37 +00:00
Ondřej Surý
4e79e9baae
Give every memory context a name
Instead of giving the memory context names with an explicit call to
isc_mem_setname(), add the name to isc_mem_create() call to have all the
memory contexts an unconditional name.
2025-05-29 05:46:46 +02:00
Evan Hunt
dd9a685f4a simplify code around isc_mem_put() and isc_mem_free()
it isn't necessary to set a pointer to NULL after calling
isc_mem_put() or isc_mem_free(), because those macros take
care of it automatically.
2025-05-28 17:22:32 -07:00
Evan Hunt
8d065fd3e1 add DNS_DBITERATOR_FOREACH and DNS_RDATASETITER_FOREACH
when iterating databases, use DNS_DBITERATOR_FOREACH and
DNS_DNSRDATASETITER_FOREACH macros where possible.
2025-05-27 21:08:09 -07:00
Evan Hunt
f10f5572ac add DNS_RDATASET_FOREACH macro
replace the pattern `for (result = dns_rdataset_first(x); result ==
ISC_R_SUCCES; result = dns_rdataset_next(x)` with a new
`DNS_RDATASET_FOREACH` macro throughout BIND.
2025-05-27 21:08:09 -07:00
Evan Hunt
8487e43ad9 make all ISC_LIST_FOREACH calls safe
previously, ISC_LIST_FOREACH and ISC_LIST_FOREACH_SAFE were
two separate macros, with the _SAFE version allowing entries
to be unlinked during the loop. ISC_LIST_FOREACH is now also
safe, and the separate _SAFE macro has been removed.

similarly, the ISC_LIST_FOREACH_REV macro is now safe, and
ISC_LIST_FOREACH_REV_SAFE has also been removed.
2025-05-23 13:09:10 -07:00
Ondřej Surý
8171bf01ed
Deprecate max-rsa-exponent-size, always use 4096 instead
The `max-rsa-exponent-size` could limit the exponents of the RSA
public keys during the DNSSEC verification.  Instead of providing
a cryptic (not cryptographic) knob, hardcode the max exponent to
be 4096 (the theoretical maximum for DNSSEC).
2025-05-21 00:50:08 +02:00
Aram Sargsyan
e42d6b4810 Implement a new 'notify-defer' configuration option
This new option sets the delay, in seconds, to wait before sending
a set of NOTIFY messages for a zone. Whenever a NOTIFY message is
ready to be sent, sending will be deferred for this duration.
2025-05-15 12:24:13 +00:00
Mark Andrews
9bb93520f1
Wrong NSEC3 chosen for NO QNAME proof
When we optimised the closest encloser NSEC3 discovery the maxlabels
variable was used in the binary search. The updated value was later
used to add the NO QNAME NSEC3 but that block of code needed the
original value. This resulted in the wrong NSEC3 sometimes being
chosen to perform this role.
2025-05-08 21:48:11 +02:00
Aram Sargsyan
74a8acdc8d Separate the single setter/getter functions for TCP timeouts
Previously all kinds of TCP timeouts had a single getter and setter
functions. Separate each timeout to its own getter/setter functions,
because in majority of cases only one is required at a time, and it's
not optimal expanding those functions every time a new timeout value
is implemented.
2025-04-23 17:03:05 +00:00
Aram Sargsyan
70ad94257d Implement tcp-primaries-timeout
The new 'tcp-primaries-timeout' configuration option works the same way
as the existing 'tcp-initial-timeout' option, but applies only to the
TCP connections made to the primary servers, so that the timeout value
can be set separately for them. The default is 15 seconds.

Also, while accommodating zone.c's code to support the new option, make
a light refactoring with the way UDP timeouts are calculated by using
definitions instead of hardcoded values.
2025-04-23 17:03:05 +00:00
Aram Sargsyan
412aa881f2 Fix a serve-stale issue with a delegated zone
When 'stale-answer-client-timeout' is 0, named is allowed to return
a stale answer immediately, while also initiating a new query to get
the real answer. This mode is activated in ns__query_start() by setting
the 'qctx->options.stalefirst' optoin to 'true' before calling the
query_lookup() function, but not when the zone is known to be
authoritative to the server. When the zone is authoritative, and
query_looup() finds out that the requested name is a delegation,
then before proceeding with the query, named tries to look it up
in the cache first. Here comes the issue that it doesn't consider
enabling 'qctx->options.stalefirst' in this case, and so the
'stale-answer-client-timeout 0' setting doesn't work for those
delegated zones - instead of immediately returning the stale answer
(if it exists), named tries to resolve it.

Fix this issue by enabling 'qctx->options.stalefirst' in the
query_zone_delegation() function just before named looks up the name
in the cache using a new query_lookup() call. Also, if nothing was
found in the cache, don't initiate another query_lookup() from inside
query_notfound(), and let query_notfound() do its work, i.e. it will
call query_delegation() for further processing.
2025-04-23 11:46:16 +00:00
Mark Andrews
0d9cab1555 Process NSID and DNS COOKIE options when returning BADVERS
This will help identify the broken server if we happen to break
EDNS version negotiation.  It will also help protect the client
from spoofed BADVERSION responses.
2025-04-15 02:38:37 +00:00
Ondřej Surý
fe48290140
Don't pass edectx from fetch_and_forget
Pass NULL as edectx for the fetch_and_forget() fetches as nobody
is reading the EDE contexts and it can mess the main client buffer.
2025-04-02 17:38:31 +02:00
Evan Hunt
ad7f744115 use ISC_LIST_FOREACH in more places
use the ISC_LIST_FOREACH pattern in places where lists had
been iterated using a different pattern from the typical
`for` loop: for example, `while (!ISC_LIST_EMPTY(...))` or
`while ((e = ISC_LIST_HEAD(...)) != NULL)`.
2025-03-31 13:45:14 -07:00
Evan Hunt
522ca7bb54 switch to ISC_LIST_FOREACH everywhere
the pattern `for (x = ISC_LIST_HEAD(...); x != NULL; ISC_LIST_NEXT(...)`
has been changed to `ISC_LIST_FOREACH` throughout BIND, except in a few
cases where the change would be excessively complex.

in most cases this was a straightforward change. in some places,
however, the list element variable was referenced after the loop
ended, and the code was refactored to avoid this necessity.

also, because `ISC_LIST_FOREACH` uses typeof(list.head) to declare
the list elements, compilation failures can occur if the list object
has a `const` qualifier.  some `const` qualifiers have been removed
from function parameters to avoid this problem, and where that was not
possible, `UNCONST` was used.
2025-03-31 13:45:10 -07:00
Evan Hunt
5cff8f9017 implicitly declare list elements in ISC_LIST_FOREACH macros
ISC_LIST_FOREACH and related macros now use 'typeof(list.head)' to
declare the list elements automatically; the caller no longer needs
to do so.

ISC_LIST_FOREACH_SAFE also now implicitly declares its own 'next'
pointer, so it only needs three parameters instead of four.
2025-03-31 13:37:47 -07:00