257 lines
6.1 KiB
C
257 lines
6.1 KiB
C
#define _GNU_SOURCE
|
|
|
|
#include <arpa/inet.h>
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/types.h>
|
|
#include <sys/un.h>
|
|
#include <sys/wait.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#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;
|
|
}
|