#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; }