|
|
|
|
@ -35,6 +35,8 @@
|
|
|
|
|
#include "../lib/debug_config.h" |
|
|
|
|
#include "../lib/mem.h" |
|
|
|
|
#include "../lib/platform_compat.h" |
|
|
|
|
#include "test_net.h" |
|
|
|
|
#include <errno.h> |
|
|
|
|
|
|
|
|
|
#include <sqlite3.h> |
|
|
|
|
#define OPENSSL_API_COMPAT 0x10100000L |
|
|
|
|
@ -72,7 +74,6 @@ struct e2e_shared {
|
|
|
|
|
uint64_t owner; |
|
|
|
|
uint64_t nid[3]; |
|
|
|
|
uint8_t x_pub[3][32], x_priv[3][32], ed_pub[3][32]; |
|
|
|
|
int port[3]; |
|
|
|
|
int degraded; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
@ -149,7 +150,7 @@ static int wf(const char* p, const char* f, ...) {
|
|
|
|
|
|
|
|
|
|
/* ── генерация данных сценария + конфигов (родитель) ── */ |
|
|
|
|
|
|
|
|
|
static void fill_shared(int base_port, int degraded) { |
|
|
|
|
static void fill_shared(int degraded) { |
|
|
|
|
memset(&g_sh, 0, sizeof(g_sh)); |
|
|
|
|
g_sh.degraded = degraded; |
|
|
|
|
snprintf(g_sh.ch_id, sizeof(g_sh.ch_id), "4242424242424242"); |
|
|
|
|
@ -159,7 +160,6 @@ static void fill_shared(int base_port, int degraded) {
|
|
|
|
|
uint8_t ed_priv[32]; |
|
|
|
|
x25519_to_ed25519(g_sh.x_priv[i], ed_priv, g_sh.ed_pub[i]); |
|
|
|
|
g_sh.nid[i] = sc_derive_node_id_from_pubkey(g_sh.x_pub[i]); |
|
|
|
|
g_sh.port[i] = base_port + i * 1000; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
gen_x25519(g_sh.ch_x_pub, g_sh.ch_x_priv); |
|
|
|
|
@ -189,15 +189,51 @@ static void write_configs(const char* dir) {
|
|
|
|
|
wf(path, |
|
|
|
|
"[global]\ntun_ip=10.98.%d.1/24\ntun_ifname=tun%d0\ndb_path=%s/db%s\n" |
|
|
|
|
"my_private_key=%s\nmy_public_key=%s\n" |
|
|
|
|
"[server: s1]\naddr=127.0.0.1:%d\ntype=public\n" |
|
|
|
|
"[server: s1]\naddr=127.0.0.1:0\ntype=public\n" |
|
|
|
|
"[chatserver]\nstorage_autoload=0\n[allowed_keys]\nallow_all=1\n", |
|
|
|
|
i, i, dir, i == IDX_A ? "a" : i == IDX_C ? "c" : "j", |
|
|
|
|
priv, pub, g_sh.port[i]); |
|
|
|
|
priv, pub); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* ── poll-хелперы (дети) ── */ |
|
|
|
|
|
|
|
|
|
// Родитель не выбирает порты заранее: A публикует endpoint своего уже открытого слушателя.
|
|
|
|
|
static int publish_bootstrap_port(struct UTUN_INSTANCE* inst, const char* dir) { |
|
|
|
|
char path[768], temporary[768]; |
|
|
|
|
snprintf(path, sizeof(path), "%s/a.port", dir); snprintf(temporary, sizeof(temporary), "%s/a.port.tmp", dir); |
|
|
|
|
uint16_t port = ss_get_port(&test_etcp_bound_socket(inst, "s1")->local_addr); |
|
|
|
|
FILE* f = fopen(temporary, "w"); |
|
|
|
|
if (!f) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "JOIN fixture: endpoint file open failed: %s", strerror(errno)); return 0; } |
|
|
|
|
int ok = fprintf(f, "%u\n", port) > 0; |
|
|
|
|
if (fclose(f)) ok = 0; |
|
|
|
|
if (!ok || rename(temporary, path)) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "JOIN fixture: endpoint publication failed: %s", strerror(errno)); return 0; |
|
|
|
|
} |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "JOIN fixture: bootstrap listener port=%u published", port); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static uint16_t wait_bootstrap_port(struct UTUN_INSTANCE* inst, const char* dir) { |
|
|
|
|
char path[768]; snprintf(path, sizeof(path), "%s/a.port", dir); |
|
|
|
|
uint64_t deadline = get_time_tb() + 50000; |
|
|
|
|
while (get_time_tb() < deadline) { |
|
|
|
|
FILE* f = fopen(path, "r"); |
|
|
|
|
if (f) { |
|
|
|
|
unsigned port = 0; int count = fscanf(f, "%u", &port), error = fclose(f); |
|
|
|
|
if (count != 1 || !port || port > UINT16_MAX || error) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "JOIN fixture: invalid bootstrap endpoint count=%d port=%u error=%d", count, port, error); |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "JOIN fixture: bootstrap listener port=%u received", port); |
|
|
|
|
return (uint16_t)port; |
|
|
|
|
} |
|
|
|
|
if (errno != ENOENT) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "JOIN fixture: endpoint read failed: %s", strerror(errno)); return 0; } |
|
|
|
|
poll_checked(inst); |
|
|
|
|
} |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "JOIN fixture: bootstrap endpoint publication timeout"); return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int wait_conn(struct UTUN_INSTANCE* inst, uint64_t nid, int max_iter) { |
|
|
|
|
for (int a = 0; a < max_iter; a++) { |
|
|
|
|
struct ETCP_CONN* c = instance_find_conn(inst, nid); |
|
|
|
|
@ -375,9 +411,11 @@ static int check_gui_superseded(struct UTUN_INSTANCE* inst) {
|
|
|
|
|
|
|
|
|
|
/* Реальный JSON control socket: два параллельных invite, затем отмена при quit. */ |
|
|
|
|
static int check_headless_invites(struct UTUN_INSTANCE* inst) { |
|
|
|
|
int port = g_sh.port[IDX_A] + 2100, ok = 0; |
|
|
|
|
int ok = 0; |
|
|
|
|
socket_t fd = SOCKET_INVALID; |
|
|
|
|
if (chat_headless_control_init(inst->ua, inst, "127.0.0.1", port) != 0) return 0; |
|
|
|
|
if (chat_headless_control_init(inst->ua, inst, "127.0.0.1", 0) != 0) return 0; |
|
|
|
|
uint16_t port = chat_headless_control_get_port(inst); |
|
|
|
|
if (!port) return 0; |
|
|
|
|
fd = socket(AF_INET, SOCK_STREAM, 0); |
|
|
|
|
if (fd == SOCKET_INVALID) goto out; |
|
|
|
|
struct sockaddr_in addr = {0}; addr.sin_family = AF_INET; addr.sin_port = htons((uint16_t)port); |
|
|
|
|
@ -488,6 +526,7 @@ static int child_main(const char* role, const char* dir, int invalid_key) {
|
|
|
|
|
utun_instance_destroy(inst); uasync_destroy(ua, 0); return 1; |
|
|
|
|
} |
|
|
|
|
chat_event_set_handler(inst, join_event_handler); |
|
|
|
|
if (!strcmp(role, "a") && !publish_bootstrap_port(inst, dir)) return 1; |
|
|
|
|
if (strcmp(role, "c") == 0) { |
|
|
|
|
scenario_dir = dir; |
|
|
|
|
connection_join_recv = inst->router_bindings.callbacks[ETCP_RT_ID_JOIN]; |
|
|
|
|
@ -497,7 +536,9 @@ static int child_main(const char* role, const char* dir, int invalid_key) {
|
|
|
|
|
/* Fixture bootstrap between the existing members uses core NCD, without UTUN. */ |
|
|
|
|
struct NODE_CONN_DIRECT* bootstrap = NULL; |
|
|
|
|
if (!strcmp(role, "c") && !g_sh.degraded) { |
|
|
|
|
struct TOPO_ADDR4 address = { .addr = {127, 0, 0, 1}, .port = g_sh.port[IDX_A], .protocol = TOPO_PROTO_UDP }; |
|
|
|
|
uint16_t port = wait_bootstrap_port(inst, dir); |
|
|
|
|
if (!port) return 1; |
|
|
|
|
struct TOPO_ADDR4 address = { .addr = {127, 0, 0, 1}, .port = port, .protocol = TOPO_PROTO_UDP }; |
|
|
|
|
struct TOPO_NODE node = { .node_id = g_sh.nid[IDX_A], .v4_addrs = &address }; |
|
|
|
|
memcpy(node.public_key, g_sh.x_pub[IDX_A], 32); |
|
|
|
|
if (node_conn_direct_open_node(inst, node.node_id, NULL, NULL, &bootstrap, &node, NULL) == NCD_ERR) return 1; |
|
|
|
|
@ -649,7 +690,7 @@ static int run_scenario(const char* name, int degraded, int invalid_key) {
|
|
|
|
|
char dir[512]; snprintf(dir, sizeof(dir), "/tmp/utun_e2e_%s_XXXXXX", name); |
|
|
|
|
(void)!mkdtemp(dir); |
|
|
|
|
|
|
|
|
|
fill_shared(56000 + (getpid() % 2000), degraded); |
|
|
|
|
fill_shared(degraded); |
|
|
|
|
write_configs(dir); |
|
|
|
|
|
|
|
|
|
const char* roles[4]; |
|
|
|
|
|