2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-09-05 00:55:24 +00:00
Files
bind/lib/isc/tests/tcpdns_test.c
Ondřej Surý 36ddefacb4 Change the isc_nm_(get|set)timeouts() to work with milliseconds
The RFC7828 specifies the keepalive interval to be 16-bit, specified in
units of 100 milliseconds and the configuration options tcp-*-timeouts
are following the suit.  The units of 100 milliseconds are very
unintuitive and while we can't change the configuration and presentation
format, we should not follow this weird unit in the API.

This commit changes the isc_nm_(get|set)timeouts() functions to work
with milliseconds and convert the values to milliseconds before passing
them to the function, not just internally.
2021-03-18 16:37:57 +01:00

879 lines
21 KiB
C

/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
#if HAVE_CMOCKA
#include <sched.h> /* IWYU pragma: keep */
#include <setjmp.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <uv.h>
#define UNIT_TESTING
#include <cmocka.h>
#include <isc/atomic.h>
#include <isc/buffer.h>
#include <isc/condition.h>
#include <isc/mutex.h>
#include <isc/netmgr.h>
#include <isc/nonce.h>
#include <isc/os.h>
#include <isc/refcount.h>
#include <isc/sockaddr.h>
#include <isc/thread.h>
#include "../netmgr/netmgr-int.h"
#include "isctest.h"
#define MAX_NM 2
static isc_sockaddr_t tcpdns_listen_addr;
static uint64_t send_magic = 0;
static uint64_t stop_magic = 0;
static uv_buf_t send_msg = { .base = (char *)&send_magic,
.len = sizeof(send_magic) };
static uv_buf_t stop_msg = { .base = (char *)&stop_magic,
.len = sizeof(stop_magic) };
static atomic_uint_fast64_t nsends;
static atomic_uint_fast64_t ssends;
static atomic_uint_fast64_t sreads;
static atomic_uint_fast64_t cconnects;
static atomic_uint_fast64_t csends;
static atomic_uint_fast64_t creads;
static atomic_uint_fast64_t ctimeouts;
static unsigned int workers = 3;
static bool reuse_supported = true;
#define NSENDS 100
#define NWRITES 10
#define CHECK_RANGE_FULL(v) \
{ \
int __v = atomic_load(&v); \
assert_true(__v > NSENDS * NWRITES * 10 / 100); \
assert_true(__v <= NSENDS * NWRITES * 110 / 100); \
}
#define CHECK_RANGE_HALF(v) \
{ \
int __v = atomic_load(&v); \
assert_true(__v > NSENDS * NWRITES * 5 / 100); \
assert_true(__v <= NSENDS * NWRITES * 60 / 100); \
}
/* Enable this to print values while running tests */
#undef PRINT_DEBUG
#ifdef PRINT_DEBUG
#define X(v) fprintf(stderr, #v " = %" PRIu64 "\n", atomic_load(&v))
#else
#define X(v)
#endif
static int
setup_ephemeral_port(isc_sockaddr_t *addr, sa_family_t family) {
isc_result_t result;
socklen_t addrlen = sizeof(*addr);
int fd;
int r;
isc_sockaddr_fromin6(addr, &in6addr_loopback, 0);
fd = socket(AF_INET6, family, 0);
if (fd < 0) {
perror("setup_ephemeral_port: socket()");
return (-1);
}
r = bind(fd, (const struct sockaddr *)&addr->type.sa,
sizeof(addr->type.sin6));
if (r != 0) {
perror("setup_ephemeral_port: bind()");
close(fd);
return (r);
}
r = getsockname(fd, (struct sockaddr *)&addr->type.sa, &addrlen);
if (r != 0) {
perror("setup_ephemeral_port: getsockname()");
close(fd);
return (r);
}
result = isc__nm_socket_reuse(fd);
if (result != ISC_R_SUCCESS && result != ISC_R_NOTIMPLEMENTED) {
fprintf(stderr,
"setup_ephemeral_port: isc__nm_socket_reuse(): %s",
isc_result_totext(result));
close(fd);
return (-1);
}
result = isc__nm_socket_reuse_lb(fd);
if (result != ISC_R_SUCCESS && result != ISC_R_NOTIMPLEMENTED) {
fprintf(stderr,
"setup_ephemeral_port: isc__nm_socket_reuse_lb(): %s",
isc_result_totext(result));
close(fd);
return (-1);
}
if (result == ISC_R_NOTIMPLEMENTED) {
reuse_supported = false;
}
#if IPV6_RECVERR
#define setsockopt_on(socket, level, name) \
setsockopt(socket, level, name, &(int){ 1 }, sizeof(int))
r = setsockopt_on(fd, IPPROTO_IPV6, IPV6_RECVERR);
if (r != 0) {
perror("setup_ephemeral_port");
close(fd);
return (r);
}
#endif
return (fd);
}
static int
_setup(void **state) {
UNUSED(state);
/* workers = isc_os_ncpus(); */
if (isc_test_begin(NULL, true, workers) != ISC_R_SUCCESS) {
return (-1);
}
signal(SIGPIPE, SIG_IGN);
return (0);
}
static int
_teardown(void **state) {
UNUSED(state);
isc_test_end();
return (0);
}
/* Generic */
static void
noop_recv_cb(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
void *cbarg) {
UNUSED(handle);
UNUSED(eresult);
UNUSED(region);
UNUSED(cbarg);
}
static unsigned int
noop_accept_cb(isc_nmhandle_t *handle, unsigned int result, void *cbarg) {
UNUSED(handle);
UNUSED(result);
UNUSED(cbarg);
return (0);
}
static void
noop_connect_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
UNUSED(handle);
UNUSED(result);
UNUSED(cbarg);
}
thread_local uint8_t tcpdns_buffer_storage[4096];
thread_local size_t tcpdns_buffer_length = 0;
static int
nm_setup(void **state) {
size_t nworkers = ISC_MAX(ISC_MIN(workers, 32), 1);
int tcpdns_listen_sock = -1;
isc_nm_t **nm = NULL;
tcpdns_listen_addr = (isc_sockaddr_t){ .length = 0 };
tcpdns_listen_sock = setup_ephemeral_port(&tcpdns_listen_addr,
SOCK_STREAM);
if (tcpdns_listen_sock < 0) {
return (-1);
}
close(tcpdns_listen_sock);
tcpdns_listen_sock = -1;
atomic_store(&nsends, NSENDS * NWRITES);
atomic_store(&csends, 0);
atomic_store(&creads, 0);
atomic_store(&sreads, 0);
atomic_store(&ssends, 0);
atomic_store(&ctimeouts, 0);
atomic_store(&cconnects, 0);
isc_nonce_buf(&send_magic, sizeof(send_magic));
isc_nonce_buf(&stop_magic, sizeof(stop_magic));
if (send_magic == stop_magic) {
return (-1);
}
nm = isc_mem_get(test_mctx, MAX_NM * sizeof(nm[0]));
for (size_t i = 0; i < MAX_NM; i++) {
nm[i] = isc_nm_start(test_mctx, nworkers);
assert_non_null(nm[i]);
}
*state = nm;
return (0);
}
static int
nm_teardown(void **state) {
isc_nm_t **nm = (isc_nm_t **)*state;
for (size_t i = 0; i < MAX_NM; i++) {
isc_nm_destroy(&nm[i]);
assert_null(nm[i]);
}
isc_mem_put(test_mctx, nm, MAX_NM * sizeof(nm[0]));
return (0);
}
thread_local size_t nwrites = NWRITES;
/* TCPDNS */
static void
tcpdns_connect_send_cb(isc_nmhandle_t *handle, isc_result_t eresult,
void *cbarg);
static void
tcpdns_connect_send(isc_nmhandle_t *handle);
static void
tcpdns_connect_send_cb(isc_nmhandle_t *handle, isc_result_t eresult,
void *cbarg) {
assert_non_null(handle);
UNUSED(cbarg);
if (eresult == ISC_R_SUCCESS) {
atomic_fetch_add(&csends, 1);
} else {
/* Send failed, we need to stop reading too */
isc_nm_cancelread(handle);
}
}
static void
tcpdns_connect_send(isc_nmhandle_t *handle) {
uint_fast64_t sends = atomic_load(&nsends);
/* Continue until we subtract or we are sent them all */
while (sends > 0) {
if (atomic_compare_exchange_weak(&nsends, &sends, sends - 1)) {
sends--;
break;
}
}
if (sends == 0) {
isc_nm_send(handle, (isc_region_t *)&stop_msg,
tcpdns_connect_send_cb, NULL);
} else {
isc_nm_send(handle, (isc_region_t *)&send_msg,
tcpdns_connect_send_cb, NULL);
}
}
static void
tcpdns_connect_read_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);
if (eresult != ISC_R_SUCCESS) {
goto unref;
}
assert_int_equal(region->length, sizeof(magic));
atomic_fetch_add(&creads, 1);
memmove(&magic, region->base, sizeof(magic));
assert_true(magic == stop_magic || magic == send_magic);
unref:
isc_nmhandle_detach(&handle);
}
static void
tcpdns_connect_connect_cb(isc_nmhandle_t *handle, isc_result_t eresult,
void *cbarg) {
isc_nmhandle_t *readhandle = NULL;
UNUSED(cbarg);
if (eresult != ISC_R_SUCCESS) {
uint_fast64_t sends = atomic_load(&nsends);
/* We failed to connect; try again */
while (sends > 0) {
/* Continue until we subtract or we are done */
if (atomic_compare_exchange_weak(&nsends, &sends,
sends - 1)) {
sends--;
break;
}
}
return;
}
atomic_fetch_add(&cconnects, 1);
isc_nmhandle_attach(handle, &readhandle);
isc_nm_read(handle, tcpdns_connect_read_cb, NULL);
tcpdns_connect_send(handle);
}
static void
tcpdns_noop(void **state) {
isc_nm_t **nm = (isc_nm_t **)*state;
isc_nm_t *listen_nm = nm[0];
isc_nm_t *connect_nm = nm[1];
isc_result_t result = ISC_R_SUCCESS;
isc_nmsocket_t *listen_sock = NULL;
isc_sockaddr_t tcpdns_connect_addr;
tcpdns_connect_addr = (isc_sockaddr_t){ .length = 0 };
isc_sockaddr_fromin6(&tcpdns_connect_addr, &in6addr_loopback, 0);
result = isc_nm_listentcpdns(
listen_nm, (isc_nmiface_t *)&tcpdns_listen_addr, noop_recv_cb,
NULL, noop_accept_cb, NULL, 0, 0, NULL, &listen_sock);
assert_int_equal(result, ISC_R_SUCCESS);
isc_nm_stoplistening(listen_sock);
isc_nmsocket_close(&listen_sock);
assert_null(listen_sock);
(void)isc_nm_tcpdnsconnect(connect_nm,
(isc_nmiface_t *)&tcpdns_connect_addr,
(isc_nmiface_t *)&tcpdns_listen_addr,
noop_connect_cb, NULL, 1000, 0);
isc_nm_closedown(connect_nm);
assert_int_equal(0, atomic_load(&cconnects));
assert_int_equal(0, atomic_load(&csends));
assert_int_equal(0, atomic_load(&creads));
assert_int_equal(0, atomic_load(&ctimeouts));
assert_int_equal(0, atomic_load(&sreads));
assert_int_equal(0, atomic_load(&ssends));
}
static void
tcpdns_noresponse(void **state) {
isc_nm_t **nm = (isc_nm_t **)*state;
isc_nm_t *listen_nm = nm[0];
isc_nm_t *connect_nm = nm[1];
isc_result_t result = ISC_R_SUCCESS;
isc_nmsocket_t *listen_sock = NULL;
isc_sockaddr_t tcpdns_connect_addr;
tcpdns_connect_addr = (isc_sockaddr_t){ .length = 0 };
isc_sockaddr_fromin6(&tcpdns_connect_addr, &in6addr_loopback, 0);
result = isc_nm_listentcpdns(
listen_nm, (isc_nmiface_t *)&tcpdns_listen_addr, noop_recv_cb,
NULL, noop_accept_cb, NULL, 0, 0, NULL, &listen_sock);
assert_int_equal(result, ISC_R_SUCCESS);
(void)isc_nm_tcpdnsconnect(connect_nm,
(isc_nmiface_t *)&tcpdns_connect_addr,
(isc_nmiface_t *)&tcpdns_listen_addr,
tcpdns_connect_connect_cb, NULL, 1000, 0);
isc_nm_stoplistening(listen_sock);
isc_nmsocket_close(&listen_sock);
assert_null(listen_sock);
isc_nm_closedown(connect_nm);
X(cconnects);
X(csends);
X(creads);
X(ctimeouts);
X(sreads);
X(ssends);
assert_true(atomic_load(&cconnects) <= 1);
assert_true(atomic_load(&csends) <= 1);
assert_int_equal(0, atomic_load(&creads));
assert_int_equal(0, atomic_load(&ctimeouts));
assert_int_equal(0, atomic_load(&sreads));
assert_int_equal(0, atomic_load(&ssends));
}
static void
tcpdns_listen_read_cb(isc_nmhandle_t *handle, isc_result_t eresult,
isc_region_t *region, void *cbarg);
static void
tcpdns_listen_send_cb(isc_nmhandle_t *handle, isc_result_t eresult,
void *cbarg) {
UNUSED(cbarg);
UNUSED(eresult);
assert_non_null(handle);
if (eresult != ISC_R_SUCCESS) {
return;
}
atomic_fetch_add(&ssends, 1);
}
static void
tcpdns_listen_read_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);
if (eresult != ISC_R_SUCCESS) {
return;
}
atomic_fetch_add(&sreads, 1);
assert_int_equal(region->length, sizeof(magic));
memmove(&magic, region->base, sizeof(magic));
assert_true(magic == stop_magic || magic == send_magic);
if (magic == send_magic) {
isc_nm_send(handle, region, tcpdns_listen_send_cb, NULL);
return;
} else if (magic == stop_magic) {
/* We are done, we don't send anything back */
}
}
static isc_result_t
tcpdns_listen_accept_cb(isc_nmhandle_t *handle, isc_result_t eresult,
void *cbarg) {
UNUSED(handle);
UNUSED(cbarg);
return (eresult);
}
static isc_threadresult_t
tcpdns_connect_thread(isc_threadarg_t arg) {
isc_nm_t *connect_nm = (isc_nm_t *)arg;
isc_sockaddr_t tcpdns_connect_addr;
tcpdns_connect_addr = (isc_sockaddr_t){ .length = 0 };
isc_sockaddr_fromin6(&tcpdns_connect_addr, &in6addr_loopback, 0);
while (atomic_load(&nsends) > 0) {
(void)isc_nm_tcpdnsconnect(
connect_nm, (isc_nmiface_t *)&tcpdns_connect_addr,
(isc_nmiface_t *)&tcpdns_listen_addr,
tcpdns_connect_connect_cb, NULL, 1000, 0);
}
return ((isc_threadresult_t)0);
}
static void
tcpdns_recv_one(void **state) {
isc_nm_t **nm = (isc_nm_t **)*state;
isc_nm_t *listen_nm = nm[0];
isc_nm_t *connect_nm = nm[1];
isc_result_t result = ISC_R_SUCCESS;
isc_nmsocket_t *listen_sock = NULL;
isc_sockaddr_t tcpdns_connect_addr;
tcpdns_connect_addr = (isc_sockaddr_t){ .length = 0 };
isc_sockaddr_fromin6(&tcpdns_connect_addr, &in6addr_loopback, 0);
atomic_store(&nsends, 1);
result = isc_nm_listentcpdns(
listen_nm, (isc_nmiface_t *)&tcpdns_listen_addr,
tcpdns_listen_read_cb, NULL, tcpdns_listen_accept_cb, NULL, 0,
0, NULL, &listen_sock);
assert_int_equal(result, ISC_R_SUCCESS);
(void)isc_nm_tcpdnsconnect(connect_nm,
(isc_nmiface_t *)&tcpdns_connect_addr,
(isc_nmiface_t *)&tcpdns_listen_addr,
tcpdns_connect_connect_cb, NULL, 1000, 0);
while (atomic_load(&nsends) > 0) {
isc_thread_yield();
}
while (atomic_load(&cconnects) != 1 || atomic_load(&ssends) != 0 ||
atomic_load(&sreads) != 1 || atomic_load(&creads) != 0 ||
atomic_load(&csends) != 1)
{
isc_thread_yield();
}
isc_nm_stoplistening(listen_sock);
isc_nmsocket_close(&listen_sock);
assert_null(listen_sock);
isc_nm_closedown(connect_nm);
X(cconnects);
X(csends);
X(creads);
X(ctimeouts);
X(sreads);
X(ssends);
assert_int_equal(atomic_load(&cconnects), 1);
assert_int_equal(atomic_load(&csends), 1);
assert_int_equal(atomic_load(&creads), 0);
assert_int_equal(atomic_load(&ctimeouts), 0);
assert_int_equal(atomic_load(&sreads), 1);
assert_int_equal(atomic_load(&ssends), 0);
}
static void
tcpdns_recv_two(void **state) {
isc_nm_t **nm = (isc_nm_t **)*state;
isc_nm_t *listen_nm = nm[0];
isc_nm_t *connect_nm = nm[1];
isc_result_t result = ISC_R_SUCCESS;
isc_nmsocket_t *listen_sock = NULL;
isc_sockaddr_t tcpdns_connect_addr;
tcpdns_connect_addr = (isc_sockaddr_t){ .length = 0 };
isc_sockaddr_fromin6(&tcpdns_connect_addr, &in6addr_loopback, 0);
atomic_store(&nsends, 2);
result = isc_nm_listentcpdns(
listen_nm, (isc_nmiface_t *)&tcpdns_listen_addr,
tcpdns_listen_read_cb, NULL, tcpdns_listen_accept_cb, NULL, 0,
0, NULL, &listen_sock);
assert_int_equal(result, ISC_R_SUCCESS);
result = isc_nm_tcpdnsconnect(connect_nm,
(isc_nmiface_t *)&tcpdns_connect_addr,
(isc_nmiface_t *)&tcpdns_listen_addr,
tcpdns_connect_connect_cb, NULL, 1000, 0);
assert_int_equal(result, ISC_R_SUCCESS);
isc_nm_settimeouts(connect_nm, 1000, 1000, 1000, 1000);
result = isc_nm_tcpdnsconnect(connect_nm,
(isc_nmiface_t *)&tcpdns_connect_addr,
(isc_nmiface_t *)&tcpdns_listen_addr,
tcpdns_connect_connect_cb, NULL, 1000, 0);
assert_int_equal(result, ISC_R_SUCCESS);
while (atomic_load(&nsends) > 0) {
isc_thread_yield();
}
while (atomic_load(&sreads) != 2 || atomic_load(&ssends) != 1 ||
atomic_load(&csends) != 2 || atomic_load(&creads) != 1)
{
isc_thread_yield();
}
isc_nm_stoplistening(listen_sock);
isc_nmsocket_close(&listen_sock);
assert_null(listen_sock);
isc_nm_closedown(connect_nm);
X(cconnects);
X(csends);
X(creads);
X(ctimeouts);
X(sreads);
X(ssends);
assert_int_equal(atomic_load(&cconnects), 2);
assert_int_equal(atomic_load(&csends), 2);
assert_int_equal(atomic_load(&creads), 1);
assert_int_equal(atomic_load(&ctimeouts), 0);
assert_int_equal(atomic_load(&sreads), 2);
assert_int_equal(atomic_load(&ssends), 1);
}
static void
tcpdns_recv_send(void **state) {
isc_nm_t **nm = (isc_nm_t **)*state;
isc_nm_t *listen_nm = nm[0];
isc_nm_t *connect_nm = nm[1];
isc_result_t result = ISC_R_SUCCESS;
isc_nmsocket_t *listen_sock = NULL;
size_t nthreads = ISC_MAX(ISC_MIN(workers, 32), 1);
isc_thread_t threads[32] = { 0 };
if (!reuse_supported) {
skip();
return;
}
result = isc_nm_listentcpdns(
listen_nm, (isc_nmiface_t *)&tcpdns_listen_addr,
tcpdns_listen_read_cb, NULL, tcpdns_listen_accept_cb, NULL, 0,
0, NULL, &listen_sock);
assert_int_equal(result, ISC_R_SUCCESS);
for (size_t i = 0; i < nthreads; i++) {
isc_thread_create(tcpdns_connect_thread, connect_nm,
&threads[i]);
}
for (size_t i = 0; i < nthreads; i++) {
isc_thread_join(threads[i], NULL);
}
isc_nm_closedown(connect_nm);
isc_nm_stoplistening(listen_sock);
isc_nmsocket_close(&listen_sock);
assert_null(listen_sock);
X(cconnects);
X(csends);
X(creads);
X(ctimeouts);
X(sreads);
X(ssends);
CHECK_RANGE_FULL(csends);
CHECK_RANGE_FULL(creads);
CHECK_RANGE_FULL(sreads);
CHECK_RANGE_FULL(ssends);
}
static void
tcpdns_recv_half_send(void **state) {
isc_nm_t **nm = (isc_nm_t **)*state;
isc_nm_t *listen_nm = nm[0];
isc_nm_t *connect_nm = nm[1];
isc_result_t result = ISC_R_SUCCESS;
isc_nmsocket_t *listen_sock = NULL;
size_t nthreads = ISC_MAX(ISC_MIN(workers, 32), 1);
isc_thread_t threads[32] = { 0 };
if (!reuse_supported) {
skip();
return;
}
result = isc_nm_listentcpdns(
listen_nm, (isc_nmiface_t *)&tcpdns_listen_addr,
tcpdns_listen_read_cb, NULL, tcpdns_listen_accept_cb, NULL, 0,
0, NULL, &listen_sock);
assert_int_equal(result, ISC_R_SUCCESS);
for (size_t i = 0; i < nthreads; i++) {
isc_thread_create(tcpdns_connect_thread, connect_nm,
&threads[i]);
}
while (atomic_load(&nsends) >= (NSENDS * NWRITES) / 2) {
isc_thread_yield();
}
isc_nm_closedown(connect_nm);
for (size_t i = 0; i < nthreads; i++) {
isc_thread_join(threads[i], NULL);
}
isc_nm_stoplistening(listen_sock);
isc_nmsocket_close(&listen_sock);
assert_null(listen_sock);
X(cconnects);
X(csends);
X(creads);
X(ctimeouts);
X(sreads);
X(ssends);
CHECK_RANGE_HALF(csends);
CHECK_RANGE_HALF(creads);
CHECK_RANGE_HALF(sreads);
CHECK_RANGE_HALF(ssends);
}
static void
tcpdns_half_recv_send(void **state) {
isc_nm_t **nm = (isc_nm_t **)*state;
isc_nm_t *listen_nm = nm[0];
isc_nm_t *connect_nm = nm[1];
isc_result_t result = ISC_R_SUCCESS;
isc_nmsocket_t *listen_sock = NULL;
size_t nthreads = ISC_MAX(ISC_MIN(workers, 32), 1);
isc_thread_t threads[32] = { 0 };
if (!reuse_supported) {
skip();
return;
}
result = isc_nm_listentcpdns(
listen_nm, (isc_nmiface_t *)&tcpdns_listen_addr,
tcpdns_listen_read_cb, NULL, tcpdns_listen_accept_cb, NULL, 0,
0, NULL, &listen_sock);
assert_int_equal(result, ISC_R_SUCCESS);
for (size_t i = 0; i < nthreads; i++) {
isc_thread_create(tcpdns_connect_thread, connect_nm,
&threads[i]);
}
while (atomic_load(&nsends) >= (NSENDS * NWRITES) / 2) {
isc_thread_yield();
}
isc_nm_stoplistening(listen_sock);
isc_nmsocket_close(&listen_sock);
assert_null(listen_sock);
for (size_t i = 0; i < nthreads; i++) {
isc_thread_join(threads[i], NULL);
}
isc_nm_closedown(connect_nm);
X(cconnects);
X(csends);
X(creads);
X(ctimeouts);
X(sreads);
X(ssends);
CHECK_RANGE_HALF(csends);
CHECK_RANGE_HALF(creads);
CHECK_RANGE_HALF(sreads);
CHECK_RANGE_HALF(ssends);
}
static void
tcpdns_half_recv_half_send(void **state) {
isc_nm_t **nm = (isc_nm_t **)*state;
isc_nm_t *listen_nm = nm[0];
isc_nm_t *connect_nm = nm[1];
isc_result_t result = ISC_R_SUCCESS;
isc_nmsocket_t *listen_sock = NULL;
size_t nthreads = ISC_MAX(ISC_MIN(workers, 32), 1);
isc_thread_t threads[32] = { 0 };
if (!reuse_supported) {
skip();
return;
}
result = isc_nm_listentcpdns(
listen_nm, (isc_nmiface_t *)&tcpdns_listen_addr,
tcpdns_listen_read_cb, NULL, tcpdns_listen_accept_cb, NULL, 0,
0, NULL, &listen_sock);
assert_int_equal(result, ISC_R_SUCCESS);
for (size_t i = 0; i < nthreads; i++) {
isc_thread_create(tcpdns_connect_thread, connect_nm,
&threads[i]);
}
while (atomic_load(&nsends) >= (NSENDS * NWRITES) / 2) {
isc_thread_yield();
}
isc_nm_closedown(connect_nm);
isc_nm_stoplistening(listen_sock);
isc_nmsocket_close(&listen_sock);
assert_null(listen_sock);
for (size_t i = 0; i < nthreads; i++) {
isc_thread_join(threads[i], NULL);
}
X(cconnects);
X(csends);
X(creads);
X(ctimeouts);
X(sreads);
X(ssends);
CHECK_RANGE_HALF(csends);
CHECK_RANGE_HALF(creads);
CHECK_RANGE_HALF(sreads);
CHECK_RANGE_HALF(ssends);
}
int
main(void) {
const struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(tcpdns_recv_one, nm_setup,
nm_teardown),
cmocka_unit_test_setup_teardown(tcpdns_recv_two, nm_setup,
nm_teardown),
cmocka_unit_test_setup_teardown(tcpdns_noop, nm_setup,
nm_teardown),
cmocka_unit_test_setup_teardown(tcpdns_noresponse, nm_setup,
nm_teardown),
cmocka_unit_test_setup_teardown(tcpdns_recv_send, nm_setup,
nm_teardown),
cmocka_unit_test_setup_teardown(tcpdns_recv_half_send, nm_setup,
nm_teardown),
cmocka_unit_test_setup_teardown(tcpdns_half_recv_send, nm_setup,
nm_teardown),
cmocka_unit_test_setup_teardown(tcpdns_half_recv_half_send,
nm_setup, nm_teardown),
};
return (cmocka_run_group_tests(tests, _setup, _teardown));
}
#else /* HAVE_CMOCKA */
#include <stdio.h>
int
main(void) {
printf("1..0 # Skipped: cmocka not available\n");
return (SKIPPED_TEST_EXIT_CODE);
}
#endif /* if HAVE_CMOCKA */