From caa5b6548a11da6ca772d6f7e10db3a164a18f8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Tue, 16 Mar 2021 09:03:02 +0100 Subject: [PATCH] Fix TCPDNS and TLSDNS timers After the TCPDNS refactoring the initial and idle timers were broken and only the tcp-initial-timeout was always applied on the whole TCP connection. This broke any TCP connection that took longer than tcp-initial-timeout, most often this would affect large zone AXFRs. This commit changes the timeout logic in this way: * On TCP connection accept the tcp-initial-timeout is applied and the timer is started * When we are processing and/or sending any DNS message the timer is stopped * When we stop processing all DNS messages, the tcp-idle-timeout is applied and the timer is started again --- lib/isc/include/isc/netmgr.h | 6 +- lib/isc/netmgr/http.c | 18 +- lib/isc/netmgr/netmgr-int.h | 35 +++- lib/isc/netmgr/netmgr.c | 337 ++++++++++++++++++++++++++++++++--- lib/isc/netmgr/tcp.c | 62 ++----- lib/isc/netmgr/tcpdns.c | 199 ++++++++++----------- lib/isc/netmgr/tlsdns.c | 124 +++++-------- lib/isc/netmgr/tlsstream.c | 15 ++ lib/isc/netmgr/udp.c | 56 +----- lib/isc/win32/libisc.def.in | 1 + 10 files changed, 538 insertions(+), 315 deletions(-) diff --git a/lib/isc/include/isc/netmgr.h b/lib/isc/include/isc/netmgr.h index fd670b190c..5a3e6d3b63 100644 --- a/lib/isc/include/isc/netmgr.h +++ b/lib/isc/include/isc/netmgr.h @@ -162,9 +162,11 @@ isc_nmhandle_setdata(isc_nmhandle_t *handle, void *arg, void isc_nmhandle_settimeout(isc_nmhandle_t *handle, uint32_t timeout); +void +isc_nmhandle_cleartimeout(isc_nmhandle_t *handle); /*%< - * Set the read/recv timeout for the socket connected to 'handle' - * to 'timeout', and reset the timer. + * Set/clear the read/recv timeout for the socket connected to 'handle' + * to 'timeout', and reset the timer, in miliseconds. * * When this is called on a 'wrapper' socket handle (for example, * a TCPDNS socket wrapping a TCP connection), the timer is set for diff --git a/lib/isc/netmgr/http.c b/lib/isc/netmgr/http.c index d3dd9a6b0e..8d0de0a7e6 100644 --- a/lib/isc/netmgr/http.c +++ b/lib/isc/netmgr/http.c @@ -1764,7 +1764,7 @@ failed_send_cb(isc_nmsocket_t *sock, isc__nm_uvreq_t *req, REQUIRE(VALID_UVREQ(req)); if (req->cb.send != NULL) { - isc__nm_sendcb(sock, req, eresult); + isc__nm_sendcb(sock, req, eresult, true); } else { isc__nm_uvreq_put(&req, sock); } @@ -2537,6 +2537,22 @@ isc__nm_http_cleanup_data(isc_nmsocket_t *sock) { } } +void +isc__nm_http_cleartimeout(isc_nmhandle_t *handle) { + isc_nmsocket_t *sock = NULL; + + REQUIRE(VALID_NMHANDLE(handle)); + REQUIRE(VALID_NMSOCK(handle->sock)); + REQUIRE(handle->sock->type == isc_nm_httpsocket); + + sock = handle->sock; + if (sock->h2.session != NULL && sock->h2.session->handle) { + INSIST(VALID_HTTP2_SESSION(sock->h2.session)); + INSIST(VALID_NMHANDLE(sock->h2.session->handle)); + isc_nmhandle_cleartimeout(sock->h2.session->handle); + } +} + void isc__nm_http_settimeout(isc_nmhandle_t *handle, uint32_t timeout) { isc_nmsocket_t *sock = NULL; diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h index 5514355520..a30c017923 100644 --- a/lib/isc/netmgr/netmgr-int.h +++ b/lib/isc/netmgr/netmgr-int.h @@ -769,6 +769,12 @@ typedef struct isc_nmsocket_h2 { } connect; } isc_nmsocket_h2_t; +typedef void (*isc_nm_closehandlecb_t)(void *arg); +/*%< + * Opaque callback function, used for isc_nmhandle 'reset' and 'free' + * callbacks. + */ + struct isc_nmsocket { /*% Unlocked, RO */ int magic; @@ -1015,7 +1021,7 @@ struct isc_nmsocket { * as the argument whenever a handle's references drop * to zero, after its reset callback has been called. */ - isc_nm_opaquecb_t closehandle_cb; + isc_nm_closehandlecb_t closehandle_cb; isc_nmhandle_t *recv_handle; isc_nm_recv_cb_t recv_cb; @@ -1154,6 +1160,16 @@ isc__nmsocket_clearcb(isc_nmsocket_t *sock); * Clear the recv and accept callbacks in 'sock'. */ +void +isc__nmsocket_timer_stop(isc_nmsocket_t *sock); +void +isc__nmsocket_timer_start(isc_nmsocket_t *sock); +void +isc__nmsocket_timer_restart(isc_nmsocket_t *sock); +/*%< + * Start/stop/restart the read timeout on the socket + */ + void isc__nm_connectcb(isc_nmsocket_t *sock, isc__nm_uvreq_t *uvreq, isc_result_t eresult); @@ -1182,7 +1198,7 @@ isc__nm_async_readcb(isc__networker_t *worker, isc__netievent_t *ev0); void isc__nm_sendcb(isc_nmsocket_t *sock, isc__nm_uvreq_t *uvreq, - isc_result_t eresult); + isc_result_t eresult, bool async); void isc__nm_async_sendcb(isc__networker_t *worker, isc__netievent_t *ev0); /*%< @@ -1238,7 +1254,7 @@ isc__nm_udp_stoplistening(isc_nmsocket_t *sock); void isc__nm_udp_settimeout(isc_nmhandle_t *handle, uint32_t timeout); /*%< - * Set the recv timeout for the UDP socket associated with 'handle'. + * Set or clear the recv timeout for the UDP socket associated with 'handle'. */ void @@ -1532,6 +1548,8 @@ isc__nm_tls_stoplistening(isc_nmsocket_t *sock); void isc__nm_tls_settimeout(isc_nmhandle_t *handle, uint32_t timeout); +void +isc__nm_tls_cleartimeout(isc_nmhandle_t *handle); /*%< * Set the read timeout and reset the timer for the socket * associated with 'handle', and the TCP socket it wraps @@ -1543,6 +1561,8 @@ isc__nm_http_stoplistening(isc_nmsocket_t *sock); void isc__nm_http_settimeout(isc_nmhandle_t *handle, uint32_t timeout); +void +isc__nm_http_cleartimeout(isc_nmhandle_t *handle); /*%< * Set the read timeout and reset the timer for the socket * associated with 'handle', and the TLS/TCP socket it wraps @@ -1821,3 +1841,12 @@ NETIEVENT_DECL(pause); NETIEVENT_DECL(resume); NETIEVENT_DECL(shutdown); NETIEVENT_DECL(stop); + +void +isc__nm_udp_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result); +void +isc__nm_tcp_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result); +void +isc__nm_tcpdns_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result); +void +isc__nm_tlsdns_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result); diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c index eb6ef11961..47add06aa1 100644 --- a/lib/isc/netmgr/netmgr.c +++ b/lib/isc/netmgr/netmgr.c @@ -1583,33 +1583,323 @@ isc_nmhandle_setdata(isc_nmhandle_t *handle, void *arg, handle->dofree = dofree; } -void -isc_nmhandle_settimeout(isc_nmhandle_t *handle, uint32_t timeout) { - REQUIRE(VALID_NMHANDLE(handle)); - - switch (handle->sock->type) { +static void +isc__nmsocket_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result) { + REQUIRE(VALID_NMSOCK(sock)); + switch (sock->type) { case isc_nm_udpsocket: - isc__nm_udp_settimeout(handle, timeout); - break; + isc__nm_udp_failed_read_cb(sock, result); + return; case isc_nm_tcpsocket: - isc__nm_tcp_settimeout(handle, timeout); + isc__nm_tcp_failed_read_cb(sock, result); + return; + case isc_nm_tcpdnssocket: + isc__nm_tcpdns_failed_read_cb(sock, result); + return; + case isc_nm_tlsdnssocket: + isc__nm_tlsdns_failed_read_cb(sock, result); + return; + default: + INSIST(0); + ISC_UNREACHABLE(); + } +} + +static void +isc__nmsocket_readtimeout_cb(uv_timer_t *timer) { + isc_nmsocket_t *sock = uv_handle_get_data((uv_handle_t *)timer); + + REQUIRE(VALID_NMSOCK(sock)); + REQUIRE(sock->tid == isc_nm_tid()); + REQUIRE(sock->reading); + + isc__nmsocket_failed_read_cb(sock, ISC_R_TIMEDOUT); +} + +void +isc__nmsocket_timer_restart(isc_nmsocket_t *sock) { + REQUIRE(VALID_NMSOCK(sock)); + + if (sock->read_timeout == 0) { + return; + } + + int r = uv_timer_start(&sock->timer, isc__nmsocket_readtimeout_cb, + sock->read_timeout, 0); + RUNTIME_CHECK(r == 0); +} + +void +isc__nmsocket_timer_start(isc_nmsocket_t *sock) { + REQUIRE(VALID_NMSOCK(sock)); + + if (uv_is_active((uv_handle_t *)&sock->timer)) { + return; + } + + isc__nmsocket_timer_restart(sock); +} + +void +isc__nmsocket_timer_stop(isc_nmsocket_t *sock) { + REQUIRE(VALID_NMSOCK(sock)); + + if (!uv_is_active((uv_handle_t *)&sock->timer)) { + return; + } + + int r = uv_timer_stop(&sock->timer); + RUNTIME_CHECK(r == 0); +} + +isc__nm_uvreq_t * +isc__nm_get_read_req(isc_nmsocket_t *sock, isc_sockaddr_t *sockaddr) { + isc__nm_uvreq_t *req = NULL; + + req = isc__nm_uvreq_get(sock->mgr, sock); + req->cb.recv = sock->recv_cb; + req->cbarg = sock->recv_cbarg; + + if (atomic_load(&sock->client)) { + isc_nmhandle_attach(sock->statichandle, &req->handle); + } else { + req->handle = isc__nmhandle_get(sock, sockaddr, NULL); + } + + return req; +} + +/*%< + * Allocator for read operations. Limited to size 2^16. + * + * Note this doesn't actually allocate anything, it just assigns the + * worker's receive buffer to a socket, and marks it as "in use". + */ +void +isc__nm_alloc_cb(uv_handle_t *handle, size_t size, uv_buf_t *buf) { + isc_nmsocket_t *sock = uv_handle_get_data(handle); + isc__networker_t *worker = NULL; + + REQUIRE(VALID_NMSOCK(sock)); + REQUIRE(isc__nm_in_netthread()); + + switch (sock->type) { + case isc_nm_udpsocket: + REQUIRE(size <= ISC_NETMGR_RECVBUF_SIZE); + size = ISC_NETMGR_RECVBUF_SIZE; break; case isc_nm_tcpdnssocket: - isc__nm_tcpdns_settimeout(handle, timeout); break; case isc_nm_tlsdnssocket: - isc__nm_tlsdns_settimeout(handle, timeout); - break; - case isc_nm_tlssocket: - isc__nm_tls_settimeout(handle, timeout); - break; - case isc_nm_httpsocket: - isc__nm_http_settimeout(handle, timeout); + /* + * We need to limit the individual chunks to be read, so the + * BIO_write() will always succeed and the consumed before the + * next readcb is called. + */ + if (size >= ISC_NETMGR_TLSBUF_SIZE) { + size = ISC_NETMGR_TLSBUF_SIZE; + } break; default: INSIST(0); ISC_UNREACHABLE(); } + + worker = &sock->mgr->workers[sock->tid]; + INSIST(!worker->recvbuf_inuse); + + buf->base = worker->recvbuf; + buf->len = size; + worker->recvbuf_inuse = true; +} + +void +isc__nm_start_reading(isc_nmsocket_t *sock) { + int r; + + if (sock->reading) { + return; + } + + switch (sock->type) { + case isc_nm_udpsocket: + r = uv_udp_recv_start(&sock->uv_handle.udp, isc__nm_alloc_cb, + isc__nm_udp_read_cb); + break; + case isc_nm_tcpdnssocket: + r = uv_read_start(&sock->uv_handle.stream, isc__nm_alloc_cb, + isc__nm_tcpdns_read_cb); + break; + case isc_nm_tlsdnssocket: + r = uv_read_start(&sock->uv_handle.stream, isc__nm_alloc_cb, + isc__nm_tlsdns_read_cb); + break; + default: + INSIST(0); + ISC_UNREACHABLE(); + } + RUNTIME_CHECK(r == 0); + sock->reading = true; +} + +void +isc__nm_stop_reading(isc_nmsocket_t *sock) { + int r; + + if (!sock->reading) { + return; + } + + switch (sock->type) { + case isc_nm_udpsocket: + r = uv_udp_recv_stop(&sock->uv_handle.udp); + break; + case isc_nm_tcpdnssocket: + case isc_nm_tlsdnssocket: + r = uv_read_stop(&sock->uv_handle.stream); + break; + default: + INSIST(0); + ISC_UNREACHABLE(); + } + RUNTIME_CHECK(r == 0); + sock->reading = false; +} + +bool +isc__nm_inactive(isc_nmsocket_t *sock) { + return (!isc__nmsocket_active(sock) || atomic_load(&sock->closing) || + atomic_load(&sock->mgr->closing) || + (sock->server != NULL && !isc__nmsocket_active(sock->server))); +} + +static isc_result_t +processbuffer(isc_nmsocket_t *sock) { + switch (sock->type) { + case isc_nm_tcpdnssocket: + return (isc__nm_tcpdns_processbuffer(sock)); + case isc_nm_tlsdnssocket: + return (isc__nm_tcpdns_processbuffer(sock)); + default: + INSIST(0); + ISC_UNREACHABLE(); + } +} + +/* + * Process a DNS message. + * + * If we only have an incomplete DNS message, we don't touch any + * timers. If we do have a full message, reset the timer. + * + * Stop reading if this is a client socket, or if the server socket + * has been set to sequential mode, or the number of queries we are + * processing simultaneously has reached the clients-per-connection + * limit. In this case we'll be called again by resume_processing() + * later. + */ +void +isc__nm_process_sock_buffer(isc_nmsocket_t *sock) { + for (;;) { + int_fast32_t ah = atomic_load(&sock->ah); + isc_result_t result = processbuffer(sock); + switch (result) { + case ISC_R_NOMORE: + /* + * Don't reset the timer until we have a + * full DNS message. + */ + isc__nm_start_reading(sock); + /* + * Start the timer only if there are no externally used + * active handles, there's always one active handle + * attached internally to sock->recv_handle in + * accept_connection() + */ + if (ah == 1) { + isc__nmsocket_timer_start(sock); + } + return; + case ISC_R_CANCELED: + isc__nmsocket_timer_stop(sock); + isc__nm_stop_reading(sock); + return; + case ISC_R_SUCCESS: + /* + * Stop the timer on the successful message read, this + * also allows to restart the timer when we have no more + * data. + */ + isc__nmsocket_timer_stop(sock); + + if (atomic_load(&sock->client) || + atomic_load(&sock->sequential) || + ah >= STREAM_CLIENTS_PER_CONN) + { + isc__nm_stop_reading(sock); + return; + } + break; + default: + INSIST(0); + } + } +} + +void +isc__nm_resume_processing(void *arg) { + isc_nmsocket_t *sock = (isc_nmsocket_t *)arg; + + REQUIRE(VALID_NMSOCK(sock)); + REQUIRE(sock->tid == isc_nm_tid()); + REQUIRE(!atomic_load(&sock->client)); + + if (isc__nm_inactive(sock)) { + return; + } + + isc__nm_process_sock_buffer(sock); +} + +void +isc_nmhandle_cleartimeout(isc_nmhandle_t *handle) { + REQUIRE(VALID_NMHANDLE(handle)); + REQUIRE(VALID_NMSOCK(handle->sock)); + + switch (handle->sock->type) { + case isc_nm_httpsocket: + isc__nm_http_cleartimeout(handle); + return; + case isc_nm_tlssocket: + isc__nm_tls_cleartimeout(handle); + return; + default: + handle->sock->read_timeout = 0; + + if (uv_is_active((uv_handle_t *)&handle->sock->timer)) { + isc__nmsocket_timer_stop(handle->sock); + } + } +} + +void +isc_nmhandle_settimeout(isc_nmhandle_t *handle, uint32_t timeout) { + REQUIRE(VALID_NMHANDLE(handle)); + REQUIRE(VALID_NMSOCK(handle->sock)); + + switch (handle->sock->type) { + case isc_nm_httpsocket: + isc__nm_http_settimeout(handle, timeout); + return; + case isc_nm_tlssocket: + isc__nm_tls_settimeout(handle, timeout); + return; + default: + handle->sock->read_timeout = timeout; + if (uv_is_active((uv_handle_t *)&handle->sock->timer)) { + isc__nmsocket_timer_restart(handle->sock); + } + } } void * @@ -1956,22 +2246,23 @@ isc__nm_async_readcb(isc__networker_t *worker, isc__netievent_t *ev0) { void isc__nm_sendcb(isc_nmsocket_t *sock, isc__nm_uvreq_t *uvreq, - isc_result_t eresult) { + isc_result_t eresult, bool async) { REQUIRE(VALID_NMSOCK(sock)); REQUIRE(VALID_UVREQ(uvreq)); REQUIRE(VALID_NMHANDLE(uvreq->handle)); - if (eresult == ISC_R_SUCCESS) { + if (!async) { isc__netievent_sendcb_t ievent = { .sock = sock, .req = uvreq, .result = eresult }; isc__nm_async_sendcb(NULL, (isc__netievent_t *)&ievent); - } else { - isc__netievent_sendcb_t *ievent = isc__nm_get_netievent_sendcb( - sock->mgr, sock, uvreq, eresult); - isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid], - (isc__netievent_t *)ievent); + return; } + + isc__netievent_sendcb_t *ievent = + isc__nm_get_netievent_sendcb(sock->mgr, sock, uvreq, eresult); + isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid], + (isc__netievent_t *)ievent); } void diff --git a/lib/isc/netmgr/tcp.c b/lib/isc/netmgr/tcp.c index 7884c1a20b..ad80a4b76f 100644 --- a/lib/isc/netmgr/tcp.c +++ b/lib/isc/netmgr/tcp.c @@ -86,9 +86,6 @@ stop_tcp_parent(isc_nmsocket_t *sock); static void stop_tcp_child(isc_nmsocket_t *sock); -static void -start_sock_timer(isc_nmsocket_t *sock); - static void start_reading(isc_nmsocket_t *sock); @@ -719,6 +716,11 @@ destroy: } } +void +isc__nm_tcp_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result) { + failed_read_cb(sock, result); +} + static void failed_send_cb(isc_nmsocket_t *sock, isc__nm_uvreq_t *req, isc_result_t eresult) { @@ -726,7 +728,7 @@ failed_send_cb(isc_nmsocket_t *sock, isc__nm_uvreq_t *req, REQUIRE(VALID_UVREQ(req)); if (req->cb.send != NULL) { - isc__nm_sendcb(sock, req, eresult); + isc__nm_sendcb(sock, req, eresult, true); } else { isc__nm_uvreq_put(&req, sock); } @@ -744,35 +746,6 @@ get_read_req(isc_nmsocket_t *sock) { return req; } -static void -readtimeout_cb(uv_timer_t *timer) { - isc_nmsocket_t *sock = uv_handle_get_data((uv_handle_t *)timer); - - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->tid == isc_nm_tid()); - REQUIRE(sock->reading); - - /* - * Timeout; stop reading and process whatever we have. - */ - failed_read_cb(sock, ISC_R_TIMEDOUT); -} - -static void -start_sock_timer(isc_nmsocket_t *sock) { - if (sock->read_timeout > 0) { - int r = uv_timer_start(&sock->timer, readtimeout_cb, - sock->read_timeout, 0); - REQUIRE(r == 0); - } -} - -static void -stop_sock_timer(isc_nmsocket_t *sock) { - int r = uv_timer_stop(&sock->timer); - REQUIRE(r == 0); -} - static void start_reading(isc_nmsocket_t *sock) { if (sock->reading) { @@ -782,8 +755,6 @@ start_reading(isc_nmsocket_t *sock) { int r = uv_read_start(&sock->uv_handle.stream, tcp_alloc_cb, read_cb); REQUIRE(r == 0); sock->reading = true; - - start_sock_timer(sock); } static void @@ -796,7 +767,7 @@ stop_reading(isc_nmsocket_t *sock) { REQUIRE(r == 0); sock->reading = false; - stop_sock_timer(sock); + isc__nmsocket_timer_stop(sock); } void @@ -879,6 +850,7 @@ isc__nm_async_tcpstartread(isc__networker_t *worker, isc__netievent_t *ev0) { } start_reading(sock); + isc__nmsocket_timer_start(sock); } void @@ -997,7 +969,7 @@ read_cb(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) { /* The readcb could have paused the reading */ if (sock->reading) { /* The timer will be updated */ - start_sock_timer(sock); + isc__nmsocket_timer_restart(sock); } free: @@ -1199,7 +1171,7 @@ tcp_send_cb(uv_write_t *req, int status) { return; } - isc__nm_sendcb(sock, uvreq, ISC_R_SUCCESS); + isc__nm_sendcb(sock, uvreq, ISC_R_SUCCESS, false); } /* @@ -1479,20 +1451,6 @@ isc__nm_async_tcpcancel(isc__networker_t *worker, isc__netievent_t *ev0) { failed_read_cb(sock, ISC_R_EOF); } -void -isc__nm_tcp_settimeout(isc_nmhandle_t *handle, uint32_t timeout) { - isc_nmsocket_t *sock = NULL; - - REQUIRE(VALID_NMHANDLE(handle)); - - sock = handle->sock; - - sock->read_timeout = timeout; - if (uv_is_active((uv_handle_t *)&sock->timer)) { - start_sock_timer(sock); - } -} - int_fast32_t isc__nm_tcp_listener_nactive(isc_nmsocket_t *listener) { int_fast32_t nactive; diff --git a/lib/isc/netmgr/tcpdns.c b/lib/isc/netmgr/tcpdns.c index d7b8bbde42..378f828186 100644 --- a/lib/isc/netmgr/tcpdns.c +++ b/lib/isc/netmgr/tcpdns.c @@ -69,8 +69,6 @@ tcpdns_connect_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req); static void tcpdns_close_direct(isc_nmsocket_t *sock); -static isc_result_t -tcpdns_send_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req); static void tcpdns_connect_cb(uv_connect_t *uvreq, int status); @@ -101,9 +99,6 @@ stop_tcpdns_parent(isc_nmsocket_t *sock); static void stop_tcpdns_child(isc_nmsocket_t *sock); -static void -start_sock_timer(isc_nmsocket_t *sock); - static void process_sock_buffer(isc_nmsocket_t *sock); @@ -730,6 +725,7 @@ failed_read_cb(isc_nmsocket_t *sock, isc_result_t result) { REQUIRE(VALID_NMSOCK(sock)); REQUIRE(result != ISC_R_SUCCESS); + isc__nmsocket_timer_stop(sock); stop_reading(sock); if (!sock->recv_read) { @@ -753,6 +749,11 @@ destroy: } } +void +isc__nm_tcpdns_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result) { + failed_read_cb(sock, result); +} + static void failed_send_cb(isc_nmsocket_t *sock, isc__nm_uvreq_t *req, isc_result_t eresult) { @@ -760,7 +761,7 @@ failed_send_cb(isc_nmsocket_t *sock, isc__nm_uvreq_t *req, REQUIRE(VALID_UVREQ(req)); if (req->cb.send != NULL) { - isc__nm_sendcb(sock, req, eresult); + isc__nm_sendcb(sock, req, eresult, true); } else { isc__nm_uvreq_put(&req, sock); } @@ -783,36 +784,6 @@ get_read_req(isc_nmsocket_t *sock) { return (req); } -static void -readtimeout_cb(uv_timer_t *timer) { - isc_nmsocket_t *sock = uv_handle_get_data((uv_handle_t *)timer); - - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->tid == isc_nm_tid()); - REQUIRE(sock->reading); - - /* - * Timeout; stop reading and process whatever we have. - */ - - failed_read_cb(sock, ISC_R_TIMEDOUT); -} - -static void -start_sock_timer(isc_nmsocket_t *sock) { - if (sock->read_timeout > 0) { - int r = uv_timer_start(&sock->timer, readtimeout_cb, - sock->read_timeout, 0); - RUNTIME_CHECK(r == 0); - } -} - -static void -stop_sock_timer(isc_nmsocket_t *sock) { - int r = uv_timer_stop(&sock->timer); - RUNTIME_CHECK(r == 0); -} - static void start_reading(isc_nmsocket_t *sock) { int r; @@ -824,8 +795,6 @@ start_reading(isc_nmsocket_t *sock) { r = uv_read_start(&sock->uv_handle.stream, tcpdns_alloc_cb, read_cb); RUNTIME_CHECK(r == 0); sock->reading = true; - - start_sock_timer(sock); } static void @@ -839,8 +808,6 @@ stop_reading(isc_nmsocket_t *sock) { r = uv_read_stop(&sock->uv_handle.stream); RUNTIME_CHECK(r == 0); sock->reading = false; - - stop_sock_timer(sock); } void @@ -1209,7 +1176,7 @@ accept_connection(isc_nmsocket_t *ssock, isc_quota_t *quota) { * prep_destroy()->tcpdns_close_direct(). */ isc_nmhandle_attach(handle, &csock->recv_handle); - start_reading(csock); + process_sock_buffer(csock); /* * The initial timer has been set, update the read timeout for the next @@ -1284,7 +1251,7 @@ tcpdns_send_cb(uv_write_t *req, int status) { return; } - isc__nm_sendcb(sock, uvreq, ISC_R_SUCCESS); + isc__nm_sendcb(sock, uvreq, ISC_R_SUCCESS, false); } /* @@ -1292,50 +1259,72 @@ tcpdns_send_cb(uv_write_t *req, int status) { */ void isc__nm_async_tcpdnssend(isc__networker_t *worker, isc__netievent_t *ev0) { - isc_result_t result; isc__netievent_tcpdnssend_t *ievent = (isc__netievent_tcpdnssend_t *)ev0; + + REQUIRE(ievent->sock->type == isc_nm_tcpdnssocket); + REQUIRE(ievent->sock->tid == isc_nm_tid()); + REQUIRE(VALID_NMSOCK(ievent->sock)); + REQUIRE(VALID_UVREQ(ievent->req)); + REQUIRE(ievent->sock->tid == isc_nm_tid()); + + isc_result_t result; isc_nmsocket_t *sock = ievent->sock; isc__nm_uvreq_t *uvreq = ievent->req; + uv_buf_t bufs[2] = { { .base = uvreq->tcplen, .len = 2 }, + { .base = uvreq->uvbuf.base, + .len = uvreq->uvbuf.len } }; + int nbufs = 2; + int r; UNUSED(worker); - REQUIRE(sock->type == isc_nm_tcpdnssocket); - REQUIRE(sock->tid == isc_nm_tid()); + if (inactive(sock)) { + result = ISC_R_CANCELED; + goto fail; + } - result = tcpdns_send_direct(sock, uvreq); + r = uv_try_write(&sock->uv_handle.stream, bufs, nbufs); + + if (r == (int)(bufs[0].len + bufs[1].len)) { + /* Wrote everything */ + isc__nm_sendcb(sock, uvreq, ISC_R_SUCCESS, true); + return; + } + + if (r == 1) { + /* Partial write of DNSMSG length */ + bufs[0].base = uvreq->tcplen + 1; + bufs[0].len = 1; + } else if (r > 0) { + /* Partial write of DNSMSG */ + nbufs = 1; + bufs[0].base = uvreq->uvbuf.base + (r - 2); + bufs[0].len = uvreq->uvbuf.len - (r - 2); + } else if (r == UV_ENOSYS || r == UV_EAGAIN) { + /* uv_try_write not support, send asynchronously */ + } else { + /* error sending data */ + result = isc__nm_uverr2result(r); + goto fail; + } + + r = uv_write(&uvreq->uv_req.write, &sock->uv_handle.stream, bufs, nbufs, + tcpdns_send_cb); + if (r < 0) { + result = isc__nm_uverr2result(r); + goto fail; + } + + return; + +fail: if (result != ISC_R_SUCCESS) { isc__nm_incstats(sock->mgr, sock->statsindex[STATID_SENDFAIL]); failed_send_cb(sock, uvreq, result); } } -static isc_result_t -tcpdns_send_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) { - int r; - - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(VALID_UVREQ(req)); - REQUIRE(sock->tid == isc_nm_tid()); - REQUIRE(sock->type == isc_nm_tcpdnssocket); - - uv_buf_t bufs[2] = { { .base = req->tcplen, .len = 2 }, - { .base = req->uvbuf.base, - .len = req->uvbuf.len } }; - - if (inactive(sock)) { - return (ISC_R_CANCELED); - } - - r = uv_write(&req->uv_req.write, &sock->uv_handle.stream, bufs, 2, - tcpdns_send_cb); - if (r < 0) { - return (isc__nm_uverr2result(r)); - } - - return (ISC_R_SUCCESS); -} - static void tcpdns_stop_cb(uv_handle_t *handle) { isc_nmsocket_t *sock = uv_handle_get_data(handle); @@ -1462,6 +1451,7 @@ tcpdns_close_direct(isc_nmsocket_t *sock) { isc_nmhandle_detach(&sock->recv_handle); } + isc__nmsocket_timer_stop(sock); stop_reading(sock); uv_close((uv_handle_t *)&sock->timer, timer_close_cb); } @@ -1567,21 +1557,6 @@ isc__nm_async_tcpdnscancel(isc__networker_t *worker, isc__netievent_t *ev0) { failed_read_cb(sock, ISC_R_EOF); } -void -isc__nm_tcpdns_settimeout(isc_nmhandle_t *handle, uint32_t timeout) { - isc_nmsocket_t *sock = NULL; - - REQUIRE(VALID_NMHANDLE(handle)); - REQUIRE(VALID_NMSOCK(handle->sock)); - - sock = handle->sock; - - sock->read_timeout = timeout; - if (uv_is_active((uv_handle_t *)&sock->timer)) { - start_sock_timer(sock); - } -} - void isc_nm_tcpdns_sequential(isc_nmhandle_t *handle) { isc_nmsocket_t *sock = NULL; @@ -1601,6 +1576,7 @@ isc_nm_tcpdns_sequential(isc_nmhandle_t *handle) { * is released. */ + isc__nmsocket_timer_stop(sock); stop_reading(sock); atomic_store(&sock->sequential, true); } @@ -1618,33 +1594,47 @@ isc_nm_tcpdns_keepalive(isc_nmhandle_t *handle, bool value) { atomic_store(&sock->keepalive, value); } +/* + * Process a DNS message. + * + * If we only have an incomplete DNS message, we don't touch any + * timers. If we do have a full message, reset the timer. + * + * Stop reading if this is a client socket, or if the server socket + * has been set to sequential mode, or the number of queries we are + * processing simultaneously has reached the clients-per-connection + * limit. In this event we'll be called again by resume_processing() + * later. + */ static void process_sock_buffer(isc_nmsocket_t *sock) { - /* - * 1. When process_buffer receives incomplete DNS message, - * we don't touch any timers - * - * 2. When we receive at least one full DNS message, we stop the timers - * until resume_processing calls this function again and restarts the - * reading and the timers - */ - - /* - * Process a DNS messages. Stop if this is client socket, or the server - * socket has been set to sequential mode or the number of queries we - * are processing simultaneously have reached the clients-per-connection - * limit. - */ for (;;) { + int_fast32_t ah = atomic_load(&sock->ah); isc_result_t result = processbuffer(sock); switch (result) { case ISC_R_NOMORE: + /* + * Don't reset the timer until we have a + * full DNS message. + */ start_reading(sock); + /* Start the timer if there are no active handles */ + if (ah == 1) { + isc__nmsocket_timer_start(sock); + } return; case ISC_R_CANCELED: + isc__nmsocket_timer_stop(sock); stop_reading(sock); return; case ISC_R_SUCCESS: + /* + * Stop the timer on the successful message read, this + * also allows to restart the timer when we have no more + * data. + */ + isc__nmsocket_timer_stop(sock); + if (atomic_load(&sock->client) || atomic_load(&sock->sequential) || atomic_load(&sock->ah) >= TCPDNS_CLIENTS_PER_CONN) @@ -1672,10 +1662,5 @@ resume_processing(void *arg) { return; } - if (atomic_load(&sock->ah) == 0) { - /* Nothing is active; sockets can timeout now */ - start_sock_timer(sock); - } - process_sock_buffer(sock); } diff --git a/lib/isc/netmgr/tlsdns.c b/lib/isc/netmgr/tlsdns.c index 67690a2ebf..7e0d8fb539 100644 --- a/lib/isc/netmgr/tlsdns.c +++ b/lib/isc/netmgr/tlsdns.c @@ -100,9 +100,6 @@ start_reading(isc_nmsocket_t *sock); static void stop_reading(isc_nmsocket_t *sock); -static void -start_sock_timer(isc_nmsocket_t *sock); - static void process_sock_buffer(isc_nmsocket_t *sock); @@ -372,7 +369,8 @@ tlsdns_connect_cb(uv_connect_t *uvreq, int status) { sock->tls.pending_req = req; - start_reading(sock); + process_sock_buffer(sock); + result = tls_cycle(sock); if (result != ISC_R_SUCCESS) { goto error; @@ -861,6 +859,7 @@ failed_read_cb(isc_nmsocket_t *sock, isc_result_t result) { REQUIRE(VALID_NMSOCK(sock)); REQUIRE(result != ISC_R_SUCCESS); + isc__nmsocket_timer_stop(sock); stop_reading(sock); if (sock->tls.pending_req) { @@ -890,6 +889,11 @@ destroy: } } +void +isc__nm_tlsdns_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result) { + failed_read_cb(sock, result); +} + static void failed_send_cb(isc_nmsocket_t *sock, isc__nm_uvreq_t *req, isc_result_t eresult) { @@ -897,7 +901,7 @@ failed_send_cb(isc_nmsocket_t *sock, isc__nm_uvreq_t *req, REQUIRE(VALID_UVREQ(req)); if (req->cb.send != NULL) { - isc__nm_sendcb(sock, req, eresult); + isc__nm_sendcb(sock, req, eresult, true); } else { isc__nm_uvreq_put(&req, sock); } @@ -920,36 +924,6 @@ get_read_req(isc_nmsocket_t *sock) { return (req); } -static void -readtimeout_cb(uv_timer_t *timer) { - isc_nmsocket_t *sock = uv_handle_get_data((uv_handle_t *)timer); - - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->tid == isc_nm_tid()); - REQUIRE(sock->reading); - - /* - * Timeout; stop reading and process whatever we have. - */ - - failed_read_cb(sock, ISC_R_TIMEDOUT); -} - -static void -start_sock_timer(isc_nmsocket_t *sock) { - if (sock->read_timeout > 0) { - int r = uv_timer_start(&sock->timer, readtimeout_cb, - sock->read_timeout, 0); - RUNTIME_CHECK(r == 0); - } -} - -static void -stop_sock_timer(isc_nmsocket_t *sock) { - int r = uv_timer_stop(&sock->timer); - RUNTIME_CHECK(r == 0); -} - static void start_reading(isc_nmsocket_t *sock) { int r; @@ -961,8 +935,6 @@ start_reading(isc_nmsocket_t *sock) { r = uv_read_start(&sock->uv_handle.stream, tlsdns_alloc_cb, read_cb); RUNTIME_CHECK(r == 0); sock->reading = true; - - start_sock_timer(sock); } static void @@ -976,8 +948,6 @@ stop_reading(isc_nmsocket_t *sock) { r = uv_read_stop(&sock->uv_handle.stream); RUNTIME_CHECK(r == 0); sock->reading = false; - - stop_sock_timer(sock); } void @@ -1069,7 +1039,6 @@ isc__nm_async_tlsdnsread(isc__networker_t *worker, isc__netievent_t *ev0) { result = tls_cycle(sock); if (result != ISC_R_SUCCESS) { - stop_reading(sock); failed_read_cb(sock, result); } } @@ -1216,7 +1185,7 @@ tls_cycle_input(isc_nmsocket_t *sock) { if (sock->tls.state == TLS_STATE_NONE && !SSL_is_init_finished(sock->tls.tls)) { sock->tls.state = TLS_STATE_HANDSHAKE; - start_reading(sock); + process_sock_buffer(sock); } /* else continue reading */ break; @@ -1269,9 +1238,8 @@ static void tls_error(isc_nmsocket_t *sock, isc_result_t result) { switch (sock->tls.state) { case TLS_STATE_HANDSHAKE: - stop_reading(sock); - break; case TLS_STATE_IO: + isc__nmsocket_timer_stop(sock); stop_reading(sock); break; case TLS_STATE_ERROR: @@ -1709,7 +1677,7 @@ accept_connection(isc_nmsocket_t *ssock, isc_quota_t *quota) { isc_nmhandle_detach(&handle); - start_reading(csock); + process_sock_buffer(csock); /* * sock is now attached to the handle. @@ -1832,7 +1800,7 @@ tlsdns_send_direct(isc_nmsocket_t *sock, isc__nm_uvreq_t *req) { /* SSL_write_ex() doesn't do partial writes */ INSIST(sendlen == bytes); - isc__nm_sendcb(sock, req, ISC_R_SUCCESS); + isc__nm_sendcb(sock, req, ISC_R_SUCCESS, true); async_tlsdns_cycle(sock); return (ISC_R_SUCCESS); } @@ -2001,6 +1969,7 @@ tlsdns_close_direct(isc_nmsocket_t *sock) { isc_nmhandle_detach(&sock->recv_handle); } + isc__nmsocket_timer_stop(sock); stop_reading(sock); uv_close((uv_handle_t *)&sock->timer, timer_close_cb); } @@ -2112,21 +2081,6 @@ isc__nm_async_tlsdnscancel(isc__networker_t *worker, isc__netievent_t *ev0) { failed_read_cb(sock, ISC_R_EOF); } -void -isc__nm_tlsdns_settimeout(isc_nmhandle_t *handle, uint32_t timeout) { - isc_nmsocket_t *sock = NULL; - - REQUIRE(VALID_NMHANDLE(handle)); - REQUIRE(VALID_NMSOCK(handle->sock)); - - sock = handle->sock; - - sock->read_timeout = timeout; - if (uv_is_active((uv_handle_t *)&sock->timer)) { - start_sock_timer(sock); - } -} - void isc_nm_tlsdns_sequential(isc_nmhandle_t *handle) { isc_nmsocket_t *sock = NULL; @@ -2146,6 +2100,7 @@ isc_nm_tlsdns_sequential(isc_nmhandle_t *handle) { * is released. */ + isc__nmsocket_timer_stop(sock); stop_reading(sock); atomic_store(&sock->sequential, true); } @@ -2163,33 +2118,47 @@ isc_nm_tlsdns_keepalive(isc_nmhandle_t *handle, bool value) { atomic_store(&sock->keepalive, value); } +/* + * Process a DNS message. + * + * If we only have an incomplete DNS message, we don't touch any + * timers. If we do have a full message, reset the timer. + * + * Stop reading if this is a client socket, or if the server socket + * has been set to sequential mode, or the number of queries we are + * processing simultaneously has reached the clients-per-connection + * limit. In this event we'll be called again by resume_processing() + * later. + */ static void process_sock_buffer(isc_nmsocket_t *sock) { - /* - * 1. When process_buffer receives incomplete DNS message, - * we don't touch any timers - * - * 2. When we receive at least one full DNS message, we stop the timers - * until resume_processing calls this function again and restarts the - * reading and the timers - */ - - /* - * Process a DNS messages. Stop if this is client socket, or the server - * socket has been set to sequential mode or the number of queries we - * are processing simultaneously have reached the clients-per-connection - * limit. - */ for (;;) { + int_fast32_t ah = atomic_load(&sock->ah); isc_result_t result = processbuffer(sock); switch (result) { case ISC_R_NOMORE: + /* + * Don't reset the timer until we have a + * full DNS message. + */ start_reading(sock); + /* Start the timer if there are no active handles */ + if (ah == 1) { + isc__nmsocket_timer_start(sock); + } return; case ISC_R_CANCELED: + isc__nmsocket_timer_stop(sock); stop_reading(sock); return; case ISC_R_SUCCESS: + /* + * Stop the timer on the successful message read, this + * also allows to restart the timer when we have no more + * data. + */ + isc__nmsocket_timer_stop(sock); + if (atomic_load(&sock->client) || atomic_load(&sock->sequential) || atomic_load(&sock->ah) >= TLSDNS_CLIENTS_PER_CONN) @@ -2217,10 +2186,5 @@ resume_processing(void *arg) { return; } - if (atomic_load(&sock->ah) == 0) { - /* Nothing is active; sockets can timeout now */ - start_sock_timer(sock); - } - process_sock_buffer(sock); } diff --git a/lib/isc/netmgr/tlsstream.c b/lib/isc/netmgr/tlsstream.c index 976970cba0..e7f0c08f6b 100644 --- a/lib/isc/netmgr/tlsstream.c +++ b/lib/isc/netmgr/tlsstream.c @@ -948,6 +948,21 @@ isc__nm_tls_cleanup_data(isc_nmsocket_t *sock) { } } +void +isc__nm_tls_cleartimeout(isc_nmhandle_t *handle) { + isc_nmsocket_t *sock = NULL; + + REQUIRE(VALID_NMHANDLE(handle)); + REQUIRE(VALID_NMSOCK(handle->sock)); + REQUIRE(handle->sock->type == isc_nm_tlssocket); + + sock = handle->sock; + if (sock->outerhandle != NULL) { + INSIST(VALID_NMHANDLE(sock->outerhandle)); + isc_nmhandle_cleartimeout(sock->outerhandle); + } +} + void isc__nm_tls_settimeout(isc_nmhandle_t *handle, uint32_t timeout) { isc_nmsocket_t *sock = NULL; diff --git a/lib/isc/netmgr/udp.c b/lib/isc/netmgr/udp.c index 8651fd2c00..dea110dd20 100644 --- a/lib/isc/netmgr/udp.c +++ b/lib/isc/netmgr/udp.c @@ -596,7 +596,7 @@ udp_send_cb(uv_udp_send_t *req, int status) { isc__nm_incstats(sock->mgr, sock->statsindex[STATID_SENDFAIL]); } - isc__nm_sendcb(sock, uvreq, result); + isc__nm_sendcb(sock, uvreq, result, false); } /* @@ -900,6 +900,11 @@ failed_read_cb(isc_nmsocket_t *sock, isc_result_t result) { } } +void +isc__nm_udp_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result) { + failed_read_cb(sock, result); +} + static void failed_send_cb(isc_nmsocket_t *sock, isc__nm_uvreq_t *req, isc_result_t eresult) { @@ -907,7 +912,7 @@ failed_send_cb(isc_nmsocket_t *sock, isc__nm_uvreq_t *req, REQUIRE(VALID_UVREQ(req)); if (req->cb.send != NULL) { - isc__nm_sendcb(sock, req, eresult); + isc__nm_sendcb(sock, req, eresult, true); } else { isc__nm_uvreq_put(&req, sock); } @@ -930,20 +935,6 @@ get_read_req(isc_nmsocket_t *sock, isc_sockaddr_t *sockaddr) { return req; } -static void -readtimeout_cb(uv_timer_t *handle) { - isc_nmsocket_t *sock = uv_handle_get_data((uv_handle_t *)handle); - - REQUIRE(VALID_NMSOCK(sock)); - REQUIRE(sock->tid == isc_nm_tid()); - REQUIRE(sock->reading); - - /* - * Timeout; stop reading and process whatever we have. - */ - failed_read_cb(sock, ISC_R_TIMEDOUT); -} - /* * Asynchronous 'udpread' call handler: start or resume reading on a * socket; pause reading and call the 'recv' callback after each @@ -966,21 +957,7 @@ isc__nm_async_udpread(isc__networker_t *worker, isc__netievent_t *ev0) { } start_reading(sock); -} - -static void -start_sock_timer(isc_nmsocket_t *sock) { - if (sock->read_timeout > 0) { - int r = uv_timer_start(&sock->timer, readtimeout_cb, - sock->read_timeout, 0); - REQUIRE(r == 0); - } -} - -static void -stop_sock_timer(isc_nmsocket_t *sock) { - int r = uv_timer_stop(&sock->timer); - REQUIRE(r == 0); + isc__nmsocket_timer_start(sock); } static void @@ -993,8 +970,6 @@ start_reading(isc_nmsocket_t *sock) { udp_read_cb); REQUIRE(r == 0); sock->reading = true; - - start_sock_timer(sock); } static void @@ -1007,7 +982,7 @@ stop_reading(isc_nmsocket_t *sock) { REQUIRE(r == 0); sock->reading = false; - stop_sock_timer(sock); + isc__nmsocket_timer_stop(sock); } void @@ -1263,16 +1238,3 @@ isc__nm_async_udpcancel(isc__networker_t *worker, isc__netievent_t *ev0) { failed_read_cb(sock, ISC_R_EOF); } - -void -isc__nm_udp_settimeout(isc_nmhandle_t *handle, uint32_t timeout) { - REQUIRE(VALID_NMHANDLE(handle)); - REQUIRE(VALID_NMSOCK(handle->sock)); - - isc_nmsocket_t *sock = handle->sock; - - sock->read_timeout = timeout; - if (uv_is_active((uv_handle_t *)&sock->timer)) { - start_sock_timer(sock); - } -} diff --git a/lib/isc/win32/libisc.def.in b/lib/isc/win32/libisc.def.in index 5b2670c3ee..27f7272b04 100644 --- a/lib/isc/win32/libisc.def.in +++ b/lib/isc/win32/libisc.def.in @@ -440,6 +440,7 @@ isc_netaddr_unspec isc_netscope_pton isc__nmhandle_attach isc__nmhandle_detach +isc_nmhandle_cleartimeout isc_nmhandle_getdata isc_nmhandle_getextra isc_nmhandle_is_stream