Commit graph

13874 commits

Author SHA1 Message Date
Artem Boldariev
4637b72da6 Change X509_STORE_up_ref() shim return value
X509_STORE_up_ref() must return 1 on success, while the previous
implementation would return the references count. This commit fixes
that.
2022-04-28 13:39:22 +03:00
Artem Boldariev
26feac0c61 Implement shim for SSL_CTX_set1_cert_store() (affects Debian 9)
This commit implements a shim for SSL_CTX_set1_cert_store() for
OpenSSL/LibreSSL versions where it is not available.
2022-04-28 13:39:22 +03:00
Artem Boldariev
6c05fb09c3 Add support for Strict/Mutual TLS into BIND
This commit adds support for Strict/Mutual TLS into BIND. It does so
by implementing the backing code for 'hostname' and 'ca-file' options
of the 'tls' statement. The commit also updates the documentation
accordingly.
2022-04-28 13:39:21 +03:00
Artem Boldariev
05091f0095 Restore disabled unused 'tls' options: 'ca-file' and 'hostname'
This commit restores the 'tls' options disabled in
78b73d0865.
2022-04-28 13:39:21 +03:00
Artem Boldariev
9b0eb3e5a3 Add ISC_R_TLSBADPEERCERT error code to the TLS related code
This commit adds support for ISC_R_TLSBADPEERCERT error code, which is
supposed to be used to signal for TLS peer certificates verification
in dig and other code.

The support for this error code is added to our TLS and TLS DNS
implementations.

This commit also adds isc_nm_verify_tls_peer_result_string() function
which is supposed to be used to get a textual description of the
reason for getting a ISC_R_TLSBADPEERCERT error.
2022-04-28 13:39:21 +03:00
Artem Boldariev
e2a3ec2ba5 Extend TLS context cache with CA certificates store
This commit adds support for keeping CA certificates stores associated
with TLS contexts. The intention is to keep one reusable store per a
set of related TLS contexts.
2022-04-28 13:39:21 +03:00
Artem Boldariev
6fdc03102a Add foundational functions to implement Strict/Mutual TLS
This commit adds a set of functions that can be used to implement
Strict and Mutual TLS:

* isc_tlsctx_load_client_ca_names();
* isc_tlsctx_load_certificate();
* isc_tls_verify_peer_result_string();
* isc_tlsctx_enable_peer_verification().
2022-04-28 13:39:21 +03:00
Artem Boldariev
b975ee7be4 Add utility functions to manipulate X509 certificate stores
This commit adds a set of high-level utility functions to manipulate
the certificate stores. The stores are needed to implement TLS
certificates verification efficiently.
2022-04-28 13:39:21 +03:00
Matthijs Mekking
125603a543 Add stale answer extended errors
Add DNS extended errors 3 (Stale Answer) and 19 (Stale NXDOMAIN Answer)
to responses. Add extra text with the reason why the stale answer was
returned.

To test, we need to change the configuration such that for the first
set of tests the stale-refresh-time window does not interfer with the
expected extended errors.

(cherry picked from commit c66b9abc0b)
2022-04-28 11:21:22 +02:00
Evan Hunt
af37c6f60c prevent a deadlock in the shutdown system test
The shutdown test sends 'rdnc status' commands in parallel with
'rndc stop' A new rndc connection arriving will reference the ACL
environment to see whether the client is allowed to connect.
Commit c0995bc380 added a mutex lock to ns_interfacemgr_getaclenv(),
but if the new connection arrives while the interfaces are being
purged during shutdown, that lock is already being held. If the
the connection event slips in ahead of one of the netmgr's "stop
listening" events on a worker thread, a deadlock can occur.

The fix is not to hold the interfacemgr lock while shutting down
interfaces; only while actually traversing the interface list to
identify interfaces needing shutdown.

(cherry picked from commit 5c4cf3fcc4)
2022-04-27 23:56:59 -07:00
Evan Hunt
9296be2130 refactor resume_dsfetch()
clean up resume_dsfetch() so that the fctx reference counting is
saner and easier to follow.

(cherry picked from commit 7b2ea97e46)
2022-04-27 16:12:55 -07:00
Evan Hunt
759e0b030e refactor validated()
minor changes to ensure that fctx reference counting is clear and correct.

(cherry picked from commit d2f407cca3)
2022-04-27 16:12:55 -07:00
Evan Hunt
50ef4a8cae rename maybe_destroy() to maybe_cancel_validators()
the maybe_destroy() function no longer destroys the fctx,
so rename it and update the comments.

(cherry picked from commit 7c5afebcdc)
2022-04-27 16:12:55 -07:00
Evan Hunt
8a90a62d0f refactor fctx_done() to set fctx to NULL
previously fctx_done() detached the fctx but did not clear the pointer
passed into it from the caller.  in some conditions, when rctx_done()
was reached while waiting for a validator to complete, fctx_done()
could be called twice on the same fetch, causing a double detach.

fctx_done() now clears the fctx pointer, to reduce the chances of
such mistakes.

(cherry picked from commit b4592d02a1)
2022-04-27 16:12:55 -07:00
Artem Boldariev
f39041ee65 Replace listener TLS contexts on reconfiguration
This commit makes use of isc_nmsocket_set_tlsctx(). Now, instead of
recreating TLS-enabled listeners (including the underlying TCP
listener sockets), only the TLS context in use is replaced.
2022-04-27 23:58:38 +03:00
Artem Boldariev
3a75b33287 Add isc_nmsocket_set_tlsctx()
This commit adds isc_nmsocket_set_tlsctx() - an asynchronous function
that replaces the TLS context within a given TLS-enabled listener
socket object. It is based on the newly added reference counting
functionality.

The intention of adding this function is to add functionality to
replace a TLS context without recreating the whole socket object,
including the underlying TCP listener socket, as a BIND process might
not have enough permissions to re-create it fully on reconfiguration.
2022-04-27 23:58:38 +03:00
Artem Boldariev
f52c06054b Maintain a per-thread TLS ctx reference in TLS stream code
This commit changes the generic TLS stream code to maintain a
per-worker thread TLS context reference.
2022-04-27 23:58:38 +03:00
Artem Boldariev
28460151ca Use isc_tlsctx_attach() in TLS DNS code
This commit adds proper reference counting for TLS contexts into
generic TLS DNS (DoT) code.
2022-04-27 23:58:38 +03:00
Artem Boldariev
ff987957e7 Use isc_tlsctx_attach() in TLS stream code
This commit adds proper reference counting for TLS contexts into
generic TLS stream code.
2022-04-27 23:58:38 +03:00
Artem Boldariev
677819d22d Add isc_tlsctx_attach()
The implementation is done on top of the reference counting
functionality found in OpenSSL/LibreSSL, which allows for avoiding
wrapping the object.

Adding this function allows using reference counting for TLS contexts
in BIND 9's codebase.
2022-04-27 23:58:38 +03:00
Aram Sargsyan
4de1f65e4d Handle ISC_R_SUCCESS on a deactivated response in udp_recv()
There is a possibility for `udp_recv()` to be called with `eresult`
being `ISC_R_SUCCESS`, but nevertheless with already deactivated `resp`,
which can happen when the request has been canceled in the meantime.

(cherry picked from commit e3a88862c0)
2022-04-27 18:08:42 +00:00
Artem Boldariev
8b19f62ac5 TLSDNS: call send callbacks after only the data was sent
This commit ensures that write callbacks are getting called only after
the data has been sent via the network.

Without this fix, a situation could appear when a write callback could
get called before the actual encrypted data would have been sent to
the network. Instead, it would get called right after it would have
been passed to the OpenSSL (i.e. encrypted).

Most likely, the issue does not reveal itself often because the
callback call was asynchronous, so in most cases it should have been
called after the data has been sent, but that was not guaranteed by
the code logic.

Also, this commit removes one memory allocation (netievent) from a hot
path, as there is no need to call this callback asynchronously
anymore.
2022-04-27 17:57:11 +03:00
Ondřej Surý
192df8d2f1 The route socket and its storage was detached while still reading
The interfacemgr and the .route was being detached while the network
manager had pending read from the socket.  Instead of detaching from the
socket, we need to cancel the read which in turn will detach the route
socket and the associated interfacemgr.

(cherry picked from commit 9ae34a04e8)
2022-04-26 16:41:24 +02:00
Ondřej Surý
8beaee0b08 Remove task exclusive mode from ns_clientmgr
The .lock, .exiting and .excl members were not using for anything else
than starting task exclusive mode, setting .exiting to true and ending
exclusive mode.

Remove all the stray members and dead code eliminating the task
exclusive mode use from ns_clientmgr.

(cherry picked from commit 4f74e1010e)
2022-04-26 15:56:30 +02:00
Ondřej Surý
ce8ffdda69 Remove exclusive mode from ns_interfacemgr
Now that the dns_aclenv_t has now properly rwlocked .localhost and
.localnets member, we can remove the task exclusive mode use from the
ns_interfacemgr.  Some light related cleanup has been also done.

(cherry picked from commit c0995bc380)
2022-04-26 14:21:57 +02:00
Ondřej Surý
ab528a0fcb Add isc_rwlock around dns_aclenv .localhost and .localnets member
In order to modify the .localhost and .localnets members of the
dns_aclenv, all other processing on the netmgr loops needed to be
stopped using the task exclusive mode.  Add the isc_rwlock to the
dns_aclenv, so any modifications to the .localhost and .localnets can be
done under the write lock.

(cherry picked from commit 8138a595d9)
2022-04-26 14:21:57 +02:00
Ondřej Surý
2a648b9078 Abort when libuv at runtime mismatches libuv at compile time
When we compile with libuv that has some capabilities via flags passed
to f.e. uv_udp_listen() or uv_udp_bind(), the call with such flags would
fail with invalid arguments when older libuv version is linked at the
runtime that doesn't understand the flag that was available at the
compile time.

Enforce minimal libuv version when flags have been available at the
compile time, but are not available at the runtime.  This check is less
strict than enforcing the runtime libuv version to be same or higher
than compile time libuv version.
2022-04-26 12:11:51 +02:00
Ondřej Surý
7e72c55ff9 Run resume_dslookup() from the correct task
The rctx_chaseds() function calls dns_resolver_createfetch(), passing
fctx->task as the target task to run resume_dslookup() from.  This
breaks task-based serialization of events as fctx->task is the task that
the dns_resolver_createfetch() caller wants to receive its fetch
completion event in; meanwhile, intermediate fetches started by the
resolver itself (e.g. related to QNAME minimization) must use
res->buckets[bucketnum].task instead.  This discrepancy may cause
trouble if the resume_dslookup() callback happens to be run concurrently
with e.g. fctx_doshutdown().

Fix by passing the correct task to dns_resolver_createfetch() in
rctx_chaseds().

(cherry picked from commit 741a7096fc)
2022-04-22 15:57:22 +02:00
Michał Kępień
4ac4640c40 Fix loading plugins using just their filenames
BIND 9 plugins are installed using Automake's pkglib_LTLIBRARIES stanza,
which causes the relevant shared objects to be placed in the
$(libdir)/@PACKAGE@/ directory, where @PACKAGE@ is expanded to the
lowercase form of the first argument passed to AC_INIT(), i.e. "bind".
Meanwhile, NAMED_PLUGINDIR - the preprocessor macro that the
ns_plugin_expandpath() function uses for determining the absolute path
to a plugin for which only a filename has been provided (rather than a
path) - is set to $(libdir)/named.  This discrepancy breaks loading
plugins using just their filenames.  Fix the issue (and also prevent it
from reoccurring) by setting NAMED_PLUGINDIR to $(pkglibdir).

(cherry picked from commit 5065c4686e)
2022-04-22 13:29:10 +02:00
Michał Kępień
2da371d005 Prevent memory bloat caused by a jemalloc quirk
Since version 5.0.0, decay-based purging is the only available dirty
page cleanup mechanism in jemalloc.  It relies on so-called tickers,
which are simple data structures used for ensuring that certain actions
are taken "once every N times".  Ticker data (state) is stored in a
thread-specific data structure called tsd in jemalloc parlance.  Ticks
are triggered when extents are allocated and deallocated.  Once every
1000 ticks, jemalloc attempts to release some of the dirty pages hanging
around (if any).  This allows memory use to be kept in check over time.

This dirty page cleanup mechanism has a quirk.  If the first
allocator-related action for a given thread is a free(), a
minimally-initialized tsd is set up which does not include ticker data.
When that thread subsequently calls *alloc(), the tsd transitions to its
nominal state, but due to a certain flag being set during minimal tsd
initialization, ticker data remains unallocated.  This prevents
decay-based dirty page purging from working, effectively enabling memory
exhaustion over time. [1]

The quirk described above has been addressed (by moving ticker state to
a different structure) in jemalloc's development branch [2], but not in
any numbered jemalloc version released to date (the latest one being
5.2.1 as of this writing).

Work around the problem by ensuring that every thread spawned by
isc_thread_create() starts with a malloc() call.  Avoid immediately
calling free() for the dummy allocation to prevent an optimizing
compiler from stripping away the malloc() + free() pair altogether.

An alternative implementation of this workaround was considered that
used a pair of isc_mem_create() + isc_mem_destroy() calls instead of
malloc() + free(), enabling the change to be fully contained within
isc__trampoline_run() (i.e. to not touch struct isc__trampoline), as the
compiler is not allowed to strip away arbitrary function calls.
However, that solution was eventually dismissed as it triggered
ThreadSanitizer reports when tools like dig, nsupdate, or rndc exited
abruptly without waiting for all worker threads to finish their work.

[1] https://github.com/jemalloc/jemalloc/issues/2251
[2] c259323ab3

(cherry picked from commit 7aa7b6474b)
2022-04-21 14:22:13 +02:00
Mark Andrews
40bfb70d6a Update the rdataset->trust field in ncache.c:rdataset_settrust
Both the trust recorded in the slab stucture and the trust on
rdataset need to be updated.

(cherry picked from commit d043a41499)
2022-04-19 09:44:09 +10:00
Aram Sargsyan
546732546f Do not use REQUIRE in dns_catz_entry_detach() after other code
The REQUIRE checks should be at the top of the function before
any assignments or code.

Move the REQUIRE check to the top.

(cherry picked from commit 99d1ec6c4b)
2022-04-14 20:53:59 +00:00
Aram Sargsyan
5037aeb5d2 Replace CATZ_OPT_MASTERS with CATZ_OPT_PRIMARIES
Update the enum entry in the continued effort of replacing some
DNS terminology.

(cherry picked from commit 59c486391d)
2022-04-14 20:53:53 +00:00
Aram Sargsyan
c37a75df5d Implement catalog zones change of ownership (coo) support
Catalog zones change of ownership is special mechanism to facilitate
controlled migration of a member zone from one catalog to another.

It is implemented using catalog zones property named "coo" and is
documented in DNS catalog zones draft version 5 document.

Implement the feature using a new hash table in the catalog zone
structure, which holds the added "coo" properties for the catalog zone
(containing the target catalog zone's name), and the key for the hash
table being the member zone's name for which the "coo" property is being
created.

Change some log messages to have consistent zone name quoting types.

Update the ARM with change of ownership documentation and usage
examples.

Add tests which check newly the added features.

(cherry picked from commit bb837db4ee)
2022-04-14 20:53:31 +00:00
Aram Sargsyan
581d7bece0 Do not cancel processing record datasets in catalog zone after an error
When there are multiple record datasets in a database node of a catalog
zone, and BIND encounters a soft error during processing of a dataset,
it breaks from the loop and doesn't process the other datasets in the
node.

There are cases when this is not desired. For example, the catalog zones
draft version 5 states that there must be a TXT RRset named
`version.$CATZ` with exactly one RR, but it doesn't set a limitation
on possible non-TXT RRsets named `version.$CATZ` existing alongside
with the TXT one. In case when one exists, we will get a processing
error and will not continue the loop to process the TXT RRset coming
next.

Remove the "break" statement to continue processing all record datasets.

(cherry picked from commit 0b2d5490cd)
2022-04-14 19:51:45 +00:00
Aram Sargsyan
d8e1f51a04 Process the 'version' record of the catalog zone first
When processing a new or updated catalog zone, the record datasets
from the database are being processed in order. This creates a
problem because we need to know the version of the catalog zone
schema to process some of the records differently, but we do not
know the version until the 'version' record gets processed.

Find the 'version' record and process it first, only then iterate over
the database to process the rest, making sure not to process the
'version' record twice.

(cherry picked from commit 6035980bb1)
2022-04-14 19:51:37 +00:00
Aram Sargsyan
f75c39811d Implement catalog zones options new syntax based on custom properties
According to DNS catalog zones draft version 5 document, catalog
zone custom properties must be placed under the "ext" label.

Make necessary changes to support the new custom properties syntax in
catalog zones with version "2" of the schema.

Change the default catalog zones schema version from "1" to "2" in
ARM to prepare for the new features and changes which come starting
from this commit in order to support the latest DNS catalog zones draft
document.

Make some restructuring in ARM and rename the term catalog zone "option"
to "custom property" to better reflect the terms used in the draft.

Change the version of 'catalog1.zone.' catalog zone in the "catz" system
test to "2", and leave the version of 'catalog2.zone.' catalog zone at
version "1" to test both versions.

Add tests to check that the new syntax works only with the new schema
version, and that the old syntax works only with the legacy schema
version catalog zones.

(cherry picked from commit cedfebc64a)
2022-04-14 19:51:22 +00:00
Matthijs Mekking
c3ab09deb5 Update dns_dnssec_syncdelete() function
Update the function that synchronizes the CDS and CDNSKEY DELETE
records. It now allows for the possibility that the CDS DELETE record
is published and the CDNSKEY DELETE record is not, and vice versa.

Also update the code in zone.c how 'dns_dnssec_syncdelete()' is called.

With KASP, we still maintain the DELETE records our self. Otherwise,
we publish the CDS and CDNSKEY DELETE record only if they are added
to the zone. We do still check if these records can be signed by a KSK.

This change will allow users to add a CDS and/or CDNSKEY DELETE record
manually, without BIND removing them on the next zone sign.

Note that this commit removes the check whether the key is a KSK, this
check is redundant because this check is also made in
'dst_key_is_signing()' when the role is set to DST_BOOL_KSK.

(cherry picked from commit 3d05c99abb)
2022-04-13 14:43:40 +02:00
Tony Finch
4191fd01be Ensure that dns_request_createvia() has a retry limit
There are a couple of problems with dns_request_createvia(): a UDP
retry count of zero means unlimited retries (it should mean no
retries), and the overall request timeout is not enforced. The
combination of these bugs means that requests can be retried forever.

This change alters calls to dns_request_createvia() to avoid the
infinite retry bug by providing an explicit retry count. Previously,
the calls specified infinite retries and relied on the limit implied
by the overall request timeout and the UDP timeout (which did not work
because the overall timeout is not enforced). The `udpretries`
argument is also changed to be the number of retries; previously, zero
was interpreted as infinity because of an underflow to UINT_MAX, which
appeared to be a mistake. And `mdig` is updated to match the change in
retry accounting.

The bug could be triggered by zone maintenance queries, including
NOTIFY messages, DS parental checks, refresh SOA queries and stub zone
nameserver lookups. It could also occur with `nsupdate -r 0`.
(But `mdig` had its own code to avoid the bug.)

(cherry picked from commit 71ce8b0a51)
2022-04-06 18:17:55 +01:00
Ondřej Surý
a1f3ff0dd1 Rename the configuration option to load balance sockets to reuseport
After some back and forth, it was decidede to match the configuration
option with unbound ("so-reuseport"), PowerDNS ("reuseport") and/or
nginx ("reuseport").

(cherry picked from commit 7e71c4d0cc)
2022-04-06 17:24:13 +02:00
Ondřej Surý
25bc461446 Revert "General cleanup of dns_rpz implementation"
This reverts commit bfee462403.
2022-04-06 10:42:29 +02:00
Ondřej Surý
242500909a Revert "Refactor the dns_rpz_add/delete to use local rpz copy"
This reverts commit f4cba0784e.
2022-04-06 10:42:22 +02:00
Ondřej Surý
9b78612e7d Revert "Run the RPZ update as offloaded work"
This reverts commit e128b6a951.
2022-04-06 10:30:06 +02:00
Ondřej Surý
df91d61dc7 Rename shutdown() to test_shutdown() in timer_test.c
The shutdown() is part of standard library (POSIX-1), don't use such
name in the timer_test.c, but rather rename it to test_shutdown().

(cherry picked from commit 7868d8145b)
2022-04-05 01:56:09 +02:00
Ondřej Surý
cd24556e14 Enable the load-balance-sockets configuration
Previously, HAVE_SO_REUSEPORT_LB has been defined only in the private
netmgr-int.h header file, making the configuration of load balanced
sockets inoperable.

Move the missing HAVE_SO_REUSEPORT_LB define the isc/netmgr.h and add
missing isc_nm_getloadbalancesockets() implementation.

(cherry picked from commit 142c63dda8)
2022-04-05 01:38:49 +02:00
Ondřej Surý
64265f1c0e Add option to configure load balance sockets
Previously, the option to enable kernel load balancing of the sockets
was always enabled when supported by the operating system (SO_REUSEPORT
on Linux and SO_REUSEPORT_LB on FreeBSD).

It was reported that in scenarios where the networking threads are also
responsible for processing long-running tasks (like RPZ processing, CATZ
processing or large zone transfers), this could lead to intermitten
brownouts for some clients, because the thread assigned by the operating
system might be busy.  In such scenarious, the overall performance would
be better served by threads competing over the sockets because the idle
threads can pick up the incoming traffic.

Add new configuration option (`load-balance-sockets`) to allow enabling
or disabling the load balancing of the sockets.

(cherry picked from commit 85c6e797aa)
2022-04-04 23:59:59 +02:00
Ondřej Surý
e128b6a951 Run the RPZ update as offloaded work
Previously, the RPZ updates ran quantized on the main nm_worker loops.
As the quantum was set to 1024, this might lead to service
interruptions when large RPZ update was processed.

Change the RPZ update process to run as the offloaded work.  The update
and cleanup loops were refactored to do as little locking of the
maintenance lock as possible for the shortest periods of time and the db
iterator is being paused for every iteration, so we don't hold the rbtdb
tree lock for prolonged periods of time.

(cherry picked from commit f106d0ed2b)
2022-04-04 22:59:59 +02:00
Ondřej Surý
f4cba0784e Refactor the dns_rpz_add/delete to use local rpz copy
Previously dns_rpz_add() were passed dns_rpz_zones_t and index to .zones
array.  Because we actually attach to dns_rpz_zone_t, we should be using
the local pointer instead of passing the index and "finding" the
dns_rpz_zone_t again.

Additionally, dns_rpz_add() and dns_rpz_delete() were used only inside
rpz.c, so make them static.

(cherry picked from commit b6e885c97f)
2022-04-04 22:59:59 +02:00
Ondřej Surý
bfee462403 General cleanup of dns_rpz implementation
Do a general cleanup of lib/dns/rpz.c style:

 * Removed deprecated and unused functions
 * Unified dns_rpz_zone_t naming to rpz
 * Unified dns_rpz_zones_t naming to rpzs
 * Add and use rpz_attach() and rpz_attach_rpzs() functions
 * Shuffled variables to be more local (cppcheck cleanup)

(cherry picked from commit 840179a247)
2022-04-04 22:59:59 +02:00
Mark Andrews
1eff9bbf37 Prevent arithmetic overflow of 'i' in master.c:generate
the value of 'i' in generate could overflow when adding 'step' to
it in the 'for' loop.  Use an unsigned int for 'i' which will give
an additional bit and prevent the overflow.  The inputs are both
less than 2^31 and and the result will be less than 2^32-1.

(cherry picked from commit 5abdee9004)
2022-04-01 21:42:35 +11:00
Tony Finch
ec54de9e47 Log "not authoritative for update zone" more clearly
Ensure the update zone name is mentioned in the NOTAUTH error message
in the server log, so that it is easier to track down problematic
update clients. There are two cases: either the update zone is
unrelated to any of the server's zones (previously no zone was
mentioned); or the update zone is a subdomain of one or more of the
server's zones (previously the name of the irrelevant parent zone was
misleadingly logged).

Closes #3209

(cherry picked from commit 84c4eb02e7)
2022-03-30 13:19:46 +01:00
Ondřej Surý
fc500b96eb Consistenly use UNREACHABLE() instead of ISC_UNREACHABLE()
In couple places, we have missed INSIST(0) or ISC_UNREACHABLE()
replacement on some branches with UNREACHABLE().  Replace all
ISC_UNREACHABLE() or INSIST(0) calls with UNREACHABLE().
2022-03-28 23:27:33 +02:00
Aram Sargsyan
a20acee77d Use 'bname' in dns_catz_update_from_db() only when it is ready
There is a possible code path of using the uninitialized `bname`
character array while logging an error message.

Initialize the `bname` buffer earlier in the function.

Also, change the initialization routine to use a helper function.

(cherry picked from commit a5a6362e92)
2022-03-28 10:26:58 +00:00
Aram Sargsyan
68bbce9813 Put some missing dns_rdata_freestruct() calls in catz.c
A successful call to `dns_rdata_tostruct()` expects an accompanying
call to `dns_rdata_freestruct()` to free up any memory that could have
been allocated during the first call.

In catz.c there are several places where `dns_rdata_freestruct()` call
is skipped.

Add the missing cleanup routines.

(cherry picked from commit f57c51fe05)
2022-03-28 10:26:49 +00:00
Tony Finch
775281d14a More explicit dns64 prefix errors
Quote the dns64 prefix in error messages that complain about
problems with it, to avoid confusion with the following ACLs.

Closes #3210

(cherry picked from commit 496c02d32a)
2022-03-25 16:08:22 +01:00
Ondřej Surý
d3727a52d7 Remove UNREACHABLE() statements after exit()
Couple of UNREACHABLE() statements following exit() were found and
removed.

(cherry picked from commit 81fdc4a822)
2022-03-25 10:07:25 +01:00
Ondřej Surý
cd13d24ba2 Remove workaround for ancient clang versions (<< 3.2 and << 4.0.1)
Some ancient versions of clang reported uninitialized memory use false
positive (see https://bugs.llvm.org/show_bug.cgi?id=14461).  Since clang
4.0.1 has been long obsoleted, just remove the workarounds.

(cherry picked from commit ae508c17bc)
2022-03-25 08:42:19 +01:00
Ondřej Surý
5e19bbb48a Remove use of the inline keyword used as suggestion to compiler
Historically, the inline keyword was a strong suggestion to the compiler
that it should inline the function marked inline.  As compilers became
better at optimising, this functionality has receded, and using inline
as a suggestion to inline a function is obsolete.  The compiler will
happily ignore it and inline something else entirely if it finds that's
a better optimisation.

Therefore, remove all the occurences of the inline keyword with static
functions inside single compilation unit and leave the decision whether
to inline a function or not entirely on the compiler

NOTE: We keep the usage the inline keyword when the purpose is to change
the linkage behaviour.

(cherry picked from commit 20f0936cf2)
2022-03-25 08:42:18 +01:00
Ondřej Surý
07022525ff Replace ISC_NORETURN with C11's noreturn
C11 has builtin support for _Noreturn function specifier with
convenience noreturn macro defined in <stdnoreturn.h> header.

Replace ISC_NORETURN macro by C11 noreturn with fallback to
__attribute__((noreturn)) if the C11 support is not complete.

(cherry picked from commit 04d0b70ba2)
2022-03-25 08:42:18 +01:00
Ondřej Surý
128c550a95 Simplify way we tag unreachable code with only ISC_UNREACHABLE()
Previously, the unreachable code paths would have to be tagged with:

    INSIST(0);
    ISC_UNREACHABLE();

There was also older parts of the code that used comment annotation:

    /* NOTREACHED */

Unify the handling of unreachable code paths to just use:

    UNREACHABLE();

The UNREACHABLE() macro now asserts when reached and also uses
__builtin_unreachable(); when such builtin is available in the compiler.

(cherry picked from commit 584f0d7a7e)
2022-03-25 08:42:16 +01:00
Ondřej Surý
c62a94363d Add FALLTHROUGH macro for __attribute__((fallthrough))
Gcc 7+ and Clang 10+ have implemented __attribute__((fallthrough)) which
is explicit version of the /* FALLTHROUGH */ comment we are currently
using.

Add and apply FALLTHROUGH macro that uses the attribute if available,
but does nothing on older compilers.

In one case (lib/dns/zone.c), using the macro revealed that we were
using the /* FALLTHROUGH */ comment in wrong place, remove that comment.

(cherry picked from commit fe7ce629f4)
2022-03-25 08:41:09 +01:00
Tony Finch
ac5d61541d Remove a redundant variable-length array
In the GSS-TSIG verification code there was an alarming
variable-length array whose size came off the network, from the
signature in the request. It turned out to be safe, because the caller
had previously checked that the signature had a reasonable size.
However, the safety checks are in the generic TSIG implementation, and
the risky VLA usage was in the GSS-specific code, and they are
separated by the DST indirection layer, so it wasn't immediately
obvious that the risky VLA was in fact safe.

In fact this risky VLA was completely unnecessary, because the GSS
signature can be verified in place without being copied to the stack,
like the message covered by the signature. The `REGION_TO_GBUFFER()`
macro backwardly assigns the region in its left argument to the GSS
buffer in its right argument; this is just a pointer and length
conversion, without copying any data. The `gss_verify_mic()` call uses
both message and signature GSS buffers in a read-only manner.

(cherry picked from commit eeead1cfe7)
2022-03-18 16:05:56 +00:00
Ondřej Surý
5e253fd629 Change xfer-out timer message log level to DEBUG(1)
When max-transfer-*-out timeouts were reintroduced, the log message
about starting the timer was errorneously left as ISC_LOG_ERROR.
Change the log level of said message to ISC_LOG_DEBUG(1).

(cherry picked from commit 8f6e4dfa15)
2022-03-17 21:38:24 +01:00
Ondřej Surý
485a2b329e Add couple missing braces around single-line statements
The clang-format-15 has new option InsertBraces that could add missing
branches around single line statements.  Use that to our advantage
without switching to not-yet-released LLVM version to add missing braces
in couple of places.
2022-03-17 18:29:57 +01:00
Aram Sargsyan
09ec28dc9e Check if the fetch is shutting down in resume_dslookup()
The fetch can be in the shutting down state when resume_dslookup() is
trying to operate on it.

This is also a security issue, because a malicious actor can set up a
name server which delays certain queries in such a way that the fetch
will time out and shut down, which will cause named to crash.

Add a check to see if the fetch has the shutting down attribute set,
and cancel any further operations on it in such case.

A similar bug had been fixed earlier for the resume_qmin() function,
see [GL #966].
2022-03-16 23:18:18 +01:00
Mark Andrews
bc8f721d59 Skip calling find_coveringnsec if we found a DNAME
This is an optimisation as we can skip a lot of pointless work when we
know there is a DNAME there.

When we have a partial match and a DNAME above the QNAME, the closest
encloser has the same owner as the DNAME, will have the DNAME bit set
in the type map, and we wouldn't use it as we would return the
DNAME + RRSIG(DNAME) instead.

So there is no point in looking for it nor in attempting to check that
it is valid for the QNAME.
2022-03-16 23:18:18 +01:00
Mark Andrews
1d32fbcd34 Only update foundname if returning DNS_R_COVERINGNSEC
'setup_delegation' depends on 'foundname' being the value returned
by 'dns_rbt_findnode' in the cache and 'find_coveringnsec' was
modifying 'foundname' when a covering NSEC was not found.
2022-03-16 23:18:18 +01:00
Ondřej Surý
6ec223a539 Run .closehandle_cb asynchrounosly in nmhandle_detach_cb()
When sock->closehandle_cb is set, we need to run nmhandle_detach_cb()
asynchronously to ensure correct order of multiple packets processing in
the isc__nm_process_sock_buffer().  When not run asynchronously, it
would cause:

  a) out-of-order processing of the return codes from processbuffer();

  b) stack growth because the next TCP DNS message read callback will
     be called from within the current TCP DNS message read callback.

The sock->closehandle_cb is set to isc__nm_resume_processing() for TCP
sockets which calls isc__nm_process_sock_buffer().  If the read callback
(called from isc__nm_process_sock_buffer()->processbuffer()) doesn't
attach to the nmhandle (f.e. because it wants to drop the processing or
we send the response directly via uv_try_write()), the
isc__nm_resume_processing() (via .closehandle_cb) would call
isc__nm_process_sock_buffer() recursively.

The below shortened code path shows how the stack can grow:

 1: ns__client_request(handle, ...);
 2: isc_nm_tcpdns_sequential(handle);
 3: ns_query_start(client, handle);
 4:   query_lookup(qctx);
 5:     query_send(qctcx->client);
 6:       isc__nmhandle_detach(&client->reqhandle);
 7:         nmhandle_detach_cb(&handle);
 8:           sock->closehandle_cb(sock); // isc__nm_resume_processing
 9:             isc__nm_process_sock_buffer(sock);
10:               processbuffer(sock); // isc__nm_tcpdns_processbuffer
11:                 isc_nmhandle_attach(req->handle, &handle);
12:                 isc__nm_readcb(sock, req, ISC_R_SUCCESS);
13:                   isc__nm_async_readcb(NULL, ...);
14:                     uvreq->cb.recv(...); // ns__client_request

Instead, if 'sock->closehandle_cb' is set, we need to run detach the
handle asynchroniously in 'isc__nmhandle_detach', so that on line 8 in
the code flow above does not start this recursion. This ensures the
correct order when processing multiple packets in the function
'isc__nm_process_sock_buffer()' and prevents the stack growth.

When not run asynchronously, the out-of-order processing leaves the
first TCP socket open until all requests on the stream have been
processed.

If the pipelining is disabled on the TCP via `keep-response-order`
configuration option, named would keep the first socket in lingering
CLOSE_WAIT state when the client sends an incomplete packet and then
closes the connection from the client side.
2022-03-16 23:18:18 +01:00
Mark Andrews
4baf22c0f0 Look for zones deeper than the current domain or forward name
When caching glue, we need to ensure that there is no closer
source of truth for the name. If the owner name for the glue
record would be answered by a locally configured zone, do not
cache.
2022-03-16 23:18:18 +01:00
Mark Andrews
0347eed567 Check cached names for possible "forward only" clause
When caching additional and glue data *not* from a forwarder, we must
check that there is no "forward only" clause covering the owner name
that would take precedence.  Such names would normally be allowed by
baliwick rules, but a "forward only" zone introduces a new baliwick
scope.
2022-03-16 23:18:18 +01:00
Mark Andrews
67179e8973 Check that the forward declaration is unchanged and not overridden
If we are using a fowarder, in addition to checking that names to
be cached are subdomains of the forwarded namespace, we must also
check that there are no subsidiary forwarded namespaces which would
take precedence. To be safe, we don't cache any responses if the
forwarding configuration has changed since the query was sent.
2022-03-16 23:18:18 +01:00
Mark Andrews
f7cb79b66a Add additional name checks when using a forwarder
When using a forwarder, check that the owner name of response
records are within the bailiwick of the forwarded name space.
2022-03-16 23:18:18 +01:00
Ondřej Surý
6fbf582f18 Cleanup the nmhandle attach/detach in httpd.c
In httpd.c, the send callback can directly call read callback without
calling isc_nm_resumeread().  When per-send timeout was added, this
could lead to use-after-free when shutting down the named.

Cleanup the way how we attach to .readhandle and .sendhandle, so there's
assurance that .readhandle will be always non-NULL when reading and
.sendhandle will be always non-NULL when sending.

Additionally, it was found that the implementation ignored the
"Connection: close" header and it worked only accidentally by closing
the connection after the first read from the TCP socket.  This has been
also fixed.

(cherry picked from commit 49c804f8b7)
2022-03-11 10:52:22 +01:00
Ondřej Surý
fd351a60ff On shutdown, reset the established TCP connections
Previously, the established TCP connections (both client and server)
would be gracefully closed waiting for the write timeout.

Don't wait for TCP connections to gracefully shutdown, but directly
reset them for faster shutdown.

(cherry picked from commit 6ddac2d56d)
2022-03-11 10:52:22 +01:00
Ondřej Surý
27e47c5101 Change single write timer to per-send timers
Previously, there was a single per-socket write timer that would get
restarted for every new write.  This turned out to be insufficient
because the other side could keep reseting the timer, and never reading
back the responses.

Change the single write timer to per-send timer which would in turn
reset the TCP connection on the first send timeout.

(cherry picked from commit a761aa59e3)
2022-03-11 10:52:22 +01:00
Ondřej Surý
913e64e8e1 Remove usage of deprecated ATOMIC_VAR_INIT() macro
The C17 standard deprecated ATOMIC_VAR_INIT() macro (see [1]).  Follow
the suite and remove the ATOMIC_VAR_INIT() usage in favor of simple
assignment of the value as this is what all supported stdatomic.h
implementations do anyway:

  * MacOSX.plaform: #define ATOMIC_VAR_INIT(__v) {__v}
  * Gcc stdatomic.h: #define ATOMIC_VAR_INIT(VALUE)	(VALUE)

1. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1138r0.pdf

(cherry picked from commit f251d69eba)
2022-03-09 09:25:37 +01:00
Ondřej Surý
ebe2baec95 Make dns_catz_get_iterator() return void
Previously, the function(s) in the commit subject could fail for various
reasons - mostly allocation failures, or other functions returning
different return code than ISC_R_SUCCESS.  Now, the aforementioned
function(s) cannot ever fail and they would always return ISC_R_SUCCESS.

Change the function(s) to return void and remove the extra checks in
the code that uses them.

(cherry picked from commit d128656d2e)
2022-03-08 20:47:06 +01:00
Ondřej Surý
ce9908cb4e Make isc_ht_init() and isc_ht_iter_create() return void
Previously, the function(s) in the commit subject could fail for various
reasons - mostly allocation failures, or other functions returning
different return code than ISC_R_SUCCESS.  Now, the aforementioned
function(s) cannot ever fail and they would always return ISC_R_SUCCESS.

Change the function(s) to return void and remove the extra checks in
the code that uses them.

(cherry picked from commit 8fa27365ec)
2022-03-08 20:47:06 +01:00
Ondřej Surý
b3d0c95e68 Make isc_heap_create() and isc_heap_insert() return void
Previously, the function(s) in the commit subject could fail for various
reasons - mostly allocation failures, or other functions returning
different return code than ISC_R_SUCCESS.  Now, the aforementioned
function(s) cannot ever fail and they would always return ISC_R_SUCCESS.

Change the function(s) to return void and remove the extra checks in
the code that uses them.

(cherry picked from commit bbb4cdb92d)
2022-03-08 20:24:54 +01:00
Ondřej Surý
445ce0c165 Set TCP maximum segment size to minimum size of 1220
Previously the socket code would set the TCPv6 maximum segment size to
minimum value to prevent IP fragmentation for TCP.  This was not yet
implemented for the network manager.

Implement network manager functions to set and use minimum MTU socket
option and set the TCP_MAXSEG socket option for both IPv4 and IPv6 and
use those to clamp the TCP maximum segment size for TCP, TCPDNS and
TLSDNS layers in the network manager to 1220 bytes, that is 1280 (IPv6
minimum link MTU) minus 40 (IPv6 fixed header) minus 20 (TCP fixed
header)

We already rely on a similar value for UDP to prevent IP fragmentation
and it make sense to use the same value for IPv4 and IPv6 because the
modern networks are required to support IPv6 packet sizes.  If there's
need for small TCP segment values, the MTU on the interfaces needs to be
properly configured.

(cherry picked from commit 8098a58581)
2022-03-08 11:12:43 +01:00
Ondřej Surý
2a31f19817 Set minimum MTU (1280) on IPv6 sockets
The IPV6_USE_MIN_MTU socket option directs the IP layer to limit the
IPv6 packet size to the minimum required supported MTU from the base
IPv6 specification, i.e. 1280 bytes.  Many implementations of TCP
running over IPv6 neglect to check the IPV6_USE_MIN_MTU value when
performing MSS negotiation and when constructing a TCP segment despite
MSS being defined to be the MTU less the IP and TCP header sizes (60
bytes for IPv6).  This leads to oversized IPv6 packets being sent
resulting in unintended Path Maximum Transport Unit Discovery (PMTUD)
being performed and to fragmented IPv6 packets being sent.

Add and use a function to set socket option to limit the MTU on IPv6
sockets to the minimum MTU (1280) both for UDP and TCP.

(cherry picked from commit 5d34a14f22)
2022-03-08 11:12:43 +01:00
Aram Sargsyan
8f2e287a56 Fix a function cleanup bug in dns_request_createraw
When get_dispatch() returns an error code, the dns_request_createraw()
function jumps to the `cleanup` label, which will leave a previous
attachment to the `request` pointer unattached.

Fix the issue by jumping to the `detach` label instead.

(cherry picked from commit 963f6a2203)
2022-03-08 10:00:00 +01:00
Ondřej Surý
d9e54ad0dd Merge lib/dns/gen.h contents to lib/dns/gen.c
Formerly, the gen.h header contained a compatibility layer between Win32
and POSIX platforms.  Since we have already dropped the Win32 build, we
can merged gen.h into gen.c as the header file is not used elsewhere.

(cherry picked from commit f24b26188d)
2022-03-08 09:55:28 +01:00
Ondřej Surý
555bdb9f82 Replace netievent lock-free queue with simple locked queue
The current implementation of isc_queue uses Michael-Scott lock-free
queue that in turn uses hazard pointers.  It was discovered that the way
we use the isc_queue, such complicated mechanism isn't really needed,
because most of the time, we either execute the work directly when on
nmthread (in case of UDP) or schedule the work from the matching
nmthreads.

Replace the current implementation of the isc_queue with a simple locked
ISC_LIST.  There's a slight improvement - since copying the whole list
is very lightweight - we move the queue into a new list before we start
the processing and locking just for moving the queue and not for every
single item on the list.

NOTE: There's a room for future improvements - since we don't guarantee
the order in which the netievents are processed, we could have two lists
- one unlocked that would be used when scheduling the work from the
matching thread and one locked that would be used from non-matching
thread.

(cherry picked from commit 6bd025942c)
2022-03-08 09:52:39 +01:00
Ondřej Surý
c6f8e68dd8 Add attach/detach for the dns_dispatch_send()
The order in which the netievents are processed on the network manager
loop is not guaranteed.  Therefore the recv/read callback can come
earlier than the send/write callback.

The dns_request API wasn't ready for this reordering and it was
destroying the dns_request_t object before the send callback has been
called.

Add additional attach/detach in the req_send()/req_senddone() functions
to make sure we don't destroy the dns_request_t while it's still being
references by asynchronous call.

(cherry picked from commit f3ca90a804)
2022-03-08 09:50:13 +01:00
Mark Andrews
d752bbfb22 Check dnssec-policy key roles for validity
For each algorithm there must be a key performing the KSK and
ZSK rolls.  After reading the keys from named.conf check that
each algorithm present has both rolls.  CSK implicitly has both
rolls.

(cherry picked from commit 9bcf45f4ce)
2022-03-08 14:28:53 +11:00
Aram Sargsyan
8336e9b90d Use autoconf check for BN_GENCB_new()
BIND unconditionally uses shims for BN_GENCB_new(), BN_GENCB_free(),
and BN_GENCB_get_arg() for all LibreSSL versions and, correctly, for
OpenSSL <1.1.0 versions.

This breaks LibreSSL compilation starting with LibreSSL 3.5.0.

Use autoconf check instead to check whether the family of the functions
are available.

(cherry picked from commit 749973f3259b7638a6af02b7da2f40ae28bdd402)
2022-03-02 10:49:47 +00:00
Aram Sargsyan
b7e84e8a26 Remove EVP_CIPHER_CTX_new() and EVP_CIPHER_CTX_free() shims
LibreSSL 3.5.0 fails to compile with these shims. We could have just
removed the LibreSSL check from the pre-processor condition, but it
seems that these shims are no longer needed because all the supported
versions of OpenSSL and LibreSSL have those functions.

According to EVP_ENCRYPTINIT(3) manual page in LibreSSL,
EVP_CIPHER_CTX_new() and EVP_CIPHER_CTX_free() first appeared in
OpenSSL 0.9.8b, and have been available since OpenBSD 4.5.

(cherry picked from commit a3789053682b57a2031de8c544134f1923e76cf3)
2022-03-02 10:49:47 +00:00
Evan Hunt
0a8dece1be document zone grammar more correctly
the "zone" clause can be documented using, for instance,
`cfg_test --zonegrammar primary", which prints only
options that are valid in primary zones. this was not
the method being used when generating the named.conf
man page; instead, "zone" was documented with all possible
options, and no zone types at all.

this commit removes "zone" from the generic documentation
and adds include statements in named.conf.rst so that
correct zone grammars will be included in the man page.

(cherry picked from commit 4ca74eee49)
2022-03-02 01:58:18 -08:00
Mark Andrews
651ef3ebb8 Grow the lex token buffer in one more place
when parsing key pairs, if the '=' character fell at max_token
a protective INSIST preventing buffer overrun could be triggered.
Attempt to grow the buffer immediately before the INSIST.

Also removed an unnecessary INSIST on the opening double quote
of key buffer pair.

(cherry picked from commit 4c356d2770)
2022-03-02 01:05:14 +00:00
Mark Andrews
c5519265df Use unsigned arithmetic when shifting by 24
By default C promotes short unsigned values to signed int which
leads to undefined behaviour when the value is shifted by too much.
Force unsigned arithmetic to be perform by explicitly casting to a
unsigned type.

(cherry picked from commit b8b99603f1)
2022-03-02 11:06:39 +11:00
Ondřej Surý
806848a440 Handle TCP sockets in isc__nmsocket_reset()
The isc__nmsocket_reset() was missing a case for raw TCP sockets (used
by RNDC and DoH) which would case a assertion failure when write timeout
would be triggered.

TCP sockets are now also properly handled in isc__nmsocket_reset().

(cherry picked from commit b220fb32bd)
2022-02-28 11:17:41 +01:00
Evan Hunt
7b604df69d remove old zone type documentation
we now document zone type as either "primary" or "secondary",
omitting the old terms (though they are still accepted).

(cherry picked from commit 0bde07261b)
2022-02-25 16:51:44 -08:00
Evan Hunt
87be8fea0d add a CFG_CLAUSEFLAG_NODOC flag for use with outdated terms
"masters" and "default-masters" are now flagged so they will
not be included in the named.conf man page, despite being
accepted as valid options by the parser for backward
compatibiility.

(cherry picked from commit 0e57fc160e)
2022-02-25 16:51:44 -08:00
Ondřej Surý
408b79ba24 Disable inactive uvreqs caching when compiled with sanitizers
When isc__nm_uvreq_t gets deactivated, it could be just put onto array
stack to be reused later to save some initialization time.
Unfortunately, this might hide some use-after-free errors.

Disable the inactive uvreqs caching when compiled with Address or
Thread Sanitizer.

(cherry picked from commit be339b3c83)
2022-02-24 00:16:25 +01:00
Ondřej Surý
dad941a288 Disable inactive handles caching when compiled with sanitizers
When isc_nmhandle_t gets deactivated, it could be just put onto array
stack to be reused later to safe some initialization time.
Unfortunately, this might hide some use-after-free errors.

Disable the inactive handles caching when compiled with Address or
Thread Sanitizer.

(cherry picked from commit 92cce1da65)
2022-02-24 00:11:03 +01:00
Ondřej Surý
51040c2806 Remove active handles tracking from isc__nmsocket_t
The isc__nmsocket_t has locked array of isc_nmhandle_t that's not used
for anything.  The isc__nmhandle_get() adds the isc_nmhandle_t to the
locked array (and resized if necessary) and removed when
isc_nmhandle_put() finally destroys the handle.  That's all it does, so
it serves no useful purpose.

Remove the .ah_handles, .ah_size, and .ah_frees members of the
isc__nmsocket_t and .ah_pos member of the isc_nmhandle_t struct.

(cherry picked from commit e2555a306f)
2022-02-23 23:49:13 +01:00
Ondřej Surý
afe8a60f98 Delay isc__nm_uvreq_t deallocation to connection callback
When the TCP, TCPDNS or TLSDNS connection times out, the isc__nm_uvreq_t
would be pushed into sock->inactivereqs before the uv_tcp_connect()
callback finishes.  Because the isc__nmsocket_t keeps the list of
inactive isc__nm_uvreq_t, this would cause use-after-free only when the
sock->inactivereqs is full (which could never happen because the failure
happens in connection timeout callback) or when the sock->inactivereqs
mechanism is completely removed (f.e. when running under Address or
Thread Sanitizer).

Delay isc__nm_uvreq_t deallocation to the connection callback and only
signal the connection callback should be called by shutting down the
libuv socket from the connection timeout callback.

(cherry picked from commit 3268627916)
2022-02-23 23:31:18 +01:00
Ondřej Surý
74948421a6 Properly free up enqueued netievents in nm_destroy()
When the isc_netmgr is being destroyed, the normal and priority queues
should be dequeued and netievents properly freed.  This wasn't the case.

(cherry picked from commit 88418c3372)
2022-02-23 22:53:31 +01:00
Michał Kępień
5929411f90 Fix more ns_statscounter_recursclients underflows
Commit aab691d512 did not fix all possible
scenarios in which the ns_statscounter_recursclients counter underflows.
The solution implemented therein can be ineffective e.g. when CNAME
chaining happens with prefetching enabled.

Here is an example recursive resolution scenario in which the
ns_statscounter_recursclients counter can underflow with the current
logic in effect:

 1. Query processing starts, the answer is not found in the cache, so
    recursion is started.  The NS_CLIENTATTR_RECURSING attribute is set.
    ns_statscounter_recursclients is incremented (Δ = +1).

 2. Recursion completes, returning a CNAME.  client->recursionquota is
    non-NULL, so the NS_CLIENTATTR_RECURSING attribute remains set.
    ns_statscounter_recursclients is decremented (Δ = 0).

 3. Query processing restarts.

 4. The current QNAME (the target of the CNAME from step 2) is found in
    the cache, with a TTL low enough to trigger a prefetch.

 5. query_prefetch() attaches to client->recursionquota.
    ns_statscounter_recursclients is not incremented because
    query_prefetch() does not do that (Δ = 0).

 6. Query processing restarts.

 7. The current QNAME (the target of the CNAME from step 4) is not found
    in the cache, so recursion is started.  client->recursionquota is
    already attached to (since step 5) and the NS_CLIENTATTR_RECURSING
    attribute is set (since step 1), so ns_statscounter_recursclients is
    not incremented (Δ = 0).

 8. The prefetch from step 5 completes.  client->recursionquota is
    detached from in prefetch_done().  ns_statscounter_recursclients is
    not decremented because prefetch_done() does not do that (Δ = 0).

 9. Recursion for the current QNAME completes.  client->recursionquota
    is already detached from, i.e. set to NULL (since step 8), and the
    NS_CLIENTATTR_RECURSING attribute is set (since step 1), so
    ns_statscounter_recursclients is decremented (Δ = -1).

Another possible scenario is that after step 7, recursion for the target
of the CNAME from step 4 completes before the prefetch for the CNAME
itself.  fetch_callback() then notices that client->recursionquota is
non-NULL and decrements ns_statscounter_recursclients, even though
client->recursionquota was attached to by query_prefetch() and therefore
not accompanied by an incrementation of ns_statscounter_recursclients.
The net result is also an underflow.

Instead of trying to properly handle all possible orderings of events
set into motion by normal recursion and prefetch-triggered recursion,
adjust ns_statscounter_recursclients whenever the recursive clients
quota is successfully attached to or detached from.  Remove the
NS_CLIENTATTR_RECURSING attribute altogether as its only purpose is made
obsolete by this change.

(cherry picked from commit f7482b68b9)
2022-02-23 14:43:09 +01:00