From 20cb29c2f8c5c87bc590896854a20b1473ceb358 Mon Sep 17 00:00:00 2001 From: "info@mode42.com" Date: Sat, 8 Aug 2026 03:54:55 +0000 Subject: #2 --- tests/test_mains_proxy_e2e.c | 360 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 360 insertions(+) create mode 100644 tests/test_mains_proxy_e2e.c (limited to 'tests/test_mains_proxy_e2e.c') diff --git a/tests/test_mains_proxy_e2e.c b/tests/test_mains_proxy_e2e.c new file mode 100644 index 0000000..bb93f0c --- /dev/null +++ b/tests/test_mains_proxy_e2e.c @@ -0,0 +1,360 @@ +#include "hybbx/mains_proxy.h" +#include "hybbx/circuit_tcp.h" +#include "hybbx/util.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static unsigned g_failures; +static pid_t g_pid_a = -1; +static pid_t g_pid_b = -1; + +static void check_int(const char *label, int got, int want) +{ + if (got != want) { + fprintf(stderr, "FAIL %s: got %d want %d\n", label, got, want); + g_failures++; + } +} + +static int write_file(const char *path, const char *text) +{ + FILE *fp = fopen(path, "w"); + if (fp == NULL) { + return -1; + } + if (fputs(text, fp) == EOF) { + fclose(fp); + return -1; + } + fclose(fp); + return 0; +} + +static int mkdir_p(const char *path) +{ + char tmp[512]; + char *p; + size_t len; + + if (path == NULL || path[0] == '\0') { + return -1; + } + + hybbx_strlcpy(tmp, path, sizeof(tmp)); + len = strlen(tmp); + if (tmp[len - 1] == '/') { + tmp[len - 1] = '\0'; + } + + for (p = tmp + 1; *p != '\0'; p++) { + if (*p == '/') { + *p = '\0'; + (void)mkdir(tmp, 0755); + *p = '/'; + } + } + (void)mkdir(tmp, 0755); + return 0; +} + +static int copy_file(const char *src, const char *dst) +{ + char cmd[1024]; + snprintf(cmd, sizeof(cmd), "cp '%s' '%s'", src, dst); + return system(cmd); +} + +static int copy_dir(const char *src, const char *dst) +{ + char cmd[1024]; + snprintf(cmd, sizeof(cmd), "cp -r '%s' '%s'", src, dst); + return system(cmd); +} + +static void write_main_ini(const char *root, const char *name, + unsigned circuit_port, unsigned peer_port, + const char *local_link_id, + const char *remote_link_id, + unsigned websocket_port) +{ + char path[512]; + char text[2048]; + + snprintf(path, sizeof(path), "%s/data", root); + mkdir_p(path); + snprintf(path, sizeof(path), "%s/logs", root); + mkdir_p(path); + snprintf(path, sizeof(path), "%s/mail", root); + mkdir_p(path); + copy_file("/home/akb/Code2/10-PROJECTS/hyBBX/share/commands.yaml", root); + copy_file("/home/akb/Code2/10-PROJECTS/hyBBX/share/areas.yaml", root); + copy_dir("/home/akb/Code2/10-PROJECTS/hyBBX/text", root); + + snprintf(path, sizeof(path), "%s/hybbx.ini", root); + snprintf(text, sizeof(text), + "[service]\n" + "name = %s\n" + "login_announce = no\n" + "\n" + "[storage]\n" + "backend = flatfile\n" + "path = data\n" + "\n" + "[auth]\n" + "auto_login = yes\n" + "\n" + "[log]\n" + "enabled = yes\n" + "dir = logs\n" + "level = info\n" + "\n" + "[security]\n" + "enabled = yes\n" + "telnet = no\n" + "ssh = no\n" + "websocket = no\n" + "circuit = yes\n" + "\n" + "[mail]\n" + "enabled = yes\n" + "path = mail\n" + "\n" + "[networks]\n" + "telnet = no\n" + "ssh = no\n" + "websocket = no\n" + "ax25 = no\n" + "baycom = no\n" + "tmodem = no\n" + "ardop = no\n" + "crdop = no\n" + "circuit = yes\n" + "mains_proxy = yes\n" + "\n" + "[circuit]\n" + "enabled = yes\n" + "bind = 127.0.0.1\n" + "port = %u\n" + "link_auth = yes\n" + "link_password = secret\n" + "max_links = 8\n" + "balance = yes\n" + "\n" + "[transport.websocket]\n" + "bind = 127.0.0.1\n" + "port = %u\n" + "\n" + "[transport.mains_proxy1]\n" + "peer_id = peer\n" + "circuit_host = 127.0.0.1\n" + "circuit_port = %u\n" + "link_id = %s\n" + "link_password = secret\n" + "wire = circuit\n" + "duplex = full\n" + "\n" + "[transport.mains_proxy2]\n" + "peer_id = peer\n" + "link_id = %s\n" + "link_password = secret\n", + name, circuit_port, websocket_port, peer_port, + local_link_id, remote_link_id); + + if (write_file(path, text) != 0) { + fprintf(stderr, "FAIL cannot write %s\n", path); + g_failures++; + } +} + +static pid_t start_hybbxd(const char *binary, const char *root) +{ + pid_t pid = fork(); + if (pid < 0) { + return -1; + } + if (pid == 0) { + char config_path[512]; + int null_fd = open("/dev/null", O_RDONLY); + int log_fd; + char log_path[512]; + + snprintf(log_path, sizeof(log_path), "%s/logs/test-stderr.log", root); + log_fd = open(log_path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + + setsid(); + if (null_fd >= 0) { + dup2(null_fd, STDIN_FILENO); + close(null_fd); + } + if (log_fd >= 0) { + dup2(log_fd, STDOUT_FILENO); + dup2(log_fd, STDERR_FILENO); + close(log_fd); + } + + snprintf(config_path, sizeof(config_path), "%s/hybbx.ini", root); + setenv("HYBBX_ROOT", root, 1); + execl(binary, binary, "-c", config_path, "-f", (char *)NULL); + _exit(127); + } + return pid; +} + +static void stop_process(pid_t pid) +{ + if (pid > 0) { + kill(pid, SIGTERM); + waitpid(pid, NULL, 0); + } +} + +static void cleanup_dirs(const char *a, const char *b) +{ + char cmd[1024]; + snprintf(cmd, sizeof(cmd), "rm -rf '%s' '%s'", a, b); + (void)system(cmd); +} + +static int log_contains(const char *root, const char *needle) +{ + char path[512]; + char buf[4096]; + FILE *fp; + size_t n; + + snprintf(path, sizeof(path), "%s/logs/test-stderr.log", root); + fp = fopen(path, "r"); + if (fp == NULL) { + return 0; + } + + while ((n = fread(buf, 1, sizeof(buf) - 1, fp)) > 0) { + buf[n] = '\0'; + if (strstr(buf, needle) != NULL) { + fclose(fp); + return 1; + } + } + fclose(fp); + return 0; +} + +static int wait_for_port(unsigned port, unsigned timeout_ms) +{ + unsigned elapsed = 0; + int fd; + struct sockaddr_in addr; + + while (elapsed < timeout_ms) { + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) { + return -1; + } + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons((uint16_t)port); + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr); + + if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == 0) { + close(fd); + return 0; + } + close(fd); + usleep(100000); + elapsed += 100; + } + return -1; +} + +int main(int argc, char **argv) +{ + const char *binary = (argc > 1) ? argv[1] : "/tmp/hybbx-p01-build/src/hybbxd"; + char root_a[256]; + char root_b[256]; + int active; + unsigned elapsed; + + snprintf(root_a, sizeof(root_a), "/tmp/hybbx-e2e-a-%d", (int)getpid()); + snprintf(root_b, sizeof(root_b), "/tmp/hybbx-e2e-b-%d", (int)getpid()); + + cleanup_dirs(root_a, root_b); + + /* Main A connects outbound as main-a; Main B accepts id=main-a. */ + write_main_ini(root_a, "main-a", 17323, 17324, "main-a", "main-b", 14591); + /* Main B connects outbound as main-b; Main A accepts id=main-b. */ + write_main_ini(root_b, "main-b", 17324, 17323, "main-b", "main-a", 14592); + + g_pid_a = start_hybbxd(binary, root_a); + g_pid_b = start_hybbxd(binary, root_b); + + check_int("start main-a", g_pid_a > 0, 1); + check_int("start main-b", g_pid_b > 0, 1); + + if (g_pid_a <= 0 || g_pid_b <= 0) { + stop_process(g_pid_b); + cleanup_dirs(root_a, root_b); + return 1; + } + + /* Wait for both circuit hubs to accept connections. */ + check_int("port a ready", wait_for_port(17323, 5000), 0); + check_int("port b ready", wait_for_port(17324, 5000), 0); + + /* Wait for mains_proxy to establish outbound links on each Main. */ + { + int a_linked = 0; + int b_linked = 0; + elapsed = 0; + while (elapsed < 10000) { + a_linked = log_contains(root_a, "[mains_proxy] linked to peer"); + b_linked = log_contains(root_b, "[mains_proxy] linked to peer"); + if (a_linked && b_linked) { + break; + } + usleep(100000); + elapsed += 100; + } + check_int("main-a linked via mains_proxy", a_linked, 1); + check_int("main-b linked via mains_proxy", b_linked, 1); + } + + /* P0.2: verify both Mains accepted the peer link via HBX auth. */ + { + int a_has_main_b = log_contains(root_a, "link authenticated id=main-b"); + int b_has_main_a = log_contains(root_b, "link authenticated id=main-a"); + check_int("main-a sees main-b authenticated", a_has_main_b, 1); + check_int("main-b sees main-a authenticated", b_has_main_a, 1); + } + + + stop_process(g_pid_a); + stop_process(g_pid_b); + + if (g_failures == 0) { + cleanup_dirs(root_a, root_b); + } else { + fprintf(stderr, " left dirs: %s %s\n", root_a, root_b); + } + + if (g_failures != 0) { + fprintf(stderr, "%u failure(s)\n", g_failures); + return 1; + } + + puts("test_mains_proxy_e2e: ok"); + return 0; +} -- cgit v1.3.1