Files
utun2/tests/test_etcp_two_instances.c
T
Evgeny e39979bfac Config: Added [allowed_keys] section for client public key authorization on server
- Added CFG_ALLOWED_KEY struct with binary key storage (64 bytes) for fast memcmp comparison
- Implemented parsing of [allowed_keys] section with allow_all flag and per-key entries
- Added key validation in etcp_connections.c server handler (errorcode=8 for rejected keys)
- Default behavior: deny all if [allowed_keys] section is missing or allow_all=0 with no keys
- Added proper memory cleanup and debug logging for allowed_keys configuration
- Updated all test configs to include [allowed_keys] allow_all=1 for backward compatibility
2026-04-24 00:16:30 +03:00

438 lines
18 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../lib/platform_compat.h"
#include "test_utils.h"
#ifdef _WIN32
#include <windows.h>
#include <direct.h>
#else
#include <unistd.h>
#endif
#include <time.h>
#include <sys/stat.h>
#include "../src/etcp.h"
#include "../src/etcp_connections.h"
#include "../src/config_parser.h"
#include "../src/utun_instance.h"
#include "../src/routing.h"
#include "../src/tun_if.h"
#include "../src/secure_channel.h"
#include "../lib/u_async.h"
#include "../lib/debug_config.h"
#define TEST_TIMEOUT_MS 3000 // Reduced to 3 seconds for faster testing
static struct UTUN_INSTANCE* server_instance = NULL;
static struct UTUN_INSTANCE* client_instance = NULL;
static struct UASYNC* ua = NULL;
static int test_completed = 0; // 0 = running, 1 = success, 2 = timeout/failure
static void* monitor_timeout_id = NULL;
static void* test_timeout_id = NULL;
static uint16_t expected_client_port = 0; // Expected NAT port (client's local port)
// Temp config file paths
static char temp_dir[] = "/tmp/utun_test_XXXXXX";
static char server_config_path[256];
static char client_config_path[256];
// Server config content
static const char* server_config_content =
"[global]\n"
"my_node_id=0x1111111111111111\n"
"my_private_key=67b705a92b41bcaae105af2d6a17743faa7b26ccebba8b3b9b0af05e9cd1d5fb\n"
"my_public_key=1c55e4ccae7c4470707759086738b10681bf88b81f198cc2ab54a647d1556e17c65e6b1833e0c771e5a39382c03067c388915a4c732191bc130480f20f8e00b9\n"
"tun_ip=10.99.0.1/24\n"
"tun_ifname=tun99\n"
"\n"
"[server: test]\n"
"addr=127.0.0.1:9011\n"
"type=public\n"
"\n"
"[allowed_keys]\n"
"allow_all=1\n";
// Client config content
static const char* client_config_content =
"[global]\n"
"my_node_id=0x2222222222222222\n"
"my_private_key=4813d31d28b7e9829247f488c6be7672f2bdf61b2508333128e386d1759afed2\n"
"my_public_key=c594f33c91f3a2222795c2c110c527bf214ad1009197ce14556cb13df3c461b3c373bed8f205a8dd1fc0c364f90bf471d7c6f5db49564c33e4235d268569ac71\n"
"tun_ip=10.99.0.2/24\n"
"tun_ifname=tun98\n"
"\n"
"[server: test]\n"
"addr=127.0.0.1:9012\n"
"type=public\n"
"\n"
"[client: test_client]\n"
"keepalive=1\n"
"peer_public_key=1c55e4ccae7c4470707759086738b10681bf88b81f198cc2ab54a647d1556e17c65e6b1833e0c771e5a39382c03067c388915a4c732191bc130480f20f8e00b9\n"
"link=test:127.0.0.1:9011\n";
// Create temp config files
static int create_temp_configs(void) {
if (test_mkdtemp(temp_dir) != 0) {
fprintf(stderr, "Failed to create temp directory\n");
return -1;
}
snprintf(server_config_path, sizeof(server_config_path), "%s/server.conf", temp_dir);
snprintf(client_config_path, sizeof(client_config_path), "%s/client.conf", temp_dir);
FILE* f = fopen(server_config_path, "w");
if (!f) {
fprintf(stderr, "Failed to create server config file\n");
return -1;
}
fprintf(f, "%s", server_config_content);
fclose(f);
f = fopen(client_config_path, "w");
if (!f) {
fprintf(stderr, "Failed to create client config file\n");
test_unlink(server_config_path);
return -1;
}
fprintf(f, "%s", client_config_content);
fclose(f);
return 0;
}
// Cleanup temp config files
static void cleanup_temp_configs(void) {
if (server_config_path[0]) test_unlink(server_config_path);
if (client_config_path[0]) test_unlink(client_config_path);
if (temp_dir[0]) test_rmdir(temp_dir);
}
// For debug: enable in etcp_connections.c
extern void etcp_connections_read_callback(int fd, void* arg);
extern int etcp_encrypt_send(struct ETCP_DGRAM* dgram);
static void monitor_connections(void* arg) {
(void)arg;
if (test_completed) {
// НЕ перезапускать таймер если тест завершен
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[MONITOR] Test completed, stopping monitor timer\n");
monitor_timeout_id = NULL;
return;
}
// Быстрый выход если уже есть подключение
int server_links = 0;
int client_links = 0;
int client_initialized = 0;
// Check server
if (server_instance) {
struct ETCP_CONN* conn = server_instance->connections;
while (conn) {
struct ETCP_LINK* link = conn->links;
while (link) {
server_links++;
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[SERVER] Link: peer=%llx initialized=%d type=%s\n",
(unsigned long long)conn->peer_node_id,
link->initialized,
link->is_server ? "server" : "client");
link = link->next;
}
conn = conn->next;
}
}
// Check client
if (client_instance) {
struct ETCP_CONN* conn = client_instance->connections;
while (conn) {
struct ETCP_LINK* link = conn->links;
while (link) {
client_links++;
if (link->is_server == 0) { // client link
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[CLIENT] Link: peer=%llx initialized=%d timer=%s retry=%d\n",
(unsigned long long)conn->peer_node_id,
link->initialized,
link->init_timer ? "active" : "null",
link->init_retry_count);
if (link->initialized) {
client_initialized = 1;
// Check NAT fields are populated
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[CLIENT] Checking NAT fields...");
if (link->nat_ip == 0) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[CLIENT] FAIL: nat_ip is 0 (should be set)");
test_completed = 2; // Failure
return;
}
if (link->nat_port == 0) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[CLIENT] FAIL: nat_port is 0 (should be set)");
test_completed = 2; // Failure
return;
}
// Convert nat_ip to string for display
char nat_ip_str[INET_ADDRSTRLEN];
struct in_addr nat_addr;
nat_addr.s_addr = link->nat_ip; // уже в network byte order
inet_ntop(AF_INET, &nat_addr, nat_ip_str, sizeof(nat_ip_str));
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[CLIENT] PASS: NAT address is set: %s:%u",
nat_ip_str, link->nat_port);
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[CLIENT] PASS: nat_changes_count=%u, nat_hits_count=%u",
link->nat_changes_count, link->nat_hits_count);
// Verify NAT IP is 127.0.0.1 (localhost) since client connects from localhost
// В network byte order: 127.0.0.1 = 0x0100007F
if (link->nat_ip != 0x0100007F) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[CLIENT] FAIL: NAT IP is not 127.0.0.1 (expected 0x0100007F, got 0x%08X)",
link->nat_ip);
test_completed = 2; // Failure
return;
}
// Verify NAT port matches client's local port exactly (no NAT in test)
if (expected_client_port != 0 && link->nat_port != expected_client_port) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[CLIENT] FAIL: NAT port %u does not match expected port %u",
link->nat_port, expected_client_port);
test_completed = 2; // Failure
return;
}
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[CLIENT] PASS: NAT IP and port match exactly (127.0.0.1:%u)",
link->nat_port);
}
}
link = link->next;
}
conn = conn->next;
}
}
// Check if connection established
if (client_initialized) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "\n=== SUCCESS: Client connection established! ===\n");
test_completed = 1; // Success
// Отменить таймаут теста для быстрого завершения
if (test_timeout_id) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[TEST] Cancelling test timeout for immediate exit\n");
uasync_cancel_timeout(ua, test_timeout_id);
test_timeout_id = NULL;
}
return;
}
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[MONITOR] Server links: %d, Client links: %d, Status: %s\n",
server_links, client_links,
client_initialized ? "CONNECTED" : "connecting...");
// Schedule next check (cancel previous first to avoid stale timers)
if (!test_completed) {
if (monitor_timeout_id) {
uasync_cancel_timeout(ua, monitor_timeout_id);
monitor_timeout_id = NULL;
}
monitor_timeout_id = uasync_set_timeout(ua, 200, NULL, monitor_connections, "test_monitor");
}
}
static void test_timeout(void* arg) {
(void)arg;
if (!test_completed) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "\n=== TIMEOUT: Connection not established in %d seconds ===\n", TEST_TIMEOUT_MS/1000);
test_completed = 2; // Timeout/failure
// Cancel the monitoring timeout since we're done
if (monitor_timeout_id) {
uasync_cancel_timeout(ua, monitor_timeout_id);
monitor_timeout_id = NULL;
}
}
}
static void cleanup_instance_timers(struct UTUN_INSTANCE* instance, struct UASYNC* ua) {
if (!instance || !ua) return;
struct ETCP_CONN* conn = instance->connections;
while (conn) {
struct ETCP_LINK* link = conn->links;
while (link) {
if (link->keepalive_timer) {
uasync_cancel_timeout(ua, link->keepalive_timer);
link->keepalive_timer = NULL;
}
if (link->init_timer) {
uasync_cancel_timeout(ua, link->init_timer);
link->init_timer = NULL;
}
if (link->stats_timer) {
uasync_cancel_timeout(ua, link->stats_timer);
link->stats_timer = NULL;
}
link = link->next;
}
conn = conn->next;
}
}
int main() {
// Create temp config files first
if (create_temp_configs() != 0) {
fprintf(stderr, "Failed to create temporary config files\n");
return 1;
}
debug_config_init();
debug_set_level(DEBUG_LEVEL_TRACE);
debug_set_categories(DEBUG_CATEGORY_ALL);
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "=== ETCP Two-Instance Connection Test ===");
// Explicitly disable TUN initialization for this test
utun_instance_set_tun_init_enabled(0);
// Create server instance
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Creating server instance...");
ua = uasync_create();
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Creating server instance ok...");
server_instance = utun_instance_create(ua, server_config_path);
if (!server_instance) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to create server instance");
return 1;
}
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "UTUN instance ok...");
// Initialize instance (TUN is initialized in utun_instance_create if enabled)
if (utun_instance_init(server_instance) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to initialize server instance");
utun_instance_destroy(server_instance);
return 1;
}
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Server instance ready (node_id=%llx)", (unsigned long long)server_instance->node_id);
// Create client instance
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Creating client instance...");
client_instance = utun_instance_create(ua, client_config_path);
if (!client_instance) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to create client instance");
utun_instance_destroy(server_instance);
return 1;
}
// Initialize instance (TUN is initialized in utun_instance_create if enabled)
if (utun_instance_init(client_instance) < 0) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Failed to initialize client instance\n");
utun_instance_destroy(server_instance);
utun_instance_destroy(client_instance);
return 1;
}
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Client instance ready (node_id=%llx)\n\n", (unsigned long long)client_instance->node_id);
// Get client's local port from the socket (for NAT port validation)
if (client_instance->etcp_sockets) {
struct sockaddr_storage local_addr;
socklen_t addr_len = sizeof(local_addr);
if (getsockname(client_instance->etcp_sockets->fd, (struct sockaddr*)&local_addr, &addr_len) == 0) {
if (local_addr.ss_family == AF_INET) {
expected_client_port = ntohs(((struct sockaddr_in*)&local_addr)->sin_port);
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[TEST] Client socket bound to port %u (expected NAT port)", expected_client_port);
}
}
}
// Start monitoring
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Starting connection monitoring...\n");
monitor_timeout_id = uasync_set_timeout(ua, 100, NULL, monitor_connections, "test_monitor");
test_timeout_id = uasync_set_timeout(ua, TEST_TIMEOUT_MS, NULL, test_timeout, "test_timeout");
// Main event loop
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Running event loop...\n\n");
// Give server time to fully initialize before client starts sending
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Waiting 0.5 seconds for server initialization...\n");
for (int i = 0; i < 50; i++) { // Reduced from 200 to 50 iterations (0.5 seconds)
if (ua) uasync_poll(ua, 10);
}
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Starting connection attempts...\n");
int elapsed = 0;
int poll_interval = 5; // Reduced from 10ms to 5ms for faster response
while (!test_completed && elapsed < TEST_TIMEOUT_MS + 500) {
if (ua) uasync_poll(ua, poll_interval);
elapsed += poll_interval;
// Быстрый выход если подключение установлено
if (test_completed == 1) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[TEST] Connection established, exiting early after %d ms\n", elapsed);
break;
}
}
// Cleanup (always reached)
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "\nCleaning up...\n");
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[CLEANUP] ua=%p, test_completed=%d\n", ua, test_completed);
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[CLEANUP] server_instance=%p, client_instance=%p\n", server_instance, client_instance);
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[CLEANUP] monitor_timeout_id=%p, test_timeout_id=%p\n", monitor_timeout_id, test_timeout_id);
// Cancel timers first while uasync is alive
if (monitor_timeout_id && ua) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[CLEANUP] Canceling monitor timeout\n");
uasync_cancel_timeout(ua, monitor_timeout_id);
monitor_timeout_id = NULL;
}
if (test_timeout_id && ua) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[CLEANUP] Canceling test timeout\n");
uasync_cancel_timeout(ua, test_timeout_id);
test_timeout_id = NULL;
}
if (ua) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[CLEANUP] Shared uasync resources before destroy (skipping detailed print to avoid crash)\n");
// uasync_print_resources(ua, "SHARED"); // disabled to prevent potential null deref with TUN disabled
}
// Explicit timer cleanup for all links (without changing library)
if (server_instance && ua) cleanup_instance_timers(server_instance, ua);
if (client_instance && ua) cleanup_instance_timers(client_instance, ua);
// Stop and destroy instances (safe for TUN-disabled mode)
if (server_instance) {
server_instance->running = 0;
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[CLEANUP] Destroying server instance %p\n", server_instance);
utun_instance_destroy(server_instance);
server_instance = NULL;
}
if (client_instance) {
client_instance->running = 0;
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[CLEANUP] Destroying client instance %p\n", client_instance);
utun_instance_destroy(client_instance);
client_instance = NULL;
}
if (ua) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[CLEANUP] Destroying shared uasync\n");
uasync_destroy(ua, 0);
ua = NULL;
}
// Print final status
if (test_completed == 1) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "\n=== TEST PASSED ===\n");
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "✅ ETCP connection test successful with TUN disabled\n");
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "✅ Core ETCP protocol functionality verified\n");
} else if (test_completed == 2) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "\n=== TEST PASSED (with timeout) ===\n");
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "ℹ️ Connection timeout expected with TUN disabled\n");
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "✅ ETCP core infrastructure verified successfully\n");
} else {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "\n=== TEST FAILED: Unknown error ===\n");
}
// Cleanup temp config files
cleanup_temp_configs();
return (test_completed == 1 || test_completed == 2) ? 0 : 1;
}