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
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);
}
}
}
+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;
} 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;
* 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;
* 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);
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);
/*%<
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
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
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
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);
handle->dofree = dofree;
}
+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_failed_read_cb(sock, result);
+ return;
+ case isc_nm_tcpsocket:
+ 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_nmhandle_settimeout(isc_nmhandle_t *handle, uint32_t timeout) {
- REQUIRE(VALID_NMHANDLE(handle));
+isc__nmsocket_timer_restart(isc_nmsocket_t *sock) {
+ REQUIRE(VALID_NMSOCK(sock));
- switch (handle->sock->type) {
+ 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:
- isc__nm_udp_settimeout(handle, timeout);
+ REQUIRE(size <= ISC_NETMGR_RECVBUF_SIZE);
+ size = ISC_NETMGR_RECVBUF_SIZE;
break;
- case isc_nm_tcpsocket:
- isc__nm_tcp_settimeout(handle, timeout);
+ case isc_nm_tcpdnssocket:
+ break;
+ case isc_nm_tlsdnssocket:
+ /*
+ * 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:
- isc__nm_tcpdns_settimeout(handle, timeout);
+ r = uv_read_start(&sock->uv_handle.stream, isc__nm_alloc_cb,
+ isc__nm_tcpdns_read_cb);
break;
case isc_nm_tlsdnssocket:
- isc__nm_tlsdns_settimeout(handle, timeout);
+ r = uv_read_start(&sock->uv_handle.stream, isc__nm_alloc_cb,
+ isc__nm_tlsdns_read_cb);
break;
- case isc_nm_tlssocket:
- isc__nm_tls_settimeout(handle, timeout);
+ 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_httpsocket:
- isc__nm_http_settimeout(handle, timeout);
+ 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 *
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
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);
}
}
+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) {
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);
}
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) {
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
REQUIRE(r == 0);
sock->reading = false;
- stop_sock_timer(sock);
+ isc__nmsocket_timer_stop(sock);
}
void
}
start_reading(sock);
+ isc__nmsocket_timer_start(sock);
}
void
/* 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:
return;
}
- isc__nm_sendcb(sock, uvreq, ISC_R_SUCCESS);
+ isc__nm_sendcb(sock, uvreq, ISC_R_SUCCESS, false);
}
/*
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;
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);
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);
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(result != ISC_R_SUCCESS);
+ isc__nmsocket_timer_stop(sock);
stop_reading(sock);
if (!sock->recv_read) {
}
}
+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) {
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);
}
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;
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
r = uv_read_stop(&sock->uv_handle.stream);
RUNTIME_CHECK(r == 0);
sock->reading = false;
-
- stop_sock_timer(sock);
}
void
* 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
return;
}
- isc__nm_sendcb(sock, uvreq, ISC_R_SUCCESS);
+ isc__nm_sendcb(sock, uvreq, ISC_R_SUCCESS, false);
}
/*
*/
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());
-
- result = tcpdns_send_direct(sock, uvreq);
- if (result != ISC_R_SUCCESS) {
- isc__nm_incstats(sock->mgr, sock->statsindex[STATID_SENDFAIL]);
- failed_send_cb(sock, uvreq, result);
+ if (inactive(sock)) {
+ result = ISC_R_CANCELED;
+ goto fail;
}
-}
-
-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);
+ r = uv_try_write(&sock->uv_handle.stream, bufs, nbufs);
- uv_buf_t bufs[2] = { { .base = req->tcplen, .len = 2 },
- { .base = req->uvbuf.base,
- .len = req->uvbuf.len } };
+ if (r == (int)(bufs[0].len + bufs[1].len)) {
+ /* Wrote everything */
+ isc__nm_sendcb(sock, uvreq, ISC_R_SUCCESS, true);
+ return;
+ }
- if (inactive(sock)) {
- return (ISC_R_CANCELED);
+ 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(&req->uv_req.write, &sock->uv_handle.stream, bufs, 2,
+ r = uv_write(&uvreq->uv_req.write, &sock->uv_handle.stream, bufs, nbufs,
tcpdns_send_cb);
if (r < 0) {
- return (isc__nm_uverr2result(r));
+ result = isc__nm_uverr2result(r);
+ goto fail;
}
- return (ISC_R_SUCCESS);
+ return;
+
+fail:
+ if (result != ISC_R_SUCCESS) {
+ isc__nm_incstats(sock->mgr, sock->statsindex[STATID_SENDFAIL]);
+ failed_send_cb(sock, uvreq, result);
+ }
}
static void
isc_nmhandle_detach(&sock->recv_handle);
}
+ isc__nmsocket_timer_stop(sock);
stop_reading(sock);
uv_close((uv_handle_t *)&sock->timer, timer_close_cb);
}
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;
* is released.
*/
+ isc__nmsocket_timer_stop(sock);
stop_reading(sock);
atomic_store(&sock->sequential, true);
}
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)
return;
}
- if (atomic_load(&sock->ah) == 0) {
- /* Nothing is active; sockets can timeout now */
- start_sock_timer(sock);
- }
-
process_sock_buffer(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);
sock->tls.pending_req = req;
- start_reading(sock);
+ process_sock_buffer(sock);
+
result = tls_cycle(sock);
if (result != ISC_R_SUCCESS) {
goto error;
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(result != ISC_R_SUCCESS);
+ isc__nmsocket_timer_stop(sock);
stop_reading(sock);
if (sock->tls.pending_req) {
}
}
+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) {
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);
}
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;
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
r = uv_read_stop(&sock->uv_handle.stream);
RUNTIME_CHECK(r == 0);
sock->reading = false;
-
- stop_sock_timer(sock);
}
void
result = tls_cycle(sock);
if (result != ISC_R_SUCCESS) {
- stop_reading(sock);
failed_read_cb(sock, result);
}
}
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;
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:
isc_nmhandle_detach(&handle);
- start_reading(csock);
+ process_sock_buffer(csock);
/*
* sock is now attached to the handle.
/* 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);
}
isc_nmhandle_detach(&sock->recv_handle);
}
+ isc__nmsocket_timer_stop(sock);
stop_reading(sock);
uv_close((uv_handle_t *)&sock->timer, timer_close_cb);
}
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;
* is released.
*/
+ isc__nmsocket_timer_stop(sock);
stop_reading(sock);
atomic_store(&sock->sequential, true);
}
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)
return;
}
- if (atomic_load(&sock->ah) == 0) {
- /* Nothing is active; sockets can timeout now */
- start_sock_timer(sock);
- }
-
process_sock_buffer(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;
isc__nm_incstats(sock->mgr, sock->statsindex[STATID_SENDFAIL]);
}
- isc__nm_sendcb(sock, uvreq, result);
+ isc__nm_sendcb(sock, uvreq, result, false);
}
/*
}
}
+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) {
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);
}
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
}
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
udp_read_cb);
REQUIRE(r == 0);
sock->reading = true;
-
- start_sock_timer(sock);
}
static void
REQUIRE(r == 0);
sock->reading = false;
- stop_sock_timer(sock);
+ isc__nmsocket_timer_stop(sock);
}
void
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);
- }
-}
isc_netscope_pton
isc__nmhandle_attach
isc__nmhandle_detach
+isc_nmhandle_cleartimeout
isc_nmhandle_getdata
isc_nmhandle_getextra
isc_nmhandle_is_stream