2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-31 06:25:31 +00:00

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
This commit is contained in:
Ondřej Surý
2021-03-16 09:03:02 +01:00
parent 64cff61c02
commit caa5b6548a
10 changed files with 538 additions and 315 deletions

View File

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