Implement TLS manual read timer control functionality

This commit adds a manual TLS read timer control mode which is
supposed to override automatic resetting of the timer when any data is
received.

It both depends and complements similar functionality in TCP.
This commit is contained in:
Artem Boldariev 2024-09-04 21:35:35 +03:00 committed by Andoni Duarte
parent a67b325542
commit 13d521fa5f
3 changed files with 166 additions and 15 deletions

View file

@ -1793,6 +1793,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);
@ -1810,6 +1813,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);

View file

@ -2137,6 +2137,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;
}
@ -2171,6 +2180,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);
}
@ -2191,6 +2209,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);
@ -3944,6 +3971,11 @@ isc__nmhandle_set_manual_timer(isc_nmhandle_t *handle, const bool manual) {
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;
};

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);
}