Commit graph

356 commits

Author SHA1 Message Date
Ondřej Surý
5588f37baa Include disptype and transport in dispatch hash key
Move disptype and transport into dispatch_hash() and dispatch_match()
so that the match function is the single source of truth for whether
two TCP dispatches are interchangeable.  This replaces the post-loop
disptype filter in dispatch_gettcp() and makes the disptype field in
struct dispatch_key actually used.

(cherry picked from commit 4654796683)
2026-05-06 15:05:48 +02:00
Ondřej Surý
abe201bac3 Do not reuse shared TCP dispatches for zone transfers
Zone transfers (XFRIN) need a dedicated TCP connection because they
are long-lived and stream the entire zone.

(cherry picked from commit 6e78094ebd)
2026-05-06 15:05:48 +02:00
Ondřej Surý
4f54328eaf Use sequential per-dispatch message IDs for TCP
TCP dispentries no longer use the global QID hash table at all.
Responses are matched by scanning disp->active, and sequential
per-dispatch IDs (bounded by the pipelining limit) are unique
within a single dispatch by construction.  Since TCP delivers
only data we asked for on a specific connection, the per-peer
uniqueness that the global table enforced was never actually
needed for TCP.

DNS_DISPATCHOPT_FIXEDID is plumbed through dns_request_createraw
-> get_dispatch -> dns_dispatch_createtcp so FIXEDID TCP requests
always get a fresh isolated dispatch — the caller-supplied ID
then cannot collide with any other in-flight query either.

(cherry picked from commit 3e364aec2b)
2026-05-06 15:05:48 +02:00
Ondřej Surý
a3ba799eae Limit TCP pipelining per shared dispatch
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'.

(cherry picked from commit 385ceabe8f)
2026-05-06 15:05:48 +02:00
Ondřej Surý
d35bc843c5 Implement seamless TCP connection reuse in dns_dispatch
Previously, the user of dns_dispatch API had to first call
dns_dispatch_gettcp() and if that failed create a new TCP dispatch with
dns_dispatch_createtcp().  This has been changed and the TCP connection
reuse happens transparently inside dns_dispatch_createtcp().  There are
separate buckets for dns_resolver, dns_request and dns_xfrin units, so
these don't get mixed together.

(cherry picked from commit d5ee86b799)
2026-05-06 15:05:48 +02:00
Ondřej Surý
d55d914cb7
Fix copy-paste typos in dns_dispatchmgr comments
The v6ports and nv6ports fields are documented as "available ports
for IPv4" instead of "IPv6".

(cherry picked from commit 0d28e1bed2)
2026-03-15 03:13:00 +01:00
Ondřej Surý
ae81ce723d
Rename isc_net_getudpportrange() to isc_net_getportrange()
This better reflects the true nature of the function as we are reading
the ephemeral port range which is not related to UDP at all.

(cherry picked from commit 295139f8ca)
2026-02-20 17:02:45 +01:00
Mark Andrews
53738b0e5e Use clang-format-20 to update formatting
(cherry picked from commit 422b9118e8)
2025-06-25 13:32:08 +10:00
Artem Boldariev
8459d99ec2 Dispatch: carefully check if the server name for SNI is a hostname
Previously the code would not check if the string intended to be used
for SNI is a hostname.

(cherry picked from commit 2592e309c7)
2025-03-31 15:07:55 +03:00
Artem Boldariev
50876e52f1 BIND - enable TLS SNI support for outgoing TLS connections
This commit ensures that BIND enables TLS SNI support for outgoing DoT
connections (when possible) in order to improve compatibility with
other DNS server software.

(cherry picked from commit 740292d3ec)
2024-12-26 18:31:31 +02:00
Artem Boldariev
4f8ade0e1e TLS SNI - add low level support for SNI to the networking code
This commit adds support for setting SNI hostnames in outgoing
connections over TLS.

Most of the changes are related to either adapting the code to accept
and extra argument in *connect() functions and a couple of changes to
the TLS Stream to actually make use of the new SNI hostname
information.

(cherry picked from commit 6691a1530d)
2024-12-26 18:31:03 +02:00
Ondřej Surý
58a15d38c2
Remove redundant parentheses from the return statement
(cherry picked from commit 0258850f20)
2024-11-19 14:26:52 +01:00
Mark Andrews
d5da28d1ca Fix TCP dispatches and transport
Dispatch needs to know the transport that is being used over the
TCP connection to correctly allow for it to be reused.  Add a
transport parameter to dns_dispatch_createtcp and dns_dispatch_gettcp
and use it when selecting a TCP socket for reuse.

(cherry picked from commit baab8a5d75)
2024-10-24 01:21:37 +00:00
Ondřej Surý
b46108b428 Skip TCP dispatch responses that are not ours
When matching the TCP dispatch responses, we should skip the responses
that do not belong to our TCP connection.  This can happen with faulty
upstream server that sends invalid QID back to us.

(cherry picked from commit 4ef316e21e)
2024-10-02 10:45:05 +00:00
Aram Sargsyan
ca966c396a Don't ignore the local port number in dns_dispatch_add() for TCP
The dns_dispatch_add() function registers the 'resp' entry in
'disp->mgr->qids' hash table with 'resp->port' being 0, but in
tcp_recv_success(), when looking up an entry in the hash table
after a successfully received data the port is used, so if the
local port was set (i.e. it was not 0) it fails to find the
entry and results in an unexpected error.

Set the 'resp->port' to the given local port value extracted from
'disp->local'.

(cherry picked from commit d49a8f518a)
2024-10-02 09:53:45 +00:00
Aram Sargsyan
346850954c Add a missing rcu_read_unlock() call on exit path
An exit path in the dns_dispatch_add() function fails to get out of
the RCU critical section when returning early. Add the missing
rcu_read_unlock() call.

(cherry picked from commit 4123d59fbc)
2024-09-27 13:53:31 +00:00
Ondřej Surý
4127068305 Modify dns_dispatch API to accept zero timeout
The dns_dispatch_add() has timeout parameter that could not be 0 (for
not timeout).  Modify the dns_dispatch implementation to accept a zero
timeout for cases where the timeouts are undesirable because they are
managed externally.

(cherry picked from commit 0f810b3144)
2024-09-21 08:53:25 +00:00
Ondřej Surý
e74c7dcf51
Always call the TCP dispatch connected callbacks asynchronously
The TCP dispatch connected callbacks could be called synchronously which
in turn could destroy xfrin before we return from dns_xfrin_create().

Delay the calling the callback called from tcp_dispatch_connect() by
calling it always asynchronously.
2024-03-04 16:34:14 +01:00
Artem Boldariev
eb52015db1 Stream DNS: add PROXY over TLS support
This commit extends Stream DNS with PROXY over TLS support.
2023-12-06 15:15:25 +02:00
Artem Boldariev
e97903ca14 Add PROXY support to Stream DNS
This commit makes it possible to use Stream DNS on top of PROXY Stream
either directly or indirectly (in the case when TLS is involved).
2023-12-06 15:15:24 +02:00
Ondřej Surý
f213f644ed
Add option to mark TCP dispatch as unshared
The current dispatch code could reuse the TCP connection when
dns_dispatch_gettcp() would be used first.  This is problematic as the
dns_resolver doesn't use TCP connection sharing, but dns_request could
get the TCP stream that was created outside of the dns_request.

Add new DNS_DISPATCHOPT_UNSHARED option to dns_dispatch_createtcp() that
would prevent the TCP stream to be reused.  Use that option in the
dns_resolver call to dns_dispatch_createtcp() to prevent dns_request
from reusing the TCP connections created by dns_resolver.

Additionally, the dns_xfrin unit added TCP connection sharing for
incoming transfers.  While interleaving *xfr streams on a TCP connection
should work this should be a deliberate change and be property of the
server that can be controlled.  Additionally some level of parallel TCP
streams is desirable.  Revert to the old behaviour by removing the
dns_dispatch_gettcp() calls from dns_xfrin and use the new option to
prevent from sharing the transfer streams with dns_request.
2023-10-24 13:07:03 +02:00
Ondřej Surý
3340c82b99
Improve isc_refcount with initializer and implicit destroy
Add ISC_REFCOUNT_INITIALIZER(x) macro and implicitly call
isc_refcount_destroy() in the ISC_REFCOUNT_IMPL() macros
to reduce code duplicities.
2023-09-24 10:08:56 +02:00
Ondřej Surý
4cf4cc484d
Use the new isc_sockaddr_hash_ex() to fix QID table hashing
The QID table hashing used a custom merging of the sockaddr, port and id
into a single hashvalue.  Normalize the QID table hashing function to
use isc_hash32 API for all the values.
2023-09-19 19:56:33 +02:00
Ondřej Surý
aa0971027c
Cleanup unused .localport member of dns_dispatch_t and some macros
The .localport member of dns_dispatch_t structure was unused, clean it
up.  Cleanup unused and/or redundant macros.
2023-09-16 07:32:18 +02:00
Ondřej Surý
c9b4b45943
Replace the linked list of TCP dispatches with hash table
Reusing TCP connections with dns_dispatch_gettcp() used linear linked
list to lookup existing outgoing TCP connections that could be reused.
Replace the linked list with per-loop cds_lfht hashtable to speedup the
lookups.  We use cds_lfht because it allows non-unique node insertion
that we need to check for dispatches in different connection states.
2023-09-16 07:32:18 +02:00
Ondřej Surý
6fd06c461b
Make dns_dispatch bound to threads
Instead of high number of dispatches (4 * named_g_udpdisp)[1], make the
dispatches bound to threads and make dns_dispatchset_t create a dispatch
for each thread (event loop).

This required couple of other changes:

1. The dns_dispatch_createudp() must be called on loop, so the isc_tid()
   is already initialized - changes to nsupdate and mdig were required.

2. The dns_requestmgr had only a single dispatch per v4 and v6.  Instead
   of using single dispatch, use dns_dispatchset_t for each protocol -
   this is same as dns_resolver.
2023-09-16 07:32:17 +02:00
Ondřej Surý
282c4709b8
Rewrite the QID lookup table to cds_lfht
Looking up unique message ID in the dns_dispatch has been using custom
hash tables.  Rewrite the custom hashtable to use cds_lfht API, removing
one extra lock in the cold-cache resolver hot path.
2023-09-16 07:32:17 +02:00
Ondřej Surý
89fcb6f897
Apply the isc_mem_cget semantic patch 2023-08-31 22:08:35 +02:00
Evan Hunt
6ac8723611
use isc_loop_now() for dispentry timeouts
store a pointer to the running loop when creating a dispatch entry
with dns_dispatch_add(), and use isc_loop_now() to get the timestamp for
the current event loop tick when we initialize the dispentry start time
and check for timeouts.
2023-07-19 15:32:21 +02:00
Evan Hunt
0e800467ee fix handling of TCP timeouts
when a TCP dispatch times out, we call tcp_recv() with a result
value of ISC_R_TIMEDOUT; this cancels the oldest dispatch
entry in the dispatch's active queue, plus any additional entries
that have waited longer than their configured timeouts. if, at
that point, there were more dispatch entries still on the active
queue, it resumes reading, but until now it failed to restart
the timer.

this has been corrected: we now calculate a new timeout
based on the oldest dispatch entry still remaining.  this
requires us to initialize the start time of each dispatch entry
when it's first added to the queue.

in order to ensure that the handling of timed-out requests is
consistent, we now calculate the runtime of each dispatch
entry based on the same value for 'now'.

incidentally also fixed a compile error that turned up when
DNS_DISPATCH_TRACE was turned on.
2023-05-26 00:41:01 -07:00
Ondřej Surý
c8e8ccd026
Honour the source-port when retrying in dns_dispatch
When retrying in the DNS dispatch, the local port would be forgotten on
ISC_R_ADDRINUSE, keep the configured source-port even when retrying.

Additionally, treat ISC_R_NOPERM same as ISC_R_ADDRINUSE.

Closes: #3986
2023-04-20 10:57:20 +02:00
Ondřej Surý
a5f5f68502
Refactor isc_time_now() to return time, and not result
The isc_time_now() and isc_time_now_hires() were used inconsistently
through the code - either with status check, or without status check,
or via TIME_NOW() macro with RUNTIME_CHECK() on failure.

Refactor the isc_time_now() and isc_time_now_hires() to always fail when
getting current time has failed, and return the isc_time_t value as
return value instead of passing the pointer to result in the argument.
2023-03-31 15:02:06 +02:00
Evan Hunt
4e93d44c74 fix a bug in dns_dispatch_getnext()
when a message arrives over a TCP connection matching an expected
QID, the dispatch is updated so it no longer expects that QID,
but continues reading. subsequent messages with the same QID are
ignored, unless the dispatch entry has called dns_dispatch_getnext()
or dns_dispatch_resume().

however, a coding error caused those functions to have no effect
when the dispatch was reading, so streams of messages with the same
QID could not be received over a single TCP connection, breaking *XFR.

this has been corrected by changing the order of operations in
tcp_dispatch_getnext() so that disp->reading isn't checked until
after the dispatch entry has been reactivated.
2023-02-24 08:30:33 +00:00
Evan Hunt
f0c766abec refactor dns_xfrin to use dns_dispatch
the dns_xfrin module was still using the network manager directly to
manage TCP connections and send and receive messages.  this commit
changes it to use the dispatch manager instead.
2023-02-24 08:30:33 +00:00
Evan Hunt
d72419d1f5 minor cleanups in dispatch.c
- simplified tcp_startrecv()
- removed a short function that was only called once
- removed an unnecessary if statement
2023-02-24 08:30:33 +00:00
Tony Finch
6927a30926 Remove do-nothing header <isc/print.h>
This one really truly did nothing. No lines added!
2023-02-15 16:44:47 +00:00
Evan Hunt
4d50c912ba use configured source ports for UDP requests
the optional 'port' option, when used with notify-source,
transfer-source, etc, is used to set up UDP dispatches with a
particular source port, but when the actual UDP connection was
established the port would be overridden with a random one. this
has been fixed.

(configuring source ports is deprecated in 9.20 and slated for
removal in 9.22, but should still work correctly until then.)
2023-02-06 15:03:58 -08:00
Evan Hunt
916ea26ead remove nonfunctional DSCP implementation
DSCP has not been fully working since the network manager was
introduced in 9.16, and has been completely broken since 9.18.
This seems to have caused very few difficulties for anyone,
so we have now marked it as obsolete and removed the
implementation.

To ensure that old config files don't fail, the code to parse
dscp key-value pairs is still present, but a warning is logged
that the feature is obsolete and should not be used. Nothing is
done with configured values, and there is no longer any
range checking.
2023-01-09 12:15:21 -08:00
Ondřej Surý
1a999353cd Pin the dns_dispatch to threads when reusing
Previously, dns_dispatch_gettcp() could pick a TCP connection created by
different thread - this breaks our contractual promise to DNS dispatch
by using the TCP connection on a different thread than it was created.
Add .tid member to the dns_dispatch_t struct and skip the dispatches
from other threads when looking up a TCP dispatch that we can reuse in
dns_request.

NOTE: This is going to be properly refactored, but this change could be
also backported to 9.18 for better stability and thread-affinity.
2023-01-05 09:11:42 +01:00
Aram Sargsyan
41ca9d419e Don't pass a NULL pointer to isc_sockaddr_format()
The 'localaddr' pointer can be NULL, which causes an assertion failure.

Use '&disp->local' instead when printing a debug log message.
2022-12-28 12:10:09 +00:00
Ondřej Surý
9dd8deaf01
Call the connected dns_dispatch callback asynchronously
The dns_request code is very sensitive about calling the connected and
deadlocks when the timing is "right" in several places.  Move the call
to the connected callback to the (udp|tcp)_connected() functions, so
they are called asynchronously instead of directly from
the (udp|tcp)_dispentry_cancel() functions.
2022-12-21 12:13:35 +01:00
Ondřej Surý
3fac4ca57e
Ignore TCP dispatches that have zero references
The TCP dispatches are removed from the dispatchmgr->list in the
dispatch_destroy() and there's a brief period of time where
dns_dispatch_gettcp() can find a dispatch in connected state that's
being destroyed.

Set the dispatch state to DNS_DISPATCHSTATE_NONE in the TCP connection
callback if there are no responses waiting, and ignore TCP dispatches
with zero references in dns_dispatch_gettcp().
2022-12-21 12:13:35 +01:00
Ondřej Surý
90cd14f620
Fix assignment vs comparison typo in tcp_connected()
In tcp_connected() a typo has turned a DbC check into an assignment
breaking the state machine and making the dns_dispatch_gettcp() try to
attach to dispatch in process of destruction.
2022-12-21 12:13:20 +01:00
Ondřej Surý
7310e2b424
Don't remove dispatches in CANCELED state from the list
In dns_dispatch_gettcp(), we can't remove canceled dispatches from the
mgr->list because ISC_LIST_NEXT() would fail in the next iteration.
2022-12-21 12:13:20 +01:00
Ondřej Surý
6d22d03208
Ignore TCP dispatches in DNS_DISPATCHSTATE_NONE state
The TCP dispatches in DNS_DISPATCHSTATE_NONE could be either very
fresh or those could be dispatches that failed connecting to the
destination.  Ignore them when trying to connect to an existing
TCP dispatch via dns_dispatch_gettcp().
2022-12-21 12:13:20 +01:00
Artem Boldariev
cce52fa4a2 BIND: use Stream DNS for DNS over TCP connections
This commit makes BIND use the new Stream DNS transport for DNS over
TCP.
2022-12-20 22:13:53 +02:00
Ondřej Surý
6f317f27ea Fix the thread safety in the dns_dispatch unit
The dispatches are not thread-bound, and used freely between various
threads (see the dns_resolver and dns_request units for details).

This refactoring make sure that all non-const dns_dispatch_t and
dns_dispentry_t members are accessed under a lock, and both object now
track their internal state (NONE, CONNECTING, CONNECTED, CANCELED)
instead of guessing the state from the state of various struct members.

During the refactoring, the artificial limit DNS_DISPATCH_SOCKSQUOTA on
UDP sockets per dispatch was removed as the limiting needs to happen and
happens on in dns_resolver and limiting the number of UDP sockets
artificially in dispatch could lead to unpredictable behaviour in case
one dispatch has the limit exhausted by others are idle.

The TCP artificial limit of DNS_DISPATCH_MAXREQUESTS makes even less
sense as the TCP connections are only reused in the dns_request API
that's not a heavy user of the outgoing connections.

As a side note, the fact that UDP and TCP dispatch pretends to be same
thing, but in fact the connected UDP is handled from dns_dispentry_t and
dns_dispatch_t acts as a broker, but connected TCP is handled from
dns_dispatch_t and dns_dispatchmgr_t acts as a broker doesn't really
help the clarity of this unit.

This refactoring kept to API almost same - only dns_dispatch_cancel()
and dns_dispatch_done() were merged into dns_dispatch_done() as we need
to cancel active netmgr handles in any case to not leave dangling
connections around.  The functions handling UDP and TCP have been mostly
split to their matching counterparts and the dns_dispatch_<function>
functions are now thing wrappers that call <udp|tcp>_dispatch_<function>
based on the socket type.

More debugging-level logging was added to the unit to accomodate for
this fact.
2022-12-19 11:42:13 +01:00
Michal Nowak
afdb41a5aa
Update sources to Clang 15 formatting 2022-11-29 08:54:34 +01:00
Aram Sargsyan
90959f6166 Implement TLS transport support for dns_request and dns_dispatch
This change prepares ground for sending DNS requests using DoT,
which, in particular, will be used for forwarding dynamic updates
to TLS-enabled primaries.
2022-09-19 16:36:28 +00:00
Evan Hunt
e1c81f9b1b remove unnecessary assertion in dns_dispatch_connect()
When a thread calls dns_dispatch_connect() on an unconnected TCP socket
it sets `tcpstate` from `DNS_DISPATCHSTATE_NONE` to `_CONNECTING`.
Previously, it then INSISTed that there were no pending connections
before calling isc_nm_tcpdnsconnect().

If a second thread called dns_dispatch_connect() during that window
of time, it could add a pending connection to the list, and trigger
an assertion failure.

This commit removes the INSIST since the condition is actually
harmless.
2022-07-14 16:31:01 -07:00