#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #define SOCKET_NAME "nsigner_test_run" #define MAX_MSG_SIZE 65536 static int g_failures = 0; static void check_condition(const char *name, int condition) { if (condition) { printf("PASS: %s\n", name); } else { printf("FAIL: %s\n", name); g_failures++; } } static int sleep_ms(int ms) { struct timespec ts; ts.tv_sec = ms / 1000; ts.tv_nsec = (long)(ms % 1000) * 1000000L; return nanosleep(&ts, NULL); } static int read_full(int fd, void *buf, size_t len) { unsigned char *p = (unsigned char *)buf; size_t off = 0; while (off < len) { ssize_t n = read(fd, p + off, len - off); if (n == 0) { return -1; } if (n < 0) { if (errno == EINTR) { continue; } return -1; } off += (size_t)n; } return 0; } static int write_full(int fd, const void *buf, size_t len) { const unsigned char *p = (const unsigned char *)buf; size_t off = 0; while (off < len) { ssize_t n = write(fd, p + off, len - off); if (n < 0) { if (errno == EINTR) { continue; } return -1; } off += (size_t)n; } return 0; } static int connect_socket_retry(const char *name, int timeout_ms) { int elapsed = 0; while (elapsed < timeout_ms) { int fd; struct sockaddr_un addr; socklen_t addr_len; fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0) { return -1; } memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; addr.sun_path[0] = '\0'; strncpy(&addr.sun_path[1], name, sizeof(addr.sun_path) - 2); addr.sun_path[sizeof(addr.sun_path) - 1] = '\0'; addr_len = (socklen_t)(sizeof(sa_family_t) + 1 + strlen(name)); if (connect(fd, (struct sockaddr *)&addr, addr_len) == 0) { return fd; } close(fd); sleep_ms(100); elapsed += 100; } return -1; } static int send_framed(int fd, const char *payload) { uint32_t len = (uint32_t)strlen(payload); uint32_t be_len = htonl(len); if (write_full(fd, &be_len, sizeof(be_len)) != 0) { return -1; } if (write_full(fd, payload, len) != 0) { return -1; } return 0; } static int recv_framed(int fd, char **out_payload) { uint32_t be_len; uint32_t len; char *payload; if (out_payload == NULL) { return -1; } *out_payload = NULL; if (read_full(fd, &be_len, sizeof(be_len)) != 0) { return -1; } len = ntohl(be_len); if (len == 0 || len > MAX_MSG_SIZE) { return -1; } payload = (char *)malloc((size_t)len + 1U); if (payload == NULL) { return -1; } if (read_full(fd, payload, len) != 0) { free(payload); return -1; } payload[len] = '\0'; *out_payload = payload; return 0; } static int request_roundtrip(const char *req, char **resp) { int fd = connect_socket_retry(SOCKET_NAME, 5000); int rc; if (fd < 0) { return -1; } rc = send_framed(fd, req); if (rc == 0) { rc = recv_framed(fd, resp); } close(fd); return rc; } int main(void) { int stdin_pipe[2]; pid_t child; const char *mnemonic = "\nabandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about\n"; char *resp = NULL; int status; if (pipe(stdin_pipe) != 0) { perror("pipe"); return 1; } child = fork(); if (child < 0) { perror("fork"); close(stdin_pipe[0]); close(stdin_pipe[1]); return 1; } if (child == 0) { int null_fd = open("/dev/null", O_WRONLY); if (null_fd >= 0) { dup2(null_fd, STDOUT_FILENO); dup2(null_fd, STDERR_FILENO); close(null_fd); } dup2(stdin_pipe[0], STDIN_FILENO); close(stdin_pipe[0]); close(stdin_pipe[1]); execl("./build/nsigner", "./build/nsigner", "--socket-name", SOCKET_NAME, (char *)NULL); _exit(127); } close(stdin_pipe[0]); if (write_full(stdin_pipe[1], mnemonic, strlen(mnemonic)) == 0) { check_condition("feed mnemonic to child stdin", 1); } else { check_condition("feed mnemonic to child stdin", 0); } close(stdin_pipe[1]); sleep_ms(800); if (request_roundtrip("{\"id\":\"1\",\"method\":\"get_public_key\",\"params\":[\"\"]}", &resp) == 0) { check_condition("get_public_key response id", strstr(resp, "\"id\":\"1\"") != NULL); check_condition("get_public_key has result", strstr(resp, "\"result\":") != NULL); } else { check_condition("get_public_key request roundtrip", 0); } free(resp); resp = NULL; if (request_roundtrip("{\"id\":\"2\",\"method\":\"sign_event\",\"params\":[\"{\\\"kind\\\":1,\\\"content\\\":\\\"hello\\\",\\\"tags\\\":[],\\\"created_at\\\":1700000000}\",{\"role\":\"main\"}]}", &resp) == 0) { check_condition("sign_event response id", strstr(resp, "\"id\":\"2\"") != NULL); check_condition("sign_event has result", strstr(resp, "\"result\":") != NULL); } else { check_condition("sign_event request roundtrip", 0); } free(resp); if (kill(child, SIGTERM) == 0) { check_condition("send SIGTERM to child", 1); } else { check_condition("send SIGTERM to child", 0); } if (waitpid(child, &status, 0) > 0) { check_condition("child exited", WIFEXITED(status) || WIFSIGNALED(status)); } else { check_condition("child exited", 0); } if (g_failures == 0) { printf("ALL TESTS PASSED\n"); return 0; } printf("TESTS FAILED: %d\n", g_failures); return 1; }