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

Merge branch '3545-prevent-unexpected-client-read-callbacks' into 'main'

Prevent unexpected UDP client read callbacks

Closes #3545

See merge request isc-projects/bind9!6777
This commit is contained in:
Ondřej Surý
2022-09-19 12:38:04 +00:00
5 changed files with 310 additions and 154 deletions

View File

@@ -1,3 +1,6 @@
5974. [bug] Fix an assertion failure in dispatch caused by
extra read callback call. [GL #3545]
5973. [bug] Fixed a possible invalid detach in UPDATE 5973. [bug] Fixed a possible invalid detach in UPDATE
processing. [GL #3522] processing. [GL #3522]

View File

@@ -297,7 +297,6 @@ typedef enum isc__netievent_type {
netievent_udplisten, netievent_udplisten,
netievent_udpstop, netievent_udpstop,
netievent_udpread,
netievent_tcplisten, netievent_tcplisten,
netievent_tcpstop, netievent_tcpstop,
@@ -1313,8 +1312,6 @@ void
isc__nm_async_udpstop(isc__networker_t *worker, isc__netievent_t *ev0); isc__nm_async_udpstop(isc__networker_t *worker, isc__netievent_t *ev0);
void void
isc__nm_async_udpcancel(isc__networker_t *worker, isc__netievent_t *ev0); isc__nm_async_udpcancel(isc__networker_t *worker, isc__netievent_t *ev0);
void
isc__nm_async_udpread(isc__networker_t *worker, isc__netievent_t *ev0);
/*%< /*%<
* Callback handlers for asynchronous UDP events (listen, stoplisten, send). * Callback handlers for asynchronous UDP events (listen, stoplisten, send).
*/ */
@@ -1834,7 +1831,6 @@ NETIEVENT_SOCKET_TYPE(tlsstartread);
NETIEVENT_SOCKET_HANDLE_TYPE(tlscancel); NETIEVENT_SOCKET_HANDLE_TYPE(tlscancel);
NETIEVENT_SOCKET_TYPE(udplisten); NETIEVENT_SOCKET_TYPE(udplisten);
NETIEVENT_SOCKET_TYPE(udpstop); NETIEVENT_SOCKET_TYPE(udpstop);
NETIEVENT_SOCKET_TYPE(udpread);
NETIEVENT_SOCKET_TYPE(tcpdnsclose); NETIEVENT_SOCKET_TYPE(tcpdnsclose);
NETIEVENT_SOCKET_TYPE(tcpdnsread); NETIEVENT_SOCKET_TYPE(tcpdnsread);
@@ -1893,7 +1889,6 @@ NETIEVENT_SOCKET_DECL(tlsstartread);
NETIEVENT_SOCKET_HANDLE_DECL(tlscancel); NETIEVENT_SOCKET_HANDLE_DECL(tlscancel);
NETIEVENT_SOCKET_DECL(udplisten); NETIEVENT_SOCKET_DECL(udplisten);
NETIEVENT_SOCKET_DECL(udpstop); NETIEVENT_SOCKET_DECL(udpstop);
NETIEVENT_SOCKET_DECL(udpread);
NETIEVENT_SOCKET_DECL(tcpdnsclose); NETIEVENT_SOCKET_DECL(tcpdnsclose);
NETIEVENT_SOCKET_DECL(tcpdnsread); NETIEVENT_SOCKET_DECL(tcpdnsread);

View File

@@ -449,7 +449,6 @@ process_netievent(void *arg) {
NETIEVENT_CASE(udplisten); NETIEVENT_CASE(udplisten);
NETIEVENT_CASE(udpstop); NETIEVENT_CASE(udpstop);
NETIEVENT_CASE(udpcancel); NETIEVENT_CASE(udpcancel);
NETIEVENT_CASE(udpread);
NETIEVENT_CASE(tcpaccept); NETIEVENT_CASE(tcpaccept);
NETIEVENT_CASE(tcpconnect); NETIEVENT_CASE(tcpconnect);
@@ -536,7 +535,6 @@ NETIEVENT_SOCKET_HANDLE_DEF(tlscancel);
NETIEVENT_SOCKET_DEF(udplisten); NETIEVENT_SOCKET_DEF(udplisten);
NETIEVENT_SOCKET_DEF(udpstop); NETIEVENT_SOCKET_DEF(udpstop);
NETIEVENT_SOCKET_HANDLE_DEF(udpcancel); NETIEVENT_SOCKET_HANDLE_DEF(udpcancel);
NETIEVENT_SOCKET_DEF(udpread);
NETIEVENT_SOCKET_DEF(tcpdnsclose); NETIEVENT_SOCKET_DEF(tcpdnsclose);
NETIEVENT_SOCKET_DEF(tcpdnsread); NETIEVENT_SOCKET_DEF(tcpdnsread);

View File

@@ -59,10 +59,6 @@
#endif /* if defined(HAVE_LINUX_NETLINK_H) && defined(HAVE_LINUX_RTNETLINK_H) \ #endif /* if defined(HAVE_LINUX_NETLINK_H) && defined(HAVE_LINUX_RTNETLINK_H) \
*/ */
static void
udp_recv_cb(uv_udp_t *handle, ssize_t nrecv, const uv_buf_t *buf,
const struct sockaddr *addr, unsigned flags);
static void static void
udp_send_cb(uv_udp_send_t *req, int status); udp_send_cb(uv_udp_send_t *req, int status);
@@ -413,7 +409,7 @@ isc__nm_async_udplisten(isc__networker_t *worker, isc__netievent_t *ev0) {
isc__nm_set_network_buffers(mgr, &sock->uv_handle.handle); isc__nm_set_network_buffers(mgr, &sock->uv_handle.handle);
r = uv_udp_recv_start(&sock->uv_handle.udp, isc__nm_alloc_cb, r = uv_udp_recv_start(&sock->uv_handle.udp, isc__nm_alloc_cb,
udp_recv_cb); isc__nm_udp_read_cb);
if (r != 0) { if (r != 0) {
isc__nm_incstats(sock, STATID_BINDFAIL); isc__nm_incstats(sock, STATID_BINDFAIL);
goto done; goto done;
@@ -511,9 +507,9 @@ isc__nm_async_udpstop(isc__networker_t *worker, isc__netievent_t *ev0) {
* reused for a series of packets, so we need to allocate a new one. * reused for a series of packets, so we need to allocate a new one.
* This new one can be reused to send the response then. * This new one can be reused to send the response then.
*/ */
static void void
udp_recv_cb(uv_udp_t *handle, ssize_t nrecv, const uv_buf_t *buf, isc__nm_udp_read_cb(uv_udp_t *handle, ssize_t nrecv, const uv_buf_t *buf,
const struct sockaddr *addr, unsigned flags) { const struct sockaddr *addr, unsigned flags) {
isc_nmsocket_t *sock = uv_handle_get_data((uv_handle_t *)handle); isc_nmsocket_t *sock = uv_handle_get_data((uv_handle_t *)handle);
isc__nm_uvreq_t *req = NULL; isc__nm_uvreq_t *req = NULL;
uint32_t maxudp; uint32_t maxudp;
@@ -562,15 +558,6 @@ udp_recv_cb(uv_udp_t *handle, ssize_t nrecv, const uv_buf_t *buf,
goto free; goto free;
} }
/*
* - If addr == NULL, in which case it's the end of stream;
* we can free the buffer and bail.
*/
if (addr == NULL) {
isc__nm_failed_read_cb(sock, ISC_R_EOF, false);
goto free;
}
/* /*
* - If the network manager is shutting down * - If the network manager is shutting down
*/ */
@@ -587,6 +574,23 @@ udp_recv_cb(uv_udp_t *handle, ssize_t nrecv, const uv_buf_t *buf,
goto free; goto free;
} }
/*
* End of the current (iteration) datagram stream, just free the buffer.
* The callback with nrecv == 0 and addr == NULL is called for both
* normal UDP sockets and recvmmsg sockets at the end of every event
* loop iteration.
*/
if (nrecv == 0 && addr == NULL) {
INSIST(flags == 0);
goto free;
}
/*
* We could receive an empty datagram in which case:
* nrecv == 0 and addr != NULL
*/
INSIST(addr != NULL);
if (!sock->route_sock) { if (!sock->route_sock) {
result = isc_sockaddr_fromsockaddr(&sockaddr, addr); result = isc_sockaddr_fromsockaddr(&sockaddr, addr);
RUNTIME_CHECK(result == ISC_R_SUCCESS); RUNTIME_CHECK(result == ISC_R_SUCCESS);
@@ -604,6 +608,16 @@ udp_recv_cb(uv_udp_t *handle, ssize_t nrecv, const uv_buf_t *buf,
sock->recv_read = false; sock->recv_read = false;
/*
* The client isc_nm_read() expects just a single message, so we need to
* stop reading now. The reading could be restarted in the read
* callback with another isc_nm_read() call.
*/
if (atomic_load(&sock->client)) {
isc__nmsocket_timer_stop(sock);
isc__nm_stop_reading(sock);
}
REQUIRE(!sock->processing); REQUIRE(!sock->processing);
sock->processing = true; sock->processing = true;
isc__nm_readcb(sock, req, ISC_R_SUCCESS); isc__nm_readcb(sock, req, ISC_R_SUCCESS);
@@ -870,31 +884,6 @@ isc_nm_udpconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
isc__nmsocket_detach(&sock); isc__nmsocket_detach(&sock);
} }
void
isc__nm_udp_read_cb(uv_udp_t *handle, ssize_t nrecv, const uv_buf_t *buf,
const struct sockaddr *addr, unsigned flags) {
isc_nmsocket_t *sock = uv_handle_get_data((uv_handle_t *)handle);
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(atomic_load(&sock->client));
REQUIRE(sock->parent == NULL);
/*
* This function can only be reached when calling isc_nm_read() on
* a UDP client socket. There's no point calling isc_nm_read() on
* a UDP listener socket; those are always reading.
*
* The reason why we stop the timer and the reading after calling the
* callback is because there's a time window where a second UDP packet
* might be received between isc__nm_stop_reading() call and
* isc_nm_read() call from the callback and such UDP datagram would be
* lost like tears in the rain.
*/
udp_recv_cb(handle, nrecv, buf, addr, flags);
isc__nmsocket_timer_stop(sock);
isc__nm_stop_reading(sock);
}
void void
isc__nm_udp_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result) { isc__nm_udp_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result) {
REQUIRE(VALID_NMSOCK(sock)); REQUIRE(VALID_NMSOCK(sock));
@@ -940,23 +929,30 @@ isc__nm_udp_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result) {
} }
} }
/*
* Asynchronous 'udpread' call handler: start or resume reading on a
* socket; pause reading and call the 'recv' callback after each
* datagram.
*/
void void
isc__nm_async_udpread(isc__networker_t *worker, isc__netievent_t *ev0) { isc__nm_udp_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) {
isc__netievent_udpread_t *ievent = (isc__netievent_udpread_t *)ev0; isc_nmsocket_t *sock = NULL;
isc_nmsocket_t *sock = ievent->sock;
isc_result_t result; isc_result_t result;
UNUSED(worker); REQUIRE(VALID_NMHANDLE(handle));
sock = handle->sock;
REQUIRE(VALID_NMSOCK(sock)); REQUIRE(VALID_NMSOCK(sock));
REQUIRE(sock->type == isc_nm_udpsocket);
REQUIRE(sock->statichandle == handle);
REQUIRE(!sock->recv_read);
REQUIRE(sock->tid == isc_tid()); REQUIRE(sock->tid == isc_tid());
if (isc__nm_closing(worker)) { /*
* We need to initialize the callback before checking for shutdown
* conditions, so the callback is always called even on error condition.
*/
sock->recv_cb = cb;
sock->recv_cbarg = cbarg;
sock->recv_read = true;
if (isc__nm_closing(sock->worker)) {
result = ISC_R_SHUTTINGDOWN; result = ISC_R_SHUTTINGDOWN;
goto fail; goto fail;
} }
@@ -971,7 +967,7 @@ isc__nm_async_udpread(isc__networker_t *worker, isc__netievent_t *ev0) {
goto fail; goto fail;
} }
isc__nmsocket_timer_start(sock); isc__nmsocket_timer_restart(sock);
return; return;
fail: fail:
@@ -979,35 +975,6 @@ fail:
isc__nm_failed_read_cb(sock, result, false); isc__nm_failed_read_cb(sock, result, false);
} }
void
isc__nm_udp_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) {
isc_nmsocket_t *sock = NULL;
REQUIRE(VALID_NMHANDLE(handle));
sock = handle->sock;
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(sock->type == isc_nm_udpsocket);
REQUIRE(sock->statichandle == handle);
REQUIRE(!sock->recv_read);
sock->recv_cb = cb;
sock->recv_cbarg = cbarg;
sock->recv_read = true;
if (!atomic_load(&sock->reading) && sock->tid == isc_tid()) {
isc__netievent_udpread_t ievent = { .sock = sock };
isc__nm_async_udpread(sock->worker,
(isc__netievent_t *)&ievent);
} else {
isc__netievent_udpread_t *ievent =
isc__nm_get_netievent_udpread(sock->worker, sock);
isc__nm_enqueue_ievent(sock->worker,
(isc__netievent_t *)ievent);
}
}
static void static void
udp_close_cb(uv_handle_t *handle) { udp_close_cb(uv_handle_t *handle) {
isc_nmsocket_t *sock = uv_handle_get_data(handle); isc_nmsocket_t *sock = uv_handle_get_data(handle);

View File

@@ -100,6 +100,8 @@ setup_test(void **state) {
isc_nonce_buf(&send_magic, sizeof(send_magic)); isc_nonce_buf(&send_magic, sizeof(send_magic));
connect_readcb = connect_read_cb;
return (0); return (0);
} }
@@ -304,7 +306,6 @@ udp_noresponse_recv_cb(isc_nmhandle_t *handle, isc_result_t eresult,
static void static void
udp_noresponse_read_cb(isc_nmhandle_t *handle, isc_result_t eresult, udp_noresponse_read_cb(isc_nmhandle_t *handle, isc_result_t eresult,
isc_region_t *region, void *cbarg) { isc_region_t *region, void *cbarg) {
UNUSED(handle);
UNUSED(region); UNUSED(region);
UNUSED(cbarg); UNUSED(cbarg);
@@ -337,9 +338,6 @@ udp_noresponse_connect_cb(isc_nmhandle_t *handle, isc_result_t eresult,
isc_nmhandle_t *readhandle = NULL; isc_nmhandle_t *readhandle = NULL;
isc_nmhandle_t *sendhandle = NULL; isc_nmhandle_t *sendhandle = NULL;
UNUSED(handle);
UNUSED(cbarg);
isc_refcount_decrement(&active_cconnects); isc_refcount_decrement(&active_cconnects);
assert_int_equal(eresult, ISC_R_SUCCESS); assert_int_equal(eresult, ISC_R_SUCCESS);
@@ -461,8 +459,6 @@ udp_timeout_recovery_connect_cb(isc_nmhandle_t *handle, isc_result_t eresult,
isc_nmhandle_t *readhandle = NULL; isc_nmhandle_t *readhandle = NULL;
isc_nmhandle_t *sendhandle = NULL; isc_nmhandle_t *sendhandle = NULL;
UNUSED(cbarg);
F(); F();
isc_refcount_decrement(&active_cconnects); isc_refcount_decrement(&active_cconnects);
@@ -555,14 +551,50 @@ ISC_LOOP_TEST_IMPL(udp_shutdown_connect) {
isc_job_run(loopmgr, udp_connect_udpconnect, netmgr); isc_job_run(loopmgr, udp_connect_udpconnect, netmgr);
} }
static void
udp_shutdown_read_recv_cb(isc_nmhandle_t *handle, isc_result_t eresult,
isc_region_t *region, void *cbarg) {
uint64_t magic = 0;
UNUSED(cbarg);
assert_non_null(handle);
F();
assert_int_equal(eresult, ISC_R_SUCCESS);
assert_true(region->length == sizeof(magic));
memmove(&magic, region->base, sizeof(magic));
assert_true(magic == send_magic);
}
static void
udp_shutdown_read_send_cb(isc_nmhandle_t *handle, isc_result_t eresult,
void *cbarg) {
UNUSED(cbarg);
F();
assert_non_null(handle);
assert_int_equal(eresult, ISC_R_SUCCESS);
atomic_fetch_add(&csends, 1);
isc_loopmgr_shutdown(loopmgr);
isc_nmhandle_detach(&handle);
isc_refcount_decrement(&active_csends);
}
static void static void
udp_shutdown_read_read_cb(isc_nmhandle_t *handle, isc_result_t eresult, udp_shutdown_read_read_cb(isc_nmhandle_t *handle, isc_result_t eresult,
isc_region_t *region, void *cbarg) { isc_region_t *region, void *cbarg) {
UNUSED(handle);
UNUSED(region); UNUSED(region);
UNUSED(cbarg); UNUSED(cbarg);
assert_int_equal(eresult, ISC_R_SHUTTINGDOWN); assert_true(eresult == ISC_R_SHUTTINGDOWN || eresult == ISC_R_TIMEDOUT);
isc_refcount_decrement(&active_creads); isc_refcount_decrement(&active_creads);
@@ -575,21 +607,25 @@ static void
udp_shutdown_read_connect_cb(isc_nmhandle_t *handle, isc_result_t eresult, udp_shutdown_read_connect_cb(isc_nmhandle_t *handle, isc_result_t eresult,
void *cbarg) { void *cbarg) {
isc_nmhandle_t *readhandle = NULL; isc_nmhandle_t *readhandle = NULL;
isc_nmhandle_t *sendhandle = NULL;
UNUSED(handle);
UNUSED(cbarg);
isc_refcount_decrement(&active_cconnects); isc_refcount_decrement(&active_cconnects);
assert_int_equal(eresult, ISC_R_SUCCESS); assert_int_equal(eresult, ISC_R_SUCCESS);
/* Read */
isc_refcount_increment0(&active_creads); isc_refcount_increment0(&active_creads);
isc_nmhandle_attach(handle, &readhandle); isc_nmhandle_attach(handle, &readhandle);
isc_nm_read(handle, udp_shutdown_read_read_cb, cbarg); isc_nm_read(handle, udp_shutdown_read_read_cb, cbarg);
atomic_fetch_add(&cconnects, 1); /* Send */
isc_refcount_increment0(&active_csends);
isc_nmhandle_attach(handle, &sendhandle);
isc_nmhandle_setwritetimeout(handle, T_IDLE);
isc_nm_send(sendhandle, (isc_region_t *)&send_msg,
udp_shutdown_read_send_cb, cbarg);
isc_loopmgr_shutdown(loopmgr); atomic_fetch_add(&cconnects, 1);
} }
ISC_SETUP_TEST_IMPL(udp_shutdown_read) { ISC_SETUP_TEST_IMPL(udp_shutdown_read) {
@@ -607,6 +643,8 @@ ISC_TEARDOWN_TEST_IMPL(udp_shutdown_read) {
} }
ISC_LOOP_TEST_IMPL(udp_shutdown_read) { ISC_LOOP_TEST_IMPL(udp_shutdown_read) {
start_listening(ISC_NM_LISTEN_ONE, udp_shutdown_read_recv_cb);
isc_refcount_increment0(&active_cconnects); isc_refcount_increment0(&active_cconnects);
isc_nm_udpconnect(netmgr, &udp_connect_addr, &udp_listen_addr, isc_nm_udpconnect(netmgr, &udp_connect_addr, &udp_listen_addr,
udp_shutdown_read_connect_cb, NULL, T_SOFT); udp_shutdown_read_connect_cb, NULL, T_SOFT);
@@ -616,7 +654,8 @@ static void
udp_cancel_read_recv_cb(isc_nmhandle_t *handle, isc_result_t eresult, udp_cancel_read_recv_cb(isc_nmhandle_t *handle, isc_result_t eresult,
isc_region_t *region, void *cbarg) { isc_region_t *region, void *cbarg) {
uint64_t magic = 0; uint64_t magic = 0;
isc_nmhandle_t *sendhandle = NULL;
UNUSED(cbarg);
assert_non_null(handle); assert_non_null(handle);
@@ -628,38 +667,6 @@ udp_cancel_read_recv_cb(isc_nmhandle_t *handle, isc_result_t eresult,
memmove(&magic, region->base, sizeof(magic)); memmove(&magic, region->base, sizeof(magic));
assert_true(magic == send_magic); assert_true(magic == send_magic);
isc_nmhandle_attach(handle, &sendhandle);
isc_refcount_increment0(&active_ssends);
isc_nmhandle_setwritetimeout(sendhandle, T_IDLE);
isc_nm_send(sendhandle, (isc_region_t *)&send_msg, listen_send_cb,
cbarg);
}
static void
udp_cancel_read_read_cb(isc_nmhandle_t *handle, isc_result_t eresult,
isc_region_t *region, void *cbarg) {
UNUSED(handle);
UNUSED(region);
UNUSED(cbarg);
F();
switch (eresult) {
case ISC_R_TIMEDOUT:
case ISC_R_EOF:
break;
default:
UNREACHABLE();
}
isc_refcount_decrement(&active_creads);
atomic_fetch_add(&creads, 1);
isc_nmhandle_detach(&handle);
isc_loopmgr_shutdown(loopmgr);
} }
static void static void
@@ -674,18 +681,56 @@ udp_cancel_read_send_cb(isc_nmhandle_t *handle, isc_result_t eresult,
atomic_fetch_add(&csends, 1); atomic_fetch_add(&csends, 1);
isc_nm_cancelread(handle);
isc_nmhandle_detach(&handle); isc_nmhandle_detach(&handle);
isc_refcount_decrement(&active_csends); isc_refcount_decrement(&active_csends);
} }
static void
udp_cancel_read_read_cb(isc_nmhandle_t *handle, isc_result_t eresult,
isc_region_t *region, void *cbarg) {
isc_nmhandle_t *sendhandle = NULL;
isc_nmhandle_t *readhandle = NULL;
UNUSED(region);
F();
switch (eresult) {
case ISC_R_TIMEDOUT:
/* Read again */
isc_refcount_increment0(&active_creads);
isc_nmhandle_attach(handle, &readhandle);
isc_nm_read(handle, udp_cancel_read_read_cb, cbarg);
/* Send */
isc_refcount_increment0(&active_csends);
isc_nmhandle_attach(handle, &sendhandle);
isc_nmhandle_setwritetimeout(handle, T_IDLE);
isc_nm_send(sendhandle, (isc_region_t *)&send_msg,
udp_cancel_read_send_cb, cbarg);
break;
case ISC_R_EOF:
/* The read has been canceled */
isc_loopmgr_shutdown(loopmgr);
break;
default:
UNREACHABLE();
}
isc_refcount_decrement(&active_creads);
atomic_fetch_add(&creads, 1);
isc_nmhandle_detach(&handle);
}
static void static void
udp_cancel_read_connect_cb(isc_nmhandle_t *handle, isc_result_t eresult, udp_cancel_read_connect_cb(isc_nmhandle_t *handle, isc_result_t eresult,
void *cbarg) { void *cbarg) {
isc_nmhandle_t *readhandle = NULL; isc_nmhandle_t *readhandle = NULL;
isc_nmhandle_t *sendhandle = NULL;
UNUSED(handle);
UNUSED(cbarg);
isc_refcount_decrement(&active_cconnects); isc_refcount_decrement(&active_cconnects);
@@ -695,22 +740,13 @@ udp_cancel_read_connect_cb(isc_nmhandle_t *handle, isc_result_t eresult,
isc_nmhandle_attach(handle, &readhandle); isc_nmhandle_attach(handle, &readhandle);
isc_nm_read(handle, udp_cancel_read_read_cb, cbarg); isc_nm_read(handle, udp_cancel_read_read_cb, cbarg);
/* Send */
isc_refcount_increment0(&active_csends);
isc_nmhandle_attach(handle, &sendhandle);
isc_nmhandle_setwritetimeout(handle, T_IDLE);
isc_nm_send(sendhandle, (isc_region_t *)&send_msg,
udp_cancel_read_send_cb, readhandle);
isc_nm_cancelread(readhandle);
atomic_fetch_add(&cconnects, 1); atomic_fetch_add(&cconnects, 1);
} }
ISC_SETUP_TEST_IMPL(udp_cancel_read) { ISC_SETUP_TEST_IMPL(udp_cancel_read) {
setup_test(state); setup_test(state);
expected_cconnects = 1; expected_cconnects = 1;
expected_creads = 1; expected_creads = 2;
return (0); return (0);
} }
@@ -733,8 +769,6 @@ static void
udp__send_cb(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) { udp__send_cb(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) {
isc_nmhandle_t *sendhandle = handle; isc_nmhandle_t *sendhandle = handle;
UNUSED(cbarg);
assert_non_null(sendhandle); assert_non_null(sendhandle);
F(); F();
@@ -780,8 +814,6 @@ udp__connect_cb(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) {
isc_nmhandle_t *readhandle = NULL; isc_nmhandle_t *readhandle = NULL;
isc_nmhandle_t *sendhandle = NULL; isc_nmhandle_t *sendhandle = NULL;
UNUSED(cbarg);
F(); F();
isc_refcount_decrement(&active_cconnects); isc_refcount_decrement(&active_cconnects);
@@ -797,7 +829,7 @@ udp__connect_cb(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) {
isc_refcount_increment0(&active_creads); isc_refcount_increment0(&active_creads);
isc_nmhandle_attach(handle, &readhandle); isc_nmhandle_attach(handle, &readhandle);
isc_nm_read(handle, connect_read_cb, cbarg); isc_nm_read(handle, connect_readcb, cbarg);
isc_refcount_increment0(&active_csends); isc_refcount_increment0(&active_csends);
isc_nmhandle_attach(handle, &sendhandle); isc_nmhandle_attach(handle, &sendhandle);
@@ -931,6 +963,166 @@ ISC_LOOP_TEST_IMPL(udp_recv_send) {
} }
} }
static void
double_read_send_cb(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) {
assert_non_null(handle);
F();
isc_refcount_decrement(&active_ssends);
switch (eresult) {
case ISC_R_SUCCESS:
if (have_expected_ssends(atomic_fetch_add(&ssends, 1) + 1)) {
do_ssends_shutdown(loopmgr);
} else {
isc_nmhandle_t *sendhandle = NULL;
isc_nmhandle_attach(handle, &sendhandle);
isc_nmhandle_setwritetimeout(sendhandle, T_IDLE);
isc_refcount_increment0(&active_ssends);
isc_nm_send(sendhandle, &send_msg, double_read_send_cb,
cbarg);
break;
}
break;
case ISC_R_CANCELED:
break;
default:
fprintf(stderr, "%s(%p, %s, %p)\n", __func__, handle,
isc_result_totext(eresult), cbarg);
assert_int_equal(eresult, ISC_R_SUCCESS);
}
isc_nmhandle_detach(&handle);
}
static void
double_read_listen_cb(isc_nmhandle_t *handle, isc_result_t eresult,
isc_region_t *region, void *cbarg) {
uint64_t magic = 0;
assert_non_null(handle);
F();
switch (eresult) {
case ISC_R_EOF:
case ISC_R_SHUTTINGDOWN:
case ISC_R_CANCELED:
break;
case ISC_R_SUCCESS:
memmove(&magic, region->base, sizeof(magic));
assert_true(magic == send_magic);
assert_true(region->length >= sizeof(magic));
memmove(&magic, region->base, sizeof(magic));
assert_true(magic == send_magic);
isc_nmhandle_t *sendhandle = NULL;
isc_nmhandle_attach(handle, &sendhandle);
isc_nmhandle_setwritetimeout(sendhandle, T_IDLE);
isc_refcount_increment0(&active_ssends);
isc_nm_send(sendhandle, &send_msg, double_read_send_cb, cbarg);
return;
default:
fprintf(stderr, "%s(%p, %s, %p)\n", __func__, handle,
isc_result_totext(eresult), cbarg);
assert_int_equal(eresult, ISC_R_SUCCESS);
}
isc_refcount_decrement(&active_sreads);
isc_nmhandle_detach(&handle);
}
static void
double_read_cb(isc_nmhandle_t *handle, isc_result_t eresult,
isc_region_t *region, void *cbarg) {
uint64_t magic = 0;
bool detach = false;
assert_non_null(handle);
F();
switch (eresult) {
case ISC_R_SUCCESS:
assert_true(region->length >= sizeof(magic));
memmove(&magic, region->base, sizeof(magic));
assert_true(magic == send_magic);
if (have_expected_creads(atomic_fetch_add(&creads, 1) + 1)) {
do_creads_shutdown(loopmgr);
detach = true;
}
if (magic == send_magic && allow_send_back) {
connect_send(handle);
return;
}
break;
case ISC_R_TIMEDOUT:
case ISC_R_EOF:
case ISC_R_SHUTTINGDOWN:
case ISC_R_CANCELED:
case ISC_R_CONNECTIONRESET:
detach = true;
break;
default:
fprintf(stderr, "%s(%p, %s, %p)\n", __func__, handle,
isc_result_totext(eresult), cbarg);
assert_int_equal(eresult, ISC_R_SUCCESS);
}
if (detach) {
isc_refcount_decrement(&active_creads);
isc_nmhandle_detach(&handle);
} else {
isc_nm_read(handle, connect_readcb, cbarg);
}
}
ISC_SETUP_TEST_IMPL(udp_double_read) {
setup_test(state);
expected_cconnects = 1;
cconnects_shutdown = false;
expected_csends = 1;
csends_shutdown = false;
expected_sreads = 1;
sreads_shutdown = false;
expected_ssends = 2;
ssends_shutdown = false;
expected_creads = 2;
creads_shutdown = true;
connect_readcb = double_read_cb;
return (0);
}
ISC_TEARDOWN_TEST_IMPL(udp_double_read) {
atomic_assert_int_eq(creads, expected_creads);
teardown_test(state);
return (0);
}
ISC_LOOP_TEST_IMPL(udp_double_read) {
start_listening(ISC_NM_LISTEN_ALL, double_read_listen_cb);
udp__connect(NULL);
}
ISC_TEST_LIST_START ISC_TEST_LIST_START
/* Mock tests are unreliable on OpenBSD */ /* Mock tests are unreliable on OpenBSD */
@@ -956,6 +1148,7 @@ ISC_TEST_ENTRY_SETUP_TEARDOWN(udp_shutdown_connect)
ISC_TEST_ENTRY_SETUP_TEARDOWN(udp_shutdown_read) ISC_TEST_ENTRY_SETUP_TEARDOWN(udp_shutdown_read)
ISC_TEST_ENTRY_SETUP_TEARDOWN(udp_cancel_read) ISC_TEST_ENTRY_SETUP_TEARDOWN(udp_cancel_read)
ISC_TEST_ENTRY_SETUP_TEARDOWN(udp_timeout_recovery) ISC_TEST_ENTRY_SETUP_TEARDOWN(udp_timeout_recovery)
ISC_TEST_ENTRY_SETUP_TEARDOWN(udp_double_read)
ISC_TEST_ENTRY_SETUP_TEARDOWN(udp_recv_one) ISC_TEST_ENTRY_SETUP_TEARDOWN(udp_recv_one)
ISC_TEST_ENTRY_SETUP_TEARDOWN(udp_recv_two) ISC_TEST_ENTRY_SETUP_TEARDOWN(udp_recv_two)
ISC_TEST_ENTRY_SETUP_TEARDOWN(udp_recv_send) ISC_TEST_ENTRY_SETUP_TEARDOWN(udp_recv_send)