commit 09b174ac49da221c68eaaedecba2bea56c9f9ac1
parent 4882b57353a03a465ced0b4b52e03e8ee5851090
Author: Robin Bron <robin.bron@yourhosting.nl>
Date: Sun, 1 Mar 2026 15:26:25 +0100
More closely follow hexagonal architecture
Diffstat:
14 files changed, 1037 insertions(+), 597 deletions(-)
diff --git a/src/common/resp.c b/src/common/resp.c
@@ -0,0 +1,437 @@
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "rxi/log.h"
+#include "common/resp.h"
+
+#define MAX_BULK_LEN (256 * 1024)
+#define LINE_BUF 4096
+
+static void resp_free_internal(resp_object *o);
+
+static int resp_read_byte_from_buf(const char **buf, size_t *len) {
+ if (*len < 1) return -1;
+ unsigned char c = (unsigned char)(*buf)[0];
+ *buf += 1;
+ *len -= 1;
+ return (int)c;
+}
+
+static int resp_read_line_from_buf(const char **buf, size_t *len, char *out, size_t out_size) {
+ size_t i = 0;
+ int prev = -1;
+ while (i + 1 < out_size) {
+ if (*len < 1) return -1;
+ int b = (int)(unsigned char)(*buf)[0];
+ *buf += 1;
+ *len -= 1;
+ if (prev == '\r' && b == '\n') {
+ out[i - 1] = '\0';
+ return 0;
+ }
+ prev = b;
+ out[i++] = (char)b;
+ }
+ return -1;
+}
+
+resp_object *resp_read_buf(const char *buf, size_t len) {
+ const char *p = buf;
+ size_t remaining = len;
+
+ int type_c = resp_read_byte_from_buf(&p, &remaining);
+ if (type_c < 0) return NULL;
+ if (type_c == -2) return NULL;
+ resp_object *o = calloc(1, sizeof(resp_object));
+ if (!o) return NULL;
+ char line[LINE_BUF];
+ switch ((char)type_c) {
+ case '+':
+ o->type = RESPT_SIMPLE;
+ if (resp_read_line_from_buf(&p, &remaining, line, sizeof(line)) != 0) { free(o); return NULL; }
+ o->u.s = strdup(line);
+ break;
+ case '-':
+ o->type = RESPT_ERROR;
+ if (resp_read_line_from_buf(&p, &remaining, line, sizeof(line)) != 0) { free(o); return NULL; }
+ o->u.s = strdup(line);
+ break;
+ case ':': {
+ if (resp_read_line_from_buf(&p, &remaining, line, sizeof(line)) != 0) { free(o); return NULL; }
+ o->type = RESPT_INT;
+ o->u.i = (long long)strtoll(line, NULL, 10);
+ break;
+ }
+ case '$': {
+ if (resp_read_line_from_buf(&p, &remaining, line, sizeof(line)) != 0) { free(o); return NULL; }
+ long blen = strtol(line, NULL, 10);
+ if (blen < 0 || blen > (long)MAX_BULK_LEN) { free(o); return NULL; }
+ o->type = RESPT_BULK;
+ if (blen == 0) {
+ o->u.s = strdup("");
+ if (resp_read_line_from_buf(&p, &remaining, line, sizeof(line)) != 0) { free(o->u.s); free(o); return NULL; }
+ } else {
+ if ((size_t)blen > remaining) { free(o); return NULL; }
+ o->u.s = malloc((size_t)blen + 1);
+ if (!o->u.s) { free(o); return NULL; }
+ memcpy(o->u.s, p, (size_t)blen);
+ p += blen;
+ remaining -= (size_t)blen;
+ o->u.s[blen] = '\0';
+ if (remaining < 2) { free(o->u.s); free(o); return NULL; }
+ if (p[0] != '\r' || p[1] != '\n') { free(o->u.s); free(o); return NULL; }
+ p += 2;
+ remaining -= 2;
+ }
+ break;
+ }
+ case '*': {
+ if (resp_read_line_from_buf(&p, &remaining, line, sizeof(line)) != 0) { free(o); return NULL; }
+ long n = strtol(line, NULL, 10);
+ if (n < 0 || n > 65536) { free(o); return NULL; }
+ o->type = RESPT_ARRAY;
+ o->u.arr.n = (size_t)n;
+ o->u.arr.elem = n ? calloc((size_t)n, sizeof(resp_object)) : NULL;
+ if (n && !o->u.arr.elem) { free(o); return NULL; }
+ for (size_t i = 0; i < (size_t)n; i++) {
+ resp_object *sub = resp_read_buf(p, remaining);
+ if (!sub) {
+ for (size_t j = 0; j < i; j++) resp_free_internal(&o->u.arr.elem[j]);
+ free(o->u.arr.elem);
+ free(o);
+ return NULL;
+ }
+ p += remaining - (sub ? remaining : 0);
+ remaining = 0;
+ o->u.arr.elem[i] = *sub;
+ free(sub);
+ }
+ break;
+ }
+ default:
+ free(o);
+ return NULL;
+ }
+ return o;
+}
+
+static int resp_read_byte(int fd) {
+ unsigned char c;
+ ssize_t n = read(fd, &c, 1);
+ if (n != 1) {
+ if (n < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
+ return -2;
+ return -1;
+ }
+ return (int)c;
+}
+
+static int resp_read_line(int fd, char *buf, size_t buf_size) {
+ size_t i = 0;
+ int prev = -1;
+ while (i + 1 < buf_size) {
+ int b = resp_read_byte(fd);
+ if (b < 0) return -1;
+ if (prev == '\r' && b == '\n') {
+ buf[i - 1] = '\0';
+ return 0;
+ }
+ prev = b;
+ buf[i++] = (char)b;
+ }
+ return -1;
+}
+
+resp_object *resp_read(int fd) {
+ int type_c = resp_read_byte(fd);
+ if (type_c < 0) return NULL;
+ if (type_c == -2) return NULL;
+ resp_object *o = calloc(1, sizeof(resp_object));
+ if (!o) return NULL;
+ char line[LINE_BUF];
+ switch ((char)type_c) {
+ case '+':
+ o->type = RESPT_SIMPLE;
+ if (resp_read_line(fd, line, sizeof(line)) != 0) { free(o); return NULL; }
+ o->u.s = strdup(line);
+ break;
+ case '-':
+ o->type = RESPT_ERROR;
+ if (resp_read_line(fd, line, sizeof(line)) != 0) { free(o); return NULL; }
+ o->u.s = strdup(line);
+ break;
+ case ':': {
+ if (resp_read_line(fd, line, sizeof(line)) != 0) { free(o); return NULL; }
+ o->type = RESPT_INT;
+ o->u.i = (long long)strtoll(line, NULL, 10);
+ break;
+ }
+ case '$': {
+ if (resp_read_line(fd, line, sizeof(line)) != 0) { free(o); return NULL; }
+ long len = strtol(line, NULL, 10);
+ if (len < 0 || len > (long)MAX_BULK_LEN) { free(o); return NULL; }
+ o->type = RESPT_BULK;
+ if (len == 0) {
+ o->u.s = strdup("");
+ if (resp_read_line(fd, line, sizeof(line)) != 0) { free(o->u.s); free(o); return NULL; }
+ } else {
+ o->u.s = malloc((size_t)len + 1);
+ if (!o->u.s) { free(o); return NULL; }
+ if (read(fd, o->u.s, (size_t)len) != (ssize_t)len) { free(o->u.s); free(o); return NULL; }
+ o->u.s[len] = '\0';
+ if (resp_read_byte(fd) != '\r' || resp_read_byte(fd) != '\n') { free(o->u.s); free(o); return NULL; }
+ }
+ break;
+ }
+ case '*': {
+ if (resp_read_line(fd, line, sizeof(line)) != 0) { free(o); return NULL; }
+ long n = strtol(line, NULL, 10);
+ if (n < 0 || n > 65536) { free(o); return NULL; }
+ o->type = RESPT_ARRAY;
+ o->u.arr.n = (size_t)n;
+ o->u.arr.elem = n ? calloc((size_t)n, sizeof(resp_object)) : NULL;
+ if (n && !o->u.arr.elem) { free(o); return NULL; }
+ for (size_t i = 0; i < (size_t)n; i++) {
+ resp_object *sub = resp_read(fd);
+ if (!sub) {
+ for (size_t j = 0; j < i; j++) resp_free_internal(&o->u.arr.elem[j]);
+ free(o->u.arr.elem);
+ free(o);
+ return NULL;
+ }
+ o->u.arr.elem[i] = *sub;
+ free(sub);
+ }
+ break;
+ }
+ default:
+ free(o);
+ return NULL;
+ }
+ return o;
+}
+
+static void resp_free_internal(resp_object *o) {
+ if (!o) return;
+ if (o->type == RESPT_SIMPLE || o->type == RESPT_ERROR || o->type == RESPT_BULK) {
+ free(o->u.s);
+ } else if (o->type == RESPT_ARRAY) {
+ for (size_t i = 0; i < o->u.arr.n; i++)
+ resp_free_internal(&o->u.arr.elem[i]);
+ free(o->u.arr.elem);
+ }
+}
+
+void resp_free(resp_object *o) {
+ resp_free_internal(o);
+ free(o);
+}
+
+resp_object *resp_deep_copy(const resp_object *o) {
+ if (!o) return NULL;
+ resp_object *c = (resp_object *)calloc(1, sizeof(resp_object));
+ if (!c) return NULL;
+ c->type = o->type;
+ if (o->type == RESPT_SIMPLE || o->type == RESPT_ERROR || o->type == RESPT_BULK) {
+ c->u.s = o->u.s ? strdup(o->u.s) : NULL;
+ if (o->u.s && !c->u.s) { free(c); return NULL; }
+ return c;
+ }
+ if (o->type == RESPT_INT) {
+ c->u.i = o->u.i;
+ return c;
+ }
+ if (o->type == RESPT_ARRAY) {
+ c->u.arr.n = o->u.arr.n;
+ c->u.arr.elem = o->u.arr.n ? (resp_object *)calloc(o->u.arr.n, sizeof(resp_object)) : NULL;
+ if (o->u.arr.n && !c->u.arr.elem) { free(c); return NULL; }
+ for (size_t i = 0; i < o->u.arr.n; i++) {
+ resp_object *sub = resp_deep_copy(&o->u.arr.elem[i]);
+ if (!sub) {
+ for (size_t j = 0; j < i; j++) resp_free_internal(&c->u.arr.elem[j]);
+ free(c->u.arr.elem);
+ free(c);
+ return NULL;
+ }
+ c->u.arr.elem[i] = *sub;
+ free(sub);
+ }
+ return c;
+ }
+ free(c);
+ return NULL;
+}
+
+resp_object *resp_map_get(const resp_object *o, const char *key) {
+ if (!o || !key || o->type != RESPT_ARRAY) return NULL;
+ size_t n = o->u.arr.n;
+ if (n & 1) return NULL;
+ for (size_t i = 0; i < n; i += 2) {
+ const resp_object *k = &o->u.arr.elem[i];
+ const char *s = (k->type == RESPT_BULK || k->type == RESPT_SIMPLE) ? k->u.s : NULL;
+ if (s && strcmp(s, key) == 0 && i + 1 < n)
+ return (resp_object *)&o->u.arr.elem[i + 1];
+ }
+ return NULL;
+}
+
+const char *resp_map_get_string(const resp_object *o, const char *key) {
+ resp_object *val = resp_map_get(o, key);
+ if (!val) return NULL;
+ if (val->type == RESPT_BULK || val->type == RESPT_SIMPLE)
+ return val->u.s;
+ return NULL;
+}
+
+void resp_map_set(resp_object *o, const char *key, resp_object *value) {
+ if (!o || !key || o->type != RESPT_ARRAY) return;
+ for (size_t i = 0; i + 1 < o->u.arr.n; i += 2) {
+ const resp_object *k = &o->u.arr.elem[i];
+ const char *s = (k->type == RESPT_BULK || k->type == RESPT_SIMPLE) ? k->u.s : NULL;
+ if (s && strcmp(s, key) == 0 && i + 1 < o->u.arr.n) {
+ resp_free(&o->u.arr.elem[i + 1]);
+ o->u.arr.elem[i + 1] = *value;
+ free(value);
+ return;
+ }
+ }
+ resp_array_append_bulk(o, key);
+ resp_array_append_obj(o, value);
+}
+
+static int resp_append_object(char **buf, size_t *cap, size_t *len, const resp_object *o) {
+ if (!o) return -1;
+ size_t need = *len + 256;
+ if (o->type == RESPT_BULK || o->type == RESPT_SIMPLE || o->type == RESPT_ERROR) {
+ size_t slen = o->u.s ? strlen(o->u.s) : 0;
+ need = *len + 32 + slen + 2;
+ } else if (o->type == RESPT_ARRAY) {
+ need = *len + 32;
+ for (size_t i = 0; i < o->u.arr.n; i++)
+ need += 64;
+ }
+ if (need > *cap) {
+ size_t newcap = need + 4096;
+ char *n = realloc(*buf, newcap);
+ if (!n) return -1;
+ *buf = n;
+ *cap = newcap;
+ }
+ switch (o->type) {
+ case RESPT_SIMPLE: {
+ const char *s = o->u.s ? o->u.s : "";
+ *len += (size_t)snprintf(*buf + *len, *cap - *len, "+%s\r\n", s);
+ break;
+ }
+ case RESPT_ERROR: {
+ const char *s = o->u.s ? o->u.s : "";
+ *len += (size_t)snprintf(*buf + *len, *cap - *len, "-%s\r\n", s);
+ break;
+ }
+ case RESPT_INT:
+ *len += (size_t)snprintf(*buf + *len, *cap - *len, ":%lld\r\n", (long long)o->u.i);
+ break;
+ case RESPT_BULK: {
+ const char *s = o->u.s ? o->u.s : "";
+ size_t slen = strlen(s);
+ *len += (size_t)snprintf(*buf + *len, *cap - *len, "$%zu\r\n%s\r\n", slen, s);
+ break;
+ }
+ case RESPT_ARRAY: {
+ size_t n = o->u.arr.n;
+ *len += (size_t)snprintf(*buf + *len, *cap - *len, "*%zu\r\n", n);
+ for (size_t i = 0; i < n; i++) {
+ if (resp_append_object(buf, cap, len, &o->u.arr.elem[i]) != 0)
+ return -1;
+ }
+ break;
+ }
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+int resp_encode_array(int argc, const resp_object *const *argv, char **out_buf, size_t *out_len) {
+ size_t cap = 64;
+ size_t len = 0;
+ char *buf = malloc(cap);
+ if (!buf) return -1;
+ len += (size_t)snprintf(buf + len, cap - len, "*%d\r\n", argc);
+ if (len >= cap) { free(buf); return -1; }
+ for (int i = 0; i < argc; i++) {
+ if (resp_append_object(&buf, &cap, &len, argv[i]) != 0) {
+ free(buf);
+ return -1;
+ }
+ }
+ *out_buf = buf;
+ *out_len = len;
+ return 0;
+}
+
+int resp_serialize(const resp_object *o, char **out_buf, size_t *out_len) {
+ size_t cap = 64;
+ size_t len = 0;
+ char *buf = malloc(cap);
+ if (!buf) return -1;
+ if (resp_append_object(&buf, &cap, &len, o) != 0) {
+ free(buf);
+ return -1;
+ }
+ *out_buf = buf;
+ *out_len = len;
+ return 0;
+}
+
+resp_object *resp_array_init(void) {
+ resp_object *o = calloc(1, sizeof(resp_object));
+ if (!o) return NULL;
+ o->type = RESPT_ARRAY;
+ o->u.arr.n = 0;
+ o->u.arr.elem = NULL;
+ return o;
+}
+
+int resp_array_append_obj(resp_object *destination, resp_object *value) {
+ if (!destination || destination->type != RESPT_ARRAY || !value) return -1;
+ size_t n = destination->u.arr.n;
+ resp_object *new_elem = realloc(destination->u.arr.elem, (n + 1) * sizeof(resp_object));
+ if (!new_elem) return -1;
+ destination->u.arr.elem = new_elem;
+ destination->u.arr.elem[n] = *value;
+ destination->u.arr.n++;
+ free(value);
+ return 0;
+}
+
+int resp_array_append_simple(resp_object *destination, const char *str) {
+ resp_object *o = calloc(1, sizeof(resp_object));
+ if (!o) return -1;
+ o->type = RESPT_SIMPLE;
+ o->u.s = strdup(str ? str : "");
+ if (!o->u.s) { free(o); return -1; }
+ if (resp_array_append_obj(destination, o) != 0) { free(o->u.s); free(o); return -1; }
+ return 0;
+}
+
+int resp_array_append_bulk(resp_object *destination, const char *str) {
+ resp_object *o = calloc(1, sizeof(resp_object));
+ if (!o) return -1;
+ o->type = RESPT_BULK;
+ o->u.s = strdup(str ? str : "");
+ if (!o->u.s) { free(o); return -1; }
+ if (resp_array_append_obj(destination, o) != 0) { free(o->u.s); free(o); return -1; }
+ return 0;
+}
+
+int resp_array_append_int(resp_object *destination, long long i) {
+ resp_object *o = malloc(sizeof(resp_object));
+ if (!o) return -1;
+ o->type = RESPT_INT;
+ o->u.i = i;
+ return resp_array_append_obj(destination, o);
+}
diff --git a/src/common/resp.h b/src/common/resp.h
@@ -0,0 +1,48 @@
+#ifndef UDPHOLE_RESP_H
+#define UDPHOLE_RESP_H
+
+#include <stddef.h>
+
+#define RESPT_SIMPLE 0
+#define RESPT_ERROR 1
+#define RESPT_BULK 2
+#define RESPT_INT 3
+#define RESPT_ARRAY 4
+
+typedef struct resp_object resp_object;
+struct resp_object {
+ int type;
+ union {
+ char *s;
+ long long i;
+ struct { resp_object *elem; size_t n; } arr;
+ } u;
+};
+
+void resp_free(resp_object *o);
+
+resp_object *resp_deep_copy(const resp_object *o);
+
+resp_object *resp_map_get(const resp_object *o, const char *key);
+
+const char *resp_map_get_string(const resp_object *o, const char *key);
+
+void resp_map_set(resp_object *map, const char *key, resp_object *value);
+
+resp_object *resp_read(int fd);
+
+resp_object *resp_read_buf(const char *buf, size_t len);
+
+int resp_encode_array(int argc, const resp_object *const *argv, char **out_buf, size_t *out_len);
+
+int resp_serialize(const resp_object *o, char **out_buf, size_t *out_len);
+
+resp_object *resp_array_init(void);
+
+int resp_array_append_obj(resp_object *destination, resp_object *value);
+
+int resp_array_append_simple(resp_object *destination, const char *str);
+int resp_array_append_bulk(resp_object *destination, const char *str);
+int resp_array_append_int(resp_object *destination, long long i);
+
+#endif
diff --git a/src/domain/config.c b/src/domain/config.c
@@ -0,0 +1,40 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "domain/config.h"
+
+udphole_config_t *domain_cfg = NULL;
+
+void domain_config_init(void) {
+ if (domain_cfg) {
+ domain_config_free();
+ }
+ domain_cfg = calloc(1, sizeof(udphole_config_t));
+ if (domain_cfg) {
+ domain_cfg->port_low = 7000;
+ domain_cfg->port_high = 7999;
+ domain_cfg->port_cur = 7000;
+ }
+}
+
+void domain_config_set_ports(int low, int high) {
+ if (!domain_cfg) domain_config_init();
+ if (!domain_cfg) return;
+ domain_cfg->port_low = low > 0 ? low : 7000;
+ domain_cfg->port_high = high > domain_cfg->port_low ? high : domain_cfg->port_low + 999;
+ domain_cfg->port_cur = domain_cfg->port_low;
+}
+
+void domain_config_set_advertise(const char *addr) {
+ if (!domain_cfg) domain_config_init();
+ if (!domain_cfg) return;
+ free(domain_cfg->advertise_addr);
+ domain_cfg->advertise_addr = addr ? strdup(addr) : NULL;
+}
+
+void domain_config_free(void) {
+ if (!domain_cfg) return;
+ free(domain_cfg->advertise_addr);
+ free(domain_cfg);
+ domain_cfg = NULL;
+}
diff --git a/src/domain/config.h b/src/domain/config.h
@@ -0,0 +1,23 @@
+#ifndef UDPHOLE_DOMAIN_CONFIG_H
+#define UDPHOLE_DOMAIN_CONFIG_H
+
+#include <stdint.h>
+
+typedef struct {
+ int port_low;
+ int port_high;
+ int port_cur;
+ char *advertise_addr;
+} udphole_config_t;
+
+extern udphole_config_t *domain_cfg;
+
+void domain_config_init(void);
+
+void domain_config_set_ports(int low, int high);
+
+void domain_config_set_advertise(const char *addr);
+
+void domain_config_free(void);
+
+#endif
diff --git a/src/domain/session.c b/src/domain/session.c
@@ -15,11 +15,11 @@
#include "rxi/log.h"
#include "domain/protothreads.h"
#include "domain/scheduler.h"
+#include "domain/config.h"
#include "common/socket_util.h"
-#include "infrastructure/config.h"
+#include "common/resp.h"
#include "tidwall/hashmap.h"
#include "session.h"
-#include "interface/api/server.h"
#define SESSION_HASH_SIZE 256
#define BUFFER_SIZE 4096
@@ -60,10 +60,6 @@ typedef struct session {
static session_t **sessions = NULL;
static size_t sessions_count = 0;
-static char *advertise_addr = NULL;
-static int port_low = 7000;
-static int port_high = 7999;
-static int port_cur = 7000;
static int running = 0;
static session_t *find_session(const char *session_id) {
@@ -98,11 +94,12 @@ static socket_t *find_socket(session_t *s, const char *socket_id) {
}
static int alloc_port(void) {
- for (int i = 0; i < port_high - port_low; i++) {
- int port = port_cur + i;
- if (port > port_high) port = port_low;
- port_cur = port + 1;
- if (port_cur > port_high) port_cur = port_low;
+ if (!domain_cfg) return 0;
+ for (int i = 0; i < domain_cfg->port_high - domain_cfg->port_low; i++) {
+ int port = domain_cfg->port_cur + i;
+ if (port > domain_cfg->port_high) port = domain_cfg->port_low;
+ domain_cfg->port_cur = port + 1;
+ if (domain_cfg->port_cur > domain_cfg->port_high) domain_cfg->port_cur = domain_cfg->port_low;
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
@@ -515,285 +512,459 @@ static void spawn_session_pt(session_t *s) {
s->task = (struct pt_task *)(intptr_t)domain_schedmod_pt_create(session_pt, s);
}
-static char cmd_session_create(api_client_t *c, char **args, int nargs) {
- if (nargs < 2) {
- return api_write_err(c, "wrong number of arguments for 'session.create'") ? 1 : 0;
+resp_object *domain_session_create(resp_object *args) {
+ if (!args || args->type != RESPT_ARRAY || args->u.arr.n < 2) {
+ resp_object *err = resp_array_init();
+ resp_array_append_simple(err, "ERR wrong number of arguments for 'session.create'");
+ return err;
+ }
+
+ const char *session_id = NULL;
+ if (args->u.arr.n > 1 && args->u.arr.elem[1].type == RESPT_BULK) {
+ session_id = args->u.arr.elem[1].u.s;
+ }
+
+ if (!session_id) {
+ resp_object *err = resp_array_init();
+ resp_array_append_simple(err, "ERR wrong number of arguments for 'session.create'");
+ return err;
}
- const char *session_id = args[1];
int idle_expiry = 0;
- if (nargs >= 3 && args[2] && args[2][0] != '\0') {
- idle_expiry = atoi(args[2]);
+ if (args->u.arr.n >= 3 && args->u.arr.elem[2].type == RESPT_BULK && args->u.arr.elem[2].u.s) {
+ idle_expiry = atoi(args->u.arr.elem[2].u.s);
}
session_t *s = create_session(session_id, idle_expiry);
if (!s) {
- return api_write_err(c, "failed to create session") ? 1 : 0;
+ resp_object *err = resp_array_init();
+ resp_array_append_simple(err, "ERR failed to create session");
+ return err;
}
spawn_session_pt(s);
- return api_write_ok(c) ? 1 : 0;
+
+ resp_object *res = resp_array_init();
+ resp_array_append_simple(res, "OK");
+ return res;
}
-static char cmd_session_list(api_client_t *c, char **args, int nargs) {
+resp_object *domain_session_list(resp_object *args) {
(void)args;
- if (nargs != 1) {
- return api_write_err(c, "wrong number of arguments for 'session.list'") ? 1 : 0;
- }
-
- if (!sessions) {
- return api_write_array(c, 0) ? 1 : 0;
- }
- if (!api_write_array(c, sessions_count)) return 0;
+ resp_object *res = resp_array_init();
+ if (!res) return NULL;
for (size_t i = 0; i < sessions_count; i++) {
session_t *s = sessions[i];
if (!s) continue;
- if (!api_write_bulk_cstr(c, s->session_id)) return 0;
+ resp_array_append_bulk(res, s->session_id);
}
- return 1;
+ return res;
}
-static char cmd_session_info(api_client_t *c, char **args, int nargs) {
- if (nargs != 2) {
- return api_write_err(c, "wrong number of arguments for 'session.info'") ? 1 : 0;
+resp_object *domain_session_info(resp_object *args) {
+ if (!args || args->type != RESPT_ARRAY || args->u.arr.n < 2) {
+ resp_object *err = resp_array_init();
+ resp_array_append_simple(err, "ERR wrong number of arguments for 'session.info'");
+ return err;
+ }
+
+ const char *session_id = NULL;
+ if (args->u.arr.elem[1].type == RESPT_BULK) {
+ session_id = args->u.arr.elem[1].u.s;
+ }
+
+ if (!session_id) {
+ resp_object *err = resp_array_init();
+ resp_array_append_simple(err, "ERR wrong number of arguments for 'session.info'");
+ return err;
}
- const char *session_id = args[1];
session_t *s = find_session(session_id);
if (!s) {
- return api_write_err(c, "session not found") ? 1 : 0;
+ resp_object *err = resp_array_init();
+ resp_array_append_simple(err, "ERR session not found");
+ return err;
}
- size_t num_items = 8;
- if (!api_write_array(c, num_items)) return 0;
+ resp_object *res = resp_array_init();
+ if (!res) return NULL;
- if (!api_write_bulk_cstr(c, "session_id")) return 0;
- if (!api_write_bulk_cstr(c, s->session_id)) return 0;
+ resp_array_append_bulk(res, "session_id");
+ resp_array_append_bulk(res, s->session_id);
- if (!api_write_bulk_cstr(c, "created")) return 0;
- if (!api_write_bulk_int(c, (int)s->created)) return 0;
+ resp_array_append_bulk(res, "created");
+ resp_array_append_int(res, (long long)s->created);
- if (!api_write_bulk_cstr(c, "last_activity")) return 0;
- if (!api_write_bulk_int(c, (int)s->last_activity)) return 0;
+ resp_array_append_bulk(res, "last_activity");
+ resp_array_append_int(res, (long long)s->last_activity);
- if (!api_write_bulk_cstr(c, "idle_expiry")) return 0;
- if (!api_write_bulk_int(c, (int)s->idle_expiry)) return 0;
+ resp_array_append_bulk(res, "idle_expiry");
+ resp_array_append_int(res, (long long)s->idle_expiry);
- if (!api_write_bulk_cstr(c, "sockets")) return 0;
- if (!api_write_array(c, s->sockets_count)) return 0;
+ resp_array_append_bulk(res, "sockets");
+ resp_object *sockets_arr = resp_array_init();
for (size_t i = 0; i < s->sockets_count; i++) {
socket_t *sock = s->sockets[i];
if (!sock) continue;
- if (!api_write_bulk_cstr(c, sock->socket_id)) return 0;
+ resp_array_append_bulk(sockets_arr, sock->socket_id);
}
+ resp_array_append_obj(res, sockets_arr);
- if (!api_write_bulk_cstr(c, "forwards")) return 0;
- if (!api_write_array(c, s->forwards_count * 2)) return 0;
+ resp_array_append_bulk(res, "forwards");
+ resp_object *forwards_arr = resp_array_init();
for (size_t i = 0; i < s->forwards_count; i++) {
- if (!api_write_bulk_cstr(c, s->forwards[i].src_socket_id)) return 0;
- if (!api_write_bulk_cstr(c, s->forwards[i].dst_socket_id)) return 0;
+ resp_array_append_bulk(forwards_arr, s->forwards[i].src_socket_id);
+ resp_array_append_bulk(forwards_arr, s->forwards[i].dst_socket_id);
}
+ resp_array_append_obj(res, forwards_arr);
- if (!api_write_bulk_cstr(c, "marked_for_deletion")) return 0;
- if (!api_write_int(c, s->marked_for_deletion)) return 0;
+ resp_array_append_bulk(res, "marked_for_deletion");
+ resp_array_append_int(res, s->marked_for_deletion);
- return 1;
+ return res;
}
-static char cmd_session_destroy(api_client_t *c, char **args, int nargs) {
- if (nargs != 2) {
- return api_write_err(c, "wrong number of arguments for 'session.destroy'") ? 1 : 0;
+resp_object *domain_session_destroy(resp_object *args) {
+ if (!args || args->type != RESPT_ARRAY || args->u.arr.n < 2) {
+ resp_object *err = resp_array_init();
+ resp_array_append_simple(err, "ERR wrong number of arguments for 'session.destroy'");
+ return err;
+ }
+
+ const char *session_id = NULL;
+ if (args->u.arr.elem[1].type == RESPT_BULK) {
+ session_id = args->u.arr.elem[1].u.s;
+ }
+
+ if (!session_id) {
+ resp_object *err = resp_array_init();
+ resp_array_append_simple(err, "ERR wrong number of arguments for 'session.destroy'");
+ return err;
}
- const char *session_id = args[1];
session_t *s = find_session(session_id);
if (!s) {
- return api_write_err(c, "session not found") ? 1 : 0;
+ resp_object *err = resp_array_init();
+ resp_array_append_simple(err, "ERR session not found");
+ return err;
}
destroy_session(s);
- return api_write_ok(c) ? 1 : 0;
+
+ resp_object *res = resp_array_init();
+ resp_array_append_simple(res, "OK");
+ return res;
}
-static char cmd_socket_create_listen(api_client_t *c, char **args, int nargs) {
- if (nargs != 3) {
- return api_write_err(c, "wrong number of arguments for 'session.socket.create.listen'") ? 1 : 0;
+resp_object *domain_socket_create_listen(resp_object *args) {
+ if (!args || args->type != RESPT_ARRAY || args->u.arr.n < 3) {
+ resp_object *err = resp_array_init();
+ resp_array_append_simple(err, "ERR wrong number of arguments for 'session.socket.create.listen'");
+ return err;
+ }
+
+ const char *session_id = NULL;
+ const char *socket_id = NULL;
+
+ if (args->u.arr.elem[1].type == RESPT_BULK) {
+ session_id = args->u.arr.elem[1].u.s;
+ }
+ if (args->u.arr.elem[2].type == RESPT_BULK) {
+ socket_id = args->u.arr.elem[2].u.s;
}
- const char *session_id = args[1];
- const char *socket_id = args[2];
+ if (!session_id || !socket_id) {
+ resp_object *err = resp_array_init();
+ resp_array_append_simple(err, "ERR wrong number of arguments for 'session.socket.create.listen'");
+ return err;
+ }
session_t *s = find_session(session_id);
if (!s) {
- return api_write_err(c, "session not found") ? 1 : 0;
+ resp_object *err = resp_array_init();
+ resp_array_append_simple(err, "ERR session not found");
+ return err;
}
socket_t *sock = create_listen_socket(s, socket_id);
if (!sock) {
- return api_write_err(c, "failed to create socket") ? 1 : 0;
+ resp_object *err = resp_array_init();
+ resp_array_append_simple(err, "ERR failed to create socket");
+ return err;
}
- if (!api_write_array(c, 2)) return 0;
- if (!api_write_bulk_int(c, sock->local_port)) return 0;
- if (!api_write_bulk_cstr(c, advertise_addr)) return 0;
-
- return 1;
+ resp_object *res = resp_array_init();
+ resp_array_append_int(res, sock->local_port);
+ resp_array_append_bulk(res, domain_cfg && domain_cfg->advertise_addr ? domain_cfg->advertise_addr : "");
+ return res;
}
-static char cmd_socket_create_connect(api_client_t *c, char **args, int nargs) {
- if (nargs != 5) {
- return api_write_err(c, "wrong number of arguments for 'session.socket.create.connect'") ? 1 : 0;
+resp_object *domain_socket_create_connect(resp_object *args) {
+ if (!args || args->type != RESPT_ARRAY || args->u.arr.n < 5) {
+ resp_object *err = resp_array_init();
+ resp_array_append_simple(err, "ERR wrong number of arguments for 'session.socket.create.connect'");
+ return err;
+ }
+
+ const char *session_id = NULL;
+ const char *socket_id = NULL;
+ const char *ip = NULL;
+ const char *port_str = NULL;
+
+ if (args->u.arr.elem[1].type == RESPT_BULK) {
+ session_id = args->u.arr.elem[1].u.s;
+ }
+ if (args->u.arr.elem[2].type == RESPT_BULK) {
+ socket_id = args->u.arr.elem[2].u.s;
+ }
+ if (args->u.arr.elem[3].type == RESPT_BULK) {
+ ip = args->u.arr.elem[3].u.s;
+ }
+ if (args->u.arr.elem[4].type == RESPT_BULK) {
+ port_str = args->u.arr.elem[4].u.s;
+ }
+
+ if (!session_id || !socket_id || !ip || !port_str) {
+ resp_object *err = resp_array_init();
+ resp_array_append_simple(err, "ERR wrong number of arguments for 'session.socket.create.connect'");
+ return err;
}
- const char *session_id = args[1];
- const char *socket_id = args[2];
- const char *ip = args[3];
- int port = atoi(args[4]);
+ int port = atoi(port_str);
session_t *s = find_session(session_id);
if (!s) {
- return api_write_err(c, "session not found") ? 1 : 0;
+ resp_object *err = resp_array_init();
+ resp_array_append_simple(err, "ERR session not found");
+ return err;
}
socket_t *sock = create_connect_socket(s, socket_id, ip, port);
if (!sock) {
- return api_write_err(c, "failed to create socket") ? 1 : 0;
+ resp_object *err = resp_array_init();
+ resp_array_append_simple(err, "ERR failed to create socket");
+ return err;
}
- if (!api_write_array(c, 2)) return 0;
- if (!api_write_bulk_int(c, sock->local_port)) return 0;
- if (!api_write_bulk_cstr(c, advertise_addr)) return 0;
-
- return 1;
+ resp_object *res = resp_array_init();
+ resp_array_append_int(res, sock->local_port);
+ resp_array_append_bulk(res, domain_cfg && domain_cfg->advertise_addr ? domain_cfg->advertise_addr : "");
+ return res;
}
-static char cmd_socket_destroy(api_client_t *c, char **args, int nargs) {
- if (nargs != 3) {
- return api_write_err(c, "wrong number of arguments for 'session.socket.destroy'") ? 1 : 0;
+resp_object *domain_socket_destroy(resp_object *args) {
+ if (!args || args->type != RESPT_ARRAY || args->u.arr.n < 3) {
+ resp_object *err = resp_array_init();
+ resp_array_append_simple(err, "ERR wrong number of arguments for 'session.socket.destroy'");
+ return err;
}
- const char *session_id = args[1];
- const char *socket_id = args[2];
+ const char *session_id = NULL;
+ const char *socket_id = NULL;
+
+ if (args->u.arr.elem[1].type == RESPT_BULK) {
+ session_id = args->u.arr.elem[1].u.s;
+ }
+ if (args->u.arr.elem[2].type == RESPT_BULK) {
+ socket_id = args->u.arr.elem[2].u.s;
+ }
+
+ if (!session_id || !socket_id) {
+ resp_object *err = resp_array_init();
+ resp_array_append_simple(err, "ERR wrong number of arguments for 'session.socket.destroy'");
+ return err;
+ }
session_t *s = find_session(session_id);
if (!s) {
- return api_write_err(c, "session not found") ? 1 : 0;
+ resp_object *err = resp_array_init();
+ resp_array_append_simple(err, "ERR session not found");
+ return err;
}
if (destroy_socket(s, socket_id) != 0) {
- return api_write_err(c, "socket not found") ? 1 : 0;
+ resp_object *err = resp_array_init();
+ resp_array_append_simple(err, "ERR socket not found");
+ return err;
}
- return api_write_ok(c) ? 1 : 0;
+ resp_object *res = resp_array_init();
+ resp_array_append_simple(res, "OK");
+ return res;
}
-static char cmd_forward_list(api_client_t *c, char **args, int nargs) {
- if (nargs != 2) {
- return api_write_err(c, "wrong number of arguments for 'session.forward.list'") ? 1 : 0;
+resp_object *domain_forward_list(resp_object *args) {
+ if (!args || args->type != RESPT_ARRAY || args->u.arr.n < 2) {
+ resp_object *err = resp_array_init();
+ resp_array_append_simple(err, "ERR wrong number of arguments for 'session.forward.list'");
+ return err;
+ }
+
+ const char *session_id = NULL;
+ if (args->u.arr.elem[1].type == RESPT_BULK) {
+ session_id = args->u.arr.elem[1].u.s;
+ }
+
+ if (!session_id) {
+ resp_object *err = resp_array_init();
+ resp_array_append_simple(err, "ERR wrong number of arguments for 'session.forward.list'");
+ return err;
}
- const char *session_id = args[1];
session_t *s = find_session(session_id);
if (!s) {
- return api_write_err(c, "session not found") ? 1 : 0;
+ resp_object *err = resp_array_init();
+ resp_array_append_simple(err, "ERR session not found");
+ return err;
}
- if (!api_write_array(c, s->forwards_count * 2)) return 0;
+ resp_object *res = resp_array_init();
for (size_t i = 0; i < s->forwards_count; i++) {
- if (!api_write_bulk_cstr(c, s->forwards[i].src_socket_id)) return 0;
- if (!api_write_bulk_cstr(c, s->forwards[i].dst_socket_id)) return 0;
+ resp_array_append_bulk(res, s->forwards[i].src_socket_id);
+ resp_array_append_bulk(res, s->forwards[i].dst_socket_id);
}
-
- return 1;
+ return res;
}
-static char cmd_forward_create(api_client_t *c, char **args, int nargs) {
- if (nargs != 4) {
- return api_write_err(c, "wrong number of arguments for 'session.forward.create'") ? 1 : 0;
+resp_object *domain_forward_create(resp_object *args) {
+ if (!args || args->type != RESPT_ARRAY || args->u.arr.n < 4) {
+ resp_object *err = resp_array_init();
+ resp_array_append_simple(err, "ERR wrong number of arguments for 'session.forward.create'");
+ return err;
+ }
+
+ const char *session_id = NULL;
+ const char *src_socket_id = NULL;
+ const char *dst_socket_id = NULL;
+
+ if (args->u.arr.elem[1].type == RESPT_BULK) {
+ session_id = args->u.arr.elem[1].u.s;
+ }
+ if (args->u.arr.elem[2].type == RESPT_BULK) {
+ src_socket_id = args->u.arr.elem[2].u.s;
+ }
+ if (args->u.arr.elem[3].type == RESPT_BULK) {
+ dst_socket_id = args->u.arr.elem[3].u.s;
}
- const char *session_id = args[1];
- const char *src_socket_id = args[2];
- const char *dst_socket_id = args[3];
+ if (!session_id || !src_socket_id || !dst_socket_id) {
+ resp_object *err = resp_array_init();
+ resp_array_append_simple(err, "ERR wrong number of arguments for 'session.forward.create'");
+ return err;
+ }
session_t *s = find_session(session_id);
if (!s) {
- return api_write_err(c, "session not found") ? 1 : 0;
+ resp_object *err = resp_array_init();
+ resp_array_append_simple(err, "ERR session not found");
+ return err;
}
socket_t *src = find_socket(s, src_socket_id);
if (!src) {
- return api_write_err(c, "source socket not found") ? 1 : 0;
+ resp_object *err = resp_array_init();
+ resp_array_append_simple(err, "ERR source socket not found");
+ return err;
}
socket_t *dst = find_socket(s, dst_socket_id);
if (!dst) {
- return api_write_err(c, "destination socket not found") ? 1 : 0;
+ resp_object *err = resp_array_init();
+ resp_array_append_simple(err, "ERR destination socket not found");
+ return err;
}
if (add_forward(s, src_socket_id, dst_socket_id) != 0) {
- return api_write_err(c, "failed to add forward") ? 1 : 0;
+ resp_object *err = resp_array_init();
+ resp_array_append_simple(err, "ERR failed to add forward");
+ return err;
}
log_debug("udphole: created forward %s -> %s in session %s", src_socket_id, dst_socket_id, session_id);
- return api_write_ok(c) ? 1 : 0;
+
+ resp_object *res = resp_array_init();
+ resp_array_append_simple(res, "OK");
+ return res;
}
-static char cmd_forward_destroy(api_client_t *c, char **args, int nargs) {
- if (nargs != 4) {
- return api_write_err(c, "wrong number of arguments for 'session.forward.destroy'") ? 1 : 0;
+resp_object *domain_forward_destroy(resp_object *args) {
+ if (!args || args->type != RESPT_ARRAY || args->u.arr.n < 4) {
+ resp_object *err = resp_array_init();
+ resp_array_append_simple(err, "ERR wrong number of arguments for 'session.forward.destroy'");
+ return err;
}
- const char *session_id = args[1];
- const char *src_socket_id = args[2];
- const char *dst_socket_id = args[3];
+ const char *session_id = NULL;
+ const char *src_socket_id = NULL;
+ const char *dst_socket_id = NULL;
+
+ if (args->u.arr.elem[1].type == RESPT_BULK) {
+ session_id = args->u.arr.elem[1].u.s;
+ }
+ if (args->u.arr.elem[2].type == RESPT_BULK) {
+ src_socket_id = args->u.arr.elem[2].u.s;
+ }
+ if (args->u.arr.elem[3].type == RESPT_BULK) {
+ dst_socket_id = args->u.arr.elem[3].u.s;
+ }
+
+ if (!session_id || !src_socket_id || !dst_socket_id) {
+ resp_object *err = resp_array_init();
+ resp_array_append_simple(err, "ERR wrong number of arguments for 'session.forward.destroy'");
+ return err;
+ }
session_t *s = find_session(session_id);
if (!s) {
- return api_write_err(c, "session not found") ? 1 : 0;
+ resp_object *err = resp_array_init();
+ resp_array_append_simple(err, "ERR session not found");
+ return err;
}
if (remove_forward(s, src_socket_id, dst_socket_id) != 0) {
- return api_write_err(c, "forward not found") ? 1 : 0;
+ resp_object *err = resp_array_init();
+ resp_array_append_simple(err, "ERR forward not found");
+ return err;
}
- return api_write_ok(c) ? 1 : 0;
+ resp_object *res = resp_array_init();
+ resp_array_append_simple(res, "OK");
+ return res;
}
-static char cmd_system_load(api_client_t *c, char **args, int nargs) {
+resp_object *domain_system_load(resp_object *args) {
(void)args;
- if (nargs != 1) {
- return api_write_err(c, "wrong number of arguments for 'system.load'") ? 1 : 0;
- }
double loadavg[3];
if (getloadavg(loadavg, 3) != 3) {
- return api_write_err(c, "failed to get load average") ? 1 : 0;
+ resp_object *err = resp_array_init();
+ resp_array_append_simple(err, "ERR failed to get load average");
+ return err;
}
- if (!api_write_array(c, 6)) return 0;
- if (!api_write_bulk_cstr(c, "1min")) return 0;
+ resp_object *res = resp_array_init();
char buf[64];
+
+ resp_array_append_bulk(res, "1min");
snprintf(buf, sizeof(buf), "%.2f", loadavg[0]);
- if (!api_write_bulk_cstr(c, buf)) return 0;
- if (!api_write_bulk_cstr(c, "5min")) return 0;
+ resp_array_append_bulk(res, buf);
+
+ resp_array_append_bulk(res, "5min");
snprintf(buf, sizeof(buf), "%.2f", loadavg[1]);
- if (!api_write_bulk_cstr(c, buf)) return 0;
- if (!api_write_bulk_cstr(c, "15min")) return 0;
+ resp_array_append_bulk(res, buf);
+
+ resp_array_append_bulk(res, "15min");
snprintf(buf, sizeof(buf), "%.2f", loadavg[2]);
- if (!api_write_bulk_cstr(c, buf)) return 0;
+ resp_array_append_bulk(res, buf);
- return 1;
+ return res;
}
-static char cmd_session_count(api_client_t *c, char **args, int nargs) {
+resp_object *domain_session_count(resp_object *args) {
(void)args;
- if (nargs != 1) {
- return api_write_err(c, "wrong number of arguments for 'session.count'") ? 1 : 0;
- }
size_t count = 0;
for (size_t i = 0; i < sessions_count; i++) {
@@ -802,23 +973,11 @@ static char cmd_session_count(api_client_t *c, char **args, int nargs) {
}
}
- return api_write_int(c, (int)count) ? 1 : 0;
-}
-
-static void register_session_commands(void) {
- api_register_cmd("session.create", cmd_session_create);
- api_register_cmd("session.list", cmd_session_list);
- api_register_cmd("session.info", cmd_session_info);
- api_register_cmd("session.destroy", cmd_session_destroy);
- api_register_cmd("session.socket.create.listen", cmd_socket_create_listen);
- api_register_cmd("session.socket.create.connect", cmd_socket_create_connect);
- api_register_cmd("session.socket.destroy", cmd_socket_destroy);
- api_register_cmd("session.forward.list", cmd_forward_list);
- api_register_cmd("session.forward.create", cmd_forward_create);
- api_register_cmd("session.forward.destroy", cmd_forward_destroy);
- api_register_cmd("session.count", cmd_session_count);
- api_register_cmd("system.load", cmd_system_load);
- log_info("udphole: registered session.* commands");
+ resp_object *res = malloc(sizeof(resp_object));
+ if (!res) return NULL;
+ res->type = RESPT_INT;
+ res->u.i = (long long)count;
+ return res;
}
PT_THREAD(session_manager_pt(struct pt *pt, int64_t timestamp, struct pt_task *task)) {
@@ -826,30 +985,10 @@ PT_THREAD(session_manager_pt(struct pt *pt, int64_t timestamp, struct pt_task *t
log_trace("session_manager: protothread entry");
PT_BEGIN(pt);
- PT_WAIT_UNTIL(pt, global_cfg);
+ PT_WAIT_UNTIL(pt, domain_cfg);
- resp_object *cfg_sec = resp_map_get(global_cfg, "udphole");
- if (!cfg_sec) {
- log_info("udphole: no [udphole] section in config, not starting");
- PT_EXIT(pt);
- }
-
- const char *ports_str = resp_map_get_string(cfg_sec, "ports");
- if (ports_str) {
- sscanf(ports_str, "%d-%d", &port_low, &port_high);
- if (port_low <= 0) port_low = 7000;
- if (port_high <= port_low) port_high = port_low + 999;
- }
- port_cur = port_low;
-
- const char *advertise_cfg = resp_map_get_string(cfg_sec, "advertise");
- if (advertise_cfg) {
- advertise_addr = strdup(advertise_cfg);
- }
-
- register_session_commands();
running = 1;
- log_info("udphole: manager started with port range %d-%d", port_low, port_high);
+ log_info("udphole: manager started with port range %d-%d", domain_cfg->port_low, domain_cfg->port_high);
int64_t last_cleanup = 0;
@@ -871,18 +1010,5 @@ PT_THREAD(session_manager_pt(struct pt *pt, int64_t timestamp, struct pt_task *t
}
}
- free(advertise_addr);
- advertise_addr = NULL;
-
PT_END(pt);
-}
-
-void domain_setup(void) {
- extern void cli_register_command(const char *name, const char *description, int (*fn)(int, const char **));
- extern int cli_cmd_daemon(int argc, const char **argv);
- cli_register_command(
- "daemon",
- "Run the udphole daemon",
- cli_cmd_daemon
- );
}
\ No newline at end of file
diff --git a/src/domain/session.h b/src/domain/session.h
@@ -4,7 +4,21 @@
#include <stdint.h>
#include "domain/scheduler.h"
+#include "common/resp.h"
PT_THREAD(session_manager_pt(struct pt *pt, int64_t timestamp, struct pt_task *task));
-#endif // UDPHOLE_SESSION_H
-\ No newline at end of file
+resp_object *domain_session_create(resp_object *args);
+resp_object *domain_session_list(resp_object *args);
+resp_object *domain_session_info(resp_object *args);
+resp_object *domain_session_destroy(resp_object *args);
+resp_object *domain_socket_create_listen(resp_object *args);
+resp_object *domain_socket_create_connect(resp_object *args);
+resp_object *domain_socket_destroy(resp_object *args);
+resp_object *domain_forward_list(resp_object *args);
+resp_object *domain_forward_create(resp_object *args);
+resp_object *domain_forward_destroy(resp_object *args);
+resp_object *domain_session_count(resp_object *args);
+resp_object *domain_system_load(resp_object *args);
+
+#endif
diff --git a/src/infrastructure/config.c b/src/infrastructure/config.c
@@ -3,7 +3,7 @@
#include "benhoyt/inih.h"
#include "rxi/log.h"
#include "infrastructure/config.h"
-#include "infrastructure/resp.h"
+#include "common/resp.h"
resp_object *global_cfg = NULL;
resp_object *pending_cfg = NULL;
diff --git a/src/infrastructure/config.h b/src/infrastructure/config.h
@@ -1,7 +1,7 @@
#ifndef UDPHOLE_CONFIG_H
#define UDPHOLE_CONFIG_H
-#include "infrastructure/resp.h"
+#include "common/resp.h"
extern resp_object *global_cfg;
extern resp_object *pending_cfg;
diff --git a/src/infrastructure/resp.c b/src/infrastructure/resp.c
@@ -1,331 +0,0 @@
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <errno.h>
-
-#include "rxi/log.h"
-#include "infrastructure/resp.h"
-
-#define MAX_BULK_LEN (256 * 1024)
-#define LINE_BUF 4096
-
-static void resp_free_internal(resp_object *o);
-
-static int resp_read_byte(int fd) {
- unsigned char c;
- ssize_t n = read(fd, &c, 1);
- if (n != 1) {
- if (n < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
- return -2;
- return -1;
- }
- return (int)c;
-}
-
-static int resp_read_line(int fd, char *buf, size_t buf_size) {
- size_t i = 0;
- int prev = -1;
- while (i + 1 < buf_size) {
- int b = resp_read_byte(fd);
- if (b < 0) return -1;
- if (prev == '\r' && b == '\n') {
- buf[i - 1] = '\0';
- return 0;
- }
- prev = b;
- buf[i++] = (char)b;
- }
- return -1;
-}
-
-resp_object *resp_read(int fd) {
- int type_c = resp_read_byte(fd);
- if (type_c < 0) return NULL;
- if (type_c == -2) return NULL;
- resp_object *o = calloc(1, sizeof(resp_object));
- if (!o) return NULL;
- char line[LINE_BUF];
- switch ((char)type_c) {
- case '+':
- o->type = RESPT_SIMPLE;
- if (resp_read_line(fd, line, sizeof(line)) != 0) { free(o); return NULL; }
- o->u.s = strdup(line);
- break;
- case '-':
- o->type = RESPT_ERROR;
- if (resp_read_line(fd, line, sizeof(line)) != 0) { free(o); return NULL; }
- o->u.s = strdup(line);
- break;
- case ':': {
- if (resp_read_line(fd, line, sizeof(line)) != 0) { free(o); return NULL; }
- o->type = RESPT_INT;
- o->u.i = (long long)strtoll(line, NULL, 10);
- break;
- }
- case '$': {
- if (resp_read_line(fd, line, sizeof(line)) != 0) { free(o); return NULL; }
- long len = strtol(line, NULL, 10);
- if (len < 0 || len > (long)MAX_BULK_LEN) { free(o); return NULL; }
- o->type = RESPT_BULK;
- if (len == 0) {
- o->u.s = strdup("");
- if (resp_read_line(fd, line, sizeof(line)) != 0) { free(o->u.s); free(o); return NULL; }
- } else {
- o->u.s = malloc((size_t)len + 1);
- if (!o->u.s) { free(o); return NULL; }
- if (read(fd, o->u.s, (size_t)len) != (ssize_t)len) { free(o->u.s); free(o); return NULL; }
- o->u.s[len] = '\0';
- if (resp_read_byte(fd) != '\r' || resp_read_byte(fd) != '\n') { free(o->u.s); free(o); return NULL; }
- }
- break;
- }
- case '*': {
- if (resp_read_line(fd, line, sizeof(line)) != 0) { free(o); return NULL; }
- long n = strtol(line, NULL, 10);
- if (n < 0 || n > 65536) { free(o); return NULL; }
- o->type = RESPT_ARRAY;
- o->u.arr.n = (size_t)n;
- o->u.arr.elem = n ? calloc((size_t)n, sizeof(resp_object)) : NULL;
- if (n && !o->u.arr.elem) { free(o); return NULL; }
- for (size_t i = 0; i < (size_t)n; i++) {
- resp_object *sub = resp_read(fd);
- if (!sub) {
- for (size_t j = 0; j < i; j++) resp_free_internal(&o->u.arr.elem[j]);
- free(o->u.arr.elem);
- free(o);
- return NULL;
- }
- o->u.arr.elem[i] = *sub;
- free(sub);
- }
- break;
- }
- default:
- free(o);
- return NULL;
- }
- return o;
-}
-
-static void resp_free_internal(resp_object *o) {
- if (!o) return;
- if (o->type == RESPT_SIMPLE || o->type == RESPT_ERROR || o->type == RESPT_BULK) {
- free(o->u.s);
- } else if (o->type == RESPT_ARRAY) {
- for (size_t i = 0; i < o->u.arr.n; i++)
- resp_free_internal(&o->u.arr.elem[i]);
- free(o->u.arr.elem);
- }
-}
-
-void resp_free(resp_object *o) {
- resp_free_internal(o);
- free(o);
-}
-
-resp_object *resp_deep_copy(const resp_object *o) {
- if (!o) return NULL;
- resp_object *c = (resp_object *)calloc(1, sizeof(resp_object));
- if (!c) return NULL;
- c->type = o->type;
- if (o->type == RESPT_SIMPLE || o->type == RESPT_ERROR || o->type == RESPT_BULK) {
- c->u.s = o->u.s ? strdup(o->u.s) : NULL;
- if (o->u.s && !c->u.s) { free(c); return NULL; }
- return c;
- }
- if (o->type == RESPT_INT) {
- c->u.i = o->u.i;
- return c;
- }
- if (o->type == RESPT_ARRAY) {
- c->u.arr.n = o->u.arr.n;
- c->u.arr.elem = o->u.arr.n ? (resp_object *)calloc(o->u.arr.n, sizeof(resp_object)) : NULL;
- if (o->u.arr.n && !c->u.arr.elem) { free(c); return NULL; }
- for (size_t i = 0; i < o->u.arr.n; i++) {
- resp_object *sub = resp_deep_copy(&o->u.arr.elem[i]);
- if (!sub) {
- for (size_t j = 0; j < i; j++) resp_free_internal(&c->u.arr.elem[j]);
- free(c->u.arr.elem);
- free(c);
- return NULL;
- }
- c->u.arr.elem[i] = *sub;
- free(sub);
- }
- return c;
- }
- free(c);
- return NULL;
-}
-
-resp_object *resp_map_get(const resp_object *o, const char *key) {
- if (!o || !key || o->type != RESPT_ARRAY) return NULL;
- size_t n = o->u.arr.n;
- if (n & 1) return NULL;
- for (size_t i = 0; i < n; i += 2) {
- const resp_object *k = &o->u.arr.elem[i];
- const char *s = (k->type == RESPT_BULK || k->type == RESPT_SIMPLE) ? k->u.s : NULL;
- if (s && strcmp(s, key) == 0 && i + 1 < n)
- return (resp_object *)&o->u.arr.elem[i + 1];
- }
- return NULL;
-}
-
-const char *resp_map_get_string(const resp_object *o, const char *key) {
- resp_object *val = resp_map_get(o, key);
- if (!val) return NULL;
- if (val->type == RESPT_BULK || val->type == RESPT_SIMPLE)
- return val->u.s;
- return NULL;
-}
-
-void resp_map_set(resp_object *o, const char *key, resp_object *value) {
- if (!o || !key || o->type != RESPT_ARRAY) return;
- for (size_t i = 0; i + 1 < o->u.arr.n; i += 2) {
- const resp_object *k = &o->u.arr.elem[i];
- const char *s = (k->type == RESPT_BULK || k->type == RESPT_SIMPLE) ? k->u.s : NULL;
- if (s && strcmp(s, key) == 0 && i + 1 < o->u.arr.n) {
- resp_free(&o->u.arr.elem[i + 1]);
- o->u.arr.elem[i + 1] = *value;
- free(value);
- return;
- }
- }
- resp_array_append_bulk(o, key);
- resp_array_append_obj(o, value);
-}
-
-static int resp_append_object(char **buf, size_t *cap, size_t *len, const resp_object *o) {
- if (!o) return -1;
- size_t need = *len + 256;
- if (o->type == RESPT_BULK || o->type == RESPT_SIMPLE || o->type == RESPT_ERROR) {
- size_t slen = o->u.s ? strlen(o->u.s) : 0;
- need = *len + 32 + slen + 2;
- } else if (o->type == RESPT_ARRAY) {
- need = *len + 32;
- for (size_t i = 0; i < o->u.arr.n; i++)
- need += 64;
- }
- if (need > *cap) {
- size_t newcap = need + 4096;
- char *n = realloc(*buf, newcap);
- if (!n) return -1;
- *buf = n;
- *cap = newcap;
- }
- switch (o->type) {
- case RESPT_SIMPLE: {
- const char *s = o->u.s ? o->u.s : "";
- *len += (size_t)snprintf(*buf + *len, *cap - *len, "+%s\r\n", s);
- break;
- }
- case RESPT_ERROR: {
- const char *s = o->u.s ? o->u.s : "";
- *len += (size_t)snprintf(*buf + *len, *cap - *len, "-%s\r\n", s);
- break;
- }
- case RESPT_INT:
- *len += (size_t)snprintf(*buf + *len, *cap - *len, ":%lld\r\n", (long long)o->u.i);
- break;
- case RESPT_BULK: {
- const char *s = o->u.s ? o->u.s : "";
- size_t slen = strlen(s);
- *len += (size_t)snprintf(*buf + *len, *cap - *len, "$%zu\r\n%s\r\n", slen, s);
- break;
- }
- case RESPT_ARRAY: {
- size_t n = o->u.arr.n;
- *len += (size_t)snprintf(*buf + *len, *cap - *len, "*%zu\r\n", n);
- for (size_t i = 0; i < n; i++) {
- if (resp_append_object(buf, cap, len, &o->u.arr.elem[i]) != 0)
- return -1;
- }
- break;
- }
- default:
- return -1;
- }
- return 0;
-}
-
-int resp_encode_array(int argc, const resp_object *const *argv, char **out_buf, size_t *out_len) {
- size_t cap = 64;
- size_t len = 0;
- char *buf = malloc(cap);
- if (!buf) return -1;
- len += (size_t)snprintf(buf + len, cap - len, "*%d\r\n", argc);
- if (len >= cap) { free(buf); return -1; }
- for (int i = 0; i < argc; i++) {
- if (resp_append_object(&buf, &cap, &len, argv[i]) != 0) {
- free(buf);
- return -1;
- }
- }
- *out_buf = buf;
- *out_len = len;
- return 0;
-}
-
-int resp_serialize(const resp_object *o, char **out_buf, size_t *out_len) {
- size_t cap = 64;
- size_t len = 0;
- char *buf = malloc(cap);
- if (!buf) return -1;
- if (resp_append_object(&buf, &cap, &len, o) != 0) {
- free(buf);
- return -1;
- }
- *out_buf = buf;
- *out_len = len;
- return 0;
-}
-
-resp_object *resp_array_init(void) {
- resp_object *o = calloc(1, sizeof(resp_object));
- if (!o) return NULL;
- o->type = RESPT_ARRAY;
- o->u.arr.n = 0;
- o->u.arr.elem = NULL;
- return o;
-}
-
-int resp_array_append_obj(resp_object *destination, resp_object *value) {
- if (!destination || destination->type != RESPT_ARRAY || !value) return -1;
- size_t n = destination->u.arr.n;
- resp_object *new_elem = realloc(destination->u.arr.elem, (n + 1) * sizeof(resp_object));
- if (!new_elem) return -1;
- destination->u.arr.elem = new_elem;
- destination->u.arr.elem[n] = *value;
- destination->u.arr.n++;
- free(value);
- return 0;
-}
-
-int resp_array_append_simple(resp_object *destination, const char *str) {
- resp_object *o = calloc(1, sizeof(resp_object));
- if (!o) return -1;
- o->type = RESPT_SIMPLE;
- o->u.s = strdup(str ? str : "");
- if (!o->u.s) { free(o); return -1; }
- if (resp_array_append_obj(destination, o) != 0) { free(o->u.s); free(o); return -1; }
- return 0;
-}
-
-int resp_array_append_bulk(resp_object *destination, const char *str) {
- resp_object *o = calloc(1, sizeof(resp_object));
- if (!o) return -1;
- o->type = RESPT_BULK;
- o->u.s = strdup(str ? str : "");
- if (!o->u.s) { free(o); return -1; }
- if (resp_array_append_obj(destination, o) != 0) { free(o->u.s); free(o); return -1; }
- return 0;
-}
-
-int resp_array_append_int(resp_object *destination, long long i) {
- resp_object *o = malloc(sizeof(resp_object));
- if (!o) return -1;
- o->type = RESPT_INT;
- o->u.i = i;
- return resp_array_append_obj(destination, o);
-}
-\ No newline at end of file
diff --git a/src/infrastructure/resp.h b/src/infrastructure/resp.h
@@ -1,46 +0,0 @@
-#ifndef UDPHOLE_RESP_H
-#define UDPHOLE_RESP_H
-
-#include <stddef.h>
-
-#define RESPT_SIMPLE 0
-#define RESPT_ERROR 1
-#define RESPT_BULK 2
-#define RESPT_INT 3
-#define RESPT_ARRAY 4
-
-typedef struct resp_object resp_object;
-struct resp_object {
- int type;
- union {
- char *s;
- long long i;
- struct { resp_object *elem; size_t n; } arr;
- } u;
-};
-
-void resp_free(resp_object *o);
-
-resp_object *resp_deep_copy(const resp_object *o);
-
-resp_object *resp_map_get(const resp_object *o, const char *key);
-
-const char *resp_map_get_string(const resp_object *o, const char *key);
-
-void resp_map_set(resp_object *map, const char *key, resp_object *value);
-
-resp_object *resp_read(int fd);
-
-int resp_encode_array(int argc, const resp_object *const *argv, char **out_buf, size_t *out_len);
-
-int resp_serialize(const resp_object *o, char **out_buf, size_t *out_len);
-
-resp_object *resp_array_init(void);
-
-int resp_array_append_obj(resp_object *destination, resp_object *value);
-
-int resp_array_append_simple(resp_object *destination, const char *str);
-int resp_array_append_bulk(resp_object *destination, const char *str);
-int resp_array_append_int(resp_object *destination, long long i);
-
-#endif
-\ No newline at end of file
diff --git a/src/interface/api/server.c b/src/interface/api/server.c
@@ -26,7 +26,7 @@
#include "common/socket_util.h"
#include "infrastructure/config.h"
#include "interface/api/server.h"
-#include "infrastructure/resp.h"
+#include "common/resp.h"
struct pt_task;
PT_THREAD(api_client_pt(struct pt *pt, int64_t timestamp, struct pt_task *task));
@@ -56,8 +56,14 @@ typedef struct {
char (*func)(api_client_t *c, char **args, int nargs);
} api_cmd_entry;
+typedef struct {
+ const char *name;
+ domain_cmd_fn func;
+} domain_cmd_entry;
+
static char *current_listen = NULL;
static struct hashmap *cmd_map = NULL;
+static struct hashmap *domain_cmd_map = NULL;
typedef struct {
int *server_fds;
@@ -308,6 +314,25 @@ void api_register_cmd(const char *name, char (*func)(api_client_t *, char **, in
log_trace("api: registered command '%s'", name);
}
+static uint64_t domain_cmd_hash(const void *item, uint64_t seed0, uint64_t seed1) {
+ const domain_cmd_entry *cmd = item;
+ return hashmap_sip(cmd->name, strlen(cmd->name), seed0, seed1);
+}
+
+static int domain_cmd_compare(const void *a, const void *b, void *udata) {
+ (void)udata;
+ const domain_cmd_entry *ca = a;
+ const domain_cmd_entry *cb = b;
+ return strcasecmp(ca->name, cb->name);
+}
+
+void api_register_domain_cmd(const char *name, domain_cmd_fn func) {
+ if (!domain_cmd_map)
+ domain_cmd_map = hashmap_new(sizeof(domain_cmd_entry), 0, 0, 0, domain_cmd_hash, domain_cmd_compare, NULL, NULL);
+ hashmap_set(domain_cmd_map, &(domain_cmd_entry){ .name = name, .func = func });
+ log_trace("api: registered domain command '%s'", name);
+}
+
static char cmdAUTH(api_client_t *c, char **args, int nargs) {
if (nargs != 3) {
api_write_err(c, "wrong number of arguments for 'auth' command (AUTH username password)");
@@ -349,25 +374,44 @@ static bool is_builtin(const char *name);
static char cmdCOMMAND(api_client_t *c, char **args, int nargs) {
(void)args;
- if (!cmd_map)
+ if (!cmd_map && !domain_cmd_map)
return api_write_array(c, 0) ? 1 : 0;
resp_object *result = resp_array_init();
if (!result) return 0;
- size_t iter = 0;
- void *item;
- while (hashmap_iter(cmd_map, &iter, &item)) {
- const api_cmd_entry *e = item;
- if (!is_builtin(e->name) && !user_has_permit(c, e->name))
- continue;
+ if (domain_cmd_map) {
+ size_t iter = 0;
+ void *item;
+ while (hashmap_iter(domain_cmd_map, &iter, &item)) {
+ const domain_cmd_entry *e = item;
+ if (!user_has_permit(c, e->name))
+ continue;
- resp_array_append_bulk(result, e->name);
- resp_object *meta = resp_array_init();
- if (!meta) { resp_free(result); return 0; }
- resp_array_append_bulk(meta, "summary");
- resp_array_append_bulk(meta, "UDP hole proxy command");
- resp_array_append_obj(result, meta);
+ resp_array_append_bulk(result, e->name);
+ resp_object *meta = resp_array_init();
+ if (!meta) { resp_free(result); return 0; }
+ resp_array_append_bulk(meta, "summary");
+ resp_array_append_bulk(meta, "UDP hole proxy command");
+ resp_array_append_obj(result, meta);
+ }
+ }
+
+ if (cmd_map) {
+ size_t iter = 0;
+ void *item;
+ while (hashmap_iter(cmd_map, &iter, &item)) {
+ const api_cmd_entry *e = item;
+ if (!is_builtin(e->name) && !user_has_permit(c, e->name))
+ continue;
+
+ resp_array_append_bulk(result, e->name);
+ resp_object *meta = resp_array_init();
+ if (!meta) { resp_free(result); return 0; }
+ resp_array_append_bulk(meta, "summary");
+ resp_array_append_bulk(meta, "UDP hole proxy command");
+ resp_array_append_obj(result, meta);
+ }
}
char *out_buf = NULL;
@@ -402,6 +446,44 @@ static void dispatch_command(api_client_t *c, char **args, int nargs) {
for (char *p = args[0]; *p; p++) *p = (char)tolower((unsigned char)*p);
+ const domain_cmd_entry *dcmd = hashmap_get(domain_cmd_map, &(domain_cmd_entry){ .name = args[0] });
+ if (dcmd) {
+ if (!is_builtin(args[0])) {
+ if (!user_has_permit(c, args[0])) {
+ api_write_err(c, "no permission");
+ return;
+ }
+ }
+
+ resp_object *domain_args = resp_array_init();
+ if (!domain_args) return;
+
+ for (int i = 0; i < nargs; i++) {
+ resp_array_append_bulk(domain_args, args[i]);
+ }
+
+ resp_object *result = dcmd->func(domain_args);
+ resp_free(domain_args);
+
+ if (!result) {
+ api_write_err(c, "command failed");
+ return;
+ }
+
+ char *out_buf = NULL;
+ size_t out_len = 0;
+ if (resp_serialize(result, &out_buf, &out_len) != 0 || !out_buf) {
+ resp_free(result);
+ api_write_err(c, "command failed");
+ return;
+ }
+ resp_free(result);
+
+ api_write_raw(c, out_buf, out_len);
+ free(out_buf);
+ return;
+ }
+
const api_cmd_entry *cmd = hashmap_get(cmd_map, &(api_cmd_entry){ .name = args[0] });
if (!cmd) {
api_write_err(c, "unknown command");
@@ -497,9 +579,10 @@ PT_THREAD(api_server_pt(struct pt *pt, int64_t timestamp, struct pt_task *task))
init_builtins();
udata->server_fds = create_listen_socket(current_listen);
if (!udata->server_fds) {
+ log_fatal("api: failed to listen on %s", current_listen);
free(current_listen);
current_listen = NULL;
- PT_EXIT(pt);
+ exit(1);
}
}
@@ -532,6 +615,10 @@ PT_THREAD(api_server_pt(struct pt *pt, int64_t timestamp, struct pt_task *task))
hashmap_free(cmd_map);
cmd_map = NULL;
}
+ if (domain_cmd_map) {
+ hashmap_free(domain_cmd_map);
+ domain_cmd_map = NULL;
+ }
PT_END(pt);
}
diff --git a/src/interface/api/server.h b/src/interface/api/server.h
@@ -5,14 +5,19 @@
#include <stdbool.h>
#include "domain/scheduler.h"
+#include "common/resp.h"
struct api_client_state;
typedef struct api_client_state api_client_t;
+typedef resp_object *(*domain_cmd_fn)(resp_object *args);
+
PT_THREAD(api_server_pt(struct pt *pt, int64_t timestamp, struct pt_task *task));
void api_register_cmd(const char *name, char (*func)(api_client_t *, char **, int));
+void api_register_domain_cmd(const char *name, domain_cmd_fn func);
+
bool api_write_ok(api_client_t *c);
bool api_write_err(api_client_t *c, const char *msg);
bool api_write_array(api_client_t *c, size_t nitems);
@@ -20,4 +25,4 @@ bool api_write_bulk_cstr(api_client_t *c, const char *s);
bool api_write_bulk_int(api_client_t *c, int val);
bool api_write_int(api_client_t *c, int val);
-#endif // UDPHOLE_API_SERVER_H
-\ No newline at end of file
+#endif
diff --git a/src/interface/cli/command/daemon.c b/src/interface/cli/command/daemon.c
@@ -9,12 +9,30 @@
#include "rxi/log.h"
#include "infrastructure/config.h"
+#include "common/resp.h"
#include "../common.h"
#include "domain/scheduler.h"
+#include "domain/config.h"
#include "daemon.h"
#include "interface/api/server.h"
#include "domain/session.h"
+static void register_domain_commands(void) {
+ api_register_domain_cmd("session.create", domain_session_create);
+ api_register_domain_cmd("session.list", domain_session_list);
+ api_register_domain_cmd("session.info", domain_session_info);
+ api_register_domain_cmd("session.destroy", domain_session_destroy);
+ api_register_domain_cmd("session.socket.create.listen", domain_socket_create_listen);
+ api_register_domain_cmd("session.socket.create.connect", domain_socket_create_connect);
+ api_register_domain_cmd("session.socket.destroy", domain_socket_destroy);
+ api_register_domain_cmd("session.forward.list", domain_forward_list);
+ api_register_domain_cmd("session.forward.create", domain_forward_create);
+ api_register_domain_cmd("session.forward.destroy", domain_forward_destroy);
+ api_register_domain_cmd("session.count", domain_session_count);
+ api_register_domain_cmd("system.load", domain_system_load);
+ log_info("udphole: registered session.* commands");
+}
+
static int do_daemonize(void) {
pid_t pid = fork();
if (pid < 0) {
@@ -67,6 +85,26 @@ int cli_cmd_daemon(int argc, const char **argv) {
do_daemonize();
}
+ domain_config_init();
+
+ if (global_cfg) {
+ resp_object *cfg_sec = resp_map_get(global_cfg, "udphole");
+ if (cfg_sec) {
+ const char *ports_str = resp_map_get_string(cfg_sec, "ports");
+ if (ports_str) {
+ int port_low = 7000, port_high = 7999;
+ sscanf(ports_str, "%d-%d", &port_low, &port_high);
+ domain_config_set_ports(port_low, port_high);
+ }
+ const char *advertise = resp_map_get_string(cfg_sec, "advertise");
+ if (advertise) {
+ domain_config_set_advertise(advertise);
+ }
+ }
+ }
+
+ register_domain_commands();
+
log_info("udphole: starting daemon");
domain_schedmod_pt_create(api_server_pt, NULL);
diff --git a/src/interface/cli/main.c b/src/interface/cli/main.c
@@ -13,14 +13,13 @@ extern "C" {
#include "interface/cli/common.h"
#include "interface/cli/command/list_commands.h"
+#include "interface/cli/command/daemon.h"
#include "infrastructure/config.h"
#ifdef __cplusplus
}
#endif
-extern void domain_setup(void);
-
#define INCBIN_SILENCE_BITCODE_WARNING
#include "graphitemaster/incbin.h"
INCTXT(License, "LICENSE.md");
@@ -175,7 +174,11 @@ int main(int argc, const char **argv) {
cli_cmd_list_commands
);
- domain_setup();
+ cli_register_command(
+ "daemon",
+ "Run the udphole daemon",
+ cli_cmd_daemon
+ );
struct argparse argparse;
struct argparse_option options[] = {