summaryrefslogtreecommitdiff
path: root/tests/test_mains_proxy_e2e.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_mains_proxy_e2e.c')
-rw-r--r--tests/test_mains_proxy_e2e.c360
1 files changed, 360 insertions, 0 deletions
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 <arpa/inet.h>
+#include <sys/socket.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <dirent.h>
+
+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;
+}
git clone -b <branch> https://cgit.mode42.com/<repo>.git
git clone -b <branch> git://cgit.mode42.com/<repo>.git

info@mode42.com