Merge tag 'v9.18.33' into bind-9.18
This commit is contained in:
@@ -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))
|
||||
|
||||
@@ -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.
|
||||
@@ -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
|
||||
|
||||
@@ -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::
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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`
|
||||
|
||||
|
||||
@@ -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`
|
||||
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
@@ -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
@@ -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,
|
||||
®ion, 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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user