diff --git a/.clang-tidy b/.clang-tidy index 8af9c0bd8..11fd3c6be 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -42,7 +42,7 @@ CheckOptions: - key: concurrency-mt-unsafe.FunctionSet value: posix - key: readability-function-cognitive-complexity.Threshold - value: 159 # TODO(iphydf): Decrease. tox_new_system is the highest at the moment. + value: 167 # TODO(iphydf): Decrease. tox_new_system is the highest at the moment. - key: cppcoreguidelines-avoid-do-while.IgnoreMacros value: true - key: readability-simplify-boolean-expr.SimplifyDeMorgan diff --git a/auto_tests/BUILD.bazel b/auto_tests/BUILD.bazel index a3075ea09..8946f4a87 100644 --- a/auto_tests/BUILD.bazel +++ b/auto_tests/BUILD.bazel @@ -17,8 +17,11 @@ cc_library( "//c-toxcore/testing:misc_tools", "//c-toxcore/toxcore:DHT", "//c-toxcore/toxcore:Messenger", + "//c-toxcore/toxcore:ev", "//c-toxcore/toxcore:mono_time", "//c-toxcore/toxcore:net_crypto", + "//c-toxcore/toxcore:network", + "//c-toxcore/toxcore:os_event", "//c-toxcore/toxcore:tox", "//c-toxcore/toxcore:tox_dispatch", "//c-toxcore/toxcore:tox_events", @@ -66,6 +69,7 @@ extra_data = { "//c-toxcore/toxcore:announce", "//c-toxcore/toxcore:ccompat", "//c-toxcore/toxcore:crypto_core", + "//c-toxcore/toxcore:ev", "//c-toxcore/toxcore:forwarding", "//c-toxcore/toxcore:friend_connection", "//c-toxcore/toxcore:logger", @@ -76,6 +80,7 @@ extra_data = { "//c-toxcore/toxcore:onion", "//c-toxcore/toxcore:onion_announce", "//c-toxcore/toxcore:onion_client", + "//c-toxcore/toxcore:os_event", "//c-toxcore/toxcore:os_memory", "//c-toxcore/toxcore:os_random", "//c-toxcore/toxcore:tox", diff --git a/auto_tests/TCP_test.c b/auto_tests/TCP_test.c index af10fcbb0..1526c7b65 100644 --- a/auto_tests/TCP_test.c +++ b/auto_tests/TCP_test.c @@ -2,7 +2,7 @@ #include #include -#include "../testing/misc_tools.h" +#include "../toxcore/os_event.h" #include "../toxcore/TCP_client.h" #include "../toxcore/TCP_common.h" #include "../toxcore/TCP_server.h" @@ -46,6 +46,68 @@ static void do_tcp_server_delay(TCP_Server *tcp_s, Mono_Time *mono_time, int del } static uint16_t ports[NUM_PORTS] = {13215, 33445, 25643}; +typedef struct { + const Random *rng; + const Network *ns; + const Memory *mem; + Mono_Time *mono_time; + Logger *logger; + Net_Profile *tcp_np; + Ev *ev; + TCP_Server *tcp_s; + TCP_Connections *tc_1; + TCP_Connections *tc_2; +} TCPTestEnvironment; + +static void setup_tcp_test_env(TCPTestEnvironment *env) +{ + env->rng = os_random(); + ck_assert(env->rng != nullptr); + env->ns = os_network(); + ck_assert(env->ns != nullptr); + env->mem = os_memory(); + ck_assert(env->mem != nullptr); + + env->mono_time = mono_time_new(env->mem, nullptr, nullptr); + env->logger = logger_new(env->mem); + + env->tcp_np = netprof_new(env->logger, env->mem); + ck_assert(env->tcp_np != nullptr); + + uint8_t self_public_key[CRYPTO_PUBLIC_KEY_SIZE]; + uint8_t self_secret_key[CRYPTO_SECRET_KEY_SIZE]; + crypto_new_keypair(env->rng, self_public_key, self_secret_key); + + env->ev = os_event_new(env->mem, env->logger); + ck_assert(env->ev != nullptr); + + env->tcp_s = new_tcp_server(env->logger, env->mem, env->rng, env->ns, env->ev, USE_IPV6, NUM_PORTS, ports, self_secret_key, nullptr, nullptr); + ck_assert_msg(pk_equal(tcp_server_public_key(env->tcp_s), self_public_key), "Wrong public key"); + + TCP_Proxy_Info proxy_info; + proxy_info.proxy_type = TCP_PROXY_NONE; + crypto_new_keypair(env->rng, self_public_key, self_secret_key); + env->tc_1 = new_tcp_connections(env->logger, env->mem, env->rng, env->ns, env->mono_time, env->ev, self_secret_key, &proxy_info, env->tcp_np); + ck_assert_msg(env->tc_1 != nullptr, "Failed to create TCP connections"); + ck_assert_msg(pk_equal(tcp_connections_public_key(env->tc_1), self_public_key), "Wrong public key"); + + crypto_new_keypair(env->rng, self_public_key, self_secret_key); + env->tc_2 = new_tcp_connections(env->logger, env->mem, env->rng, env->ns, env->mono_time, env->ev, self_secret_key, &proxy_info, env->tcp_np); + ck_assert_msg(env->tc_2 != nullptr, "Failed to create TCP connections"); + ck_assert_msg(pk_equal(tcp_connections_public_key(env->tc_2), self_public_key), "Wrong public key"); +} + +static void teardown_tcp_test_env(TCPTestEnvironment *env) +{ + kill_tcp_server(env->tcp_s); + kill_tcp_connections(env->tc_1); + kill_tcp_connections(env->tc_2); + netprof_kill(env->mem, env->tcp_np); + logger_kill(env->logger); + ev_kill(env->ev); + mono_time_free(env->mem, env->mono_time); +} + static void test_basic(void) { const Random *rng = os_random(); @@ -59,11 +121,14 @@ static void test_basic(void) Logger *logger = logger_new(mem); logger_callback_log(logger, print_debug_logger, nullptr, nullptr); + Ev *ev = os_event_new(mem, logger); + ck_assert(ev != nullptr); + // Attempt to create a new TCP_Server instance. uint8_t self_public_key[CRYPTO_PUBLIC_KEY_SIZE]; uint8_t self_secret_key[CRYPTO_SECRET_KEY_SIZE]; crypto_new_keypair(rng, self_public_key, self_secret_key); - TCP_Server *tcp_s = new_tcp_server(logger, mem, rng, ns, USE_IPV6, NUM_PORTS, ports, self_secret_key, nullptr, nullptr); + TCP_Server *tcp_s = new_tcp_server(logger, mem, rng, ns, ev, USE_IPV6, NUM_PORTS, ports, self_secret_key, nullptr, nullptr); ck_assert_msg(tcp_s != nullptr, "Failed to create a TCP relay server."); ck_assert_msg(tcp_server_listen_count(tcp_s) == NUM_PORTS, "Failed to bind a TCP relay server to all %d attempted ports.", NUM_PORTS); @@ -196,6 +261,7 @@ static void test_basic(void) // Closing connections. kill_sock(ns, sock); kill_tcp_server(tcp_s); + ev_kill(ev); logger_kill(logger); mono_time_free(mem, mono_time); @@ -325,10 +391,13 @@ static void test_some(void) Mono_Time *mono_time = mono_time_new(mem, nullptr, nullptr); Logger *logger = logger_new(mem); + Ev *ev = os_event_new(mem, logger); + ck_assert(ev != nullptr); + uint8_t self_public_key[CRYPTO_PUBLIC_KEY_SIZE]; uint8_t self_secret_key[CRYPTO_SECRET_KEY_SIZE]; crypto_new_keypair(rng, self_public_key, self_secret_key); - TCP_Server *tcp_s = new_tcp_server(logger, mem, rng, ns, USE_IPV6, NUM_PORTS, ports, self_secret_key, nullptr, nullptr); + TCP_Server *tcp_s = new_tcp_server(logger, mem, rng, ns, ev, USE_IPV6, NUM_PORTS, ports, self_secret_key, nullptr, nullptr); ck_assert_msg(tcp_s != nullptr, "Failed to create TCP relay server"); ck_assert_msg(tcp_server_listen_count(tcp_s) == NUM_PORTS, "Failed to bind to all ports."); @@ -422,6 +491,7 @@ static void test_some(void) // Kill off the connections kill_tcp_server(tcp_s); + ev_kill(ev); kill_tcp_con(con1); kill_tcp_con(con2); kill_tcp_con(con3); @@ -522,11 +592,12 @@ static void test_client(void) Logger *logger = logger_new(mem); Mono_Time *mono_time = mono_time_new(mem, nullptr, nullptr); + Ev *ev = os_event_new(mem, logger); uint8_t self_public_key[CRYPTO_PUBLIC_KEY_SIZE]; uint8_t self_secret_key[CRYPTO_SECRET_KEY_SIZE]; crypto_new_keypair(rng, self_public_key, self_secret_key); - TCP_Server *tcp_s = new_tcp_server(logger, mem, rng, ns, USE_IPV6, NUM_PORTS, ports, self_secret_key, nullptr, nullptr); + TCP_Server *tcp_s = new_tcp_server(logger, mem, rng, ns, ev, USE_IPV6, NUM_PORTS, ports, self_secret_key, nullptr, nullptr); ck_assert_msg(tcp_s != nullptr, "Failed to create a TCP relay server."); ck_assert_msg(tcp_server_listen_count(tcp_s) == NUM_PORTS, "Failed to bind the relay server to all ports."); @@ -538,7 +609,7 @@ static void test_client(void) ip_port_tcp_s.port = net_htons(ports[random_u32(rng) % NUM_PORTS]); ip_port_tcp_s.ip = get_loopback(); - TCP_Client_Connection *conn = new_tcp_connection(logger, mem, mono_time, rng, ns, &ip_port_tcp_s, self_public_key, f_public_key, f_secret_key, nullptr, nullptr); + TCP_Client_Connection *conn = new_tcp_connection(logger, mem, mono_time, rng, ns, ev, &ip_port_tcp_s, self_public_key, f_public_key, f_secret_key, nullptr, nullptr); ck_assert_msg(conn != nullptr, "Failed to create a TCP client connection."); // TCP sockets might need a moment before they can be written to. c_sleep(50); @@ -574,7 +645,7 @@ static void test_client(void) uint8_t f2_secret_key[CRYPTO_SECRET_KEY_SIZE]; crypto_new_keypair(rng, f2_public_key, f2_secret_key); ip_port_tcp_s.port = net_htons(ports[random_u32(rng) % NUM_PORTS]); - TCP_Client_Connection *conn2 = new_tcp_connection(logger, mem, mono_time, rng, ns, &ip_port_tcp_s, self_public_key, f2_public_key, + TCP_Client_Connection *conn2 = new_tcp_connection(logger, mem, mono_time, rng, ns, ev, &ip_port_tcp_s, self_public_key, f2_public_key, f2_secret_key, nullptr, nullptr); ck_assert_msg(conn2 != nullptr, "Failed to create a second TCP client connection."); c_sleep(50); @@ -645,6 +716,7 @@ static void test_client(void) kill_tcp_connection(conn2); logger_kill(logger); + ev_kill(ev); mono_time_free(mem, mono_time); } @@ -672,7 +744,8 @@ static void test_client_invalid(void) ip_port_tcp_s.port = net_htons(ports[random_u32(rng) % NUM_PORTS]); ip_port_tcp_s.ip = get_loopback(); - TCP_Client_Connection *conn = new_tcp_connection(logger, mem, mono_time, rng, ns, &ip_port_tcp_s, + Ev *ev = os_event_new(mem, logger); + TCP_Client_Connection *conn = new_tcp_connection(logger, mem, mono_time, rng, ns, ev, &ip_port_tcp_s, self_public_key, f_public_key, f_secret_key, nullptr, nullptr); ck_assert_msg(conn != nullptr, "Failed to create a TCP client connection."); @@ -700,6 +773,7 @@ static void test_client_invalid(void) kill_tcp_connection(conn); logger_kill(logger); + ev_kill(ev); mono_time_free(mem, mono_time); } @@ -730,101 +804,67 @@ static int tcp_data_callback(void *object, int id, const uint8_t *data, uint16_t static void test_tcp_connection(void) { - const Random *rng = os_random(); - ck_assert(rng != nullptr); - const Network *ns = os_network(); - ck_assert(ns != nullptr); - const Memory *mem = os_memory(); - ck_assert(mem != nullptr); - - Mono_Time *mono_time = mono_time_new(mem, nullptr, nullptr); - Logger *logger = logger_new(mem); - - Net_Profile *tcp_np = netprof_new(logger, mem); - ck_assert(tcp_np != nullptr); + TCPTestEnvironment env; + setup_tcp_test_env(&env); tcp_data_callback_called = 0; - uint8_t self_public_key[CRYPTO_PUBLIC_KEY_SIZE]; - uint8_t self_secret_key[CRYPTO_SECRET_KEY_SIZE]; - crypto_new_keypair(rng, self_public_key, self_secret_key); - TCP_Server *tcp_s = new_tcp_server(logger, mem, rng, ns, USE_IPV6, NUM_PORTS, ports, self_secret_key, nullptr, nullptr); - ck_assert_msg(pk_equal(tcp_server_public_key(tcp_s), self_public_key), "Wrong public key"); - - TCP_Proxy_Info proxy_info; - proxy_info.proxy_type = TCP_PROXY_NONE; - crypto_new_keypair(rng, self_public_key, self_secret_key); - TCP_Connections *tc_1 = new_tcp_connections(logger, mem, rng, ns, mono_time, self_secret_key, &proxy_info, tcp_np); - ck_assert_msg(tc_1 != nullptr, "Failed to create TCP connections"); - ck_assert_msg(pk_equal(tcp_connections_public_key(tc_1), self_public_key), "Wrong public key"); - - crypto_new_keypair(rng, self_public_key, self_secret_key); - TCP_Connections *tc_2 = new_tcp_connections(logger, mem, rng, ns, mono_time, self_secret_key, &proxy_info, tcp_np); - ck_assert_msg(tc_2 != nullptr, "Failed to create TCP connections"); - ck_assert_msg(pk_equal(tcp_connections_public_key(tc_2), self_public_key), "Wrong public key"); IP_Port ip_port_tcp_s; - ip_port_tcp_s.port = net_htons(ports[random_u32(rng) % NUM_PORTS]); + ip_port_tcp_s.port = net_htons(ports[random_u32(env.rng) % NUM_PORTS]); ip_port_tcp_s.ip = get_loopback(); - int connection = new_tcp_connection_to(tc_1, tcp_connections_public_key(tc_2), 123); + int connection = new_tcp_connection_to(env.tc_1, tcp_connections_public_key(env.tc_2), 123); ck_assert_msg(connection == 0, "Connection id wrong"); - ck_assert_msg(add_tcp_relay_connection(tc_1, connection, &ip_port_tcp_s, tcp_server_public_key(tcp_s)) == 0, + ck_assert_msg(add_tcp_relay_connection(env.tc_1, connection, &ip_port_tcp_s, tcp_server_public_key(env.tcp_s)) == 0, "Could not add tcp relay to connection\n"); - ip_port_tcp_s.port = net_htons(ports[random_u32(rng) % NUM_PORTS]); - connection = new_tcp_connection_to(tc_2, tcp_connections_public_key(tc_1), 123); + ip_port_tcp_s.port = net_htons(ports[random_u32(env.rng) % NUM_PORTS]); + connection = new_tcp_connection_to(env.tc_2, tcp_connections_public_key(env.tc_1), 123); ck_assert_msg(connection == 0, "Connection id wrong"); - ck_assert_msg(add_tcp_relay_connection(tc_2, connection, &ip_port_tcp_s, tcp_server_public_key(tcp_s)) == 0, + ck_assert_msg(add_tcp_relay_connection(env.tc_2, connection, &ip_port_tcp_s, tcp_server_public_key(env.tcp_s)) == 0, "Could not add tcp relay to connection\n"); - ck_assert_msg(new_tcp_connection_to(tc_2, tcp_connections_public_key(tc_1), 123) == -1, + ck_assert_msg(new_tcp_connection_to(env.tc_2, tcp_connections_public_key(env.tc_1), 123) == -1, "Managed to read same connection\n"); - do_tcp_server_delay(tcp_s, mono_time, 50); + do_tcp_server_delay(env.tcp_s, env.mono_time, 50); - do_tcp_connections(logger, tc_1, nullptr); - do_tcp_connections(logger, tc_2, nullptr); + do_tcp_connections(env.logger, env.tc_1, nullptr); + do_tcp_connections(env.logger, env.tc_2, nullptr); - do_tcp_server_delay(tcp_s, mono_time, 50); + do_tcp_server_delay(env.tcp_s, env.mono_time, 50); - do_tcp_connections(logger, tc_1, nullptr); - do_tcp_connections(logger, tc_2, nullptr); + do_tcp_connections(env.logger, env.tc_1, nullptr); + do_tcp_connections(env.logger, env.tc_2, nullptr); - do_tcp_server_delay(tcp_s, mono_time, 50); + do_tcp_server_delay(env.tcp_s, env.mono_time, 50); - do_tcp_connections(logger, tc_1, nullptr); - do_tcp_connections(logger, tc_2, nullptr); + do_tcp_connections(env.logger, env.tc_1, nullptr); + do_tcp_connections(env.logger, env.tc_2, nullptr); - int ret = send_packet_tcp_connection(tc_1, 0, (const uint8_t *)"Gentoo", 6); + int ret = send_packet_tcp_connection(env.tc_1, 0, (const uint8_t *)"Gentoo", 6); ck_assert_msg(ret == 0, "could not send packet."); - set_packet_tcp_connection_callback(tc_2, &tcp_data_callback, (void *) 120397); + set_packet_tcp_connection_callback(env.tc_2, &tcp_data_callback, (void *) 120397); - do_tcp_server_delay(tcp_s, mono_time, 50); + do_tcp_server_delay(env.tcp_s, env.mono_time, 50); - do_tcp_connections(logger, tc_1, nullptr); - do_tcp_connections(logger, tc_2, nullptr); + do_tcp_connections(env.logger, env.tc_1, nullptr); + do_tcp_connections(env.logger, env.tc_2, nullptr); ck_assert_msg(tcp_data_callback_called, "could not recv packet."); - ck_assert_msg(tcp_connection_to_online_tcp_relays(tc_1, 0) == 1, "Wrong number of connected relays"); - ck_assert_msg(kill_tcp_connection_to(tc_1, 0) == 0, "could not kill connection to\n"); + ck_assert_msg(tcp_connection_to_online_tcp_relays(env.tc_1, 0) == 1, "Wrong number of connected relays"); + ck_assert_msg(kill_tcp_connection_to(env.tc_1, 0) == 0, "could not kill connection to\n"); - do_tcp_server_delay(tcp_s, mono_time, 50); + do_tcp_server_delay(env.tcp_s, env.mono_time, 50); - do_tcp_connections(logger, tc_1, nullptr); - do_tcp_connections(logger, tc_2, nullptr); + do_tcp_connections(env.logger, env.tc_1, nullptr); + do_tcp_connections(env.logger, env.tc_2, nullptr); - ck_assert_msg(send_packet_tcp_connection(tc_1, 0, (const uint8_t *)"Gentoo", 6) == -1, "could send packet."); - ck_assert_msg(kill_tcp_connection_to(tc_2, 0) == 0, "could not kill connection to\n"); + ck_assert_msg(send_packet_tcp_connection(env.tc_1, 0, (const uint8_t *)"Gentoo", 6) == -1, "could send packet."); + ck_assert_msg(kill_tcp_connection_to(env.tc_2, 0) == 0, "could not kill connection to\n"); - kill_tcp_server(tcp_s); - kill_tcp_connections(tc_1); - kill_tcp_connections(tc_2); - - netprof_kill(mem, tcp_np); - - logger_kill(logger); - mono_time_free(mem, mono_time); + teardown_tcp_test_env(&env); } static bool tcp_oobdata_callback_called; @@ -850,96 +890,61 @@ static int tcp_oobdata_callback(void *object, const uint8_t *public_key, unsigne static void test_tcp_connection2(void) { - const Random *rng = os_random(); - ck_assert(rng != nullptr); - const Network *ns = os_network(); - ck_assert(ns != nullptr); - const Memory *mem = os_memory(); - ck_assert(mem != nullptr); - - Mono_Time *mono_time = mono_time_new(mem, nullptr, nullptr); - Logger *logger = logger_new(mem); - - Net_Profile *tcp_np = netprof_new(logger, mem); - ck_assert(tcp_np != nullptr); + TCPTestEnvironment env; + setup_tcp_test_env(&env); tcp_oobdata_callback_called = 0; tcp_data_callback_called = 0; - uint8_t self_public_key[CRYPTO_PUBLIC_KEY_SIZE]; - uint8_t self_secret_key[CRYPTO_SECRET_KEY_SIZE]; - crypto_new_keypair(rng, self_public_key, self_secret_key); - TCP_Server *tcp_s = new_tcp_server(logger, mem, rng, ns, USE_IPV6, NUM_PORTS, ports, self_secret_key, nullptr, nullptr); - ck_assert_msg(pk_equal(tcp_server_public_key(tcp_s), self_public_key), "Wrong public key"); - - TCP_Proxy_Info proxy_info; - proxy_info.proxy_type = TCP_PROXY_NONE; - crypto_new_keypair(rng, self_public_key, self_secret_key); - TCP_Connections *tc_1 = new_tcp_connections(logger, mem, rng, ns, mono_time, self_secret_key, &proxy_info, tcp_np); - ck_assert_msg(tc_1 != nullptr, "Failed to create TCP connections"); - ck_assert_msg(pk_equal(tcp_connections_public_key(tc_1), self_public_key), "Wrong public key"); - - crypto_new_keypair(rng, self_public_key, self_secret_key); - TCP_Connections *tc_2 = new_tcp_connections(logger, mem, rng, ns, mono_time, self_secret_key, &proxy_info, tcp_np); - ck_assert_msg(tc_2 != nullptr, "Failed to create TCP connections"); - ck_assert_msg(pk_equal(tcp_connections_public_key(tc_2), self_public_key), "Wrong public key"); - IP_Port ip_port_tcp_s; - ip_port_tcp_s.port = net_htons(ports[random_u32(rng) % NUM_PORTS]); + ip_port_tcp_s.port = net_htons(ports[random_u32(env.rng) % NUM_PORTS]); ip_port_tcp_s.ip = get_loopback(); - int connection = new_tcp_connection_to(tc_1, tcp_connections_public_key(tc_2), 123); + int connection = new_tcp_connection_to(env.tc_1, tcp_connections_public_key(env.tc_2), 123); ck_assert_msg(connection == 0, "Connection id wrong"); - ck_assert_msg(add_tcp_relay_connection(tc_1, connection, &ip_port_tcp_s, tcp_server_public_key(tcp_s)) == 0, + ck_assert_msg(add_tcp_relay_connection(env.tc_1, connection, &ip_port_tcp_s, tcp_server_public_key(env.tcp_s)) == 0, "Could not add tcp relay to connection\n"); - ck_assert_msg(add_tcp_relay_global(tc_2, &ip_port_tcp_s, tcp_server_public_key(tcp_s)) == 0, + ck_assert_msg(add_tcp_relay_global(env.tc_2, &ip_port_tcp_s, tcp_server_public_key(env.tcp_s)) == 0, "Could not add global relay"); - do_tcp_server_delay(tcp_s, mono_time, 50); + do_tcp_server_delay(env.tcp_s, env.mono_time, 50); - do_tcp_connections(logger, tc_1, nullptr); - do_tcp_connections(logger, tc_2, nullptr); + do_tcp_connections(env.logger, env.tc_1, nullptr); + do_tcp_connections(env.logger, env.tc_2, nullptr); - do_tcp_server_delay(tcp_s, mono_time, 50); + do_tcp_server_delay(env.tcp_s, env.mono_time, 50); - do_tcp_connections(logger, tc_1, nullptr); - do_tcp_connections(logger, tc_2, nullptr); + do_tcp_connections(env.logger, env.tc_1, nullptr); + do_tcp_connections(env.logger, env.tc_2, nullptr); - do_tcp_server_delay(tcp_s, mono_time, 50); + do_tcp_server_delay(env.tcp_s, env.mono_time, 50); - do_tcp_connections(logger, tc_1, nullptr); - do_tcp_connections(logger, tc_2, nullptr); + do_tcp_connections(env.logger, env.tc_1, nullptr); + do_tcp_connections(env.logger, env.tc_2, nullptr); - int ret = send_packet_tcp_connection(tc_1, 0, (const uint8_t *)"Gentoo", 6); + int ret = send_packet_tcp_connection(env.tc_1, 0, (const uint8_t *)"Gentoo", 6); ck_assert_msg(ret == 0, "could not send packet."); - set_oob_packet_tcp_connection_callback(tc_2, &tcp_oobdata_callback, tc_2); - set_packet_tcp_connection_callback(tc_1, &tcp_data_callback, (void *) 120397); + set_oob_packet_tcp_connection_callback(env.tc_2, &tcp_oobdata_callback, env.tc_2); + set_packet_tcp_connection_callback(env.tc_1, &tcp_data_callback, (void *) 120397); - do_tcp_server_delay(tcp_s, mono_time, 50); + do_tcp_server_delay(env.tcp_s, env.mono_time, 50); - do_tcp_connections(logger, tc_1, nullptr); - do_tcp_connections(logger, tc_2, nullptr); + do_tcp_connections(env.logger, env.tc_1, nullptr); + do_tcp_connections(env.logger, env.tc_2, nullptr); ck_assert_msg(tcp_oobdata_callback_called, "could not recv packet."); - do_tcp_server_delay(tcp_s, mono_time, 50); + do_tcp_server_delay(env.tcp_s, env.mono_time, 50); - do_tcp_connections(logger, tc_1, nullptr); - do_tcp_connections(logger, tc_2, nullptr); + do_tcp_connections(env.logger, env.tc_1, nullptr); + do_tcp_connections(env.logger, env.tc_2, nullptr); ck_assert_msg(tcp_data_callback_called, "could not recv packet."); - ck_assert_msg(kill_tcp_connection_to(tc_1, 0) == 0, "could not kill connection to\n"); + ck_assert_msg(kill_tcp_connection_to(env.tc_1, 0) == 0, "could not kill connection to\n"); - netprof_kill(mem, tcp_np); - - kill_tcp_server(tcp_s); - kill_tcp_connections(tc_1); - kill_tcp_connections(tc_2); - - logger_kill(logger); - mono_time_free(mem, mono_time); + teardown_tcp_test_env(&env); } static void tcp_suite(void) diff --git a/auto_tests/announce_test.c b/auto_tests/announce_test.c index 008af93d6..dcf6665ea 100644 --- a/auto_tests/announce_test.c +++ b/auto_tests/announce_test.c @@ -6,6 +6,7 @@ #include "../toxcore/forwarding.h" #include "../toxcore/os_memory.h" #include "../toxcore/os_random.h" +#include "../toxcore/os_event.h" #include "auto_test_support.h" #include "check_compat.h" @@ -60,7 +61,9 @@ static void test_store_data(void) logger_callback_log(log, print_debug_logger, nullptr, nullptr); Mono_Time *mono_time = mono_time_new(mem, nullptr, nullptr); ck_assert(mono_time != nullptr); - Networking_Core *net = new_networking_no_udp(log, mem, ns); + Ev *ev = os_event_new(mem, log); + ck_assert(ev != nullptr); + Networking_Core *net = new_networking_no_udp(log, mem, ns, ev); ck_assert(net != nullptr); DHT *dht = new_dht(log, mem, rng, ns, mono_time, net, true, true); ck_assert(dht != nullptr); @@ -108,6 +111,7 @@ static void test_store_data(void) kill_forwarding(forwarding); kill_dht(dht); kill_networking(net); + ev_kill(ev); mono_time_free(mem, mono_time); logger_kill(log); } diff --git a/auto_tests/forwarding_test.c b/auto_tests/forwarding_test.c index 722f16d0d..297e842d1 100644 --- a/auto_tests/forwarding_test.c +++ b/auto_tests/forwarding_test.c @@ -10,6 +10,7 @@ #include "../toxcore/net_crypto.h" #include "../toxcore/os_memory.h" #include "../toxcore/os_random.h" +#include "../toxcore/os_event.h" #include "auto_test_support.h" #include "check_compat.h" @@ -96,6 +97,7 @@ static bool all_returned(Test_Data *test_data) typedef struct Forwarding_Subtox { Logger *log; Mono_Time *mono_time; + Ev *ev; Networking_Core *net; Net_Profile *tcp_np; DHT *dht; @@ -118,12 +120,13 @@ static Forwarding_Subtox *new_forwarding_subtox(const Memory *mem, bool no_udp, ck_assert(subtox->log != nullptr); logger_callback_log(subtox->log, print_debug_logger, nullptr, index); subtox->mono_time = mono_time_new(mem, nullptr, nullptr); + subtox->ev = os_event_new(mem, subtox->log); if (no_udp) { - subtox->net = new_networking_no_udp(subtox->log, mem, ns); + subtox->net = new_networking_no_udp(subtox->log, mem, ns, subtox->ev); } else { const IP ip = get_loopback(); - subtox->net = new_networking_ex(subtox->log, mem, ns, &ip, port, port, nullptr); + subtox->net = new_networking_ex(subtox->log, mem, ns, subtox->ev, &ip, port, port, nullptr); } subtox->dht = new_dht(subtox->log, mem, rng, ns, subtox->mono_time, subtox->net, true, true); @@ -132,7 +135,7 @@ static Forwarding_Subtox *new_forwarding_subtox(const Memory *mem, bool no_udp, ck_assert(subtox->tcp_np != nullptr); const TCP_Proxy_Info inf = {{{{0}}}}; - subtox->c = new_net_crypto(subtox->log, mem, rng, ns, subtox->mono_time, subtox->net, subtox->dht, &auto_test_dht_funcs, &inf, subtox->tcp_np); + subtox->c = new_net_crypto(subtox->log, mem, rng, ns, subtox->mono_time, subtox->ev, subtox->net, subtox->dht, &auto_test_dht_funcs, &inf, subtox->tcp_np); subtox->forwarding = new_forwarding(subtox->log, mem, rng, subtox->mono_time, subtox->dht, subtox->net); ck_assert(subtox->forwarding != nullptr); @@ -151,6 +154,7 @@ static void kill_forwarding_subtox(const Memory *mem, Forwarding_Subtox *subtox) netprof_kill(mem, subtox->tcp_np); kill_dht(subtox->dht); kill_networking(subtox->net); + ev_kill(subtox->ev); mono_time_free(mem, subtox->mono_time); logger_kill(subtox->log); free(subtox); diff --git a/auto_tests/onion_test.c b/auto_tests/onion_test.c index 875bbe866..c4db479f7 100644 --- a/auto_tests/onion_test.c +++ b/auto_tests/onion_test.c @@ -9,6 +9,7 @@ #include "../toxcore/onion_client.h" #include "../toxcore/os_memory.h" #include "../toxcore/os_random.h" +#include "../toxcore/os_event.h" #include "auto_test_support.h" #include "check_compat.h" @@ -215,9 +216,9 @@ static void send_onion_packet(const Networking_Core *net, const Memory *mem, con /** Initialize networking. * Added for reverse compatibility with old new_networking calls. */ -static Networking_Core *new_networking(const Logger *log, const Memory *mem, const Network *ns, const IP *ip, uint16_t port) +static Networking_Core *new_networking(const Logger *log, const Memory *mem, const Network *ns, Ev *ev, const IP *ip, uint16_t port) { - return new_networking_ex(log, mem, ns, ip, port, port + (TOX_PORTRANGE_TO - TOX_PORTRANGE_FROM), nullptr); + return new_networking_ex(log, mem, ns, ev, ip, port, port + (TOX_PORTRANGE_TO - TOX_PORTRANGE_FROM), nullptr); } static void test_basic(void) @@ -238,10 +239,13 @@ static void test_basic(void) Mono_Time *mono_time1 = mono_time_new(mem, nullptr, nullptr); Mono_Time *mono_time2 = mono_time_new(mem, nullptr, nullptr); + Ev *ev1 = os_event_new(mem, log1); + Ev *ev2 = os_event_new(mem, log2); + IP ip = get_loopback(); - Networking_Core *net1 = new_networking(log1, mem, ns, &ip, 36567); + Networking_Core *net1 = new_networking(log1, mem, ns, ev1, &ip, 36567); Onion *onion1 = new_onion(log1, mem, mono_time1, rng, new_dht(log1, mem, rng, ns, mono_time1, net1, true, false), net1); - Networking_Core *net2 = new_networking(log2, mem, ns, &ip, 36568); + Networking_Core *net2 = new_networking(log2, mem, ns, ev2, &ip, 36568); Onion *onion2 = new_onion(log2, mem, mono_time2, rng, new_dht(log2, mem, rng, ns, mono_time2, net2, true, false), net2); ck_assert_msg((onion1 != nullptr) && (onion2 != nullptr), "Onion failed initializing."); networking_registerhandler(onion2->net, NET_PACKET_ANNOUNCE_REQUEST, &handle_test_1, onion2); @@ -337,8 +341,9 @@ static void test_basic(void) logger_callback_log(log3, print_debug_logger, nullptr, &index[2]); Mono_Time *mono_time3 = mono_time_new(mem, nullptr, nullptr); + Ev *ev3 = os_event_new(mem, log3); - Networking_Core *net3 = new_networking(log3, mem, ns, &ip, 36569); + Networking_Core *net3 = new_networking(log3, mem, ns, ev3, &ip, 36569); Onion *onion3 = new_onion(log3, mem, mono_time3, rng, new_dht(log3, mem, rng, ns, mono_time3, net3, true, false), net3); ck_assert_msg((onion3 != nullptr), "Onion failed initializing."); @@ -369,6 +374,7 @@ static void test_basic(void) kill_onion(onion); kill_dht(dht); kill_networking(net); + ev_kill(ev3); mono_time_free(mem, mono_time3); logger_kill(log3); } @@ -381,6 +387,7 @@ static void test_basic(void) kill_onion(onion); kill_dht(dht); kill_networking(net); + ev_kill(ev2); mono_time_free(mem, mono_time2); logger_kill(log2); } @@ -393,6 +400,7 @@ static void test_basic(void) kill_onion(onion); kill_dht(dht); kill_networking(net); + ev_kill(ev1); mono_time_free(mem, mono_time1); logger_kill(log1); } @@ -401,6 +409,7 @@ static void test_basic(void) typedef struct { Logger *log; Mono_Time *mono_time; + Ev *ev; Net_Crypto *nc; Net_Profile *tcp_np; Onion *onion; @@ -429,14 +438,17 @@ static Onions *new_onions(const Memory *mem, const Random *rng, uint16_t port, u logger_callback_log(on->log, print_debug_logger, nullptr, index); on->mono_time = mono_time_new(mem, nullptr, nullptr); + on->ev = os_event_new(mem, on->log); - if (!on->mono_time) { + if (!on->mono_time || !on->ev) { + ev_kill(on->ev); + mono_time_free(mem, on->mono_time); logger_kill(on->log); free(on); return nullptr; } - Networking_Core *net = new_networking(on->log, mem, ns, &ip, port); + Networking_Core *net = new_networking(on->log, mem, ns, on->ev, &ip, port); if (!net) { mono_time_free(mem, on->mono_time); @@ -491,8 +503,8 @@ static Onions *new_onions(const Memory *mem, const Random *rng, uint16_t port, u return nullptr; } - TCP_Proxy_Info inf = {{{{0}}}}; - on->nc = new_net_crypto(on->log, mem, rng, ns, on->mono_time, net, dht, &auto_test_dht_funcs, &inf, on->tcp_np); + const TCP_Proxy_Info inf = {{{{0}}}}; + on->nc = new_net_crypto(on->log, mem, rng, ns, on->mono_time, on->ev, net, dht, &auto_test_dht_funcs, &inf, on->tcp_np); on->onion_c = new_onion_client(on->log, mem, rng, on->mono_time, on->nc, dht, net); if (!on->onion_c) { @@ -531,6 +543,7 @@ static void kill_onions(const Memory *mem, Onions *on) netprof_kill(mem, on->tcp_np); kill_dht(dht); kill_networking(net); + ev_kill(on->ev); mono_time_free(mem, on->mono_time); logger_kill(on->log); free(on); diff --git a/other/BUILD.bazel b/other/BUILD.bazel index c314b82cf..35775e032 100644 --- a/other/BUILD.bazel +++ b/other/BUILD.bazel @@ -31,6 +31,7 @@ cc_binary( "//c-toxcore/toxcore:network", "//c-toxcore/toxcore:onion", "//c-toxcore/toxcore:onion_announce", + "//c-toxcore/toxcore:os_event", "//c-toxcore/toxcore:os_memory", "//c-toxcore/toxcore:os_random", "//c-toxcore/toxcore:tox", diff --git a/other/DHT_bootstrap.c b/other/DHT_bootstrap.c index 547e32411..a6e360d20 100644 --- a/other/DHT_bootstrap.c +++ b/other/DHT_bootstrap.c @@ -26,6 +26,7 @@ #include "../toxcore/network.h" #include "../toxcore/onion.h" #include "../toxcore/onion_announce.h" +#include "../toxcore/os_event.h" #include "../toxcore/os_memory.h" #include "../toxcore/os_random.h" #include "../toxcore/tox.h" @@ -157,9 +158,10 @@ int main(int argc, char *argv[]) } Mono_Time *mono_time = mono_time_new(mem, nullptr, nullptr); + Ev *ev = os_event_new(mem, logger); const uint16_t start_port = PORT; const uint16_t end_port = start_port + (TOX_PORTRANGE_TO - TOX_PORTRANGE_FROM); - Networking_Core *net = new_networking_ex(logger, mem, ns, &ip, start_port, end_port, nullptr); + Networking_Core *net = new_networking_ex(logger, mem, ns, ev, &ip, start_port, end_port, nullptr); DHT *dht = new_dht(logger, mem, rng, ns, mono_time, net, true, true); Onion *onion = new_onion(logger, mem, mono_time, rng, dht, net); Forwarding *forwarding = new_forwarding(logger, mem, rng, mono_time, dht, net); @@ -189,7 +191,7 @@ int main(int argc, char *argv[]) #ifdef TCP_RELAY_ENABLED #define NUM_PORTS 3 const uint16_t ports[NUM_PORTS] = {443, 3389, PORT}; - TCP_Server *tcp_s = new_tcp_server(logger, mem, rng, ns, ipv6enabled, NUM_PORTS, ports, dht_get_self_secret_key(dht), onion, forwarding); + TCP_Server *tcp_s = new_tcp_server(logger, mem, rng, ns, ev, ipv6enabled, NUM_PORTS, ports, dht_get_self_secret_key(dht), onion, forwarding); if (tcp_s == nullptr) { printf("TCP server failed to initialize.\n"); diff --git a/other/bootstrap_daemon/BUILD.bazel b/other/bootstrap_daemon/BUILD.bazel index 824fcdd19..e4b4af934 100644 --- a/other/bootstrap_daemon/BUILD.bazel +++ b/other/bootstrap_daemon/BUILD.bazel @@ -25,6 +25,7 @@ cc_binary( "//c-toxcore/toxcore:network", "//c-toxcore/toxcore:onion", "//c-toxcore/toxcore:onion_announce", + "//c-toxcore/toxcore:os_event", "//c-toxcore/toxcore:os_memory", "//c-toxcore/toxcore:os_random", "//c-toxcore/toxcore:tox", diff --git a/other/bootstrap_daemon/src/tox-bootstrapd.c b/other/bootstrap_daemon/src/tox-bootstrapd.c index 5b4790139..51177c67b 100644 --- a/other/bootstrap_daemon/src/tox-bootstrapd.c +++ b/other/bootstrap_daemon/src/tox-bootstrapd.c @@ -40,6 +40,7 @@ #include "../../../toxcore/network.h" #include "../../../toxcore/onion.h" #include "../../../toxcore/onion_announce.h" +#include "../../../toxcore/os_event.h" #include "../../../toxcore/os_memory.h" #include "../../../toxcore/os_random.h" @@ -298,15 +299,16 @@ int main(int argc, char *argv[]) logger_callback_log(logger, toxcore_logger_callback, nullptr, nullptr); + Ev *ev = os_event_new(mem, logger); const uint16_t end_port = start_port + (TOX_PORTRANGE_TO - TOX_PORTRANGE_FROM); - Networking_Core *net = new_networking_ex(logger, mem, ns, &ip, start_port, end_port, nullptr); + Networking_Core *net = new_networking_ex(logger, mem, ns, ev, &ip, start_port, end_port, nullptr); if (net == nullptr) { if (enable_ipv6 && enable_ipv4_fallback) { LOG_WRITE(LOG_LEVEL_WARNING, "Couldn't initialize IPv6 networking. Falling back to using IPv4.\n"); enable_ipv6 = false; ip_init(&ip, enable_ipv6); - net = new_networking_ex(logger, mem, ns, &ip, start_port, end_port, nullptr); + net = new_networking_ex(logger, mem, ns, ev, &ip, start_port, end_port, nullptr); if (net == nullptr) { LOG_WRITE(LOG_LEVEL_ERROR, "Couldn't fallback to IPv4. Exiting.\n"); @@ -494,7 +496,7 @@ int main(int argc, char *argv[]) return 1; } - tcp_server = new_tcp_server(logger, mem, rng, ns, enable_ipv6, + tcp_server = new_tcp_server(logger, mem, rng, ns, ev, enable_ipv6, tcp_relay_port_count, tcp_relay_ports, dht_get_self_secret_key(dht), onion, forwarding); @@ -639,6 +641,7 @@ int main(int argc, char *argv[]) kill_dht(dht); mono_time_free(mem, mono_time); kill_networking(net); + ev_kill(ev); logger_kill(logger); return 0; diff --git a/testing/support/BUILD.bazel b/testing/support/BUILD.bazel index 8d1e1c67d..35ffeb037 100644 --- a/testing/support/BUILD.bazel +++ b/testing/support/BUILD.bazel @@ -3,6 +3,7 @@ load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") cc_library( name = "support", srcs = [ + "doubles/fake_event.cc", "doubles/fake_network_stack.cc", "doubles/fake_sockets.cc", "doubles/network_universe.cc", @@ -22,6 +23,7 @@ cc_library( ], hdrs = [ "doubles/fake_clock.hh", + "doubles/fake_event.hh", "doubles/fake_memory.hh", "doubles/fake_network_stack.hh", "doubles/fake_random.hh", @@ -47,9 +49,11 @@ cc_library( visibility = ["//visibility:public"], deps = [ "//c-toxcore/toxcore:attributes", + "//c-toxcore/toxcore:ev", "//c-toxcore/toxcore:mem", "//c-toxcore/toxcore:net", "//c-toxcore/toxcore:network", + "//c-toxcore/toxcore:os_event", "//c-toxcore/toxcore:rng", "//c-toxcore/toxcore:tox", "//c-toxcore/toxcore:tox_events", @@ -58,6 +62,16 @@ cc_library( ], ) +cc_test( + name = "fake_event_test", + srcs = ["doubles/fake_event_test.cc"], + deps = [ + ":support", + "@com_google_googletest//:gtest", + "@com_google_googletest//:gtest_main", + ], +) + cc_test( name = "fake_sockets_test", srcs = ["doubles/fake_sockets_test.cc"], diff --git a/testing/support/CMakeLists.txt b/testing/support/CMakeLists.txt index 712888132..12f39e410 100644 --- a/testing/support/CMakeLists.txt +++ b/testing/support/CMakeLists.txt @@ -3,6 +3,7 @@ if(NOT UNITTEST) endif() set(support_SOURCES + doubles/fake_event.cc doubles/fake_network_stack.cc doubles/fake_sockets.cc doubles/network_universe.cc @@ -19,6 +20,7 @@ set(support_SOURCES src/simulation.cc src/tox_network.cc src/tox_runner.cc + doubles/fake_event.hh doubles/fake_clock.hh doubles/fake_memory.hh doubles/fake_network_stack.hh @@ -68,6 +70,7 @@ if(TARGET GTest::gtest_main) endfunction() support_test(fake_sockets_test doubles/fake_sockets_test.cc) + support_test(fake_event_test doubles/fake_event_test.cc) support_test(fake_network_stack_test doubles/fake_network_stack_test.cc) support_test(fake_network_udp_test doubles/fake_network_udp_test.cc) support_test(fake_network_tcp_test doubles/fake_network_tcp_test.cc) diff --git a/testing/support/doubles/fake_event.cc b/testing/support/doubles/fake_event.cc new file mode 100644 index 000000000..37cb91c90 --- /dev/null +++ b/testing/support/doubles/fake_event.cc @@ -0,0 +1,106 @@ +#include "fake_event.hh" + +#include +#include + +#include "../../../toxcore/net.h" + +namespace tox::test { + +static const Ev_Funcs kEventVtable = { + .add_callback + = [](void *self, Socket sock, Ev_Events events, + void *data) { return static_cast(self)->add(sock, events, data); }, + .mod_callback + = [](void *self, Socket sock, Ev_Events events, + void *data) { return static_cast(self)->mod(sock, events, data); }, + .del_callback + = [](void *self, Socket sock) { return static_cast(self)->del(sock); }, + .run_callback = + [](void *self, Ev_Result results[], uint32_t max_results, int32_t timeout_ms) { + return static_cast(self)->run(results, max_results, timeout_ms); + }, + .kill_callback = [](Ev *ev) { delete static_cast(ev->user_data); }, +}; + +FakeEvent::FakeEvent(FakeNetworkStack &net) + : net_(net) +{ + c_ev_.funcs = &kEventVtable; + c_ev_.user_data = this; +} + +FakeEvent::~FakeEvent() = default; + +struct Ev *FakeEvent::c_event() { return &c_ev_; } + +bool FakeEvent::add(Socket sock, Ev_Events events, void *data) +{ + std::lock_guard lock(mutex_); + int fd = net_socket_to_native(sock); + if (monitored_.find(fd) != monitored_.end()) { + return false; + } + monitored_[fd] = {events, data}; + return true; +} + +bool FakeEvent::mod(Socket sock, Ev_Events events, void *data) +{ + std::lock_guard lock(mutex_); + int fd = net_socket_to_native(sock); + auto it = monitored_.find(fd); + if (it == monitored_.end()) { + return false; + } + it->second = {events, data}; + return true; +} + +bool FakeEvent::del(Socket sock) +{ + std::lock_guard lock(mutex_); + int fd = net_socket_to_native(sock); + return monitored_.erase(fd) > 0; +} + +int32_t FakeEvent::run(Ev_Result results[], uint32_t max_results, int32_t timeout_ms) +{ + std::lock_guard lock(mutex_); + uint32_t count = 0; + + for (const auto &pair : monitored_) { + if (count >= max_results) + break; + + int fd = pair.first; + const auto &mon = pair.second; + Socket sock = net_socket_from_native(fd); + + Ev_Events triggered = 0; + + FakeSocket *fs = net_.get_sock(sock); + if (!fs) { + // Socket closed? + triggered |= EV_ERROR; + } else { + if ((mon.events & EV_READ) && fs->is_readable()) { + triggered |= EV_READ; + } + if ((mon.events & EV_WRITE) && fs->is_writable()) { + triggered |= EV_WRITE; + } + } + + if (triggered) { + results[count].sock = sock; + results[count].events = triggered; + results[count].data = mon.data; + count++; + } + } + + return count; +} + +} // namespace tox::test diff --git a/testing/support/doubles/fake_event.hh b/testing/support/doubles/fake_event.hh new file mode 100644 index 000000000..9f999983d --- /dev/null +++ b/testing/support/doubles/fake_event.hh @@ -0,0 +1,38 @@ +#ifndef C_TOXCORE_TESTING_SUPPORT_DOUBLES_FAKE_EVENT_H +#define C_TOXCORE_TESTING_SUPPORT_DOUBLES_FAKE_EVENT_H + +#include +#include +#include + +#include "../../../toxcore/ev.h" +#include "fake_network_stack.hh" + +namespace tox::test { + +class FakeEvent { +public: + explicit FakeEvent(FakeNetworkStack &net); + ~FakeEvent(); + + struct Ev *c_event(); + + bool add(Socket sock, Ev_Events events, void *data); + bool mod(Socket sock, Ev_Events events, void *data); + bool del(Socket sock); + int32_t run(Ev_Result results[], uint32_t max_results, int32_t timeout_ms); + +private: + FakeNetworkStack &net_; + struct MonitoredSocket { + Ev_Events events; + void *data; + }; + std::map monitored_; + std::mutex mutex_; + struct Ev c_ev_; +}; + +} // namespace tox::test + +#endif // C_TOXCORE_TESTING_SUPPORT_DOUBLES_FAKE_EVENT_H diff --git a/testing/support/doubles/fake_event_test.cc b/testing/support/doubles/fake_event_test.cc new file mode 100644 index 000000000..8b548ed52 --- /dev/null +++ b/testing/support/doubles/fake_event_test.cc @@ -0,0 +1,114 @@ +#include "fake_event.hh" + +#include + +#ifdef _WIN32 +#include +#else +#include +#endif + +#include "fake_network_stack.hh" +#include "network_universe.hh" + +namespace tox::test { +namespace { + + class FakeEventTest : public ::testing::Test { + protected: + NetworkUniverse universe; + IP ip; + FakeNetworkStack net_stack{universe, ip}; + FakeEvent events{net_stack}; + + FakeEventTest() + { + ip_init(&ip, false); + ip.ip.v4.uint32 = net_htonl(0x7F000001); + } + }; + + TEST_F(FakeEventTest, AddModDel) + { + Socket sock = net_stack.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + int data = 123; + + EXPECT_TRUE(events.add(sock, EV_READ, &data)); + // Duplicate add should fail + EXPECT_FALSE(events.add(sock, EV_READ, &data)); + + EXPECT_TRUE(events.mod(sock, EV_WRITE, &data)); + // Modify non-existent should fail + Socket invalid = net_socket_from_native(-1); + EXPECT_FALSE(events.mod(invalid, EV_READ, &data)); + + EXPECT_TRUE(events.del(sock)); + // Delete non-existent should fail + EXPECT_FALSE(events.del(sock)); + + net_stack.close(sock); + } + + TEST_F(FakeEventTest, RunReadEvent) + { + Socket server = net_stack.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + IP_Port addr; + addr.ip = ip; + addr.port = net_htons(12345); + net_stack.bind(server, &addr); + + int user_data = 42; + events.add(server, EV_READ, &user_data); + + Ev_Result results[1]; + // Initially no events + EXPECT_EQ(events.run(results, 1, 0), 0); + + // Send packet to server + Socket client = net_stack.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + uint8_t msg[] = "ping"; + net_stack.sendto(client, msg, sizeof(msg), &addr); + universe.process_events(0); + + // Now should have read event + EXPECT_EQ(events.run(results, 1, 0), 1); + EXPECT_EQ(results[0].sock.value, server.value); + EXPECT_EQ(results[0].events, EV_READ); + EXPECT_EQ(results[0].data, &user_data); + + net_stack.close(server); + net_stack.close(client); + } + + TEST_F(FakeEventTest, RunWriteEvent) + { + Socket tcp_sock = net_stack.socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + // Bind and connect to make it established (writable) + IP_Port server_addr; + server_addr.ip = ip; + server_addr.port = net_htons(8080); + + Socket server = net_stack.socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + net_stack.bind(server, &server_addr); + net_stack.listen(server, 1); + + net_stack.connect(tcp_sock, &server_addr); + universe.process_events(0); // SYN + universe.process_events(0); // SYN-ACK + universe.process_events(0); // ACK + + int user_data = 99; + events.add(tcp_sock, EV_WRITE, &user_data); + + Ev_Result results[1]; + EXPECT_EQ(events.run(results, 1, 0), 1); + EXPECT_EQ(results[0].sock.value, tcp_sock.value); + EXPECT_TRUE(results[0].events & EV_WRITE); + EXPECT_EQ(results[0].data, &user_data); + + net_stack.close(tcp_sock); + net_stack.close(server); + } + +} // namespace +} // namespace tox::test diff --git a/testing/support/doubles/fake_network_stack.hh b/testing/support/doubles/fake_network_stack.hh index 6dcb9f8a5..15957ed63 100644 --- a/testing/support/doubles/fake_network_stack.hh +++ b/testing/support/doubles/fake_network_stack.hh @@ -13,7 +13,11 @@ namespace tox::test { +class FakeEvent; + class FakeNetworkStack : public NetworkSystem { + friend class FakeEvent; + public: explicit FakeNetworkStack(NetworkUniverse &universe, const IP &node_ip); ~FakeNetworkStack() override; diff --git a/testing/support/src/simulation.cc b/testing/support/src/simulation.cc index 1d1fdea2f..55f026a63 100644 --- a/testing/support/src/simulation.cc +++ b/testing/support/src/simulation.cc @@ -5,6 +5,9 @@ #include #include +#include "../../../toxcore/os_event.h" +#include "../doubles/fake_event.hh" + namespace tox::test { // --- LogFilter --- @@ -307,6 +310,9 @@ SimulatedNode::ToxPtr SimulatedNode::create_tox(const Tox_Options *_Nullable opt system.ns = &c_network; system.rng = &c_random; system.mem = &c_memory; + // We hand over ownership of FakeEvent to Tox (it will call ev_kill -> kill_callback -> delete) + auto *fake_ev = new FakeEvent(*network_); + system.ev = fake_ev->c_event(); system.mono_time_callback = [](void *_Nullable user_data) -> uint64_t { return static_cast(user_data)->current_time_ms(); }; diff --git a/toxcore/BUILD.bazel b/toxcore/BUILD.bazel index 782e8f418..861ed2e2b 100644 --- a/toxcore/BUILD.bazel +++ b/toxcore/BUILD.bazel @@ -1007,6 +1007,32 @@ cc_test( ], ) +cc_test( + name = "TCP_server_test", + size = "small", + srcs = ["TCP_server_test.cc"], + deps = [ + ":TCP_client", + ":TCP_common", + ":TCP_server", + ":attributes", + ":crypto_core", + ":logger", + ":mono_time", + ":network", + ":os_event", + ":os_memory", + ":os_network", + ":os_random", + ":rng", + ":test_util", + ":util", + "//c-toxcore/testing/support", + "@com_google_googletest//:gtest", + "@com_google_googletest//:gtest_main", + ], +) + cc_library( name = "net_crypto", srcs = ["net_crypto.c"], diff --git a/toxcore/DHT_test.cc b/toxcore/DHT_test.cc index 123eab30f..6671eae83 100644 --- a/toxcore/DHT_test.cc +++ b/toxcore/DHT_test.cc @@ -17,6 +17,7 @@ #include "mono_time.h" #include "network.h" #include "network_test_util.hh" +#include "os_event.h" #include "test_util.hh" namespace { @@ -363,7 +364,8 @@ TEST(AnnounceNodes, SetAndTest) }, &env.fake_clock()); - Ptr net(new_networking_no_udp(log, &c_mem, &net_struct)); + Ptr ev(os_event_new(&c_mem, log)); + Ptr net(new_networking_no_udp(log, &c_mem, &net_struct, ev.get())); ASSERT_NE(net, nullptr); Ptr dht(new_dht(log, &c_mem, &c_rng, &net_struct, mono_time, net.get(), true, true)); ASSERT_NE(dht, nullptr); diff --git a/toxcore/DHT_test_util.cc b/toxcore/DHT_test_util.cc index f35a684c7..dbdfd1e67 100644 --- a/toxcore/DHT_test_util.cc +++ b/toxcore/DHT_test_util.cc @@ -1,20 +1,16 @@ #include "DHT_test_util.hh" +#include #include #include -#include #include #include "../testing/support/public/simulated_environment.hh" -#include "DHT.h" #include "crypto_core.h" #include "crypto_core_test_util.hh" -#include "network.h" #include "network_test_util.hh" - -using tox::test::FakeClock; - -// --- Mock DHT Implementation --- +#include "os_event.h" +#include "test_util.hh" MockDHT::MockDHT(const Random *_Nonnull rng) { @@ -23,20 +19,19 @@ MockDHT::MockDHT(const Random *_Nonnull rng) const std::uint8_t *_Nullable MockDHT::get_shared_key(const std::uint8_t *_Nonnull pk) { - std::array pk_arr; - std::copy(pk, pk + CRYPTO_PUBLIC_KEY_SIZE, pk_arr.begin()); - auto it = shared_keys.find(pk_arr); - if (it != shared_keys.end()) { - return it->second.data(); - } + std::array pk_array; + std::copy(pk, pk + pk_array.size(), pk_array.begin()); - ++computation_count; + auto it = shared_keys.find(pk_array); - // Compute new shared key - std::array sk; - encrypt_precompute(pk, self_secret_key, sk.data()); - shared_keys[pk_arr] = sk; - return shared_keys[pk_arr].data(); + if (it == shared_keys.end()) { + std::array shared_key; + encrypt_precompute(pk, self_secret_key, shared_key.data()); + it = shared_keys.emplace(pk_array, shared_key).first; + ++computation_count; + } + + return it->second.data(); } const Net_Crypto_DHT_Funcs MockDHT::funcs = { @@ -59,6 +54,7 @@ WrappedMockDHT::WrappedMockDHT(tox::test::SimulatedEnvironment &env, std::uint16 }, &env.fake_clock()), [mem = &node_->c_memory](Mono_Time *t) { mono_time_free(mem, t); }) + , ev_(os_event_new(&node_->c_memory, logger_.get()), ev_kill) , networking_(nullptr, [](Networking_Core *n) { kill_networking(n); }) , dht_(&node_->c_random) { @@ -66,8 +62,8 @@ WrappedMockDHT::WrappedMockDHT(tox::test::SimulatedEnvironment &env, std::uint16 IP ip; ip_init(&ip, false); unsigned int error = 0; - networking_.reset(new_networking_ex( - logger_.get(), &node_->c_memory, &node_->c_network, &ip, port, port + 1, &error)); + networking_.reset(new_networking_ex(logger_.get(), &node_->c_memory, &node_->c_network, + ev_.get(), &ip, port, port + 1, &error)); assert(error == 0); node_->endpoint = node_->node->get_primary_socket(); @@ -101,10 +97,11 @@ WrappedDHT::WrappedDHT(tox::test::SimulatedEnvironment &env, std::uint16_t port) , mono_time_(mono_time_new( &node_->c_memory, [](void *_Nullable ud) -> std::uint64_t { - return static_cast(ud)->current_time_ms(); + return static_cast(ud)->current_time_ms(); }, &env.fake_clock()), [mem = &node_->c_memory](Mono_Time *t) { mono_time_free(mem, t); }) + , ev_(os_event_new(&node_->c_memory, logger_.get()), ev_kill) , networking_(nullptr, [](Networking_Core *n) { kill_networking(n); }) , dht_(nullptr, [](DHT *d) { kill_dht(d); }) { @@ -112,8 +109,8 @@ WrappedDHT::WrappedDHT(tox::test::SimulatedEnvironment &env, std::uint16_t port) IP ip; ip_init(&ip, false); unsigned int error = 0; - networking_.reset(new_networking_ex( - logger_.get(), &node_->c_memory, &node_->c_network, &ip, port, port + 1, &error)); + networking_.reset(new_networking_ex(logger_.get(), &node_->c_memory, &node_->c_network, + ev_.get(), &ip, port, port + 1, &error)); assert(error == 0); node_->endpoint = node_->node->get_primary_socket(); diff --git a/toxcore/DHT_test_util.hh b/toxcore/DHT_test_util.hh index 81dc711b4..ac6fdc11f 100644 --- a/toxcore/DHT_test_util.hh +++ b/toxcore/DHT_test_util.hh @@ -12,10 +12,12 @@ #include "DHT.h" #include "attributes.h" #include "crypto_core.h" +#include "ev.h" #include "logger.h" #include "mono_time.h" #include "net.h" #include "net_crypto.h" +#include "os_event.h" #include "rng.h" #include "test_util.hh" @@ -27,6 +29,9 @@ struct ScopedToxSystem; template <> struct Deleter : Function_Deleter { }; +template <> +struct Deleter : Function_Deleter { }; + bool operator==(Node_format const &a, Node_format const &b); std::ostream &operator<<(std::ostream &out, Node_format const &v); @@ -71,6 +76,7 @@ public: Networking_Core *_Nonnull networking() { return networking_.get(); } Mono_Time *_Nonnull mono_time() { return mono_time_.get(); } Logger *_Nonnull logger() { return logger_.get(); } + Ev *_Nonnull ev() { return ev_.get(); } ~WrappedMockDHT(); @@ -80,6 +86,7 @@ private: std::unique_ptr node_; std::unique_ptr logger_; std::unique_ptr> mono_time_; + std::unique_ptr ev_; std::unique_ptr networking_; MockDHT dht_; }; @@ -104,6 +111,7 @@ public: Networking_Core *_Nonnull networking() { return networking_.get(); } Mono_Time *_Nonnull mono_time() { return mono_time_.get(); } Logger *_Nonnull logger() { return logger_.get(); } + Ev *_Nonnull ev() { return ev_.get(); } ~WrappedDHT(); @@ -113,6 +121,7 @@ private: std::unique_ptr node_; std::unique_ptr logger_; std::unique_ptr> mono_time_; + std::unique_ptr ev_; std::unique_ptr networking_; std::unique_ptr dht_; }; diff --git a/toxcore/Messenger.c b/toxcore/Messenger.c index c43ca8e83..24a7ccd0b 100644 --- a/toxcore/Messenger.c +++ b/toxcore/Messenger.c @@ -3399,6 +3399,7 @@ Messenger *new_messenger(Mono_Time *mono_time, const Memory *mem, const Random * m->mem = mem; m->rng = rng; m->ns = ns; + m->ev = options->ev; m->forwarding = nullptr; m->announce = nullptr; m->tcp_server = nullptr; @@ -3420,11 +3421,11 @@ Messenger *new_messenger(Mono_Time *mono_time, const Memory *mem, const Random * Networking_Core *net; if (options->udp_disabled) { - net = new_networking_no_udp(m->log, m->mem, m->ns); + net = new_networking_no_udp(m->log, m->mem, m->ns, m->ev); } else { IP ip; ip_init(&ip, options->ipv6enabled); - net = new_networking_ex(m->log, m->mem, m->ns, &ip, options->port_range[0], options->port_range[1], &net_err); + net = new_networking_ex(m->log, m->mem, m->ns, m->ev, &ip, options->port_range[0], options->port_range[1], &net_err); } if (net == nullptr) { @@ -3460,7 +3461,7 @@ Messenger *new_messenger(Mono_Time *mono_time, const Memory *mem, const Random * } m->tcp_np = tcp_np; - Net_Crypto *net_crypto = new_net_crypto(m->log, m->mem, m->rng, m->ns, m->mono_time, m->net, m->dht, &m_dht_funcs, &options->proxy_info, m->tcp_np); + Net_Crypto *net_crypto = new_net_crypto(m->log, m->mem, m->rng, m->ns, m->mono_time, m->ev, m->net, m->dht, &m_dht_funcs, &options->proxy_info, m->tcp_np); if (net_crypto == nullptr) { LOGGER_WARNING(m->log, "net_crypto initialisation failed"); @@ -3552,7 +3553,7 @@ Messenger *new_messenger(Mono_Time *mono_time, const Memory *mem, const Random * m->group_handler = group_handler; if (options->tcp_server_port != 0) { - m->tcp_server = new_tcp_server(m->log, m->mem, m->rng, m->ns, options->ipv6enabled, 1, + m->tcp_server = new_tcp_server(m->log, m->mem, m->rng, m->ns, m->ev, options->ipv6enabled, 1, &options->tcp_server_port, dht_get_self_secret_key(m->dht), m->onion, m->forwarding); diff --git a/toxcore/Messenger.h b/toxcore/Messenger.h index efec19660..b395b15bc 100644 --- a/toxcore/Messenger.h +++ b/toxcore/Messenger.h @@ -19,6 +19,7 @@ #include "announce.h" #include "attributes.h" #include "crypto_core.h" +#include "ev.h" #include "forwarding.h" #include "friend_connection.h" #include "friend_requests.h" @@ -91,6 +92,8 @@ typedef struct Messenger_Options { uint8_t state_plugins_length; bool dns_enabled; + + Ev *_Nonnull ev; } Messenger_Options; struct Receipts { @@ -248,6 +251,7 @@ struct Messenger { const Memory *_Nonnull mem; const Random *_Nonnull rng; const Network *_Nonnull ns; + Ev *_Nonnull ev; Networking_Core *_Nonnull net; Net_Crypto *_Nonnull net_crypto; diff --git a/toxcore/TCP_client.c b/toxcore/TCP_client.c index 0e71ddc87..ea4b339a1 100644 --- a/toxcore/TCP_client.c +++ b/toxcore/TCP_client.c @@ -33,6 +33,7 @@ typedef struct TCP_Client_Conn { struct TCP_Client_Connection { TCP_Connection con; + Ev *_Nonnull ev; TCP_Client_Status status; uint8_t self_public_key[CRYPTO_PUBLIC_KEY_SIZE]; /* our public key */ uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE]; /* public key of the server */ @@ -594,7 +595,7 @@ void forwarding_handler(TCP_Client_Connection *con, forwarded_response_cb *forwa /** Create new TCP connection to ip_port/public_key */ TCP_Client_Connection *new_tcp_connection( - const Logger *logger, const Memory *mem, const Mono_Time *mono_time, const Random *rng, const Network *ns, + const Logger *logger, const Memory *mem, const Mono_Time *mono_time, const Random *rng, const Network *ns, Ev *ev, const IP_Port *ip_port, const uint8_t *public_key, const uint8_t *self_public_key, const uint8_t *self_secret_key, const TCP_Proxy_Info *proxy_info, Net_Profile *_Nullable net_profile) { @@ -603,6 +604,7 @@ TCP_Client_Connection *new_tcp_connection( assert(mono_time != nullptr); assert(rng != nullptr); assert(ns != nullptr); + assert(ev != nullptr); if (!net_family_is_ipv4(ip_port->ip.family) && !net_family_is_ipv6(ip_port->ip.family)) { LOGGER_ERROR(logger, "Invalid IP family: %d", ip_port->ip.family.value); @@ -662,12 +664,15 @@ TCP_Client_Connection *new_tcp_connection( temp->con.sock = sock; temp->con.ip_port = *ip_port; temp->con.net_profile = net_profile; + temp->ev = ev; memcpy(temp->public_key, public_key, CRYPTO_PUBLIC_KEY_SIZE); memcpy(temp->self_public_key, self_public_key, CRYPTO_PUBLIC_KEY_SIZE); encrypt_precompute(temp->public_key, self_secret_key, temp->con.shared_key); temp->ip_port = *ip_port; temp->proxy_info = *proxy_info; + ev_add(ev, sock, EV_READ, temp); + switch (proxy_info->proxy_type) { case TCP_PROXY_HTTP: { temp->status = TCP_CLIENT_PROXY_HTTP_CONNECTING; @@ -1044,6 +1049,7 @@ void kill_tcp_connection(TCP_Client_Connection *tcp_connection) const Memory *mem = tcp_connection->con.mem; + ev_del(tcp_connection->ev, tcp_connection->con.sock); wipe_priority_list(tcp_connection->con.mem, tcp_connection->con.priority_queue_start); kill_sock(tcp_connection->con.ns, tcp_connection->con.sock); crypto_memzero(tcp_connection, sizeof(TCP_Client_Connection)); diff --git a/toxcore/TCP_client.h b/toxcore/TCP_client.h index 353d7b97b..c3540adbb 100644 --- a/toxcore/TCP_client.h +++ b/toxcore/TCP_client.h @@ -63,7 +63,7 @@ void tcp_con_set_custom_uint(TCP_Client_Connection *_Nonnull con, uint32_t value /** Create new TCP connection to ip_port/public_key */ TCP_Client_Connection *_Nullable new_tcp_connection( - const Logger *_Nonnull logger, const Memory *_Nonnull mem, const Mono_Time *_Nonnull mono_time, const Random *_Nonnull rng, const Network *_Nonnull ns, + const Logger *_Nonnull logger, const Memory *_Nonnull mem, const Mono_Time *_Nonnull mono_time, const Random *_Nonnull rng, const Network *_Nonnull ns, Ev *_Nonnull ev, const IP_Port *_Nonnull ip_port, const uint8_t *_Nonnull public_key, const uint8_t *_Nonnull self_public_key, const uint8_t *_Nonnull self_secret_key, const TCP_Proxy_Info *_Nullable proxy_info, Net_Profile *_Nullable net_profile); /** Run the TCP connection */ diff --git a/toxcore/TCP_client_test.cc b/toxcore/TCP_client_test.cc index afe832608..71f7af566 100644 --- a/toxcore/TCP_client_test.cc +++ b/toxcore/TCP_client_test.cc @@ -1,6 +1,7 @@ // clang-format off #include "../testing/support/public/simulated_environment.hh" #include "TCP_client.h" +#include "os_event.h" // clang-format on #include @@ -86,6 +87,7 @@ TEST_F(TCPClientTest, ConnectsToRelay) crypto_new_keypair(&client_node->c_random, client_pk, client_sk); Net_Profile *client_profile = netprof_new(client_log, &client_node->c_memory); + Ev *client_ev = os_event_new(&client_node->c_memory, client_log); // 2. Client connects to Server IP_Port server_ip_port; @@ -93,8 +95,8 @@ TEST_F(TCPClientTest, ConnectsToRelay) server_ip_port.port = net_htons(33445); TCP_Client_Connection *client_conn = new_tcp_connection(client_log, &client_node->c_memory, - client_time, &client_node->c_random, &client_node->c_network, &server_ip_port, server_pk, - client_pk, client_sk, nullptr, client_profile); + client_time, &client_node->c_random, &client_node->c_network, client_ev, &server_ip_port, + server_pk, client_pk, client_sk, nullptr, client_profile); ASSERT_NE(client_conn, nullptr); // 3. Simulation Loop @@ -189,6 +191,7 @@ TEST_F(TCPClientTest, ConnectsToRelay) // Cleanup kill_tcp_connection(client_conn); + ev_kill(client_ev); net_profile_deleter(client_profile, &client_node->c_memory); kill_sock(&server_node->c_network, server_sock); if (sock_valid(accepted_sock)) @@ -227,14 +230,15 @@ TEST_F(TCPClientTest, SendDataIntegerOverflow) crypto_new_keypair(&client_node->c_random, client_pk, client_sk); Net_Profile *client_profile = netprof_new(client_log, &client_node->c_memory); + Ev *client_ev = os_event_new(&client_node->c_memory, client_log); IP_Port server_ip_port; server_ip_port.ip = server_node->node->ip; server_ip_port.port = net_htons(33446); TCP_Client_Connection *client_conn = new_tcp_connection(client_log, &client_node->c_memory, - client_time, &client_node->c_random, &client_node->c_network, &server_ip_port, server_pk, - client_pk, client_sk, nullptr, client_profile); + client_time, &client_node->c_random, &client_node->c_network, client_ev, &server_ip_port, + server_pk, client_pk, client_sk, nullptr, client_profile); ASSERT_NE(client_conn, nullptr); bool connected = false; @@ -362,6 +366,7 @@ TEST_F(TCPClientTest, SendDataIntegerOverflow) // Cleanup kill_tcp_connection(client_conn); + ev_kill(client_ev); net_profile_deleter(client_profile, &client_node->c_memory); kill_sock(&server_node->c_network, server_sock); if (sock_valid(accepted_sock)) diff --git a/toxcore/TCP_connection.c b/toxcore/TCP_connection.c index 08cb944f2..403758f3c 100644 --- a/toxcore/TCP_connection.c +++ b/toxcore/TCP_connection.c @@ -30,6 +30,7 @@ struct TCP_Connections { const Random *_Nonnull rng; Mono_Time *_Nonnull mono_time; const Network *_Nonnull ns; + Ev *_Nonnull ev; DHT *_Nonnull dht; uint8_t self_public_key[CRYPTO_PUBLIC_KEY_SIZE]; @@ -955,7 +956,8 @@ static int reconnect_tcp_relay_connection(TCP_Connections *_Nonnull tcp_c, int t uint8_t relay_pk[CRYPTO_PUBLIC_KEY_SIZE]; memcpy(relay_pk, tcp_con_public_key(tcp_con->connection), CRYPTO_PUBLIC_KEY_SIZE); kill_tcp_connection(tcp_con->connection); - tcp_con->connection = new_tcp_connection(tcp_c->logger, tcp_c->mem, tcp_c->mono_time, tcp_c->rng, tcp_c->ns, &ip_port, relay_pk, tcp_c->self_public_key, tcp_c->self_secret_key, &tcp_c->proxy_info, + tcp_con->connection = new_tcp_connection(tcp_c->logger, tcp_c->mem, tcp_c->mono_time, tcp_c->rng, tcp_c->ns, tcp_c->ev, &ip_port, relay_pk, tcp_c->self_public_key, tcp_c->self_secret_key, + &tcp_c->proxy_info, tcp_c->net_profile); if (tcp_con->connection == nullptr) { @@ -1046,7 +1048,7 @@ static int unsleep_tcp_relay_connection(TCP_Connections *_Nonnull tcp_c, int tcp } tcp_con->connection = new_tcp_connection( - tcp_c->logger, tcp_c->mem, tcp_c->mono_time, tcp_c->rng, tcp_c->ns, &tcp_con->ip_port, + tcp_c->logger, tcp_c->mem, tcp_c->mono_time, tcp_c->rng, tcp_c->ns, tcp_c->ev, &tcp_con->ip_port, tcp_con->relay_pk, tcp_c->self_public_key, tcp_c->self_secret_key, &tcp_c->proxy_info, tcp_c->net_profile); if (tcp_con->connection == nullptr) { @@ -1339,7 +1341,7 @@ static int add_tcp_relay_instance(TCP_Connections *_Nonnull tcp_c, const IP_Port TCP_con *tcp_con = &tcp_c->tcp_connections[tcp_connections_number]; tcp_con->connection = new_tcp_connection( - tcp_c->logger, tcp_c->mem, tcp_c->mono_time, tcp_c->rng, tcp_c->ns, &ipp_copy, + tcp_c->logger, tcp_c->mem, tcp_c->mono_time, tcp_c->rng, tcp_c->ns, tcp_c->ev, &ipp_copy, relay_pk, tcp_c->self_public_key, tcp_c->self_secret_key, &tcp_c->proxy_info, tcp_c->net_profile); if (tcp_con->connection == nullptr) { @@ -1619,13 +1621,14 @@ int set_tcp_onion_status(TCP_Connections *tcp_c, bool status) * Returns NULL on failure. */ TCP_Connections *new_tcp_connections(const Logger *logger, const Memory *mem, const Random *rng, const Network *ns, - Mono_Time *mono_time, const uint8_t *secret_key, const TCP_Proxy_Info *proxy_info, Net_Profile *tcp_np) + Mono_Time *mono_time, Ev *ev, const uint8_t *secret_key, const TCP_Proxy_Info *proxy_info, Net_Profile *tcp_np) { assert(logger != nullptr); assert(mem != nullptr); assert(rng != nullptr); assert(ns != nullptr); assert(mono_time != nullptr); + assert(ev != nullptr); if (secret_key == nullptr) { return nullptr; @@ -1643,6 +1646,7 @@ TCP_Connections *new_tcp_connections(const Logger *logger, const Memory *mem, co temp->rng = rng; temp->mono_time = mono_time; temp->ns = ns; + temp->ev = ev; memcpy(temp->self_secret_key, secret_key, CRYPTO_SECRET_KEY_SIZE); crypto_derive_public_key(temp->self_public_key, temp->self_secret_key); diff --git a/toxcore/TCP_connection.h b/toxcore/TCP_connection.h index defe52c5e..883a7a94d 100644 --- a/toxcore/TCP_connection.h +++ b/toxcore/TCP_connection.h @@ -272,6 +272,7 @@ uint32_t tcp_copy_connected_relays_index(const TCP_Connections *_Nonnull tcp_c, * Returns NULL on failure. */ TCP_Connections *_Nullable new_tcp_connections(const Logger *_Nonnull logger, const Memory *_Nonnull mem, const Random *_Nonnull rng, const Network *_Nonnull ns, Mono_Time *_Nonnull mono_time, + Ev *_Nonnull ev, const uint8_t *_Nonnull secret_key, const TCP_Proxy_Info *_Nonnull proxy_info, Net_Profile *_Nonnull tcp_np); int kill_tcp_relay_connection(TCP_Connections *_Nonnull tcp_c, int tcp_connections_number); diff --git a/toxcore/TCP_server.c b/toxcore/TCP_server.c index 6de6e36d2..1c93bf35d 100644 --- a/toxcore/TCP_server.c +++ b/toxcore/TCP_server.c @@ -31,12 +31,15 @@ #include "network.h" #include "onion.h" -#ifdef TCP_SERVER_USE_EPOLL #define TCP_SOCKET_LISTENING 0 #define TCP_SOCKET_INCOMING 1 #define TCP_SOCKET_UNCONFIRMED 2 #define TCP_SOCKET_CONFIRMED 3 -#endif /* TCP_SERVER_USE_EPOLL */ + +typedef struct TCP_Server_Event_Data { + uint8_t type; + uint32_t index; +} TCP_Server_Event_Data; typedef struct TCP_Secure_Conn { uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE]; @@ -59,8 +62,12 @@ typedef struct TCP_Secure_Connection { uint64_t last_pinged; uint64_t ping_id; + + TCP_Server_Event_Data event_data; } TCP_Secure_Connection; +static void kill_tcp_secure_connection(Ev *_Nullable ev, TCP_Secure_Connection *_Nullable con); + static const TCP_Secure_Connection empty_tcp_secure_connection = {{nullptr}}; struct TCP_Server { @@ -68,6 +75,7 @@ struct TCP_Server { const Memory *_Nonnull mem; const Random *_Nonnull rng; const Network *_Nonnull ns; + Ev *_Nonnull ev; Onion *_Nullable onion; Forwarding *_Nullable forwarding; @@ -77,6 +85,7 @@ struct TCP_Server { #endif /* TCP_SERVER_USE_EPOLL */ Socket *_Nullable socks_listening; unsigned int num_listening_socks; + TCP_Server_Event_Data *_Nullable listening_event_data; uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE]; uint8_t secret_key[CRYPTO_SECRET_KEY_SIZE]; @@ -143,6 +152,15 @@ static int alloc_new_connections(TCP_Server *_Nonnull tcp_server, uint32_t num) new_connections[old_size + i] = empty_tcp_secure_connection; } + if (new_connections != tcp_server->accepted_connection_array) { + for (uint32_t i = 0; i < old_size; ++i) { + TCP_Secure_Connection *con = &new_connections[i]; + if (con->status != TCP_STATUS_NO_STATUS) { + ev_mod(tcp_server->ev, con->con.sock, EV_READ, &con->event_data); + } + } + } + tcp_server->accepted_connection_array = new_connections; tcp_server->size_accepted_connections = new_size; return 0; @@ -169,7 +187,7 @@ static void free_accepted_connection_array(TCP_Server *_Nonnull tcp_server) } for (uint32_t i = 0; i < tcp_server->size_accepted_connections; ++i) { - wipe_secure_connection(&tcp_server->accepted_connection_array[i]); + kill_tcp_secure_connection(tcp_server->ev, &tcp_server->accepted_connection_array[i]); } mem_delete(tcp_server->mem, tcp_server->accepted_connection_array); @@ -268,12 +286,16 @@ static int del_accepted(TCP_Server *_Nonnull tcp_server, int index) } /** Kill a TCP_Secure_Connection */ -static void kill_tcp_secure_connection(TCP_Secure_Connection *_Nullable con) +static void kill_tcp_secure_connection(Ev *_Nullable ev, TCP_Secure_Connection *_Nullable con) { - if (con == nullptr) { + if (con == nullptr || con->status == 0) { return; } + if (ev != nullptr) { + ev_del(ev, con->con.sock); + } + kill_sock(con->con.ns, con->con.sock); wipe_secure_connection(con); } @@ -296,6 +318,7 @@ static int kill_accepted(TCP_Server *tcp_server, int index) } const Socket sock = tcp_server->accepted_connection_array[index].con.sock; + ev_del(tcp_server->ev, sock); if (del_accepted(tcp_server, index) != 0) { return -1; @@ -663,6 +686,12 @@ static int handle_tcp_packet(TCP_Server *_Nonnull tcp_server, uint32_t con_id, c return -1; } + Tcp_Packet type; + const char *type_name = tcp_packet_from_int(data[0], &type) ? tcp_packet_type_to_string(type) : ""; + + LOGGER_TRACE(tcp_server->logger, "Received TCP packet for connection %u of type %s (%u) and length %u.", con_id, + type_name, data[0], length); + TCP_Secure_Connection *const con = &tcp_server->accepted_connection_array[con_id]; netprof_record_packet(con->con.net_profile, data[0], length, PACKET_DIRECTION_RECV); @@ -839,10 +868,15 @@ static int confirm_tcp_connection(TCP_Server *_Nonnull tcp_server, const Mono_Ti if (index == -1) { LOGGER_DEBUG(tcp_server->logger, "dropping connection %u: not accepted", (unsigned int)con->identifier); - kill_tcp_secure_connection(con); + kill_tcp_secure_connection(tcp_server->ev, con); return -1; } + TCP_Secure_Connection *conn_accepted = &tcp_server->accepted_connection_array[index]; + conn_accepted->event_data.type = TCP_SOCKET_CONFIRMED; + conn_accepted->event_data.index = index; + ev_mod(tcp_server->ev, conn_accepted->con.sock, EV_READ, &conn_accepted->event_data); + wipe_secure_connection(con); if (handle_tcp_packet(tcp_server, index, data, length) == -1) { @@ -881,7 +915,7 @@ static int accept_connection(TCP_Server *_Nonnull tcp_server, Socket sock) if (conn->status != TCP_STATUS_NO_STATUS) { LOGGER_DEBUG(tcp_server->logger, "connection %d dropped before accepting", index); - kill_tcp_secure_connection(conn); + kill_tcp_secure_connection(tcp_server->ev, conn); } conn->status = TCP_STATUS_CONNECTED; @@ -891,6 +925,10 @@ static int accept_connection(TCP_Server *_Nonnull tcp_server, Socket sock) conn->con.sock = sock; conn->next_packet_length = 0; + conn->event_data.type = TCP_SOCKET_INCOMING; + conn->event_data.index = index; + ev_add(tcp_server->ev, sock, EV_READ, &conn->event_data); + ++tcp_server->incoming_connection_queue_index; return index; } @@ -929,7 +967,7 @@ static Socket new_listening_tcp_socket(const Logger *_Nonnull logger, const Memo } TCP_Server *new_tcp_server(const Logger *logger, const Memory *mem, const Random *rng, const Network *ns, - bool ipv6_enabled, uint16_t num_sockets, + Ev *ev, bool ipv6_enabled, uint16_t num_sockets, const uint16_t *ports, const uint8_t *secret_key, Onion *onion, Forwarding *forwarding) { if (num_sockets == 0 || ports == nullptr) { @@ -961,6 +999,7 @@ TCP_Server *new_tcp_server(const Logger *logger, const Memory *mem, const Random temp->mem = mem; temp->ns = ns; temp->rng = rng; + temp->ev = ev; Socket *socks_listening = (Socket *)mem_valloc(mem, num_sockets, sizeof(Socket)); @@ -973,6 +1012,19 @@ TCP_Server *new_tcp_server(const Logger *logger, const Memory *mem, const Random temp->socks_listening = socks_listening; + TCP_Server_Event_Data *listening_event_data = (TCP_Server_Event_Data *)mem_valloc(mem, num_sockets, + sizeof(TCP_Server_Event_Data)); + + if (listening_event_data == nullptr) { + LOGGER_ERROR(logger, "event data allocation failed"); + netprof_kill(mem, temp->net_profile); + mem_delete(mem, socks_listening); + mem_delete(mem, temp); + return nullptr; + } + + temp->listening_event_data = listening_event_data; + #ifdef TCP_SERVER_USE_EPOLL temp->efd = epoll_create1(EPOLL_CLOEXEC); @@ -980,10 +1032,10 @@ TCP_Server *new_tcp_server(const Logger *logger, const Memory *mem, const Random LOGGER_ERROR(logger, "epoll initialisation failed"); netprof_kill(mem, temp->net_profile); mem_delete(mem, socks_listening); + mem_delete(mem, listening_event_data); mem_delete(mem, temp); return nullptr; } - #endif /* TCP_SERVER_USE_EPOLL */ const Family family = ipv6_enabled ? net_family_ipv6() : net_family_ipv4(); @@ -995,16 +1047,21 @@ TCP_Server *new_tcp_server(const Logger *logger, const Memory *mem, const Random continue; } + temp->listening_event_data[temp->num_listening_socks].type = TCP_SOCKET_LISTENING; + temp->listening_event_data[temp->num_listening_socks].index = i; + ev_add(ev, sock, EV_READ, &temp->listening_event_data[temp->num_listening_socks]); + #ifdef TCP_SERVER_USE_EPOLL - struct epoll_event ev; + struct epoll_event ev_epoll; - ev.events = EPOLLIN | EPOLLET; - ev.data.u64 = net_socket_to_native(sock) | ((uint64_t)TCP_SOCKET_LISTENING << 32); + ev_epoll.events = EPOLLIN | EPOLLET; + ev_epoll.data.u64 = net_socket_to_native(sock) | ((uint64_t)TCP_SOCKET_LISTENING << 32); - if (epoll_ctl(temp->efd, EPOLL_CTL_ADD, net_socket_to_native(sock), &ev) == -1) { + if (epoll_ctl(temp->efd, EPOLL_CTL_ADD, net_socket_to_native(sock), &ev_epoll) == -1) { + ev_del(ev, sock); + kill_sock(ns, sock); continue; } - #endif /* TCP_SERVER_USE_EPOLL */ temp->socks_listening[temp->num_listening_socks] = sock; @@ -1014,6 +1071,7 @@ TCP_Server *new_tcp_server(const Logger *logger, const Memory *mem, const Random if (temp->num_listening_socks == 0) { netprof_kill(mem, temp->net_profile); mem_delete(mem, temp->socks_listening); + mem_delete(mem, temp->listening_event_data); mem_delete(mem, temp); return nullptr; } @@ -1066,7 +1124,7 @@ static int do_incoming(TCP_Server *_Nonnull tcp_server, uint32_t i) if (ret == -1) { LOGGER_TRACE(tcp_server->logger, "incoming connection %u dropped due to failed handshake", i); - kill_tcp_secure_connection(conn); + kill_tcp_secure_connection(tcp_server->ev, conn); return -1; } @@ -1080,12 +1138,16 @@ static int do_incoming(TCP_Server *_Nonnull tcp_server, uint32_t i) if (conn_new->status != TCP_STATUS_NO_STATUS) { LOGGER_ERROR(tcp_server->logger, "incoming connection %u would overwrite existing", i); - kill_tcp_secure_connection(conn_new); + kill_tcp_secure_connection(tcp_server->ev, conn_new); } move_secure_connection(conn_new, conn_old); ++tcp_server->unconfirmed_connection_queue_index; + conn_new->event_data.type = TCP_SOCKET_UNCONFIRMED; + conn_new->event_data.index = index_new; + ev_mod(tcp_server->ev, conn_new->con.sock, EV_READ, &conn_new->event_data); + return index_new; } @@ -1108,7 +1170,7 @@ static int do_unconfirmed(TCP_Server *_Nonnull tcp_server, const Mono_Time *_Non } if (len == -1) { - kill_tcp_secure_connection(conn); + kill_tcp_secure_connection(tcp_server->ev, conn); return -1; } @@ -1244,13 +1306,13 @@ static bool tcp_epoll_process(TCP_Server *_Nonnull tcp_server, const Mono_Time * case TCP_SOCKET_INCOMING: { LOGGER_TRACE(tcp_server->logger, "incoming connection %d dropped", index); - kill_tcp_secure_connection(&tcp_server->incoming_connection_queue[index]); + kill_tcp_secure_connection(tcp_server->ev, &tcp_server->incoming_connection_queue[index]); break; } case TCP_SOCKET_UNCONFIRMED: { LOGGER_TRACE(tcp_server->logger, "unconfirmed connection %d dropped", index); - kill_tcp_secure_connection(&tcp_server->unconfirmed_connection_queue[index]); + kill_tcp_secure_connection(tcp_server->ev, &tcp_server->unconfirmed_connection_queue[index]); break; } @@ -1284,15 +1346,15 @@ static bool tcp_epoll_process(TCP_Server *_Nonnull tcp_server, const Mono_Time * continue; } - struct epoll_event ev; + struct epoll_event ev_epoll; - ev.events = EPOLLIN | EPOLLET | EPOLLRDHUP; + ev_epoll.events = EPOLLIN | EPOLLET | EPOLLRDHUP; - ev.data.u64 = net_socket_to_native(sock_new) | ((uint64_t)TCP_SOCKET_INCOMING << 32) | ((uint64_t)index_new << 40); + ev_epoll.data.u64 = net_socket_to_native(sock_new) | ((uint64_t)TCP_SOCKET_INCOMING << 32) | ((uint64_t)index_new << 40); - if (epoll_ctl(tcp_server->efd, EPOLL_CTL_ADD, net_socket_to_native(sock_new), &ev) == -1) { + if (epoll_ctl(tcp_server->efd, EPOLL_CTL_ADD, net_socket_to_native(sock_new), &ev_epoll) == -1) { LOGGER_DEBUG(tcp_server->logger, "new connection %d was dropped due to epoll error %d", index, net_error()); - kill_tcp_secure_connection(&tcp_server->incoming_connection_queue[index_new]); + kill_tcp_secure_connection(tcp_server->ev, &tcp_server->incoming_connection_queue[index_new]); continue; } } @@ -1310,7 +1372,7 @@ static bool tcp_epoll_process(TCP_Server *_Nonnull tcp_server, const Mono_Time * if (epoll_ctl(tcp_server->efd, EPOLL_CTL_MOD, net_socket_to_native(sock), &events[n]) == -1) { LOGGER_DEBUG(tcp_server->logger, "incoming connection %d was dropped due to epoll error %d", index, net_error()); - kill_tcp_secure_connection(&tcp_server->unconfirmed_connection_queue[index_new]); + kill_tcp_secure_connection(tcp_server->ev, &tcp_server->unconfirmed_connection_queue[index_new]); break; } } @@ -1377,9 +1439,12 @@ void kill_tcp_server(TCP_Server *tcp_server) } for (uint32_t i = 0; i < tcp_server->num_listening_socks; ++i) { + ev_del(tcp_server->ev, tcp_server->socks_listening[i]); kill_sock(tcp_server->ns, tcp_server->socks_listening[i]); } + mem_delete(tcp_server->mem, tcp_server->listening_event_data); + if (tcp_server->onion != nullptr) { set_callback_handle_recv_1(tcp_server->onion, nullptr, nullptr); } @@ -1395,8 +1460,8 @@ void kill_tcp_server(TCP_Server *tcp_server) #endif /* TCP_SERVER_USE_EPOLL */ for (uint32_t i = 0; i < MAX_INCOMING_CONNECTIONS; ++i) { - wipe_secure_connection(&tcp_server->incoming_connection_queue[i]); - wipe_secure_connection(&tcp_server->unconfirmed_connection_queue[i]); + kill_tcp_secure_connection(tcp_server->ev, &tcp_server->incoming_connection_queue[i]); + kill_tcp_secure_connection(tcp_server->ev, &tcp_server->unconfirmed_connection_queue[i]); } free_accepted_connection_array(tcp_server); diff --git a/toxcore/TCP_server.h b/toxcore/TCP_server.h index b5f1ee0db..aca5774b7 100644 --- a/toxcore/TCP_server.h +++ b/toxcore/TCP_server.h @@ -14,6 +14,7 @@ #include "attributes.h" #include "crypto_core.h" +#include "ev.h" #include "forwarding.h" #include "logger.h" #include "mem.h" @@ -48,7 +49,7 @@ size_t tcp_server_listen_count(const TCP_Server *_Nonnull tcp_server); /** Create new TCP server instance. */ TCP_Server *_Nullable new_tcp_server(const Logger *_Nonnull logger, const Memory *_Nonnull mem, const Random *_Nonnull rng, const Network *_Nonnull ns, - bool ipv6_enabled, uint16_t num_sockets, const uint16_t *_Nonnull ports, + Ev *_Nonnull ev, bool ipv6_enabled, uint16_t num_sockets, const uint16_t *_Nonnull ports, const uint8_t *_Nonnull secret_key, Onion *_Nullable onion, Forwarding *_Nullable forwarding); /** Run the TCP_server */ void do_tcp_server(TCP_Server *_Nonnull tcp_server, const Mono_Time *_Nonnull mono_time); diff --git a/toxcore/TCP_server_test.cc b/toxcore/TCP_server_test.cc new file mode 100644 index 000000000..5403142e8 --- /dev/null +++ b/toxcore/TCP_server_test.cc @@ -0,0 +1,894 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * Copyright © 2026 The TokTok team. + */ + +#include "TCP_server.h" + +#include + +#include +#include +#include +#include +#include + +#include "TCP_client.h" +#include "TCP_common.h" +#include "os_event.h" +#include "os_memory.h" +#include "os_network.h" +#include "os_random.h" +#include "test_util.hh" +#include "util.h" + +[[maybe_unused]] static std::ostream &operator<<(std::ostream &os, const IP &ip) +{ + Ip_Ntoa ntoa; + return os << net_ip_ntoa(&ip, &ntoa); +} + +[[maybe_unused]] static std::ostream &operator<<(std::ostream &os, const IP_Port &ipp) +{ + Ip_Ntoa ntoa; + return os << net_ip_ntoa(&ipp.ip, &ntoa) << ":" << net_ntohs(ipp.port); +} + +[[maybe_unused]] static std::ostream &operator<<(std::ostream &os, TCP_Client_Status status) +{ + switch (status) { + case TCP_CLIENT_NO_STATUS: + return os << "TCP_CLIENT_NO_STATUS"; + case TCP_CLIENT_PROXY_HTTP_CONNECTING: + return os << "TCP_CLIENT_PROXY_HTTP_CONNECTING"; + case TCP_CLIENT_PROXY_SOCKS5_CONNECTING: + return os << "TCP_CLIENT_PROXY_SOCKS5_CONNECTING"; + case TCP_CLIENT_PROXY_SOCKS5_UNCONFIRMED: + return os << "TCP_CLIENT_PROXY_SOCKS5_UNCONFIRMED"; + case TCP_CLIENT_CONNECTING: + return os << "TCP_CLIENT_CONNECTING"; + case TCP_CLIENT_UNCONFIRMED: + return os << "TCP_CLIENT_UNCONFIRMED"; + case TCP_CLIENT_CONFIRMED: + return os << "TCP_CLIENT_CONFIRMED"; + case TCP_CLIENT_DISCONNECTED: + return os << "TCP_CLIENT_DISCONNECTED"; + } + return os << "TCP_CLIENT_UNKNOWN(" << static_cast(status) << ")"; +} + +[[maybe_unused]] static std::ostream &operator<<(std::ostream &os, TCP_Status status) +{ + switch (status) { + case TCP_STATUS_NO_STATUS: + return os << "TCP_STATUS_NO_STATUS"; + case TCP_STATUS_CONNECTED: + return os << "TCP_STATUS_CONNECTED"; + case TCP_STATUS_UNCONFIRMED: + return os << "TCP_STATUS_UNCONFIRMED"; + case TCP_STATUS_CONFIRMED: + return os << "TCP_STATUS_CONFIRMED"; + } + return os << "TCP_STATUS_UNKNOWN(" << static_cast(status) << ")"; +} + +namespace { + +void c_sleep(uint32_t ms) { std::this_thread::sleep_for(std::chrono::milliseconds(ms)); } + +class TCPServerTest : public ::testing::Test { +protected: + const Memory *_Nonnull mem = REQUIRE_NOT_NULL(os_memory()); + const Network *_Nonnull ns = REQUIRE_NOT_NULL(os_network()); + const Random *_Nonnull rng = REQUIRE_NOT_NULL(os_random()); + + std::uint64_t current_time_ms = 1000000; + + static void log_cb(void *_Nullable context, Logger_Level level, const char *_Nonnull file, + std::uint32_t line, const char *_Nonnull func, const char *_Nonnull message, + void *_Nullable userdata) + { + const char *name = userdata ? static_cast(userdata) : "Unknown"; + if (level >= LOGGER_LEVEL_DEBUG) { + fprintf(stderr, "[%s][%d] %s:%u %s: %s\n", name, level, file, line, func, message); + } + } + + Mono_Time *create_mono_time() + { + Mono_Time *mt = REQUIRE_NOT_NULL(mono_time_new(mem, nullptr, nullptr)); + mono_time_set_current_time_callback( + mt, [](void *userdata) -> uint64_t { return *static_cast(userdata); }, + ¤t_time_ms); + return mt; + } + + IP get_loopback() + { + IP ip; + ip.family = net_family_ipv4(); + ip.ip.v4 = net_get_ip4_loopback(); + return ip; + } +}; + +TEST_F(TCPServerTest, BasicStartup) +{ + Logger *log = logger_new(mem); + logger_callback_log(log, &TCPServerTest::log_cb, nullptr, const_cast("Server")); + Ev *ev = os_event_new(mem, log); + + std::uint8_t sk[CRYPTO_SECRET_KEY_SIZE]; + std::uint8_t pk[CRYPTO_PUBLIC_KEY_SIZE]; + crypto_new_keypair(rng, pk, sk); + + std::uint16_t ports[] = {33441}; + TCP_Server *server + = new_tcp_server(log, mem, rng, ns, ev, false, 1, ports, sk, nullptr, nullptr); + ASSERT_NE(server, nullptr); + EXPECT_EQ(tcp_server_listen_count(server), 1); + + kill_tcp_server(server); + ev_kill(ev); + logger_kill(log); +} + +TEST_F(TCPServerTest, MultipleClients) +{ + Logger *server_log = logger_new(mem); + logger_callback_log(server_log, &TCPServerTest::log_cb, nullptr, const_cast("Server")); + Ev *server_ev = os_event_new(mem, server_log); + Mono_Time *server_time = create_mono_time(); + + std::uint8_t server_sk[CRYPTO_SECRET_KEY_SIZE]; + std::uint8_t server_pk[CRYPTO_PUBLIC_KEY_SIZE]; + crypto_new_keypair(rng, server_pk, server_sk); + + std::uint16_t ports[] = {33443}; + TCP_Server *server = new_tcp_server( + server_log, mem, rng, ns, server_ev, false, 1, ports, server_sk, nullptr, nullptr); + ASSERT_NE(server, nullptr); + + const int NUM_CLIENTS = 10; + struct Client { + Logger *log; + Ev *ev; + Mono_Time *time; + TCP_Client_Connection *conn; + std::uint8_t pk[CRYPTO_PUBLIC_KEY_SIZE]; + std::uint8_t sk[CRYPTO_SECRET_KEY_SIZE]; + char name[32]; + }; + std::vector clients(NUM_CLIENTS); + + IP_Port server_ip_port; + server_ip_port.ip = get_loopback(); + server_ip_port.port = net_htons(33443); + + for (int i = 0; i < NUM_CLIENTS; ++i) { + clients[i].log = logger_new(mem); + snprintf(clients[i].name, sizeof(clients[i].name), "Client %d", i); + logger_callback_log(clients[i].log, &TCPServerTest::log_cb, nullptr, clients[i].name); + clients[i].ev = os_event_new(mem, clients[i].log); + clients[i].time = create_mono_time(); + crypto_new_keypair(rng, clients[i].pk, clients[i].sk); + + clients[i].conn + = new_tcp_connection(clients[i].log, mem, clients[i].time, rng, ns, clients[i].ev, + &server_ip_port, server_pk, clients[i].pk, clients[i].sk, nullptr, nullptr); + ASSERT_NE(clients[i].conn, nullptr); + } + + // Run simulation for handshakes + for (int i = 0; i < 1000; ++i) { + current_time_ms += 10; + mono_time_update(server_time); + do_tcp_server(server, server_time); + for (int j = 0; j < NUM_CLIENTS; ++j) { + mono_time_update(clients[j].time); + do_tcp_connection(clients[j].log, clients[j].time, clients[j].conn, nullptr); + } + bool all_connected = true; + for (int j = 0; j < NUM_CLIENTS; ++j) { + if (tcp_con_status(clients[j].conn) != TCP_CLIENT_CONFIRMED) { + all_connected = false; + break; + } + } + if (all_connected) + break; + c_sleep(1); + } + + for (int i = 0; i < NUM_CLIENTS; ++i) { + EXPECT_EQ(tcp_con_status(clients[i].conn), TCP_CLIENT_CONFIRMED) + << "Client " << i << " failed to connect"; + } + + // Cleanup + for (int i = 0; i < NUM_CLIENTS; ++i) { + kill_tcp_connection(clients[i].conn); + ev_kill(clients[i].ev); + logger_kill(clients[i].log); + mono_time_free(mem, clients[i].time); + } + kill_tcp_server(server); + ev_kill(server_ev); + logger_kill(server_log); + mono_time_free(mem, server_time); +} + +TEST_F(TCPServerTest, RelayRoutingRealNet) +{ + Logger *server_log = logger_new(mem); + logger_callback_log(server_log, &TCPServerTest::log_cb, nullptr, const_cast("Server")); + Ev *server_ev = os_event_new(mem, server_log); + Mono_Time *server_time = create_mono_time(); + + std::uint8_t server_sk[CRYPTO_SECRET_KEY_SIZE]; + std::uint8_t server_pk[CRYPTO_PUBLIC_KEY_SIZE]; + crypto_new_keypair(rng, server_pk, server_sk); + + // Fixed port for now, similar to TCP_test.c + uint16_t test_port = 33445; + TCP_Server *server = new_tcp_server( + server_log, mem, rng, ns, server_ev, false, 1, &test_port, server_sk, nullptr, nullptr); + if (!server) { + test_port = 33446; + server = new_tcp_server( + server_log, mem, rng, ns, server_ev, false, 1, &test_port, server_sk, nullptr, nullptr); + } + ASSERT_NE(server, nullptr) << "Could not bind to a test port"; + + IP_Port server_ip_port; + server_ip_port.ip = get_loopback(); + server_ip_port.port = net_htons(test_port); + + // Setup two clients + struct Client { + Logger *log; + Ev *ev; + Mono_Time *time; + TCP_Client_Connection *conn; + std::uint8_t pk[CRYPTO_PUBLIC_KEY_SIZE]; + std::uint8_t sk[CRYPTO_SECRET_KEY_SIZE]; + std::vector> received_data; + std::uint8_t response_id = 255; + bool status_connected = false; + const char *name; + } c1, c2; + + auto setup_client = [&](Client &c, const char *name) { + c.name = name; + c.log = logger_new(mem); + logger_callback_log(c.log, &TCPServerTest::log_cb, nullptr, const_cast(c.name)); + c.ev = os_event_new(mem, c.log); + c.time = create_mono_time(); + crypto_new_keypair(rng, c.pk, c.sk); + c.conn = new_tcp_connection(c.log, mem, c.time, rng, ns, c.ev, + &server_ip_port, server_pk, c.pk, c.sk, nullptr, nullptr); + + routing_response_handler( + c.conn, + [](void *obj, std::uint8_t id, const std::uint8_t *pk) -> int { + static_cast(obj)->response_id = id; + return 0; + }, + &c); + + routing_status_handler( + c.conn, + [](void *obj, std::uint32_t number, std::uint8_t connection_id, + std::uint8_t status) -> int { + if (status == 2) + static_cast(obj)->status_connected = true; + return 0; + }, + &c); + + routing_data_handler( + c.conn, + [](void *obj, std::uint32_t number, std::uint8_t connection_id, + const std::uint8_t *data, std::uint16_t length, void *userdata) -> int { + static_cast(obj)->received_data.push_back( + std::vector(data, data + length)); + return 0; + }, + &c); + }; + + setup_client(c1, "C1"); + setup_client(c2, "C2"); + + // Handshakes + for (int i = 0; i < 1000; ++i) { + current_time_ms += 10; + mono_time_update(server_time); + mono_time_update(c1.time); + mono_time_update(c2.time); + do_tcp_server(server, server_time); + do_tcp_connection(c1.log, c1.time, c1.conn, nullptr); + do_tcp_connection(c2.log, c2.time, c2.conn, nullptr); + if (tcp_con_status(c1.conn) == TCP_CLIENT_CONFIRMED + && tcp_con_status(c2.conn) == TCP_CLIENT_CONFIRMED) + break; + c_sleep(1); + } + ASSERT_EQ(tcp_con_status(c1.conn), TCP_CLIENT_CONFIRMED); + ASSERT_EQ(tcp_con_status(c2.conn), TCP_CLIENT_CONFIRMED); + + fprintf(stderr, "Clients connected to relay. Requesting routing...\n"); + + // C1 requests to route to C2 + send_routing_request(c1.log, c1.conn, c2.pk); + // C2 requests to route to C1 + send_routing_request(c2.log, c2.conn, c1.pk); + + for (int i = 0; i < 1000; ++i) { + current_time_ms += 10; + mono_time_update(server_time); + mono_time_update(c1.time); + mono_time_update(c2.time); + do_tcp_server(server, server_time); + do_tcp_connection(c1.log, c1.time, c1.conn, nullptr); + do_tcp_connection(c2.log, c2.time, c2.conn, nullptr); + if (c1.status_connected && c2.status_connected) + break; + c_sleep(1); + } + + EXPECT_EQ(c1.response_id, 0); + EXPECT_EQ(c2.response_id, 0); + ASSERT_TRUE(c1.status_connected); + ASSERT_TRUE(c2.status_connected); + + fprintf(stderr, "Clients routed to each other. Sending data...\n"); + + // Send data C1 -> C2 + std::uint8_t test_data[] = "Hello C2"; + send_data(c1.log, c1.conn, c1.response_id, test_data, sizeof(test_data)); + + for (int i = 0; i < 500; ++i) { + current_time_ms += 10; + mono_time_update(server_time); + mono_time_update(c1.time); + mono_time_update(c2.time); + do_tcp_server(server, server_time); + do_tcp_connection(c1.log, c1.time, c1.conn, nullptr); + do_tcp_connection(c2.log, c2.time, c2.conn, nullptr); + if (!c2.received_data.empty()) + break; + c_sleep(1); + } + + ASSERT_FALSE(c2.received_data.empty()); + EXPECT_EQ(c2.received_data[0].size(), sizeof(test_data)); + EXPECT_EQ(0, std::memcmp(c2.received_data[0].data(), test_data, sizeof(test_data))); + + // Cleanup + kill_tcp_connection(c1.conn); + kill_tcp_connection(c2.conn); + ev_kill(c1.ev); + ev_kill(c2.ev); + logger_kill(c1.log); + logger_kill(c2.log); + mono_time_free(mem, c1.time); + mono_time_free(mem, c2.time); + kill_tcp_server(server); + ev_kill(server_ev); + logger_kill(server_log); + mono_time_free(mem, server_time); +} + +TEST_F(TCPServerTest, DuplicateConnection) +{ + Logger *server_log = logger_new(mem); + logger_callback_log(server_log, &TCPServerTest::log_cb, nullptr, const_cast("Server")); + Ev *server_ev = os_event_new(mem, server_log); + Mono_Time *server_time = create_mono_time(); + + std::uint8_t server_sk[CRYPTO_SECRET_KEY_SIZE]; + std::uint8_t server_pk[CRYPTO_PUBLIC_KEY_SIZE]; + crypto_new_keypair(rng, server_pk, server_sk); + + uint16_t test_port = 33447; + TCP_Server *server = new_tcp_server( + server_log, mem, rng, ns, server_ev, false, 1, &test_port, server_sk, nullptr, nullptr); + if (!server) { + test_port = 33448; + server = new_tcp_server( + server_log, mem, rng, ns, server_ev, false, 1, &test_port, server_sk, nullptr, nullptr); + } + ASSERT_NE(server, nullptr); + + IP_Port server_ip_port; + server_ip_port.ip = get_loopback(); + server_ip_port.port = net_htons(test_port); + + std::uint8_t client_pk[CRYPTO_PUBLIC_KEY_SIZE]; + std::uint8_t client_sk[CRYPTO_SECRET_KEY_SIZE]; + crypto_new_keypair(rng, client_pk, client_sk); + + Logger *client_log1 = logger_new(mem); + logger_callback_log(client_log1, &TCPServerTest::log_cb, nullptr, const_cast("C1")); + Ev *client_ev1 = os_event_new(mem, client_log1); + + Mono_Time *client_time1 = create_mono_time(); + TCP_Client_Connection *conn1 = new_tcp_connection(client_log1, mem, client_time1, rng, ns, + client_ev1, &server_ip_port, server_pk, client_pk, client_sk, nullptr, nullptr); + + for (int i = 0; i < 1000; ++i) { + current_time_ms += 10; + mono_time_update(server_time); + mono_time_update(client_time1); + do_tcp_server(server, server_time); + do_tcp_connection(client_log1, client_time1, conn1, nullptr); + if (tcp_con_status(conn1) == TCP_CLIENT_CONFIRMED) + break; + c_sleep(1); + } + ASSERT_EQ(tcp_con_status(conn1), TCP_CLIENT_CONFIRMED); + + Logger *client_log2 = logger_new(mem); + logger_callback_log(client_log2, &TCPServerTest::log_cb, nullptr, const_cast("C2")); + Ev *client_ev2 = os_event_new(mem, client_log2); + Mono_Time *client_time2 = create_mono_time(); + TCP_Client_Connection *conn2 = new_tcp_connection(client_log2, mem, client_time2, rng, ns, + client_ev2, &server_ip_port, server_pk, client_pk, client_sk, nullptr, nullptr); + + // Handshake for second connection + for (int i = 0; i < 1000; ++i) { + current_time_ms += 10; + mono_time_update(server_time); + mono_time_update(client_time2); + do_tcp_server(server, server_time); + do_tcp_connection(client_log2, client_time2, conn2, nullptr); + if (tcp_con_status(conn2) == TCP_CLIENT_CONFIRMED) + break; + c_sleep(1); + } + ASSERT_EQ(tcp_con_status(conn2), TCP_CLIENT_CONFIRMED); + + send_routing_request(client_log2, conn2, server_pk); + + // Now pump both to let C1 notice it was dropped. + for (int i = 0; i < 1000; ++i) { + current_time_ms += 1000; + mono_time_update(server_time); + mono_time_update(client_time1); + mono_time_update(client_time2); + do_tcp_server(server, server_time); + do_tcp_connection(client_log1, client_time1, conn1, nullptr); + do_tcp_connection(client_log2, client_time2, conn2, nullptr); + if (tcp_con_status(conn1) == TCP_CLIENT_DISCONNECTED) + break; + c_sleep(1); + } + EXPECT_EQ(tcp_con_status(conn1), TCP_CLIENT_DISCONNECTED); + + kill_tcp_connection(conn1); + kill_tcp_connection(conn2); + kill_tcp_server(server); + ev_kill(client_ev1); + ev_kill(client_ev2); + ev_kill(server_ev); + logger_kill(client_log1); + logger_kill(client_log2); + logger_kill(server_log); + mono_time_free(mem, client_time1); + mono_time_free(mem, client_time2); + mono_time_free(mem, server_time); +} + +TEST_F(TCPServerTest, OOBData) +{ + Logger *server_log = logger_new(mem); + logger_callback_log(server_log, &TCPServerTest::log_cb, nullptr, const_cast("Server")); + Ev *server_ev = os_event_new(mem, server_log); + Mono_Time *server_time = create_mono_time(); + + std::uint8_t server_sk[CRYPTO_SECRET_KEY_SIZE]; + std::uint8_t server_pk[CRYPTO_PUBLIC_KEY_SIZE]; + crypto_new_keypair(rng, server_pk, server_sk); + + uint16_t test_port = 33449; + TCP_Server *server = new_tcp_server( + server_log, mem, rng, ns, server_ev, false, 1, &test_port, server_sk, nullptr, nullptr); + if (!server) { + test_port = 33450; + server = new_tcp_server( + server_log, mem, rng, ns, server_ev, false, 1, &test_port, server_sk, nullptr, nullptr); + } + ASSERT_NE(server, nullptr); + + IP_Port server_ip_port; + server_ip_port.ip = get_loopback(); + server_ip_port.port = net_htons(test_port); + + struct Client { + Logger *log; + Ev *ev; + Mono_Time *time; + TCP_Client_Connection *conn; + std::uint8_t pk[CRYPTO_PUBLIC_KEY_SIZE]; + std::uint8_t sk[CRYPTO_SECRET_KEY_SIZE]; + std::vector> received_oob; + const char *name; + } c1, c2; + + auto setup_client = [&](Client &c, const char *name) { + c.name = name; + c.log = logger_new(mem); + logger_callback_log(c.log, &TCPServerTest::log_cb, nullptr, const_cast(c.name)); + c.ev = os_event_new(mem, c.log); + c.time = create_mono_time(); + crypto_new_keypair(rng, c.pk, c.sk); + c.conn = new_tcp_connection(c.log, mem, c.time, rng, ns, c.ev, + &server_ip_port, server_pk, c.pk, c.sk, nullptr, nullptr); + + oob_data_handler( + c.conn, + [](void *obj, const std::uint8_t *pk, const std::uint8_t *data, std::uint16_t length, + void *userdata) -> int { + static_cast(obj)->received_oob.push_back( + std::vector(data, data + length)); + return 0; + }, + &c); + }; + + setup_client(c1, "C1"); + setup_client(c2, "C2"); + + for (int i = 0; i < 500; ++i) { + current_time_ms += 10; + mono_time_update(server_time); + mono_time_update(c1.time); + mono_time_update(c2.time); + do_tcp_server(server, server_time); + do_tcp_connection(c1.log, c1.time, c1.conn, nullptr); + do_tcp_connection(c2.log, c2.time, c2.conn, nullptr); + if (tcp_con_status(c1.conn) == TCP_CLIENT_CONFIRMED + && tcp_con_status(c2.conn) == TCP_CLIENT_CONFIRMED) + break; + c_sleep(1); + } + + // Server only knows about keys after they send something. + send_routing_request(c1.log, c1.conn, server_pk); + send_routing_request(c2.log, c2.conn, server_pk); + + for (int i = 0; i < 200; ++i) { + current_time_ms += 10; + mono_time_update(server_time); + mono_time_update(c1.time); + mono_time_update(c2.time); + do_tcp_server(server, server_time); + do_tcp_connection(c1.log, c1.time, c1.conn, nullptr); + do_tcp_connection(c2.log, c2.time, c2.conn, nullptr); + c_sleep(1); + } + + std::uint8_t oob_msg[] = "OOB message"; + send_oob_packet(c1.log, c1.conn, c2.pk, oob_msg, sizeof(oob_msg)); + + for (int i = 0; i < 200; ++i) { + current_time_ms += 10; + mono_time_update(server_time); + mono_time_update(c1.time); + mono_time_update(c2.time); + do_tcp_server(server, server_time); + do_tcp_connection(c1.log, c1.time, c1.conn, nullptr); + do_tcp_connection(c2.log, c2.time, c2.conn, nullptr); + if (!c2.received_oob.empty()) + break; + c_sleep(1); + } + + ASSERT_FALSE(c2.received_oob.empty()); + EXPECT_EQ(c2.received_oob[0].size(), sizeof(oob_msg)); + EXPECT_EQ(0, std::memcmp(c2.received_oob[0].data(), oob_msg, sizeof(oob_msg))); + + kill_tcp_connection(c1.conn); + kill_tcp_connection(c2.conn); + kill_tcp_server(server); + ev_kill(c1.ev); + ev_kill(c2.ev); + ev_kill(server_ev); + logger_kill(c1.log); + logger_kill(c2.log); + logger_kill(server_log); + mono_time_free(mem, c1.time); + mono_time_free(mem, c2.time); + mono_time_free(mem, server_time); +} + +TEST_F(TCPServerTest, DisconnectNotification) +{ + Logger *server_log = logger_new(mem); + logger_callback_log(server_log, &TCPServerTest::log_cb, nullptr, const_cast("Server")); + Ev *server_ev = os_event_new(mem, server_log); + Mono_Time *server_time = create_mono_time(); + + std::uint8_t server_sk[CRYPTO_SECRET_KEY_SIZE]; + std::uint8_t server_pk[CRYPTO_PUBLIC_KEY_SIZE]; + crypto_new_keypair(rng, server_pk, server_sk); + + uint16_t test_port = 33451; + TCP_Server *server = new_tcp_server( + server_log, mem, rng, ns, server_ev, false, 1, &test_port, server_sk, nullptr, nullptr); + ASSERT_NE(server, nullptr); + + IP_Port server_ip_port; + server_ip_port.ip = get_loopback(); + server_ip_port.port = net_htons(test_port); + + struct Client { + Logger *log; + Ev *ev; + Mono_Time *time; + TCP_Client_Connection *conn; + std::uint8_t pk[CRYPTO_PUBLIC_KEY_SIZE]; + std::uint8_t sk[CRYPTO_SECRET_KEY_SIZE]; + bool status_disconnected = false; + const char *name; + } c1, c2; + + auto setup_client = [&](Client &c, const char *name) { + c.name = name; + c.log = logger_new(mem); + logger_callback_log(c.log, &TCPServerTest::log_cb, nullptr, const_cast(c.name)); + c.ev = os_event_new(mem, c.log); + c.time = create_mono_time(); + crypto_new_keypair(rng, c.pk, c.sk); + c.conn = new_tcp_connection(c.log, mem, c.time, rng, ns, c.ev, + &server_ip_port, server_pk, c.pk, c.sk, nullptr, nullptr); + + routing_status_handler( + c.conn, + [](void *obj, std::uint32_t number, std::uint8_t connection_id, + std::uint8_t status) -> int { + fprintf(stderr, + "Callback: DisconnectNotification Status received for %s, id=%u, status=%u\n", + static_cast(obj)->name, connection_id, status); + if (status == 1) + static_cast(obj)->status_disconnected = true; + return 0; + }, + &c); + }; + + setup_client(c1, "C1"); + setup_client(c2, "C2"); + + for (int i = 0; i < 500; ++i) { + current_time_ms += 10; + mono_time_update(server_time); + mono_time_update(c1.time); + mono_time_update(c2.time); + do_tcp_server(server, server_time); + do_tcp_connection(c1.log, c1.time, c1.conn, nullptr); + do_tcp_connection(c2.log, c2.time, c2.conn, nullptr); + if (tcp_con_status(c1.conn) == TCP_CLIENT_CONFIRMED + && tcp_con_status(c2.conn) == TCP_CLIENT_CONFIRMED) + break; + c_sleep(1); + } + + send_routing_request(c1.log, c1.conn, c2.pk); + send_routing_request(c2.log, c2.conn, c1.pk); + + for (int i = 0; i < 500; ++i) { + current_time_ms += 10; + mono_time_update(server_time); + mono_time_update(c1.time); + mono_time_update(c2.time); + do_tcp_server(server, server_time); + do_tcp_connection(c1.log, c1.time, c1.conn, nullptr); + do_tcp_connection(c2.log, c2.time, c2.conn, nullptr); + c_sleep(1); + } + + // Now C2 disconnects + fprintf(stderr, "C2 disconnecting...\n"); + kill_tcp_connection(c2.conn); + c2.conn = nullptr; + + for (int i = 0; i < 1000; ++i) { + current_time_ms += 1000; + mono_time_update(server_time); + mono_time_update(c1.time); + do_tcp_server(server, server_time); + do_tcp_connection(c1.log, c1.time, c1.conn, nullptr); + if (c1.status_disconnected) + break; + c_sleep(1); + } + + EXPECT_TRUE(c1.status_disconnected); + + kill_tcp_connection(c1.conn); + kill_tcp_server(server); + ev_kill(c1.ev); + ev_kill(c2.ev); + ev_kill(server_ev); + logger_kill(c1.log); + logger_kill(c2.log); + logger_kill(server_log); + mono_time_free(mem, c1.time); + mono_time_free(mem, c2.time); + mono_time_free(mem, server_time); +} + +TEST_F(TCPServerTest, InvalidHandshakeGarbage) +{ + Logger *server_log = logger_new(mem); + logger_callback_log(server_log, &TCPServerTest::log_cb, nullptr, const_cast("Server")); + Ev *server_ev = os_event_new(mem, server_log); + Mono_Time *server_time = create_mono_time(); + + std::uint8_t server_sk[CRYPTO_SECRET_KEY_SIZE]; + std::uint8_t server_pk[CRYPTO_PUBLIC_KEY_SIZE]; + crypto_new_keypair(rng, server_pk, server_sk); + + uint16_t test_port = 33453; + TCP_Server *server = new_tcp_server( + server_log, mem, rng, ns, server_ev, false, 1, &test_port, server_sk, nullptr, nullptr); + ASSERT_NE(server, nullptr); + + IP_Port server_ip_port; + server_ip_port.ip = get_loopback(); + server_ip_port.port = net_htons(test_port); + + Socket client_sock = net_socket(ns, net_family_ipv4(), TOX_SOCK_STREAM, TOX_PROTO_TCP); + Net_Err_Connect conn_err; + bool ok = net_connect(ns, mem, server_log, client_sock, &server_ip_port, &conn_err); + ASSERT_TRUE(ok); + + std::uint8_t garbage[TCP_CLIENT_HANDSHAKE_SIZE]; + std::memset(garbage, 0x42, sizeof(garbage)); + net_send(ns, server_log, client_sock, garbage, sizeof(garbage), &server_ip_port, nullptr); + + for (int i = 0; i < 50; ++i) { + current_time_ms += 10; + mono_time_update(server_time); + do_tcp_server(server, server_time); + c_sleep(1); + } + + std::uint8_t dummy; + int res = net_recv(ns, server_log, client_sock, &dummy, 1, &server_ip_port); + EXPECT_LE(res, 0); + + kill_sock(ns, client_sock); + kill_tcp_server(server); + ev_kill(server_ev); + logger_kill(server_log); + mono_time_free(mem, server_time); +} + +TEST_F(TCPServerTest, DISABLED_HandshakeTimeout) +{ + Logger *server_log = logger_new(mem); + logger_callback_log(server_log, &TCPServerTest::log_cb, nullptr, const_cast("Server")); + Ev *server_ev = os_event_new(mem, server_log); + Mono_Time *server_time = create_mono_time(); + + std::uint8_t server_sk[CRYPTO_SECRET_KEY_SIZE]; + std::uint8_t server_pk[CRYPTO_PUBLIC_KEY_SIZE]; + crypto_new_keypair(rng, server_pk, server_sk); + + uint16_t test_port = 33455; + TCP_Server *server = new_tcp_server( + server_log, mem, rng, ns, server_ev, false, 1, &test_port, server_sk, nullptr, nullptr); + ASSERT_NE(server, nullptr); + + IP_Port server_ip_port; + server_ip_port.ip = get_loopback(); + server_ip_port.port = net_htons(test_port); + + Socket client_sock = net_socket(ns, net_family_ipv4(), TOX_SOCK_STREAM, TOX_PROTO_TCP); + Net_Err_Connect conn_err; + bool ok = net_connect(ns, mem, server_log, client_sock, &server_ip_port, &conn_err); + ASSERT_TRUE(ok); + + for (int i = 0; i < 20; ++i) { + current_time_ms += 1000; + mono_time_update(server_time); + do_tcp_server(server, server_time); + c_sleep(1); + } + + std::uint8_t dummy; + int res = net_recv(ns, server_log, client_sock, &dummy, 1, &server_ip_port); + EXPECT_LE(res, 0); + + kill_sock(ns, client_sock); + kill_tcp_server(server); + ev_kill(server_ev); + logger_kill(server_log); + mono_time_free(mem, server_time); +} + +TEST_F(TCPServerTest, RoutingToSelf) +{ + Logger *server_log = logger_new(mem); + logger_callback_log(server_log, &TCPServerTest::log_cb, nullptr, const_cast("Server")); + Ev *server_ev = os_event_new(mem, server_log); + Mono_Time *server_time = create_mono_time(); + + std::uint8_t server_sk[CRYPTO_SECRET_KEY_SIZE]; + std::uint8_t server_pk[CRYPTO_PUBLIC_KEY_SIZE]; + crypto_new_keypair(rng, server_pk, server_sk); + + uint16_t test_port = 33457; + TCP_Server *server = new_tcp_server( + server_log, mem, rng, ns, server_ev, false, 1, &test_port, server_sk, nullptr, nullptr); + ASSERT_NE(server, nullptr); + + IP_Port server_ip_port; + server_ip_port.ip = get_loopback(); + server_ip_port.port = net_htons(test_port); + + Logger *client_log = logger_new(mem); + logger_callback_log(client_log, &TCPServerTest::log_cb, nullptr, const_cast("Client")); + Ev *client_ev = os_event_new(mem, client_log); + Mono_Time *client_time = create_mono_time(); + std::uint8_t client_pk[CRYPTO_PUBLIC_KEY_SIZE]; + std::uint8_t client_sk[CRYPTO_SECRET_KEY_SIZE]; + crypto_new_keypair(rng, client_pk, client_sk); + + TCP_Client_Connection *conn = new_tcp_connection(client_log, mem, client_time, rng, ns, + client_ev, &server_ip_port, server_pk, client_pk, client_sk, nullptr, nullptr); + + struct ResponseData { + int response_called = 0; + std::uint8_t response_id = 255; + } rd; + + routing_response_handler( + conn, + [](void *obj, std::uint8_t id, const std::uint8_t *pk) -> int { + auto *response_data = static_cast(obj); + fprintf(stderr, "Callback: Routing Response received, id=%u\n", id); + response_data->response_called++; + response_data->response_id = id; + return 0; + }, + &rd); + + for (int i = 0; i < 500; ++i) { + current_time_ms += 10; + mono_time_update(server_time); + mono_time_update(client_time); + do_tcp_server(server, server_time); + do_tcp_connection(client_log, client_time, conn, nullptr); + if (tcp_con_status(conn) == TCP_CLIENT_CONFIRMED) + break; + c_sleep(1); + } + ASSERT_EQ(tcp_con_status(conn), TCP_CLIENT_CONFIRMED); + + send_routing_request(client_log, conn, client_pk); + + for (int i = 0; i < 500; ++i) { + current_time_ms += 10; + mono_time_update(server_time); + mono_time_update(client_time); + do_tcp_server(server, server_time); + do_tcp_connection(client_log, client_time, conn, nullptr); + c_sleep(1); + } + + EXPECT_EQ(rd.response_called, 0); + + kill_tcp_connection(conn); + kill_tcp_server(server); + ev_kill(client_ev); + ev_kill(server_ev); + logger_kill(client_log); + logger_kill(server_log); + mono_time_free(mem, client_time); + mono_time_free(mem, server_time); +} + +} // namespace diff --git a/toxcore/forwarding_fuzz_test.cc b/toxcore/forwarding_fuzz_test.cc index 9bf481e85..b6d5e10a2 100644 --- a/toxcore/forwarding_fuzz_test.cc +++ b/toxcore/forwarding_fuzz_test.cc @@ -8,6 +8,8 @@ #include "../testing/support/public/fuzz_data.hh" #include "../testing/support/public/fuzz_helpers.hh" #include "../testing/support/public/simulated_environment.hh" +#include "ev.h" +#include "os_event.h" namespace { @@ -64,9 +66,14 @@ void TestSendForwardRequest(Fuzz_Data &input) return; } + const Ptr ev(os_event_new(&node->c_memory, logger.get()), ev_kill); + if (ev == nullptr) { + return; + } + const Ptr net( - new_networking_ex(logger.get(), &node->c_memory, &node->c_network, &ipp.ip, ipp.port, - ipp.port + 100, nullptr), + new_networking_ex(logger.get(), &node->c_memory, &node->c_network, ev.get(), &ipp.ip, + ipp.port, ipp.port + 100, nullptr), kill_networking); if (net == nullptr) { return; @@ -95,9 +102,14 @@ void TestForwardReply(Fuzz_Data &input) return; } + const Ptr ev(os_event_new(&node->c_memory, logger.get()), ev_kill); + if (ev == nullptr) { + return; + } + const Ptr net( - new_networking_ex(logger.get(), &node->c_memory, &node->c_network, &ipp.ip, ipp.port, - ipp.port + 100, nullptr), + new_networking_ex(logger.get(), &node->c_memory, &node->c_network, ev.get(), &ipp.ip, + ipp.port, ipp.port + 100, nullptr), kill_networking); if (net == nullptr) { return; diff --git a/toxcore/friend_connection_test.cc b/toxcore/friend_connection_test.cc index 19d04fd81..f26767e16 100644 --- a/toxcore/friend_connection_test.cc +++ b/toxcore/friend_connection_test.cc @@ -53,8 +53,8 @@ class FriendConnTestNode { TCP_Proxy_Info proxy_info = {{0}, TCP_PROXY_NONE}; net_crypto_.reset(new_net_crypto(dht_wrapper_.logger(), &dht_wrapper_.node().c_memory, &dht_wrapper_.node().c_random, &dht_wrapper_.node().c_network, dht_wrapper_.mono_time(), - dht_wrapper_.networking(), dht_wrapper_.get_dht(), &DHTWrapper::funcs, &proxy_info, - net_profile_.get())); + dht_wrapper_.ev(), dht_wrapper_.networking(), dht_wrapper_.get_dht(), + &DHTWrapper::funcs, &proxy_info, net_profile_.get())); new_keys(net_crypto_.get()); diff --git a/toxcore/group_chats.c b/toxcore/group_chats.c index 53b6b11ca..4bba64615 100644 --- a/toxcore/group_chats.c +++ b/toxcore/group_chats.c @@ -7259,7 +7259,7 @@ static bool init_gc_tcp_connection(const GC_Session *_Nonnull c, GC_Chat *_Nonnu { const Messenger *m = c->messenger; - TCP_Connections *tcp_conn = new_tcp_connections(chat->log, chat->mem, chat->rng, m->ns, chat->mono_time, chat->self_secret_key.enc, + TCP_Connections *tcp_conn = new_tcp_connections(chat->log, chat->mem, chat->rng, m->ns, chat->mono_time, m->ev, chat->self_secret_key.enc, &m->options.proxy_info, c->tcp_np); if (tcp_conn == nullptr) { diff --git a/toxcore/net_crypto.c b/toxcore/net_crypto.c index 2841f9611..0e74c32ff 100644 --- a/toxcore/net_crypto.c +++ b/toxcore/net_crypto.c @@ -137,7 +137,8 @@ struct Net_Crypto { const Random *_Nonnull rng; Mono_Time *_Nonnull mono_time; const Network *_Nonnull ns; - Networking_Core *net; + Ev *_Nonnull ev; + Networking_Core *_Nonnull net; void *_Nonnull dht; const Net_Crypto_DHT_Funcs *_Nonnull dht_funcs; @@ -2961,7 +2962,7 @@ void load_secret_key(Net_Crypto *c, const uint8_t *sk) * Sets all the global connection variables to their default values. */ Net_Crypto *new_net_crypto(const Logger *log, const Memory *mem, const Random *rng, const Network *ns, - Mono_Time *mono_time, Networking_Core *net, void *dht, const Net_Crypto_DHT_Funcs *dht_funcs, const TCP_Proxy_Info *proxy_info, Net_Profile *tcp_np) + Mono_Time *mono_time, Ev *ev, Networking_Core *net, void *dht, const Net_Crypto_DHT_Funcs *dht_funcs, const TCP_Proxy_Info *proxy_info, Net_Profile *tcp_np) { if (dht == nullptr || dht_funcs == nullptr || dht_funcs->get_shared_key_sent == nullptr || dht_funcs->get_self_public_key == nullptr || dht_funcs->get_self_secret_key == nullptr) { return nullptr; @@ -2978,12 +2979,13 @@ Net_Crypto *new_net_crypto(const Logger *log, const Memory *mem, const Random *r temp->rng = rng; temp->mono_time = mono_time; temp->ns = ns; + temp->ev = ev; temp->net = net; temp->dht = dht; temp->dht_funcs = dht_funcs; - TCP_Connections *const tcp_c = new_tcp_connections(log, mem, rng, ns, mono_time, dht_funcs->get_self_secret_key(dht), proxy_info, tcp_np); + TCP_Connections *const tcp_c = new_tcp_connections(log, mem, rng, ns, mono_time, ev, dht_funcs->get_self_secret_key(dht), proxy_info, tcp_np); if (tcp_c == nullptr) { mem_delete(mem, temp); diff --git a/toxcore/net_crypto.h b/toxcore/net_crypto.h index 244e18417..6ef23f27d 100644 --- a/toxcore/net_crypto.h +++ b/toxcore/net_crypto.h @@ -384,7 +384,7 @@ void load_secret_key(Net_Crypto *_Nonnull c, const uint8_t *_Nonnull sk); * Sets all the global connection variables to their default values. */ Net_Crypto *_Nullable new_net_crypto(const Logger *_Nonnull log, const Memory *_Nonnull mem, const Random *_Nonnull rng, const Network *_Nonnull ns, Mono_Time *_Nonnull mono_time, - Networking_Core *_Nonnull net, void *_Nonnull dht, const Net_Crypto_DHT_Funcs *_Nonnull dht_funcs, + Ev *_Nonnull ev, Networking_Core *_Nonnull net, void *_Nonnull dht, const Net_Crypto_DHT_Funcs *_Nonnull dht_funcs, const TCP_Proxy_Info *_Nonnull proxy_info, Net_Profile *_Nonnull tcp_np); /** return the optimal interval in ms for running do_net_crypto. */ diff --git a/toxcore/net_crypto_fuzz_test.cc b/toxcore/net_crypto_fuzz_test.cc index bfea1e457..1780c8cbf 100644 --- a/toxcore/net_crypto_fuzz_test.cc +++ b/toxcore/net_crypto_fuzz_test.cc @@ -12,8 +12,10 @@ #include "DHT.h" #include "TCP_client.h" #include "attributes.h" +#include "ev.h" #include "net_profile.h" #include "network.h" +#include "os_event.h" namespace { @@ -63,9 +65,14 @@ void TestNetCrypto(Fuzz_Data &input) return; } + const Ptr ev(os_event_new(&node->c_memory, logger.get()), ev_kill); + if (ev == nullptr) { + return; + } + const Ptr net( - new_networking_ex(logger.get(), &node->c_memory, &node->c_network, &ipp.ip, ipp.port, - ipp.port + 100, nullptr), + new_networking_ex(logger.get(), &node->c_memory, &node->c_network, ev.get(), &ipp.ip, + ipp.port, ipp.port + 100, nullptr), kill_networking); if (net == nullptr) { return; @@ -101,7 +108,7 @@ void TestNetCrypto(Fuzz_Data &input) const Ptr net_crypto( new_net_crypto(logger.get(), &node->c_memory, &node->c_random, &node->c_network, - mono_time.get(), net.get(), dht.get(), &dht_funcs, &proxy_info, tcp_np), + mono_time.get(), ev.get(), net.get(), dht.get(), &dht_funcs, &proxy_info, tcp_np), kill_net_crypto); if (net_crypto == nullptr) { netprof_kill(&node->c_memory, tcp_np); diff --git a/toxcore/net_crypto_test.cc b/toxcore/net_crypto_test.cc index ea57cfb38..f9f339218 100644 --- a/toxcore/net_crypto_test.cc +++ b/toxcore/net_crypto_test.cc @@ -55,8 +55,8 @@ class TestNode { TCP_Proxy_Info proxy_info = {{0}, TCP_PROXY_NONE}; net_crypto_.reset(new_net_crypto(dht_wrapper_.logger(), &dht_wrapper_.node().c_memory, &dht_wrapper_.node().c_random, &dht_wrapper_.node().c_network, dht_wrapper_.mono_time(), - dht_wrapper_.networking(), dht_wrapper_.get_dht(), &DHTWrapper::funcs, &proxy_info, - net_profile_.get())); + dht_wrapper_.ev(), dht_wrapper_.networking(), dht_wrapper_.get_dht(), + &DHTWrapper::funcs, &proxy_info, net_profile_.get())); // 4. Register Callbacks new_connection_handler(net_crypto_.get(), &TestNode::static_new_connection_cb, this); @@ -208,57 +208,63 @@ using RealDHTNode = TestNode; class NetCryptoTest : public ::testing::Test { protected: SimulatedEnvironment env; -}; - -TEST_F(NetCryptoTest, EndToEndDataExchange) -{ - NetCryptoNode alice(env, 33445); - NetCryptoNode bob(env, 33446); - - // 1. Alice initiates connection to Bob - int alice_conn_id = alice.connect_to(bob); - ASSERT_NE(alice_conn_id, -1); - // 2. Run simulation until connected - auto start = env.clock().current_time_ms(); - int bob_conn_id = -1; - bool connected = false; + template + void RunEndToEndDataExchange(Node &alice, Node &bob) + { + // 1. Alice initiates connection to Bob + int alice_conn_id = alice.connect_to(bob); + ASSERT_NE(alice_conn_id, -1); - while ((env.clock().current_time_ms() - start) < 5000) { - alice.poll(); - bob.poll(); - env.advance_time(10); // 10ms steps + // 2. Run simulation until connected + auto start = env.clock().current_time_ms(); + int bob_conn_id = -1; + bool connected = false; - bob_conn_id = bob.get_connection_id_by_pk(alice.real_public_key()); - if (alice.is_connected(alice_conn_id) && bob_conn_id != -1 - && bob.is_connected(bob_conn_id)) { - connected = true; - break; + while ((env.clock().current_time_ms() - start) < 5000) { + alice.poll(); + bob.poll(); + env.advance_time(10); // 10ms steps + + bob_conn_id = bob.get_connection_id_by_pk(alice.real_public_key()); + if (alice.is_connected(alice_conn_id) && bob_conn_id != -1 + && bob.is_connected(bob_conn_id)) { + connected = true; + break; + } } - } - ASSERT_TRUE(connected) << "Failed to establish connection within timeout"; + ASSERT_TRUE(connected) << "Failed to establish connection within timeout"; - // 3. Exchange Data - // Packet ID must be in custom range (160+) - std::vector message = {160, 'H', 'e', 'l', 'l', 'o'}; + // 3. Exchange Data + // Packet ID must be in custom range (160+) + std::vector message = {160, 'H', 'e', 'l', 'l', 'o'}; - EXPECT_TRUE(alice.send_data(alice_conn_id, message)); + EXPECT_TRUE(alice.send_data(alice_conn_id, message)); - start = env.clock().current_time_ms(); - bool data_received = false; - while ((env.clock().current_time_ms() - start) < 1000) { - alice.poll(); - bob.poll(); - env.advance_time(10); + start = env.clock().current_time_ms(); + bool data_received = false; + while ((env.clock().current_time_ms() - start) < 1000) { + alice.poll(); + bob.poll(); + env.advance_time(10); - if (bob.get_last_received_data(bob_conn_id) == message) { - data_received = true; - break; + if (bob.get_last_received_data(bob_conn_id) == message) { + data_received = true; + break; + } } + + EXPECT_TRUE(data_received) << "Bob did not receive the correct data"; } +}; + +TEST_F(NetCryptoTest, EndToEndDataExchange) +{ + NetCryptoNode alice(env, 33445); + NetCryptoNode bob(env, 33446); - EXPECT_TRUE(data_received) << "Bob did not receive the correct data"; + RunEndToEndDataExchange(alice, bob); } TEST_F(NetCryptoTest, ConnectionTimeout) @@ -536,50 +542,7 @@ TEST_F(NetCryptoTest, EndToEndDataExchange_RealDHT) RealDHTNode alice(env, 33445); RealDHTNode bob(env, 33446); - // 1. Alice initiates connection to Bob - int alice_conn_id = alice.connect_to(bob); - ASSERT_NE(alice_conn_id, -1); - - // 2. Run simulation until connected - auto start = env.clock().current_time_ms(); - int bob_conn_id = -1; - bool connected = false; - - while ((env.clock().current_time_ms() - start) < 5000) { - alice.poll(); - bob.poll(); - env.advance_time(10); // 10ms steps - - bob_conn_id = bob.get_connection_id_by_pk(alice.real_public_key()); - if (alice.is_connected(alice_conn_id) && bob_conn_id != -1 - && bob.is_connected(bob_conn_id)) { - connected = true; - break; - } - } - - ASSERT_TRUE(connected) << "Failed to establish connection within timeout"; - - // 3. Exchange Data - // Packet ID must be in custom range (160+) - std::vector message = {160, 'H', 'e', 'l', 'l', 'o'}; - - EXPECT_TRUE(alice.send_data(alice_conn_id, message)); - - start = env.clock().current_time_ms(); - bool data_received = false; - while ((env.clock().current_time_ms() - start) < 1000) { - alice.poll(); - bob.poll(); - env.advance_time(10); - - if (bob.get_last_received_data(bob_conn_id) == message) { - data_received = true; - break; - } - } - - EXPECT_TRUE(data_received) << "Bob did not receive the correct data"; + RunEndToEndDataExchange(alice, bob); } } // namespace diff --git a/toxcore/network.c b/toxcore/network.c index 4bb23dbc6..96c844b2c 100644 --- a/toxcore/network.c +++ b/toxcore/network.c @@ -123,6 +123,7 @@ typedef struct Packet_Handler { struct Networking_Core { const Logger *_Nonnull log; const Memory *_Nonnull mem; + Ev *_Nonnull ev; Packet_Handler packethandlers[256]; const Network *_Nonnull ns; @@ -154,13 +155,16 @@ int net_send_packet(const Networking_Core *net, const IP_Port *ip_port, Net_Pack if (net_family_is_unspec(ip_port->ip.family)) { // TODO(iphydf): Make this an error. Currently this fails sometimes when // called from DHT.c:do_ping_and_sendnode_requests. + LOGGER_WARNING(net->log, "Failed to send packet because the target address family is unspecified (length %u).", + packet.length); return -1; } if (net_family_is_unspec(net->family)) { /* Socket not initialized */ // TODO(iphydf): Make this an error. Currently, the onion client calls // this via DHT nodes requests. - LOGGER_WARNING(net->log, "attempted to send message of length %u on uninitialised socket", packet.length); + LOGGER_WARNING(net->log, "Failed to send packet on an uninitialised socket (length %u, family %s, port %u, socket %d).", + packet.length, net_family_to_string(net->family), net_ntohs(net->port), net_socket_to_native(net->sock)); return -1; } @@ -295,7 +299,7 @@ void networking_poll(const Networking_Core *net, void *userdata) * If error is non NULL it is set to 0 if no issues, 1 if socket related error, 2 if other. */ Networking_Core *new_networking_ex( - const Logger *log, const Memory *mem, const Network *ns, const IP *ip, + const Logger *log, const Memory *mem, const Network *ns, Ev *ev, const IP *ip, uint16_t port_from, uint16_t port_to, unsigned int *error) { /* If both from and to are 0, use default port range @@ -319,6 +323,8 @@ Networking_Core *new_networking_ex( *error = 2; } + LOGGER_DEBUG(log, "Creating networking core for address family %s.", net_family_to_string(ip->family)); + /* maybe check for invalid IPs like 224+.x.y.z? if there is any IP set ever */ if (!net_family_is_ipv4(ip->family) && !net_family_is_ipv6(ip->family)) { LOGGER_ERROR(log, "invalid address family: %u", ip->family.value); @@ -342,6 +348,7 @@ Networking_Core *new_networking_ex( temp->ns = ns; temp->log = log; temp->mem = mem; + temp->ev = ev; temp->family = ip->family; temp->port = 0; @@ -471,6 +478,8 @@ Networking_Core *new_networking_ex( *error = 0; } + ev_add(ev, temp->sock, EV_READ, temp); + return temp; } @@ -497,8 +506,9 @@ Networking_Core *new_networking_ex( return nullptr; } -Networking_Core *new_networking_no_udp(const Logger *log, const Memory *mem, const Network *ns) +Networking_Core *new_networking_no_udp(const Logger *log, const Memory *mem, const Network *ns, Ev *ev) { + LOGGER_DEBUG(log, "Creating networking core with UDP disabled."); /* this is the easiest way to completely disable UDP without changing too much code. */ Networking_Core *net = (Networking_Core *)mem_alloc(mem, sizeof(Networking_Core)); @@ -509,6 +519,11 @@ Networking_Core *new_networking_no_udp(const Logger *log, const Memory *mem, con net->ns = ns; net->log = log; net->mem = mem; + net->ev = ev; + net->family = net_family_unspec(); + net->port = 0; + net->sock = net_invalid_socket(); + net->udp_net_profile = nullptr; return net; } @@ -522,6 +537,7 @@ void kill_networking(Networking_Core *net) if (!net_family_is_unspec(net->family)) { /* Socket is initialized, so we close it. */ + ev_del(net->ev, net->sock); kill_sock(net->ns, net->sock); } diff --git a/toxcore/network.h b/toxcore/network.h index f44f9eac9..b4f46f654 100644 --- a/toxcore/network.h +++ b/toxcore/network.h @@ -15,6 +15,7 @@ #include "attributes.h" #include "bin_pack.h" +#include "ev.h" #include "logger.h" #include "mem.h" #include "net.h" @@ -393,9 +394,10 @@ bool bind_to_port(const Network *_Nonnull ns, Socket sock, Family family, uint16 * If error is non NULL it is set to 0 if no issues, 1 if socket related error, 2 if other. */ Networking_Core *_Nullable new_networking_ex( - const Logger *_Nonnull log, const Memory *_Nonnull mem, const Network *_Nonnull ns, const IP *_Nonnull ip, - uint16_t port_from, uint16_t port_to, unsigned int *_Nullable error); -Networking_Core *_Nullable new_networking_no_udp(const Logger *_Nonnull log, const Memory *_Nonnull mem, const Network *_Nonnull ns); + const Logger *_Nonnull log, const Memory *_Nonnull mem, const Network *_Nonnull ns, Ev *_Nonnull ev, + const IP *_Nonnull ip, uint16_t port_from, uint16_t port_to, unsigned int *_Nullable error); +Networking_Core *_Nullable new_networking_no_udp(const Logger *_Nonnull log, const Memory *_Nonnull mem, + const Network *_Nonnull ns, Ev *_Nonnull ev); /** Function to cleanup networking stuff (doesn't do much right now). */ void kill_networking(Networking_Core *_Nullable net); diff --git a/toxcore/onion_client_fuzz_test.cc b/toxcore/onion_client_fuzz_test.cc index 4481b1547..2b3ce040f 100644 --- a/toxcore/onion_client_fuzz_test.cc +++ b/toxcore/onion_client_fuzz_test.cc @@ -14,6 +14,7 @@ #include "net_crypto.h" #include "net_profile.h" #include "network.h" +#include "os_event.h" #include "test_util.hh" namespace { @@ -44,14 +45,15 @@ class FuzzDHT { }, &env.fake_clock()), [mem = &node_->c_memory](Mono_Time *t) { mono_time_free(mem, t); }) + , ev_(os_event_new(&node_->c_memory, logger_.get()), ev_kill) , networking_(nullptr, [](Networking_Core *n) { kill_networking(n); }) , dht_(nullptr, [](DHT *d) { kill_dht(d); }) { IP ip; ip_init(&ip, true); unsigned int error = 0; - networking_.reset(new_networking_ex( - logger_.get(), &node_->c_memory, &node_->c_network, &ip, port, port + 1, &error)); + networking_.reset(new_networking_ex(logger_.get(), &node_->c_memory, &node_->c_network, + ev_.get(), &ip, port, port + 1, &error)); // In fuzzing we might ignore assert, but setup should succeed node_->endpoint = node_->node->get_primary_socket(); @@ -63,6 +65,7 @@ class FuzzDHT { Networking_Core *_Nonnull networking() { return REQUIRE_NOT_NULL(networking_.get()); } Mono_Time *_Nonnull mono_time() { return REQUIRE_NOT_NULL(mono_time_.get()); } Logger *_Nonnull logger() { return REQUIRE_NOT_NULL(logger_.get()); } + Ev *_Nonnull ev() { return REQUIRE_NOT_NULL(ev_.get()); } tox::test::ScopedToxSystem &node() { return *node_; } FakeUdpSocket *_Nullable endpoint() { return node_->endpoint; } @@ -72,6 +75,7 @@ class FuzzDHT { std::unique_ptr node_; std::unique_ptr logger_; std::unique_ptr> mono_time_; + std::unique_ptr ev_; std::unique_ptr networking_; std::unique_ptr dht_; }; @@ -96,8 +100,8 @@ class OnionClientFuzzer { { TCP_Proxy_Info proxy_info = {{0}, TCP_PROXY_NONE}; net_crypto_.reset(new_net_crypto(dht_.logger(), &dht_.node().c_memory, - &dht_.node().c_random, &dht_.node().c_network, dht_.mono_time(), dht_.networking(), - dht_.get_dht(), &FuzzDHT::funcs, &proxy_info, net_profile_.get())); + &dht_.node().c_random, &dht_.node().c_network, dht_.mono_time(), dht_.ev(), + dht_.networking(), dht_.get_dht(), &FuzzDHT::funcs, &proxy_info, net_profile_.get())); onion_client_.reset( new_onion_client(dht_.logger(), &dht_.node().c_memory, &dht_.node().c_random, diff --git a/toxcore/onion_client_test.cc b/toxcore/onion_client_test.cc index 7c27777b0..17b99eca1 100644 --- a/toxcore/onion_client_test.cc +++ b/toxcore/onion_client_test.cc @@ -47,8 +47,8 @@ class OnionTestNode { TCP_Proxy_Info proxy_info = {{0}, TCP_PROXY_NONE}; net_crypto_.reset(new_net_crypto(dht_wrapper_.logger(), &dht_wrapper_.node().c_memory, &dht_wrapper_.node().c_random, &dht_wrapper_.node().c_network, dht_wrapper_.mono_time(), - dht_wrapper_.networking(), dht_wrapper_.get_dht(), &DHTWrapper::funcs, &proxy_info, - net_profile_.get())); + dht_wrapper_.ev(), dht_wrapper_.networking(), dht_wrapper_.get_dht(), + &DHTWrapper::funcs, &proxy_info, net_profile_.get())); // Setup Onion Client onion_client_.reset(new_onion_client(dht_wrapper_.logger(), &dht_wrapper_.node().c_memory, @@ -121,6 +121,107 @@ class OnionClientTest : public ::testing::Test { protected: SimulatedEnvironment env; + + void InterceptAndRespondMaliciously(const std::vector &data, const IP_Port &from, + bool &triggered, const std::uint8_t *bob_sk, const Memory *mem, FakeUdpSocket *bob_socket, + const Random *alice_random) + { + if (triggered) + return; + if (data.size() < 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_MAC_SIZE) + return; + + if (data[0] != NET_PACKET_ONION_SEND_INITIAL) + return; + + std::uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE]; + std::uint8_t nonce[CRYPTO_NONCE_SIZE]; + std::memcpy(nonce, data.data() + 1, CRYPTO_NONCE_SIZE); + const std::uint8_t *ephem_pk = data.data() + 1 + CRYPTO_NONCE_SIZE; + + encrypt_precompute(ephem_pk, bob_sk, shared_key); + + std::vector decrypted1( + data.size() - (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_MAC_SIZE)); + int dlen = decrypt_data_symmetric(mem, shared_key, nonce, + data.data() + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE, + data.size() - (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE), decrypted1.data()); + if (dlen <= 0) + return; + + std::size_t offset = SIZE_IPPORT + CRYPTO_PUBLIC_KEY_SIZE; + if (static_cast(dlen) <= offset + CRYPTO_MAC_SIZE) + return; + ephem_pk = decrypted1.data() + SIZE_IPPORT; + + encrypt_precompute(ephem_pk, bob_sk, shared_key); + + std::vector decrypted2(dlen - offset - CRYPTO_MAC_SIZE); + dlen = decrypt_data_symmetric( + mem, shared_key, nonce, decrypted1.data() + offset, dlen - offset, decrypted2.data()); + if (dlen <= 0) + return; + + if (static_cast(dlen) <= offset + CRYPTO_MAC_SIZE) + return; + ephem_pk = decrypted2.data() + SIZE_IPPORT; + encrypt_precompute(ephem_pk, bob_sk, shared_key); + + std::vector decrypted3(dlen - offset - CRYPTO_MAC_SIZE); + dlen = decrypt_data_symmetric( + mem, shared_key, nonce, decrypted2.data() + offset, dlen - offset, decrypted3.data()); + if (dlen <= 0) + return; + + std::size_t data_offset = SIZE_IPPORT; + if (static_cast(dlen) <= data_offset) + return; + std::uint8_t *req = decrypted3.data() + data_offset; + std::size_t req_len = dlen - data_offset; + + if (req[0] != 0x87 && req[0] != 0x83) + return; + + std::uint8_t req_nonce[CRYPTO_NONCE_SIZE]; + std::memcpy(req_nonce, req + 1, CRYPTO_NONCE_SIZE); + std::uint8_t alice_pk[CRYPTO_PUBLIC_KEY_SIZE]; + std::memcpy(alice_pk, req + 1 + CRYPTO_NONCE_SIZE, CRYPTO_PUBLIC_KEY_SIZE); + + std::uint8_t *req_enc = req + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE; + std::size_t req_enc_len = req_len - (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE); + + std::vector req_plain(req_enc_len - CRYPTO_MAC_SIZE); + int plen = decrypt_data( + mem, alice_pk, bob_sk, req_nonce, req_enc, req_enc_len, req_plain.data()); + + if (plen <= 0) + return; + + std::size_t sendback_offset = 32 + 32 + 32; + if (static_cast(plen) < sendback_offset + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH) + return; + std::uint8_t *sendback = req_plain.data() + sendback_offset; + std::size_t sendback_len = ONION_ANNOUNCE_SENDBACK_DATA_LENGTH; + + std::vector resp; + resp.push_back(NET_PACKET_ANNOUNCE_RESPONSE); + resp.insert(resp.end(), sendback, sendback + sendback_len); + + std::uint8_t resp_nonce[CRYPTO_NONCE_SIZE]; + random_nonce(alice_random, resp_nonce); + resp.insert(resp.end(), resp_nonce, resp_nonce + CRYPTO_NONCE_SIZE); + + std::vector payload(33, 0); + + std::vector ciphertext(payload.size() + CRYPTO_MAC_SIZE); + encrypt_data( + mem, alice_pk, bob_sk, resp_nonce, payload.data(), payload.size(), ciphertext.data()); + + resp.insert(resp.end(), ciphertext.begin(), ciphertext.end()); + + bob_socket->sendto(resp.data(), resp.size(), &from); + triggered = true; + } }; TEST_F(OnionClientTest, CreationAndDestruction) @@ -302,110 +403,8 @@ TEST_F(OnionClientTest, OOBReadInHandleAnnounceResponse) // Observer bool triggered = false; bob_socket->set_recv_observer([&](const std::vector &data, const IP_Port &from) { - if (triggered) - return; - if (data.size() < 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_MAC_SIZE) - return; - - // Layer 1 - if (data[0] != NET_PACKET_ONION_SEND_INITIAL) - return; - - std::uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE]; - std::uint8_t nonce[CRYPTO_NONCE_SIZE]; - std::memcpy(nonce, data.data() + 1, CRYPTO_NONCE_SIZE); - const std::uint8_t *ephem_pk = data.data() + 1 + CRYPTO_NONCE_SIZE; - - encrypt_precompute(ephem_pk, bob_sk, shared_key); - - std::vector decrypted1( - data.size() - (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_MAC_SIZE)); - int dlen = decrypt_data_symmetric(mem, shared_key, nonce, - data.data() + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE, - data.size() - (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE), decrypted1.data()); - if (dlen <= 0) - return; - - // Decrypted 1: [IP] [PK] [Encrypted 2] - std::size_t offset = SIZE_IPPORT + CRYPTO_PUBLIC_KEY_SIZE; - if (static_cast(dlen) <= offset + CRYPTO_MAC_SIZE) - return; - ephem_pk = decrypted1.data() + SIZE_IPPORT; - - encrypt_precompute(ephem_pk, bob_sk, shared_key); - - std::vector decrypted2(dlen - offset - CRYPTO_MAC_SIZE); - dlen = decrypt_data_symmetric( - mem, shared_key, nonce, decrypted1.data() + offset, dlen - offset, decrypted2.data()); - if (dlen <= 0) - return; - - // Decrypted 2: [IP] [PK] [Encrypted 3] - if (static_cast(dlen) <= offset + CRYPTO_MAC_SIZE) - return; - ephem_pk = decrypted2.data() + SIZE_IPPORT; - encrypt_precompute(ephem_pk, bob_sk, shared_key); - - std::vector decrypted3(dlen - offset - CRYPTO_MAC_SIZE); - dlen = decrypt_data_symmetric( - mem, shared_key, nonce, decrypted2.data() + offset, dlen - offset, decrypted3.data()); - if (dlen <= 0) - return; - - // Decrypted 3: [IP] [Data] - std::size_t data_offset = SIZE_IPPORT; - if (static_cast(dlen) <= data_offset) - return; - std::uint8_t *req = decrypted3.data() + data_offset; - std::size_t req_len = dlen - data_offset; - - // Announce Request: [131] [Nonce] [Alice PK] [Encrypted] - if (req[0] != 0x87 && req[0] != 0x83) - return; - - std::uint8_t req_nonce[CRYPTO_NONCE_SIZE]; - std::memcpy(req_nonce, req + 1, CRYPTO_NONCE_SIZE); - std::uint8_t alice_pk[CRYPTO_PUBLIC_KEY_SIZE]; - std::memcpy(alice_pk, req + 1 + CRYPTO_NONCE_SIZE, CRYPTO_PUBLIC_KEY_SIZE); - - std::uint8_t *req_enc = req + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE; - std::size_t req_enc_len = req_len - (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE); - - std::vector req_plain(req_enc_len - CRYPTO_MAC_SIZE); - int plen = decrypt_data( - mem, alice_pk, bob_sk, req_nonce, req_enc, req_enc_len, req_plain.data()); - - if (plen <= 0) - return; - - // Payload: [Ping ID (32)] [Search ID (32)] [Data PK (32)] [Sendback (Rest)] - std::size_t sendback_offset = 32 + 32 + 32; - if (static_cast(plen) < sendback_offset + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH) - return; - std::uint8_t *sendback = req_plain.data() + sendback_offset; - std::size_t sendback_len = ONION_ANNOUNCE_SENDBACK_DATA_LENGTH; - - // Construct Malicious Response - std::vector resp; - resp.push_back(NET_PACKET_ANNOUNCE_RESPONSE); - resp.insert(resp.end(), sendback, sendback + sendback_len); - - std::uint8_t resp_nonce[CRYPTO_NONCE_SIZE]; - random_nonce(alice.get_random(), resp_nonce); - resp.insert(resp.end(), resp_nonce, resp_nonce + CRYPTO_NONCE_SIZE); - - // Encrypted Payload: [is_stored (1)] [ping_id (32)] - // Total 33 bytes. OMIT count. - std::vector payload(33, 0); - - std::vector ciphertext(33 + CRYPTO_MAC_SIZE); - encrypt_data(mem, alice_pk, bob_sk, resp_nonce, payload.data(), 33, ciphertext.data()); - - resp.insert(resp.end(), ciphertext.begin(), ciphertext.end()); - - // Send to Alice - bob_socket->sendto(resp.data(), resp.size(), &from); - triggered = true; + InterceptAndRespondMaliciously( + data, from, triggered, bob_sk, mem, bob_socket, alice.get_random()); }); // Run simulation @@ -501,103 +500,8 @@ TEST_F(OnionClientTest, OnionAnnounceResponse_TooShort) const Memory *mem = &mem_struct; bob_socket->set_recv_observer([&](const std::vector &data, const IP_Port &from) { - if (triggered) - return; - if (data.size() < 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_MAC_SIZE) - return; - - if (data[0] != NET_PACKET_ONION_SEND_INITIAL) - return; - - std::uint8_t shared_key[CRYPTO_SHARED_KEY_SIZE]; - std::uint8_t nonce[CRYPTO_NONCE_SIZE]; - std::memcpy(nonce, data.data() + 1, CRYPTO_NONCE_SIZE); - const std::uint8_t *ephem_pk = data.data() + 1 + CRYPTO_NONCE_SIZE; - - encrypt_precompute(ephem_pk, bob_sk, shared_key); - - std::vector decrypted1( - data.size() - (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_MAC_SIZE)); - int dlen = decrypt_data_symmetric(mem, shared_key, nonce, - data.data() + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE, - data.size() - (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE), decrypted1.data()); - if (dlen <= 0) - return; - - std::size_t offset = SIZE_IPPORT + CRYPTO_PUBLIC_KEY_SIZE; - if (static_cast(dlen) <= offset + CRYPTO_MAC_SIZE) - return; - ephem_pk = decrypted1.data() + SIZE_IPPORT; - - encrypt_precompute(ephem_pk, bob_sk, shared_key); - - std::vector decrypted2(dlen - offset - CRYPTO_MAC_SIZE); - dlen = decrypt_data_symmetric( - mem, shared_key, nonce, decrypted1.data() + offset, dlen - offset, decrypted2.data()); - if (dlen <= 0) - return; - - if (static_cast(dlen) <= offset + CRYPTO_MAC_SIZE) - return; - ephem_pk = decrypted2.data() + SIZE_IPPORT; - encrypt_precompute(ephem_pk, bob_sk, shared_key); - - std::vector decrypted3(dlen - offset - CRYPTO_MAC_SIZE); - dlen = decrypt_data_symmetric( - mem, shared_key, nonce, decrypted2.data() + offset, dlen - offset, decrypted3.data()); - if (dlen <= 0) - return; - - std::size_t data_offset = SIZE_IPPORT; - if (static_cast(dlen) <= data_offset) - return; - std::uint8_t *req = decrypted3.data() + data_offset; - std::size_t req_len = dlen - data_offset; - - if (req[0] != 0x87 && req[0] != 0x83) - return; - - std::uint8_t req_nonce[CRYPTO_NONCE_SIZE]; - std::memcpy(req_nonce, req + 1, CRYPTO_NONCE_SIZE); - std::uint8_t alice_pk[CRYPTO_PUBLIC_KEY_SIZE]; - std::memcpy(alice_pk, req + 1 + CRYPTO_NONCE_SIZE, CRYPTO_PUBLIC_KEY_SIZE); - - std::uint8_t *req_enc = req + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE; - std::size_t req_enc_len = req_len - (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE); - - std::vector req_plain(req_enc_len - CRYPTO_MAC_SIZE); - int plen = decrypt_data( - mem, alice_pk, bob_sk, req_nonce, req_enc, req_enc_len, req_plain.data()); - - if (plen <= 0) - return; - - std::size_t sendback_offset = 32 + 32 + 32; - if (static_cast(plen) < sendback_offset + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH) - return; - std::uint8_t *sendback = req_plain.data() + sendback_offset; - std::size_t sendback_len = ONION_ANNOUNCE_SENDBACK_DATA_LENGTH; - - std::vector resp; - resp.push_back(NET_PACKET_ANNOUNCE_RESPONSE); - resp.insert(resp.end(), sendback, sendback + sendback_len); - - std::uint8_t resp_nonce[CRYPTO_NONCE_SIZE]; - random_nonce(alice.get_random(), resp_nonce); - resp.insert(resp.end(), resp_nonce, resp_nonce + CRYPTO_NONCE_SIZE); - - // PAYLOAD SIZE 33 (1 + 32) - // This is exactly what triggers the missing byte read for nodes_count - std::vector payload(33, 0); - - std::vector ciphertext(payload.size() + CRYPTO_MAC_SIZE); - encrypt_data( - mem, alice_pk, bob_sk, resp_nonce, payload.data(), payload.size(), ciphertext.data()); - - resp.insert(resp.end(), ciphertext.begin(), ciphertext.end()); - - bob_socket->sendto(resp.data(), resp.size(), &from); - triggered = true; + InterceptAndRespondMaliciously( + data, from, triggered, bob_sk, mem, bob_socket, alice.get_random()); }); for (int i = 0; i < 200; ++i) { diff --git a/toxcore/os_network.c b/toxcore/os_network.c index 174e6889f..19ed8c978 100644 --- a/toxcore/os_network.c +++ b/toxcore/os_network.c @@ -273,13 +273,13 @@ static const char *inet_ntop6(const struct in6_addr *_Nonnull addr, char *_Nonnu return buf; } -static int inet_pton4(const char *_Nonnull addrString, struct in_addr *_Nonnull addrbuf) +static int inet_pton4(const char *_Nonnull addr_string, struct in_addr *_Nonnull addrbuf) { struct sockaddr_in saddr = {0}; INT len = sizeof(saddr); - if (WSAStringToAddressA((LPSTR)addrString, AF_INET, nullptr, (LPSOCKADDR)&saddr, &len)) { + if (WSAStringToAddressA((LPSTR)addr_string, AF_INET, nullptr, (LPSOCKADDR)&saddr, &len)) { return 0; } @@ -288,13 +288,13 @@ static int inet_pton4(const char *_Nonnull addrString, struct in_addr *_Nonnull return 1; } -static int inet_pton6(const char *_Nonnull addrString, struct in6_addr *_Nonnull addrbuf) +static int inet_pton6(const char *_Nonnull addr_string, struct in6_addr *_Nonnull addrbuf) { struct sockaddr_in6 saddr = {0}; INT len = sizeof(saddr); - if (WSAStringToAddressA((LPSTR)addrString, AF_INET6, nullptr, (LPSOCKADDR)&saddr, &len)) { + if (WSAStringToAddressA((LPSTR)addr_string, AF_INET6, nullptr, (LPSOCKADDR)&saddr, &len)) { return 0; } diff --git a/toxcore/tox.c b/toxcore/tox.c index 95ac27e79..66ce571cd 100644 --- a/toxcore/tox.c +++ b/toxcore/tox.c @@ -32,6 +32,7 @@ #include "net_crypto.h" #include "network.h" #include "onion_client.h" +#include "os_event.h" #include "state.h" #include "tox_log_level.h" #include "tox_options.h" @@ -778,10 +779,26 @@ static Tox *tox_new_system(const struct Tox_Options *_Nullable options, Tox_Err_ } tox->sys = *sys; + tox->iterate_timeout_ms = tox_options_get_experimental_iterate_timeout_ms(opts); + + if (sys->ev != nullptr) { + tox->ev = sys->ev; + } else { + tox->ev = os_event_new(mem, tox->log); + } + + if (tox->ev == nullptr) { + SET_ERROR_PARAMETER(error, TOX_ERR_NEW_MALLOC); + logger_kill(tox->log); + mem_delete(mem, tox); + tox_options_free(default_options); + return nullptr; + } if (m_options.proxy_info.proxy_type != TCP_PROXY_NONE) { if (tox_options_get_proxy_port(opts) == 0) { SET_ERROR_PARAMETER(error, TOX_ERR_NEW_PROXY_BAD_PORT); + ev_kill(tox->ev); logger_kill(tox->log); mem_delete(mem, tox); tox_options_free(default_options); @@ -801,6 +818,7 @@ static Tox *tox_new_system(const struct Tox_Options *_Nullable options, Tox_Err_ || !addr_resolve_or_parse_ip(ns, mem, proxy_host, &m_options.proxy_info.ip_port.ip, nullptr, dns_enabled)) { SET_ERROR_PARAMETER(error, TOX_ERR_NEW_PROXY_BAD_HOST); // TODO(irungentoo): TOX_ERR_NEW_PROXY_NOT_FOUND if domain. + ev_kill(tox->ev); logger_kill(tox->log); mem_delete(mem, tox); tox_options_free(default_options); @@ -814,6 +832,7 @@ static Tox *tox_new_system(const struct Tox_Options *_Nullable options, Tox_Err_ if (temp_mono_time == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_NEW_MALLOC); + ev_kill(tox->ev); logger_kill(tox->log); mem_delete(mem, tox); tox_options_free(default_options); @@ -827,6 +846,7 @@ static Tox *tox_new_system(const struct Tox_Options *_Nullable options, Tox_Err_ if (mutex == nullptr) { SET_ERROR_PARAMETER(error, TOX_ERR_NEW_MALLOC); mono_time_free(mem, tox->mono_time); + ev_kill(tox->ev); logger_kill(tox->log); mem_delete(mem, tox); tox_options_free(default_options); @@ -840,6 +860,7 @@ static Tox *tox_new_system(const struct Tox_Options *_Nullable options, Tox_Err_ tox->mutex = nullptr; } + m_options.ev = tox->ev; tox_lock(tox); Messenger_Error m_error; @@ -867,6 +888,7 @@ static Tox *tox_new_system(const struct Tox_Options *_Nullable options, Tox_Err_ } mem_delete(mem, tox->mutex); + ev_kill(tox->ev); logger_kill(tox->log); mem_delete(mem, tox); tox_options_free(default_options); @@ -887,6 +909,7 @@ static Tox *tox_new_system(const struct Tox_Options *_Nullable options, Tox_Err_ } mem_delete(mem, tox->mutex); + ev_kill(tox->ev); logger_kill(tox->log); mem_delete(mem, tox); @@ -908,6 +931,7 @@ static Tox *tox_new_system(const struct Tox_Options *_Nullable options, Tox_Err_ } mem_delete(mem, tox->mutex); + ev_kill(tox->ev); logger_kill(tox->log); mem_delete(mem, tox); @@ -1012,6 +1036,7 @@ void tox_kill(Tox *tox) kill_groupchats(tox->m->conferences_object); kill_messenger(tox->m); logger_kill(tox->log); + ev_kill(tox->ev); mono_time_free(tox->sys.mem, tox->mono_time); tox_unlock(tox); @@ -1089,15 +1114,21 @@ static int32_t resolve_bootstrap_node(Tox *_Nullable tox, const char *_Nullable } const int32_t count = net_getipport(tox->sys.ns, tox->sys.mem, host, root, TOX_SOCK_DGRAM, tox->m->options.dns_enabled); + IP_Port *ips = *root; + + if (count > 0) { + Ip_Ntoa buf; + LOGGER_DEBUG(tox->m->log, "net_getipport resolved '%s' to %s (count=%d)", host, net_ip_ntoa(&ips[0].ip, &buf), count); + } if (count < 1) { LOGGER_DEBUG(tox->m->log, "could not resolve bootstrap node '%s'", host); - net_freeipport(tox->sys.mem, *root); + net_freeipport(tox->sys.mem, ips); SET_ERROR_PARAMETER(error, TOX_ERR_BOOTSTRAP_BAD_HOST); return -1; } - assert(*root != nullptr); + assert(ips != nullptr); return count; } @@ -1236,6 +1267,16 @@ void tox_iterate(Tox *tox, void *user_data) assert(tox != nullptr); tox_lock(tox); + if (tox->iterate_timeout_ms != 0) { + Ev_Result results[32]; + const int32_t n = ev_run(tox->ev, results, 32, tox->iterate_timeout_ms); + + if (n > 0) { + /* Events occurred, but do_messenger will poll all sockets anyway. */ + // TODO(iphydf): Optimize do_messenger to only process ready sockets. + } + } + mono_time_update(tox->mono_time); struct Tox_Userdata tox_data = { tox, user_data }; diff --git a/toxcore/tox_options.c b/toxcore/tox_options.c index 58c7b25d2..31285e3bd 100644 --- a/toxcore/tox_options.c +++ b/toxcore/tox_options.c @@ -199,6 +199,16 @@ void tox_options_set_experimental_owned_data( options->experimental_owned_data = experimental_owned_data; } +int32_t tox_options_get_experimental_iterate_timeout_ms(const Tox_Options *options) +{ + return options->experimental_iterate_timeout_ms; +} + +void tox_options_set_experimental_iterate_timeout_ms(Tox_Options *options, int32_t experimental_iterate_timeout_ms) +{ + options->experimental_iterate_timeout_ms = experimental_iterate_timeout_ms; +} + const uint8_t *tox_options_get_savedata_data(const Tox_Options *options) { return options->savedata_data; @@ -257,6 +267,7 @@ void tox_options_default(Tox_Options *options) tox_options_set_experimental_groups_persistence(options, false); tox_options_set_experimental_disable_dns(options, false); tox_options_set_experimental_owned_data(options, false); + tox_options_set_experimental_iterate_timeout_ms(options, 0); } } diff --git a/toxcore/tox_options.h b/toxcore/tox_options.h index 13936c8be..85d68459d 100644 --- a/toxcore/tox_options.h +++ b/toxcore/tox_options.h @@ -311,6 +311,15 @@ struct Tox_Options { * @private */ char *owned_proxy_host; + + /** + * @brief Timeout in milliseconds for tox_iterate and tox_events_iterate. + * + * 0: Non-blocking (default). + * Positive: Wait up to this many milliseconds for events. + * -1: Wait indefinitely. + */ + int32_t experimental_iterate_timeout_ms; }; #endif /* TOX_HIDE_DEPRECATED */ @@ -398,6 +407,10 @@ bool tox_options_get_experimental_disable_dns(const Tox_Options *options); void tox_options_set_experimental_disable_dns(Tox_Options *options, bool experimental_disable_dns); +int32_t tox_options_get_experimental_iterate_timeout_ms(const Tox_Options *options); + +void tox_options_set_experimental_iterate_timeout_ms(Tox_Options *options, int32_t experimental_iterate_timeout_ms); + /** * @brief Initialises a Tox_Options object with the default options. * diff --git a/toxcore/tox_private.h b/toxcore/tox_private.h index d50e1bad3..b6e142e08 100644 --- a/toxcore/tox_private.h +++ b/toxcore/tox_private.h @@ -10,6 +10,7 @@ #include #include +#include "ev.h" #include "tox.h" #include "tox_attributes.h" #include "tox_options.h" @@ -30,6 +31,7 @@ typedef struct Tox_System { const Random *_Nullable rng; const Network *_Nullable ns; const Memory *_Nullable mem; + Ev *_Nullable ev; // _Owned } Tox_System; Tox_System tox_default_system(void); diff --git a/toxcore/tox_struct.h b/toxcore/tox_struct.h index 3a8e8b3f7..24c134b2a 100644 --- a/toxcore/tox_struct.h +++ b/toxcore/tox_struct.h @@ -8,6 +8,7 @@ #include +#include "ev.h" #include "mono_time.h" #include "tox.h" #include "tox_options.h" // tox_log_cb @@ -24,6 +25,9 @@ struct Tox { Tox_System sys; pthread_mutex_t *_Nullable mutex; + Ev *_Nullable ev; // _Owned + int32_t iterate_timeout_ms; + tox_log_cb *_Nullable log_callback; tox_self_connection_status_cb *_Nullable self_connection_status_callback; tox_friend_name_cb *_Nullable friend_name_callback;