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:
@@ -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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user