Merge tag 'v9.18.33' into bind-9.18

This commit is contained in:
Andoni Duarte Pintado
2025-01-29 17:23:11 +01:00
19 changed files with 1023 additions and 98 deletions
+1 -1
View File
@@ -279,7 +279,7 @@ n=$((n + 1))
echo_i "testing with 'minimal-any no;' ($n)"
ret=0
$DIG $DIGOPTS -t ANY www.rt.example @10.53.0.1 >dig.out.$n || ret=1
grep "ANSWER: 3, AUTHORITY: 2, ADDITIONAL: 2" dig.out.$n >/dev/null || ret=1
grep "ANSWER: 3, AUTHORITY: 2, ADDITIONAL: 1" dig.out.$n >/dev/null || ret=1
if [ $ret -eq 1 ]; then
echo_i "failed"
status=$((status + 1))
-12
View File
@@ -1,12 +0,0 @@
Copyright (C) Internet Systems Consortium, Inc. ("ISC")
SPDX-License-Identifier: MPL-2.0
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, you can obtain one at https://mozilla.org/MPL/2.0/.
See the COPYRIGHT file distributed with this work for additional
information regarding copyright ownership.
Add -T noaa.
+8
View File
@@ -322,6 +322,10 @@ done
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
stop_server ns4
touch ns4/named.noaa
start_server --noclean --restart --port ${PORT} ns4 || ret=1
n=$((n + 1))
echo_i "RT21594 regression test check setup ($n)"
ret=0
@@ -358,6 +362,10 @@ grep "status: NXDOMAIN" dig.ns5.out.${n} >/dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
stop_server ns4
rm ns4/named.noaa
start_server --noclean --restart --port ${PORT} ns4 || ret=1
n=$((n + 1))
echo_i "check that replacement of additional data by a negative cache no data entry clears the additional RRSIGs ($n)"
ret=0
+1 -1
View File
@@ -99,7 +99,7 @@ from a primary server, the secondary checks to see that its version of
the zone is the current version and, if not, initiates a zone transfer.
For more information about DNS NOTIFY, see the description of the
:namedconf:ref:`notify` and :namedconf:ref`also-notify` statements.
:namedconf:ref:`notify` and :namedconf:ref:`also-notify` statements.
The NOTIFY protocol is specified in :rfc:`1996`.
.. note::
+2
View File
@@ -18,6 +18,8 @@ Changelog
development. Regular users should refer to :ref:`Release Notes <relnotes>`
for changes relevant to them.
.. include:: ../changelog/changelog-9.18.33-S1.rst
.. include:: ../changelog/changelog-9.18.33.rst
.. include:: ../changelog/changelog-9.18.32-S1.rst
.. include:: ../changelog/changelog-9.18.32.rst
.. include:: ../changelog/changelog-9.18.31-S1.rst
+1
View File
@@ -45,6 +45,7 @@ The list of known issues affecting the latest version in the 9.18 branch can be
found at
https://gitlab.isc.org/isc-projects/bind9/-/wikis/Known-Issues-in-BIND-9.18
.. include:: ../notes/notes-9.18.33.rst
.. include:: ../notes/notes-9.18.32.rst
.. include:: ../notes/notes-9.18.31.rst
.. include:: ../notes/notes-9.18.30.rst
+11
View File
@@ -0,0 +1,11 @@
.. Copyright (C) Internet Systems Consortium, Inc. ("ISC")
..
.. SPDX-License-Identifier: MPL-2.0
..
.. This Source Code Form is subject to the terms of the Mozilla Public
.. License, v. 2.0. If a copy of the MPL was not distributed with this
.. file, you can obtain one at https://mozilla.org/MPL/2.0/.
..
.. See the COPYRIGHT file distributed with this work for additional
.. information regarding copyright ownership.
+126
View File
@@ -0,0 +1,126 @@
.. Copyright (C) Internet Systems Consortium, Inc. ("ISC")
..
.. SPDX-License-Identifier: MPL-2.0
..
.. This Source Code Form is subject to the terms of the Mozilla Public
.. License, v. 2.0. If a copy of the MPL was not distributed with this
.. file, you can obtain one at https://mozilla.org/MPL/2.0/.
..
.. See the COPYRIGHT file distributed with this work for additional
.. information regarding copyright ownership.
BIND 9.18.33
------------
Security Fixes
~~~~~~~~~~~~~~
- [CVE-2024-12705] DNS-over-HTTP(s) flooding fixes. ``e733e624147``
Fix DNS-over-HTTP(S) implementation issues that arise under heavy
query load. Optimize resource usage for :iscman:`named` instances that
accept queries over DNS-over-HTTP(S).
Previously, :iscman:`named` would process all incoming HTTP/2 data at
once, which could overwhelm the server, especially when dealing with
clients that send requests but don't wait for responses. That has been
fixed. Now, :iscman:`named` handles HTTP/2 data in smaller chunks and
throttles reading until the remote side reads the response data. It
also throttles clients that send too many requests at once.
Additionally, :iscman:`named` now carefully processes data sent by
some clients, which can be considered "flooding." It logs these
clients and drops connections from them. :gl:`#4795`
In some cases, :iscman:`named` could leave DNS-over-HTTP(S)
connections in the `CLOSE_WAIT` state indefinitely. That also has been
fixed. ISC would like to thank JF Billaud for thoroughly investigating
the issue and verifying the fix. :gl:`#5083` :gl:`#4795` :gl:`#5083`
- [CVE-2024-11187] Limit the additional processing for large RDATA sets.
``c6e6a7af8ac``
When answering queries, don't add data to the additional section if
the answer has more than 13 names in the RDATA. This limits the number
of lookups into the database(s) during a single client query, reducing
query processing load. :gl:`#5034`
New Features
~~~~~~~~~~~~
- Add a new option to configure the maximum number of outgoing queries
per client request. ``64b2b6edffa``
The configuration option 'max-query-count' sets how many outgoing
queries per client request is allowed. The existing
'max-recursion-queries' is the number of permissible queries for a
single name and is reset on every CNAME redirection. This new option
is a global limit on the client request. The default is 200.
This allows us to send a bit more queries while looking up a single
name. The default for 'max-recursion-queries' is changed from 32 to
50. :gl:`#4980` :gl:`#4921` :gl:`!9847`
Feature Changes
~~~~~~~~~~~~~~~
- Update picohttpparser.{c,h} with upstream repository. ``326b445e469``
:gl:`#4485` :gl:`!9864`
- Remove unused maxquerycount. ``c30067bb2f3``
Related to #4980 :gl:`!9854`
- Use query counters in validator code. ``b1207ea9ed6``
Commit af7db8951364a89c468eda1535efb3f53adc2c1f as part of #4141 was
supposed to apply the 'max-recursion-queries' quota to validator
queries, but the counter was never actually passed on to
'dns_resolver_createfetch()'. This has been fixed, and the global
query counter ('max-query-count', per client request) is now also
added.
Related to #4980 :gl:`!9867`
Bug Fixes
~~~~~~~~~
- Fix nsupdate hang when processing a large update. ``9a0588f7cf2``
To mitigate DNS flood attacks over a single TCP connection, we
throttle the connection when the other side does not read the data.
Throttling should only occur on server-side sockets, but erroneously
also happened for nsupdate, which acts as a client. When nsupdate
started throttling the connection, it never attempts to read again.
This has been fixed. :gl:`#4910` :gl:`!9835`
- Fix possible assertion failure when reloading server while processing
updates. ``1d4e60c9ba9``
:gl:`#5006` :gl:`!9821`
- Fix dnssec-signzone signing non-DNSKEY RRsets with revoked keys.
``bf2f4d4aad8``
`dnssec-signzone` was using revoked keys for signing RRsets other than
DNSKEY. This has been corrected. :gl:`#5070` :gl:`!9841`
- Unknown directive in resolv.conf not handled properly. ``75ae186fa1f``
The line after an unknown directive in resolv.conf could accidentally
be skipped, potentially affecting dig, host, nslookup, nsupdate, or
delv. This has been fixed. :gl:`#5084` :gl:`!9878`
- Fix a bug in isc_rwlock_trylock() ``f68e60b3dc4``
When isc_rwlock_trylock() fails to get a read lock because another
writer was faster, it should wake up other waiting writers in case
there are no other readers, but the current code forgets about the
currently active writer when evaluating 'cntflag'.
Unset the WRITER_ACTIVE bit in 'cntflag' before checking to see if
there are other readers, otherwise the waiting writers, if they exist,
might not wake up. :gl:`#5121` :gl:`!9937`
+97
View File
@@ -0,0 +1,97 @@
.. Copyright (C) Internet Systems Consortium, Inc. ("ISC")
..
.. SPDX-License-Identifier: MPL-2.0
..
.. This Source Code Form is subject to the terms of the Mozilla Public
.. License, v. 2.0. If a copy of the MPL was not distributed with this
.. file, you can obtain one at https://mozilla.org/MPL/2.0/.
..
.. See the COPYRIGHT file distributed with this work for additional
.. information regarding copyright ownership.
Notes for BIND 9.18.33
----------------------
Security Fixes
~~~~~~~~~~~~~~
- DNS-over-HTTPS flooding fixes. :cve:`2024-12705`
Fix DNS-over-HTTPS implementation issues that arise under heavy
query load. Optimize resource usage for :iscman:`named` instances that
accept queries over DNS-over-HTTPS.
Previously, :iscman:`named` processed all incoming HTTP/2 data at
once, which could overwhelm the server, especially when dealing with
clients that sent requests but did not wait for responses. That has been
fixed. Now, :iscman:`named` handles HTTP/2 data in smaller chunks and
throttles reading until the remote side reads the response data. It
also throttles clients that send too many requests at once.
In addition, :iscman:`named` now evaluates excessive streams opened by
clients that include no DNS data, which is considered "flooding." It
logs these clients and drops connections from them. :gl:`#4795`
In some cases, :iscman:`named` could leave DNS-over-HTTPS
connections in the `CLOSE_WAIT` state indefinitely. That has also been
fixed. :gl:`#5083`
ISC would like to thank Jean-François Billaud for his assistance with
investigating this issue.
- Limit additional section processing for large RDATA sets.
:cve:`2024-11187`
When answering queries, don't add data to the additional section if
the answer has more than 13 names in the RDATA. This limits the number
of lookups into the database(s) during a single client query, reducing
the query-processing load. :gl:`#5034`
ISC would like to thank Toshifumi Sakaguchi for bringing this
vulnerability to our attention.
New Features
~~~~~~~~~~~~
- Add a new option to configure the maximum number of outgoing queries
per client request.
The configuration option :any:`max-query-count` sets how many outgoing
queries per client request are allowed. The existing
:any:`max-recursion-queries` value is the number of permissible queries for a
single name and is reset on every CNAME redirection. This new option
is a global limit on the client request. The default is 200.
The default for :any:`max-recursion-queries` is changed from 32 to
50. This allows :any:`named` to send a few more queries
while looking up a single name. :gl:`#4980` :gl:`#4921`
Bug Fixes
~~~~~~~~~
- Fix :iscman:`nsupdate` hang when processing a large update.
To mitigate DNS flood attacks over a single TCP connection, throttle
the connection when the other side does not read the data. Throttling
should only occur on server-side sockets, but erroneously also
happened for :iscman:`nsupdate`, which acts as a client. When
:iscman:`nsupdate` started throttling the connection, it never
attempted to read again. This has been fixed. :gl:`#4910`
- Fix possible assertion failure when reloading server while processing
update policy rules. :gl:`#5006`
- Fix :iscman:`dnssec-signzone` signing non-DNSKEY RRsets with revoked keys.
:any:`dnssec-signzone` was using revoked keys for signing RRsets other than
DNSKEY. This has been corrected. :gl:`#5070`
- Fix improper handling of unknown directives in ``resolv.conf``.
The line after an unknown directive in ``resolv.conf`` could accidentally be
skipped, potentially affecting :iscman:`dig`, :iscman:`host`,
:iscman:`nslookup`, :iscman:`nsupdate`, or :iscman:`delv`. This has been
fixed. :gl:`#5084`
+9 -1
View File
@@ -54,6 +54,8 @@
#include <dns/rdatastruct.h>
#include <dns/types.h>
#define DNS_RDATASET_MAXADDITIONAL 13
ISC_LANG_BEGINDECLS
typedef enum {
@@ -454,7 +456,8 @@ dns_rdataset_towirepartial(dns_rdataset_t *rdataset,
isc_result_t
dns_rdataset_additionaldata(dns_rdataset_t *rdataset,
const dns_name_t *owner_name,
dns_additionaldatafunc_t add, void *arg);
dns_additionaldatafunc_t add, void *arg,
size_t limit);
/*%<
* For each rdata in rdataset, call 'add' for each name and type in the
* rdata which is subject to additional section processing.
@@ -473,10 +476,15 @@ dns_rdataset_additionaldata(dns_rdataset_t *rdataset,
*\li If a call to dns_rdata_additionaldata() is not successful, the
* result returned will be the result of dns_rdataset_additionaldata().
*
*\li If 'limit' is non-zero and the number of the rdatasets is larger
* than 'limit', no additional data will be processed.
*
* Returns:
*
*\li #ISC_R_SUCCESS
*
*\li #DNS_R_TOOMANYRECORDS in case rdataset count is larger than 'limit'
*
*\li Any error that dns_rdata_additionaldata() can return.
*/
+1 -1
View File
@@ -10339,7 +10339,7 @@ no_glue:
idx = hash_32(hash, rbtversion->glue_table_bits);
(void)dns_rdataset_additionaldata(rdataset, dns_rootname,
glue_nsdname_cb, &ctx);
glue_nsdname_cb, &ctx, 0);
cur = isc_mem_get(rbtdb->common.mctx, sizeof(*cur));
+6 -1
View File
@@ -577,7 +577,8 @@ dns_rdataset_towire(dns_rdataset_t *rdataset, const dns_name_t *owner_name,
isc_result_t
dns_rdataset_additionaldata(dns_rdataset_t *rdataset,
const dns_name_t *owner_name,
dns_additionaldatafunc_t add, void *arg) {
dns_additionaldatafunc_t add, void *arg,
size_t limit) {
dns_rdata_t rdata = DNS_RDATA_INIT;
isc_result_t result;
@@ -589,6 +590,10 @@ dns_rdataset_additionaldata(dns_rdataset_t *rdataset,
REQUIRE(DNS_RDATASET_VALID(rdataset));
REQUIRE((rdataset->attributes & DNS_RDATASETATTR_QUESTION) == 0);
if (limit != 0 && dns_rdataset_count(rdataset) > limit) {
return DNS_R_TOOMANYRECORDS;
}
result = dns_rdataset_first(rdataset);
if (result != ISC_R_SUCCESS) {
return result;
+12 -7
View File
@@ -9060,7 +9060,7 @@ rctx_answer_any(respctx_t *rctx) {
rdataset->trust = rctx->trust;
(void)dns_rdataset_additionaldata(rdataset, rctx->aname,
check_related, rctx);
check_related, rctx, 0);
}
return ISC_R_SUCCESS;
@@ -9108,7 +9108,7 @@ rctx_answer_match(respctx_t *rctx) {
rctx->ardataset->attributes |= DNS_RDATASETATTR_CACHE;
rctx->ardataset->trust = rctx->trust;
(void)dns_rdataset_additionaldata(rctx->ardataset, rctx->aname,
check_related, rctx);
check_related, rctx, 0);
for (sigrdataset = ISC_LIST_HEAD(rctx->aname->list);
sigrdataset != NULL;
@@ -9315,7 +9315,7 @@ rctx_authority_positive(respctx_t *rctx) {
*/
(void)dns_rdataset_additionaldata(
rdataset, name, check_related,
rctx);
rctx, 0);
done = true;
}
}
@@ -9822,8 +9822,12 @@ rctx_referral(respctx_t *rctx) {
*/
INSIST(rctx->ns_rdataset != NULL);
FCTX_ATTR_SET(fctx, FCTX_ATTR_GLUING);
/*
* Mark the glue records in the additional section to be cached.
*/
(void)dns_rdataset_additionaldata(rctx->ns_rdataset, rctx->ns_name,
check_related, rctx);
check_related, rctx, 0);
#if CHECK_FOR_GLUE_IN_ANSWER
/*
* Look in the answer section for "glue" that is incorrectly
@@ -9835,8 +9839,9 @@ rctx_referral(respctx_t *rctx) {
if (rctx->glue_in_answer &&
(fctx->type == dns_rdatatype_aaaa || fctx->type == dns_rdatatype_a))
{
(void)dns_rdataset_additionaldata(
rctx->ns_rdataset, rctx->ns_name, check_answer, fctx);
(void)dns_rdataset_additionaldata(rctx->ns_rdataset,
rctx->ns_name, check_answer,
fctx, 0);
}
#endif /* if CHECK_FOR_GLUE_IN_ANSWER */
FCTX_ATTR_CLR(fctx, FCTX_ATTR_GLUING);
@@ -9938,7 +9943,7 @@ again:
if (CHASE(rdataset)) {
rdataset->attributes &= ~DNS_RDATASETATTR_CHASE;
(void)dns_rdataset_additionaldata(
rdataset, name, check_related, rctx);
rdataset, name, check_related, rctx, 0);
rescan = true;
}
}
+438 -50
View File
@@ -85,6 +85,37 @@
#define INITIAL_DNS_MESSAGE_BUFFER_SIZE (512)
/*
* The value should be small enough to not allow a server to open too
* many streams at once. It should not be too small either because
* the incoming data will be split into too many chunks with each of
* them processed asynchronously.
*/
#define INCOMING_DATA_CHUNK_SIZE (256)
/*
* Often processing a chunk does not change the number of streams. In
* that case we can process more than once, but we still should have a
* hard limit on that.
*/
#define INCOMING_DATA_MAX_CHUNKS_AT_ONCE (4)
/*
* These constants define the grace period to help detect flooding clients.
*
* The first one defines how much data can be processed before opening
* a first stream and received at least some useful (=DNS) data.
*
* The second one defines how much data from a client we read before
* trying to drop a clients who sends not enough useful data.
*
* The third constant defines how many streams we agree to process
* before checking if there was at least one DNS request received.
*/
#define INCOMING_DATA_INITIAL_STREAM_SIZE (1536)
#define INCOMING_DATA_GRACE_SIZE (MAX_ALLOWED_DATA_IN_HEADERS)
#define MAX_STREAMS_BEFORE_FIRST_REQUEST (50)
typedef struct isc_nm_http_response_status {
size_t code;
size_t content_length;
@@ -143,6 +174,7 @@ struct isc_nm_http_session {
ISC_LIST(http_cstream_t) cstreams;
ISC_LIST(isc_nmsocket_h2_t) sstreams;
size_t nsstreams;
uint64_t total_opened_sstreams;
isc_nmhandle_t *handle;
isc_nmhandle_t *client_httphandle;
@@ -155,6 +187,18 @@ struct isc_nm_http_session {
isc__nm_http_pending_callbacks_t pending_write_callbacks;
isc_buffer_t *pending_write_data;
/*
* The statistical values below are for usage on server-side
* only. They are meant to detect clients that are taking too many
* resources from the server.
*/
uint64_t received; /* How many requests have been received. */
uint64_t submitted; /* How many responses were submitted to send */
uint64_t processed; /* How many responses were processed. */
uint64_t processed_incoming_data;
uint64_t processed_useful_data; /* DNS data */
};
typedef enum isc_http_error_responses {
@@ -177,6 +221,7 @@ typedef struct isc_http_send_req {
void *cbarg;
isc_buffer_t *pending_write_data;
isc__nm_http_pending_callbacks_t pending_write_callbacks;
uint64_t submitted;
} isc_http_send_req_t;
#define HTTP_ENDPOINTS_MAGIC ISC_MAGIC('H', 'T', 'E', 'P')
@@ -189,10 +234,26 @@ static bool
http_send_outgoing(isc_nm_http_session_t *session, isc_nmhandle_t *httphandle,
isc_nm_cb_t cb, void *cbarg);
static void
http_log_flooding_peer(isc_nm_http_session_t *session);
static bool
http_is_flooding_peer(isc_nm_http_session_t *session);
static ssize_t
http_process_input_data(isc_nm_http_session_t *session,
isc_buffer_t *input_data);
static inline bool
http_too_many_active_streams(isc_nm_http_session_t *session);
static void
http_do_bio(isc_nm_http_session_t *session, isc_nmhandle_t *send_httphandle,
isc_nm_cb_t send_cb, void *send_cbarg);
static void
http_do_bio_async(isc_nm_http_session_t *session);
static void
failed_httpstream_read_cb(isc_nmsocket_t *sock, isc_result_t result,
isc_nm_http_session_t *session);
@@ -494,6 +555,16 @@ finish_http_session(isc_nm_http_session_t *session) {
if (!session->closed) {
session->closed = true;
isc_nm_cancelread(session->handle);
isc__nmsocket_timer_stop(session->handle->sock);
}
/*
* Free any unprocessed incoming data in order to not process
* it during indirect calls to http_do_bio() that might happen
* when calling the failed callbacks.
*/
if (session->buf != NULL) {
isc_buffer_free(&session->buf);
}
if (session->client) {
@@ -567,6 +638,7 @@ on_server_data_chunk_recv_callback(int32_t stream_id, const uint8_t *data,
if (new_bufsize <= MAX_DNS_MESSAGE_SIZE &&
new_bufsize <= h2->content_length)
{
session->processed_useful_data += len;
isc_buffer_putmem(&h2->rbuf, data, len);
break;
}
@@ -615,6 +687,9 @@ call_unlink_cstream_readcb(http_cstream_t *cstream,
isc_buffer_usedregion(cstream->rbuf, &read_data);
cstream->read_cb(session->client_httphandle, result, &read_data,
cstream->read_cbarg);
if (result == ISC_R_SUCCESS) {
isc__nmsocket_timer_restart(session->handle->sock);
}
put_http_cstream(session->mctx, cstream);
}
@@ -656,6 +731,9 @@ on_server_stream_close_callback(int32_t stream_id,
ISC_LIST_UNLINK(session->sstreams, &sock->h2, link);
session->nsstreams--;
if (sock->h2.request_received) {
session->submitted++;
}
/*
* By making a call to isc__nmsocket_prep_destroy(), we ensure that
@@ -967,6 +1045,182 @@ client_submit_request(isc_nm_http_session_t *session, http_cstream_t *stream) {
return ISC_R_SUCCESS;
}
static ssize_t
http_process_input_data(isc_nm_http_session_t *session,
isc_buffer_t *input_data) {
ssize_t readlen = 0;
ssize_t processed = 0;
isc_region_t chunk = { 0 };
size_t before, after;
size_t i;
REQUIRE(VALID_HTTP2_SESSION(session));
REQUIRE(input_data != NULL);
if (!http_session_active(session)) {
return 0;
}
/*
* For clients that initiate request themselves just process
* everything.
*/
if (session->client) {
isc_buffer_remainingregion(input_data, &chunk);
if (chunk.length == 0) {
return 0;
}
readlen = nghttp2_session_mem_recv(session->ngsession,
chunk.base, chunk.length);
if (readlen >= 0) {
isc_buffer_forward(input_data, readlen);
session->processed_incoming_data += readlen;
}
return readlen;
}
/*
* If no streams are created during processing, we might process
* more than one chunk at a time. Still we should not overdo that
* to avoid processing too much data at once as such behaviour is
* known for trashing the memory allocator at times.
*/
for (before = after = session->nsstreams, i = 0;
after <= before && i < INCOMING_DATA_MAX_CHUNKS_AT_ONCE;
after = session->nsstreams, i++)
{
const uint64_t active_streams =
(session->received - session->processed);
/*
* If there are non completed send requests in flight -let's
* not process any incoming data, as it could lead to piling
* up too much send data in send buffers. With many clients
* connected it can lead to excessive memory consumption on
* the server instance.
*/
if (session->sending > 0) {
break;
}
/*
* If we have reached the maximum number of streams used, we
* might stop processing for now, as nghttp2 will happily
* consume as much data as possible.
*/
if (session->nsstreams >= session->max_concurrent_streams &&
active_streams > 0)
{
break;
}
if (http_too_many_active_streams(session)) {
break;
}
isc_buffer_remainingregion(input_data, &chunk);
if (chunk.length == 0) {
break;
}
chunk.length = ISC_MIN(chunk.length, INCOMING_DATA_CHUNK_SIZE);
readlen = nghttp2_session_mem_recv(session->ngsession,
chunk.base, chunk.length);
if (readlen >= 0) {
isc_buffer_forward(input_data, readlen);
session->processed_incoming_data += readlen;
processed += readlen;
} else {
isc_buffer_clear(input_data);
return readlen;
}
}
return processed;
}
static void
http_log_flooding_peer(isc_nm_http_session_t *session) {
const int log_level = ISC_LOG_DEBUG(1);
if (session->handle != NULL && isc_log_wouldlog(isc_lctx, log_level)) {
char client_sabuf[ISC_SOCKADDR_FORMATSIZE];
char local_sabuf[ISC_SOCKADDR_FORMATSIZE];
isc_sockaddr_format(&session->handle->sock->peer, client_sabuf,
sizeof(client_sabuf));
isc_sockaddr_format(&session->handle->sock->iface, local_sabuf,
sizeof(local_sabuf));
isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
ISC_LOGMODULE_NETMGR, log_level,
"Dropping a flooding HTTP/2 peer "
"%s (on %s) - processed: %" PRIu64
" bytes, of them useful: %" PRIu64 "",
client_sabuf, local_sabuf,
session->processed_incoming_data,
session->processed_useful_data);
}
}
static bool
http_is_flooding_peer(isc_nm_http_session_t *session) {
if (session->client) {
return false;
}
/*
* A flooding client can try to open a lot of streams before
* submitting a request. Let's drop such clients.
*/
if (session->received == 0 &&
session->total_opened_sstreams > MAX_STREAMS_BEFORE_FIRST_REQUEST)
{
return true;
}
/*
* We have processed enough data to open at least one stream and
* get some useful data.
*/
if (session->processed_incoming_data >
INCOMING_DATA_INITIAL_STREAM_SIZE &&
(session->total_opened_sstreams == 0 ||
session->processed_useful_data == 0))
{
return true;
}
if (session->processed_incoming_data < INCOMING_DATA_GRACE_SIZE) {
return false;
}
/*
* The overhead of DoH per DNS message can be minimum 160-180
* bytes. We should allow more for extra information that can be
* included in headers, so let's use 256 bytes. Minimum DNS
* message size is 12 bytes. So, (256+12)/12=22. Even that can be
* too restricting for some edge cases, but should be good enough
* for any practical purposes. Not to mention that HTTP/2 may
* include legitimate data that is completely useless for DNS
* purposes...
*
* Anyway, at that point we should have processed enough requests
* for such clients (if any).
*/
if (session->processed_useful_data == 0 ||
(session->processed_incoming_data /
session->processed_useful_data) > 22)
{
return true;
}
return false;
}
/*
* Read callback from TLS socket.
*/
@@ -976,6 +1230,7 @@ http_readcb(isc_nmhandle_t *handle, isc_result_t result, isc_region_t *region,
isc_nm_http_session_t *session = (isc_nm_http_session_t *)data;
isc_nm_http_session_t *tmpsess = NULL;
ssize_t readlen;
isc_buffer_t input;
REQUIRE(VALID_HTTP2_SESSION(session));
@@ -994,11 +1249,17 @@ http_readcb(isc_nmhandle_t *handle, isc_result_t result, isc_region_t *region,
goto done;
}
readlen = nghttp2_session_mem_recv(session->ngsession, region->base,
region->length);
isc_buffer_init(&input, region->base, region->length);
isc_buffer_add(&input, region->length);
readlen = http_process_input_data(session, &input);
if (readlen < 0) {
failed_read_cb(ISC_R_UNEXPECTED, session);
goto done;
} else if (http_is_flooding_peer(session)) {
http_log_flooding_peer(session);
failed_read_cb(ISC_R_RANGE, session);
goto done;
}
if ((size_t)readlen < region->length) {
@@ -1011,11 +1272,12 @@ http_readcb(isc_nmhandle_t *handle, isc_result_t result, isc_region_t *region,
isc_buffer_putmem(session->buf, region->base + readlen,
unread_size);
isc_nm_pauseread(session->handle);
http_do_bio_async(session);
} else {
/* We might have something to receive or send, do IO */
http_do_bio(session, NULL, NULL, NULL);
}
/* We might have something to receive or send, do IO */
http_do_bio(session, NULL, NULL, NULL);
done:
isc__nm_httpsession_detach(&tmpsess);
}
@@ -1053,14 +1315,18 @@ http_writecb(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
}
isc_buffer_free(&req->pending_write_data);
session->processed += req->submitted;
isc_mem_put(session->mctx, req, sizeof(*req));
session->sending--;
http_do_bio(session, NULL, NULL, NULL);
isc_nmhandle_detach(&transphandle);
if (result != ISC_R_SUCCESS && session->sending == 0) {
if (result == ISC_R_SUCCESS) {
http_do_bio(session, NULL, NULL, NULL);
} else {
finish_http_session(session);
}
isc_nmhandle_detach(&transphandle);
isc__nm_httpsession_detach(&session);
}
@@ -1206,7 +1472,9 @@ http_send_outgoing(isc_nm_http_session_t *session, isc_nmhandle_t *httphandle,
*send = (isc_http_send_req_t){ .pending_write_data =
session->pending_write_data,
.cb = cb,
.cbarg = cbarg };
.cbarg = cbarg,
.submitted = session->submitted };
session->submitted = 0;
session->pending_write_data = NULL;
move_pending_send_callbacks(session, send);
@@ -1227,6 +1495,27 @@ nothing_to_send:
return false;
}
static inline bool
http_too_many_active_streams(isc_nm_http_session_t *session) {
const uint64_t active_streams = session->received - session->processed;
const uint64_t max_active_streams = ISC_MIN(
STREAM_CLIENTS_PER_CONN, session->max_concurrent_streams);
if (session->client) {
return false;
}
/*
* Do not process incoming data if there are too many active DNS
* clients (streams) per connection.
*/
if (active_streams >= max_active_streams) {
return true;
}
return false;
}
static void
http_do_bio(isc_nm_http_session_t *session, isc_nmhandle_t *send_httphandle,
isc_nm_cb_t send_cb, void *send_cbarg) {
@@ -1242,59 +1531,140 @@ http_do_bio(isc_nm_http_session_t *session, isc_nmhandle_t *send_httphandle,
finish_http_session(session);
}
return;
} else if (nghttp2_session_want_read(session->ngsession) == 0 &&
nghttp2_session_want_write(session->ngsession) == 0 &&
session->pending_write_data == NULL)
{
session->closing = true;
return;
}
if (nghttp2_session_want_read(session->ngsession) != 0) {
if (!session->reading) {
/* We have not yet started reading from this handle */
isc_nm_read(session->handle, http_readcb, session);
session->reading = true;
} else if (session->buf != NULL) {
size_t remaining =
isc_buffer_remaininglength(session->buf);
/* Leftover data in the buffer, use it */
size_t readlen = nghttp2_session_mem_recv(
session->ngsession,
isc_buffer_current(session->buf), remaining);
if (readlen == remaining) {
isc_buffer_free(&session->buf);
} else {
isc_buffer_forward(session->buf, readlen);
}
http_do_bio(session, send_httphandle, send_cb,
send_cbarg);
return;
} else {
/* Resume reading, it's idempotent, wait for more */
isc_nm_resumeread(session->handle);
}
} else {
/* We don't want more data, stop reading for now */
isc_nm_pauseread(session->handle);
}
if (send_cb != NULL) {
INSIST(VALID_NMHANDLE(send_httphandle));
(void)http_send_outgoing(session, send_httphandle, send_cb,
send_cbarg);
} else {
INSIST(send_httphandle == NULL);
INSIST(send_cb == NULL);
INSIST(send_cbarg == NULL);
return;
}
INSIST(send_httphandle == NULL);
INSIST(send_cb == NULL);
INSIST(send_cbarg == NULL);
if (session->pending_write_data != NULL && session->sending == 0) {
(void)http_send_outgoing(session, NULL, NULL, NULL);
return;
}
if (nghttp2_session_want_read(session->ngsession) != 0) {
if (!session->reading) {
/* We have not yet started reading from this handle */
isc__nmsocket_timer_start(session->handle->sock);
isc_nm_read(session->handle, http_readcb, session);
session->reading = true;
} else if (session->buf != NULL) {
size_t remaining =
isc_buffer_remaininglength(session->buf);
/* Leftover data in the buffer, use it */
size_t remaining_after = 0;
ssize_t readlen = 0;
isc_nm_http_session_t *tmpsess = NULL;
/*
* Let's ensure that HTTP/2 session and its associated
* data will not go "out of scope" too early.
*/
isc__nm_httpsession_attach(session, &tmpsess);
readlen = http_process_input_data(session,
session->buf);
remaining_after =
isc_buffer_remaininglength(session->buf);
if (readlen < 0) {
failed_read_cb(ISC_R_UNEXPECTED, session);
} else if (http_is_flooding_peer(session)) {
http_log_flooding_peer(session);
failed_read_cb(ISC_R_RANGE, session);
} else if ((size_t)readlen == remaining) {
isc_buffer_free(&session->buf);
http_do_bio(session, NULL, NULL, NULL);
} else if (remaining_after > 0 &&
remaining_after < remaining)
{
/*
* We have processed a part of the data, now
* let's delay processing of whatever is left
* here. We want it to be an async operation so
* that we will:
*
* a) let other things run;
* b) have finer grained control over how much
* data is processed at once, because nghttp2
* would happily consume as much data we pass to
* it and that could overwhelm the server.
*/
http_do_bio_async(session);
} else {
(void)http_send_outgoing(session, NULL, NULL,
NULL);
}
isc__nm_httpsession_detach(&tmpsess);
return;
} else {
/* Resume reading, it's idempotent, wait for more */
isc_nm_resumeread(session->handle);
isc__nmsocket_timer_start(session->handle->sock);
}
} else {
/* We don't want more data, stop reading for now */
isc_nm_pauseread(session->handle);
}
/* we might have some data to send after processing */
(void)http_send_outgoing(session, NULL, NULL, NULL);
if (nghttp2_session_want_read(session->ngsession) == 0 &&
nghttp2_session_want_write(session->ngsession) == 0 &&
session->pending_write_data == NULL)
{
session->closing = true;
isc_nm_pauseread(session->handle);
if (session->sending == 0) {
finish_http_session(session);
}
}
return;
}
static void
http_do_bio_async_cb(void *arg) {
isc_nm_http_session_t *session = arg;
REQUIRE(VALID_HTTP2_SESSION(session));
if (session->handle != NULL &&
!isc__nmsocket_closing(session->handle->sock))
{
http_do_bio(session, NULL, NULL, NULL);
}
isc__nm_httpsession_detach(&session);
}
static void
http_do_bio_async(isc_nm_http_session_t *session) {
isc_nm_http_session_t *tmpsess = NULL;
REQUIRE(VALID_HTTP2_SESSION(session));
if (session->handle == NULL ||
isc__nmsocket_closing(session->handle->sock))
{
return;
}
isc__nm_httpsession_attach(session, &tmpsess);
isc__nm_async_run(
&session->handle->sock->mgr->workers[session->handle->sock->tid],
http_do_bio_async_cb, tmpsess);
}
static isc_result_t
get_http_cstream(isc_nmsocket_t *sock, http_cstream_t **streamp) {
http_cstream_t *cstream = sock->h2.connect.cstream;
@@ -1424,6 +1794,7 @@ transport_connect_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
}
http_transpost_tcp_nodelay(handle);
isc__nmhandle_set_manual_timer(session->handle, true);
http_call_connect_cb(http_sock, session, result);
@@ -1670,6 +2041,7 @@ server_on_begin_headers_callback(nghttp2_session *ngsession,
socket->tid = session->handle->sock->tid;
ISC_LINK_INIT(&socket->h2, link);
ISC_LIST_APPEND(session->sstreams, &socket->h2, link);
session->total_opened_sstreams++;
nghttp2_session_set_stream_user_data(ngsession, frame->hd.stream_id,
socket);
@@ -1730,6 +2102,8 @@ server_handle_path_header(isc_nmsocket_t *socket, const uint8_t *value,
socket->mgr->mctx, dns_value,
dns_value_len,
&socket->h2.query_data_len);
socket->h2.session->processed_useful_data +=
dns_value_len;
} else {
socket->h2.query_too_large = true;
return ISC_HTTP_ERROR_PAYLOAD_TOO_LARGE;
@@ -2038,6 +2412,12 @@ server_call_cb(isc_nmsocket_t *socket, const isc_result_t result,
handle = isc__nmhandle_get(socket, NULL, NULL);
if (result != ISC_R_SUCCESS) {
data = NULL;
} else if (socket->h2.session->handle != NULL) {
isc__nmsocket_timer_restart(socket->h2.session->handle->sock);
}
if (result == ISC_R_SUCCESS) {
socket->h2.request_received = true;
socket->h2.session->received++;
}
socket->h2.cb(handle, result, data, socket->h2.cbarg);
isc_nmhandle_detach(&handle);
@@ -2054,6 +2434,12 @@ isc__nm_http_bad_request(isc_nmhandle_t *handle) {
REQUIRE(!atomic_load(&sock->client));
REQUIRE(VALID_HTTP2_SESSION(sock->h2.session));
if (sock->h2.response_submitted ||
!http_session_active(sock->h2.session))
{
return;
}
(void)server_send_error_response(ISC_HTTP_ERROR_BAD_REQUEST,
sock->h2.session->ngsession, sock);
}
@@ -2475,6 +2861,8 @@ httplisten_acceptcb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
isc__nmsocket_attach(httplistensock, &session->serversocket);
server_send_connection_header(session);
isc__nmhandle_set_manual_timer(session->handle, true);
/* TODO H2 */
http_do_bio(session, NULL, NULL, NULL);
return ISC_R_SUCCESS;
+80 -1
View File
@@ -338,6 +338,7 @@ typedef enum isc__netievent_type {
netievent_privilegedtask,
netievent_settlsctx,
netievent_asyncrun,
/*
* event type values higher than this will be treated
@@ -704,6 +705,42 @@ typedef struct isc__netievent__tlsctx {
}
#ifdef HAVE_LIBNGHTTP2
typedef void (*isc__nm_asyncrun_cb_t)(void *);
typedef struct isc__netievent__asyncrun {
isc__netievent_type type;
ISC_LINK(isc__netievent_t) link;
isc__nm_asyncrun_cb_t cb;
void *cbarg;
} isc__netievent__asyncrun_t;
#define NETIEVENT_ASYNCRUN_TYPE(type) \
typedef isc__netievent__asyncrun_t isc__netievent_##type##_t;
#define NETIEVENT_ASYNCRUN_DECL(type) \
isc__netievent_##type##_t *isc__nm_get_netievent_##type( \
isc_nm_t *nm, isc__nm_asyncrun_cb_t cb, void *cbarg); \
void isc__nm_put_netievent_##type(isc_nm_t *nm, \
isc__netievent_##type##_t *ievent);
#define NETIEVENT_ASYNCRUN_DEF(type) \
isc__netievent_##type##_t *isc__nm_get_netievent_##type( \
isc_nm_t *nm, isc__nm_asyncrun_cb_t cb, void *cbarg) { \
isc__netievent_##type##_t *ievent = \
isc__nm_get_netievent(nm, netievent_##type); \
ievent->cb = cb; \
ievent->cbarg = cbarg; \
\
return (ievent); \
} \
\
void isc__nm_put_netievent_##type(isc_nm_t *nm, \
isc__netievent_##type##_t *ievent) { \
ievent->cb = NULL; \
ievent->cbarg = NULL; \
isc__nm_put_netievent(nm, ievent); \
}
typedef struct isc__netievent__http_eps {
NETIEVENT__SOCKET;
isc_nm_http_endpoints_t *endpoints;
@@ -748,6 +785,7 @@ typedef union {
isc__netievent_tlsconnect_t nitc;
isc__netievent__tlsctx_t nitls;
#ifdef HAVE_LIBNGHTTP2
isc__netievent__asyncrun_t niasync;
isc__netievent__http_eps_t nihttpeps;
#endif /* HAVE_LIBNGHTTP2 */
} isc__netievent_storage_t;
@@ -940,6 +978,7 @@ typedef struct isc_nmsocket_h2 {
isc_nm_http_endpoints_t *peer_endpoints;
bool request_received;
bool response_submitted;
struct {
char *uri;
@@ -1224,6 +1263,7 @@ struct isc_nmsocket {
isc_barrier_t barrier;
bool barrier_initialised;
atomic_bool manual_read_timer;
#ifdef NETMGR_TRACE
void *backtrace[TRACE_SIZE];
int backtrace_size;
@@ -1542,6 +1582,9 @@ isc__nm_tcp_settimeout(isc_nmhandle_t *handle, uint32_t timeout);
* Set the read timeout for the TCP socket associated with 'handle'.
*/
void
isc__nmhandle_tcp_set_manual_timer(isc_nmhandle_t *handle, const bool manual);
void
isc__nm_async_tcpconnect(isc__networker_t *worker, isc__netievent_t *ev0);
void
@@ -1784,6 +1827,9 @@ isc__nm_tls_cleartimeout(isc_nmhandle_t *handle);
* around.
*/
void
isc__nmhandle_tls_set_manual_timer(isc_nmhandle_t *handle, const bool manual);
const char *
isc__nm_tls_verify_tls_peer_result_string(const isc_nmhandle_t *handle);
@@ -1801,6 +1847,15 @@ void
isc__nmhandle_tls_setwritetimeout(isc_nmhandle_t *handle,
uint64_t write_timeout);
bool
isc__nmsocket_tls_timer_running(isc_nmsocket_t *sock);
void
isc__nmsocket_tls_timer_restart(isc_nmsocket_t *sock);
void
isc__nmsocket_tls_timer_stop(isc_nmsocket_t *sock);
void
isc__nm_http_stoplistening(isc_nmsocket_t *sock);
@@ -1893,7 +1948,10 @@ void
isc__nm_http_set_max_streams(isc_nmsocket_t *listener,
const uint32_t max_concurrent_streams);
#endif
void
isc__nm_async_asyncrun(isc__networker_t *worker, isc__netievent_t *ev0);
#endif /* HAVE_LIBNGHTTP2 */
void
isc__nm_async_settlsctx(isc__networker_t *worker, isc__netievent_t *ev0);
@@ -2083,6 +2141,8 @@ NETIEVENT_SOCKET_TYPE(tlsdnscycle);
NETIEVENT_SOCKET_REQ_TYPE(httpsend);
NETIEVENT_SOCKET_TYPE(httpclose);
NETIEVENT_SOCKET_HTTP_EPS_TYPE(httpendpoints);
NETIEVENT_ASYNCRUN_TYPE(asyncrun);
#endif /* HAVE_LIBNGHTTP2 */
NETIEVENT_SOCKET_REQ_TYPE(tcpconnect);
@@ -2157,6 +2217,8 @@ NETIEVENT_SOCKET_DECL(tlsdnscycle);
NETIEVENT_SOCKET_REQ_DECL(httpsend);
NETIEVENT_SOCKET_DECL(httpclose);
NETIEVENT_SOCKET_HTTP_EPS_DECL(httpendpoints);
NETIEVENT_ASYNCRUN_DECL(asyncrun);
#endif /* HAVE_LIBNGHTTP2 */
NETIEVENT_SOCKET_REQ_DECL(tcpconnect);
@@ -2273,3 +2335,20 @@ isc__nmsocket_writetimeout_cb(void *data, isc_result_t eresult);
void
isc__nmsocket_log_tls_session_reuse(isc_nmsocket_t *sock, isc_tls_t *tls);
void
isc__nmhandle_set_manual_timer(isc_nmhandle_t *handle, const bool manual);
/*
* Set manual read timer control mode - so that it will not get reset
* automatically on read nor get started when read is initiated.
*/
#if HAVE_LIBNGHTTP2
void
isc__nm_async_run(isc__networker_t *worker, isc__nm_asyncrun_cb_t cb,
void *cbarg);
/*
* Call the given callback asynchronously by the give network manager
* worker, pass the given argument to it.
*/
#endif /* HAVE_LIBNGHTTP2 */
+78
View File
@@ -998,6 +998,8 @@ process_netievent(isc__networker_t *worker, isc__netievent_t *ievent) {
NETIEVENT_CASE(httpsend);
NETIEVENT_CASE(httpclose);
NETIEVENT_CASE(httpendpoints);
NETIEVENT_CASE(asyncrun);
#endif
NETIEVENT_CASE(settlsctx);
NETIEVENT_CASE(sockstop);
@@ -1116,6 +1118,8 @@ NETIEVENT_SOCKET_DEF(tlsdnsshutdown);
NETIEVENT_SOCKET_REQ_DEF(httpsend);
NETIEVENT_SOCKET_DEF(httpclose);
NETIEVENT_SOCKET_HTTP_EPS_DEF(httpendpoints);
NETIEVENT_ASYNCRUN_DEF(asyncrun);
#endif /* HAVE_LIBNGHTTP2 */
NETIEVENT_SOCKET_REQ_DEF(tcpconnect);
@@ -1627,6 +1631,7 @@ isc___nmsocket_init(isc_nmsocket_t *sock, isc_nm_t *mgr, isc_nmsocket_type type,
atomic_init(&sock->keepalive, false);
atomic_init(&sock->connected, false);
atomic_init(&sock->timedout, false);
atomic_init(&sock->manual_read_timer, false);
atomic_init(&sock->active_child_connections, 0);
@@ -2136,6 +2141,15 @@ void
isc__nmsocket_timer_restart(isc_nmsocket_t *sock) {
REQUIRE(VALID_NMSOCK(sock));
switch (sock->type) {
#if HAVE_LIBNGHTTP2
case isc_nm_tlssocket:
return isc__nmsocket_tls_timer_restart(sock);
#endif /* HAVE_LIBNGHTTP2 */
default:
break;
}
if (uv_is_closing((uv_handle_t *)&sock->read_timer)) {
return;
}
@@ -2170,6 +2184,15 @@ bool
isc__nmsocket_timer_running(isc_nmsocket_t *sock) {
REQUIRE(VALID_NMSOCK(sock));
switch (sock->type) {
#if HAVE_LIBNGHTTP2
case isc_nm_tlssocket:
return isc__nmsocket_tls_timer_running(sock);
#endif /* HAVE_LIBNGHTTP2 */
default:
break;
}
return uv_is_active((uv_handle_t *)&sock->read_timer);
}
@@ -2190,6 +2213,15 @@ isc__nmsocket_timer_stop(isc_nmsocket_t *sock) {
REQUIRE(VALID_NMSOCK(sock));
switch (sock->type) {
#if HAVE_LIBNGHTTP2
case isc_nm_tlssocket:
return isc__nmsocket_tls_timer_stop(sock);
#endif /* HAVE_LIBNGHTTP2 */
default:
break;
}
/* uv_timer_stop() is idempotent, no need to check if running */
r = uv_timer_stop(&sock->read_timer);
@@ -3932,6 +3964,52 @@ isc__nmsocket_log_tls_session_reuse(isc_nmsocket_t *sock, isc_tls_t *tls) {
client_sabuf, local_sabuf);
}
void
isc__nmhandle_set_manual_timer(isc_nmhandle_t *handle, const bool manual) {
REQUIRE(VALID_NMHANDLE(handle));
REQUIRE(VALID_NMSOCK(handle->sock));
isc_nmsocket_t *sock = handle->sock;
switch (sock->type) {
case isc_nm_tcpsocket:
isc__nmhandle_tcp_set_manual_timer(handle, manual);
return;
#if HAVE_LIBNGHTTP2
case isc_nm_tlssocket:
isc__nmhandle_tls_set_manual_timer(handle, manual);
return;
#endif /* HAVE_LIBNGHTTP2 */
default:
break;
};
UNREACHABLE();
}
#if HAVE_LIBNGHTTP2
void
isc__nm_async_run(isc__networker_t *worker, isc__nm_asyncrun_cb_t cb,
void *cbarg) {
isc__netievent__asyncrun_t *ievent = NULL;
REQUIRE(worker != NULL);
REQUIRE(cb != NULL);
ievent = isc__nm_get_netievent_asyncrun(worker->mgr, cb, cbarg);
isc__nm_enqueue_ievent(worker, (isc__netievent_t *)ievent);
}
void
isc__nm_async_asyncrun(isc__networker_t *worker, isc__netievent_t *ev0) {
isc__netievent_asyncrun_t *ievent = (isc__netievent_asyncrun_t *)ev0;
UNUSED(worker);
ievent->cb(ievent->cbarg);
}
#endif /* HAVE_LIBNGHTTP2 */
#ifdef NETMGR_TRACE
/*
* Dump all active sockets in netmgr. We output to stderr
+22 -4
View File
@@ -784,7 +784,9 @@ isc__nm_async_tcpstartread(isc__networker_t *worker, isc__netievent_t *ev0) {
return;
}
isc__nmsocket_timer_start(sock);
if (!atomic_load(&sock->manual_read_timer)) {
isc__nmsocket_timer_start(sock);
}
}
void
@@ -822,7 +824,9 @@ isc__nm_async_tcppauseread(isc__networker_t *worker, isc__netievent_t *ev0) {
REQUIRE(sock->tid == isc_nm_tid());
UNUSED(worker);
isc__nmsocket_timer_stop(sock);
if (!atomic_load(&sock->manual_read_timer)) {
isc__nmsocket_timer_stop(sock);
}
isc__nm_stop_reading(sock);
}
@@ -931,8 +935,10 @@ isc__nm_tcp_read_cb(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) {
}
}
/* The timer will be updated */
isc__nmsocket_timer_restart(sock);
if (!atomic_load(&sock->manual_read_timer)) {
/* The timer will be updated */
isc__nmsocket_timer_restart(sock);
}
}
free:
@@ -1521,3 +1527,15 @@ isc__nm_tcp_listener_nactive(isc_nmsocket_t *listener) {
INSIST(nactive >= 0);
return nactive;
}
void
isc__nmhandle_tcp_set_manual_timer(isc_nmhandle_t *handle, const bool manual) {
isc_nmsocket_t *sock;
REQUIRE(VALID_NMHANDLE(handle));
sock = handle->sock;
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(sock->type == isc_nm_tcpsocket);
atomic_store(&sock->manual_read_timer, manual);
}
+122 -15
View File
@@ -60,6 +60,12 @@ tls_error_to_result(const int tls_err, const int tls_state, isc_tls_t *tls) {
}
}
static void
tls_read_start(isc_nmsocket_t *sock);
static void
tls_read_stop(isc_nmsocket_t *sock);
static void
tls_failed_read_cb(isc_nmsocket_t *sock, const isc_result_t result);
@@ -203,8 +209,13 @@ tls_failed_read_cb(isc_nmsocket_t *sock, const isc_result_t result) {
tls_call_connect_cb(sock, handle, result);
isc__nmsocket_clearcb(sock);
isc_nmhandle_detach(&handle);
} else if (sock->recv_cb != NULL && sock->statichandle != NULL &&
(sock->recv_read || result == ISC_R_TIMEDOUT))
goto do_destroy;
}
isc__nmsocket_timer_stop(sock);
if (sock->recv_cb != NULL && sock->statichandle != NULL &&
(sock->recv_read || result == ISC_R_TIMEDOUT))
{
isc__nm_uvreq_t *req = NULL;
INSIST(VALID_NMHANDLE(sock->statichandle));
@@ -218,13 +229,13 @@ tls_failed_read_cb(isc_nmsocket_t *sock, const isc_result_t result) {
}
isc__nm_readcb(sock, req, result);
if (result == ISC_R_TIMEDOUT &&
(sock->outerhandle == NULL ||
isc__nmsocket_timer_running(sock->outerhandle->sock)))
isc__nmsocket_timer_running(sock))
{
destroy = false;
}
}
do_destroy:
if (destroy) {
isc__nmsocket_prep_destroy(sock);
}
@@ -344,6 +355,8 @@ tls_try_handshake(isc_nmsocket_t *sock, isc_result_t *presult) {
INSIST(sock->statichandle == NULL);
isc__nmsocket_log_tls_session_reuse(sock, sock->tlsstream.tls);
tlshandle = isc__nmhandle_get(sock, &sock->peer, &sock->iface);
isc__nmsocket_timer_stop(sock);
tls_read_stop(sock);
if (isc__nm_closing(sock)) {
result = ISC_R_SHUTTINGDOWN;
@@ -437,6 +450,7 @@ tls_do_bio(isc_nmsocket_t *sock, isc_region_t *received_data,
sock->tlsstream.state = TLS_HANDSHAKE;
rv = tls_try_handshake(sock, NULL);
INSIST(SSL_is_init_finished(sock->tlsstream.tls) == 0);
isc__nmsocket_timer_restart(sock);
} else if (sock->tlsstream.state == TLS_CLOSED) {
return;
} else { /* initialised and doing I/O */
@@ -502,6 +516,7 @@ tls_do_bio(isc_nmsocket_t *sock, isc_region_t *received_data,
!atomic_load(&sock->readpaused) &&
sock->statichandle != NULL && !finish)
{
bool was_new_data = false;
uint8_t recv_buf[TLS_BUF_SIZE];
INSIST(sock->tlsstream.state > TLS_HANDSHAKE);
while ((rv = SSL_read_ex(sock->tlsstream.tls, recv_buf,
@@ -510,7 +525,7 @@ tls_do_bio(isc_nmsocket_t *sock, isc_region_t *received_data,
isc_region_t region;
region = (isc_region_t){ .base = &recv_buf[0],
.length = len };
was_new_data = true;
INSIST(VALID_NMHANDLE(sock->statichandle));
sock->recv_cb(sock->statichandle, ISC_R_SUCCESS,
&region, sock->recv_cbarg);
@@ -547,8 +562,29 @@ tls_do_bio(isc_nmsocket_t *sock, isc_region_t *received_data,
break;
}
}
if (was_new_data && !sock->manual_read_timer) {
/*
* Some data has been decrypted, it is the right
* time to stop the read timer as it will be
* restarted on the next read attempt.
*/
isc__nmsocket_timer_stop(sock);
}
}
}
/*
* Setting 'finish' to 'true' means that we are about to close the
* TLS stream (we intend to send TLS shutdown message to the
* remote side). After that no new data can be received, so we
* should stop the timer regardless of the
* 'sock->manual_read_timer' value.
*/
if (finish) {
isc__nmsocket_timer_stop(sock);
}
errno = 0;
tls_status = SSL_get_error(sock->tlsstream.tls, rv);
saved_errno = errno;
@@ -601,14 +637,7 @@ tls_do_bio(isc_nmsocket_t *sock, isc_region_t *received_data,
return;
}
INSIST(VALID_NMHANDLE(sock->outerhandle));
if (sock->tlsstream.reading) {
isc_nm_resumeread(sock->outerhandle);
} else if (sock->tlsstream.state == TLS_HANDSHAKE) {
sock->tlsstream.reading = true;
isc_nm_read(sock->outerhandle, tls_readcb, sock);
}
tls_read_start(sock);
return;
default:
result = tls_error_to_result(tls_status, sock->tlsstream.state,
@@ -743,6 +772,7 @@ tlslisten_acceptcb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
RUNTIME_CHECK(result == ISC_R_SUCCESS);
/* TODO: catch failure code, detach tlssock, and log the error */
isc__nmhandle_set_manual_timer(tlssock->outerhandle, true);
tls_do_bio(tlssock, NULL, NULL, false);
return result;
}
@@ -898,6 +928,29 @@ isc__nm_tls_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) {
(isc__netievent_t *)ievent);
}
static void
tls_read_start(isc_nmsocket_t *sock) {
INSIST(VALID_NMHANDLE(sock->outerhandle));
if (sock->tlsstream.reading) {
isc_nm_resumeread(sock->outerhandle);
} else if (sock->tlsstream.state == TLS_HANDSHAKE) {
sock->tlsstream.reading = true;
isc_nm_read(sock->outerhandle, tls_readcb, sock);
}
if (!sock->manual_read_timer) {
isc__nmsocket_timer_start(sock);
}
}
static void
tls_read_stop(isc_nmsocket_t *sock) {
if (sock->outerhandle != NULL) {
isc_nm_pauseread(sock->outerhandle);
}
}
void
isc__nm_tls_pauseread(isc_nmhandle_t *handle) {
REQUIRE(VALID_NMHANDLE(handle));
@@ -906,9 +959,11 @@ isc__nm_tls_pauseread(isc_nmhandle_t *handle) {
if (atomic_compare_exchange_strong(&handle->sock->readpaused,
&(bool){ false }, true))
{
if (handle->sock->outerhandle != NULL) {
isc_nm_pauseread(handle->sock->outerhandle);
if (!atomic_load(&handle->sock->manual_read_timer)) {
isc__nmsocket_timer_stop(handle->sock);
}
tls_read_stop(handle->sock);
}
}
@@ -937,6 +992,7 @@ tls_close_direct(isc_nmsocket_t *sock) {
* external references, we can close everything.
*/
if (sock->outerhandle != NULL) {
isc__nmsocket_timer_stop(sock);
isc_nm_pauseread(sock->outerhandle);
isc__nmsocket_clearcb(sock->outerhandle->sock);
isc_nmhandle_detach(&sock->outerhandle);
@@ -1085,6 +1141,7 @@ tcp_connected(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
*/
handle->sock->tlsstream.tlssocket = tlssock;
isc__nmhandle_set_manual_timer(tlssock->outerhandle, true);
tls_do_bio(tlssock, NULL, NULL, false);
return;
error:
@@ -1251,6 +1308,44 @@ isc__nmhandle_tls_setwritetimeout(isc_nmhandle_t *handle,
}
}
bool
isc__nmsocket_tls_timer_running(isc_nmsocket_t *sock) {
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(sock->type == isc_nm_tlssocket);
if (sock->outerhandle != NULL) {
INSIST(VALID_NMHANDLE(sock->outerhandle));
REQUIRE(VALID_NMSOCK(sock->outerhandle->sock));
return isc__nmsocket_timer_running(sock->outerhandle->sock);
}
return false;
}
void
isc__nmsocket_tls_timer_restart(isc_nmsocket_t *sock) {
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(sock->type == isc_nm_tlssocket);
if (sock->outerhandle != NULL) {
INSIST(VALID_NMHANDLE(sock->outerhandle));
REQUIRE(VALID_NMSOCK(sock->outerhandle->sock));
isc__nmsocket_timer_restart(sock->outerhandle->sock);
}
}
void
isc__nmsocket_tls_timer_stop(isc_nmsocket_t *sock) {
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(sock->type == isc_nm_tlssocket);
if (sock->outerhandle != NULL) {
INSIST(VALID_NMHANDLE(sock->outerhandle));
REQUIRE(VALID_NMSOCK(sock->outerhandle->sock));
isc__nmsocket_timer_stop(sock->outerhandle->sock);
}
}
const char *
isc__nm_tls_verify_tls_peer_result_string(const isc_nmhandle_t *handle) {
isc_nmsocket_t *sock = NULL;
@@ -1351,3 +1446,15 @@ tls_try_shutdown(isc_tls_t *tls, const bool force) {
(void)SSL_shutdown(tls);
}
}
void
isc__nmhandle_tls_set_manual_timer(isc_nmhandle_t *handle, const bool manual) {
isc_nmsocket_t *sock;
REQUIRE(VALID_NMHANDLE(handle));
sock = handle->sock;
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(sock->type == isc_nm_tlssocket);
atomic_store(&sock->manual_read_timer, manual);
}
+8 -4
View File
@@ -2098,7 +2098,8 @@ addname:
if (trdataset != NULL && dns_rdatatype_followadditional(type)) {
if (client->additionaldepth++ < client->view->max_restarts) {
eresult = dns_rdataset_additionaldata(
trdataset, fname, query_additional_cb, qctx);
trdataset, fname, query_additional_cb, qctx,
DNS_RDATASET_MAXADDITIONAL);
}
client->additionaldepth--;
}
@@ -2198,7 +2199,7 @@ regular:
* We don't care if dns_rdataset_additionaldata() fails.
*/
(void)dns_rdataset_additionaldata(rdataset, name, query_additional_cb,
qctx);
qctx, DNS_RDATASET_MAXADDITIONAL);
CTRACE(ISC_LOG_DEBUG(3), "query_additional: done");
}
@@ -2224,7 +2225,8 @@ query_addrrset(query_ctx_t *qctx, dns_name_t **namep,
* To the current response for 'client', add the answer RRset
* '*rdatasetp' and an optional signature set '*sigrdatasetp', with
* owner name '*namep', to section 'section', unless they are
* already there. Also add any pertinent additional data.
* already there. Also add any pertinent additional data, unless
* the query was for type ANY.
*
* If 'dbuf' is not NULL, then '*namep' is the name whose data is
* stored in 'dbuf'. In this case, query_addrrset() guarantees that
@@ -2279,7 +2281,9 @@ query_addrrset(query_ctx_t *qctx, dns_name_t **namep,
*/
query_addtoname(mname, rdataset);
query_setorder(qctx, mname, rdataset);
query_additional(qctx, mname, rdataset);
if (qctx->qtype != dns_rdatatype_any) {
query_additional(qctx, mname, rdataset);
}
/*
* Note: we only add SIGs if we've added the type they cover, so