diff --git a/tests/Makefile.am b/tests/Makefile.am index 7edaa50a..b12bd522 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -392,9 +392,10 @@ test_attachment_send_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS) test_dm_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/transport_layer -I$(top_srcdir)/lib test_dm_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS) -test_transport_SOURCES = test_stcp_link.c +test_transport_SOURCES = test_stcp_link.c $(test_clock_sources) test_transport_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib test_transport_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS) +test_transport_LDFLAGS = $(test_clock_ldflags) test_etcp_transport_SOURCES = test_etcp_stcp.c $(test_clock_sources) test_etcp_transport_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib diff --git a/tests/test_stcp_link.c b/tests/test_stcp_link.c index 71997eb4..cf2f0447 100644 --- a/tests/test_stcp_link.c +++ b/tests/test_stcp_link.c @@ -3,6 +3,8 @@ #include "stcp_client.h" #include "secure_channel.h" #include "utun_instance.h" +#include "etcp.h" +#include "test_clock.h" #include "../lib/u_async.h" #include "../lib/debug_config.h" #include "../lib/mem.h" @@ -55,12 +57,13 @@ static int test1_basic(void) { TASSERT(sc); int ticks = 0; - while ((!g_on_srv_link || !cli_ready) && ticks < 5000) { uasync_poll(ua, 10); ticks++; } + while ((!g_on_srv_link || !cli_ready) && ticks < 5000) { test_clock_poll(ua, 10); ticks++; } TASSERT(g_on_srv_link != NULL); TASSERT(cli_ready); stcp_client_destroy(sc); stcp_link_close(g_on_srv_link); stcp_link_server_destroy(ss); + uasync_drain_immediate(ua); u_free(inst); uasync_destroy(ua, 1); return 0; @@ -86,7 +89,7 @@ static int test2_wrong_key(void) { // Неверный ключ должен завершить handshake закрытием, без создания серверного линка. struct stcp_conn* conn = stcp_client_get_conn(sc); TASSERT(conn); uint64_t start = get_time_tb(), deadline = start + 20000; - while (conn->state != STCP_STATE_CLOSED && get_time_tb() < deadline) uasync_poll(ua, 10); + while (conn->state != STCP_STATE_CLOSED && get_time_tb() < deadline) test_clock_poll(ua, 10); TASSERT(conn->state == STCP_STATE_CLOSED); TASSERT(!cli_ready); TASSERT(!g_on_srv_link); // should NOT connect with wrong key @@ -95,22 +98,103 @@ static int test2_wrong_key(void) { stcp_client_destroy(sc); stcp_link_server_destroy(ss); + uasync_drain_immediate(ua); u_free(inst); uasync_destroy(ua, 1); return 0; } +struct failure_case { + struct stcp_link *link; + unsigned closed, ready; + int error, close_inside; +}; + +static void adapter_ready(struct stcp_link *link, void *arg) { + (void)link; + ((struct failure_case *)arg)->ready++; +} + +static void adapter_closed(struct stcp_link *link, int err, void *arg) { + struct failure_case *result = arg; + result->closed++; result->error = err; + DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "adapter close: count=%u err=%d close_inside=%d", result->closed, err, result->close_inside); + if (result->close_inside) { + stcp_link_close(link); + stcp_link_close(link); /* Повторный close внутри того же callback не планирует второй free. */ + result->link = NULL; + } +} + +/* 0: refused; 1: отмена до poll; 2: неверный ключ; 3: молчащий сервер; 4: close из close-callback. */ +static int test_adapter_failure(unsigned scenario) { + size_t before = u_get_allocated_count(); + struct UASYNC *ua = uasync_create(); TASSERT(ua); + struct UTUN_INSTANCE *inst = make_inst(ua, &c_keys); TASSERT(inst); + struct ETCP_CONN parent = {.instance = inst}; + struct ETCP_LINK path = {.etcp = &parent, .is_tcp = 1}; + struct failure_case result = {.close_inside = scenario == 4}; + struct sockaddr_storage address = {0}; + struct sockaddr_in *v4 = (struct sockaddr_in *)&address; + v4->sin_family = AF_INET; v4->sin_addr.s_addr = htonl(INADDR_LOOPBACK); + struct stcp_server *server = NULL; + socket_t reserved = SOCKET_INVALID; + int server_ready = 0; + if (scenario == 2) { + server = stcp_server_create(ua, 0, &s_keys, NULL, NULL, on_cli_ready, &server_ready, NULL, NULL, AF_INET); + TASSERT(server); + v4->sin_port = htons(stcp_server_get_port(server)); + struct SC_MYKEYS rogue; TASSERT(sc_generate_keypair(&rogue) == SC_OK); + memcpy(parent.crypto_ctx.peer_public_key, rogue.public_key, SC_PUBKEY_SIZE); + } else { + reserved = socket(AF_INET, SOCK_STREAM, 0); TASSERT(reserved != SOCKET_INVALID); + TASSERT(bind(reserved, (struct sockaddr *)v4, sizeof(*v4)) == 0); + socklen_t length = sizeof(*v4); + TASSERT(getsockname(reserved, (struct sockaddr *)v4, &length) == 0); + if (scenario == 3) TASSERT(listen(reserved, 1) == 0); + memcpy(parent.crypto_ctx.peer_public_key, s_keys.public_key, SC_PUBKEY_SIZE); + } + result.link = stcp_link_connect(&path, &address, ntohs(v4->sin_port)); TASSERT(result.link); + path.tcp_link = result.link; + stcp_link_set_on_ready(result.link, adapter_ready, &result); + stcp_link_set_on_close(result.link, adapter_closed, &result); + if (scenario == 1) { stcp_link_close(result.link); result.link = NULL; } + uint64_t deadline = get_time_tb() + (scenario == 3 ? STCP_CONNECT_TIMEOUT + 1000 : 20000); + while (!result.closed && get_time_tb() < deadline) test_clock_poll(ua, 100); + TASSERT(!result.ready && !server_ready); + TASSERT(result.closed == (scenario == 1 ? 0 : 1)); + if (scenario == 0 || scenario == 4) TASSERT(result.error == ECONNREFUSED); + if (scenario == 3) TASSERT(result.error == ETIMEDOUT); + test_clock_run_for(ua, 1000); + TASSERT(result.closed == (scenario == 1 ? 0 : 1)); + if (result.link) stcp_link_close(result.link); + if (server) stcp_server_destroy(server); + if (reserved != SOCKET_INVALID) socket_close_wrapper(reserved); + uasync_drain_immediate(ua); + TASSERT(ua->timer_alloc_count == ua->timer_free_count && ua->socket_alloc_count == ua->socket_free_count); + u_free(inst); uasync_destroy(ua, 0); + TASSERT(u_get_allocated_count() == before); + DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "PASS adapter failure scenario=%u callbacks=%u", scenario, result.closed); + return 0; +} + int main(void) { debug_config_init(); debug_set_level(DEBUG_LEVEL_INFO); debug_set_categories(DEBUG_CATEGORY_GENERAL | DEBUG_CATEGORY_SOCKET | DEBUG_CATEGORY_CRYPTO); + debug_set_category_level(DEBUG_CATEGORY_DEBUG, DEBUG_LEVEL_INFO); + test_clock_init(100); TASSERT(sc_generate_keypair(&s_keys) == SC_OK); TASSERT(sc_generate_keypair(&c_keys) == SC_OK); - test1_basic(); - test2_wrong_key(); + tests_total++; if (!test1_basic()) tests_passed++; + tests_total++; if (!test2_wrong_key()) tests_passed++; + for (unsigned scenario = 0; scenario < 5; scenario++) { + tests_total++; if (!test_adapter_failure(scenario)) tests_passed++; + } - DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "Results: %d/%d passed", 2 - (test_failed ? 1 : 0), 2); + DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "Results: %d/%d passed", tests_passed, tests_total); + test_clock_finish(); return test_failed ? 1 : 0; }