summaryrefslogtreecommitdiff
path: root/server.c
diff options
context:
space:
mode:
authorDavid Härdeman <david@hardeman.nu>2020-06-05 14:09:18 +0200
committerDavid Härdeman <david@hardeman.nu>2020-06-05 14:09:18 +0200
commitdb66484c4300f5f0e857eff01d15fd3593002a79 (patch)
treea787b9f0da1243ae0391d5931ecb9cb0f29d3ee4 /server.c
Initial commit
Diffstat (limited to 'server.c')
-rw-r--r--server.c423
1 files changed, 423 insertions, 0 deletions
diff --git a/server.c b/server.c
new file mode 100644
index 0000000..1045e43
--- /dev/null
+++ b/server.c
@@ -0,0 +1,423 @@
+#include <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "main.h"
+#include "uring.h"
+#include "config.h"
+#include "server.h"
+
+#define ADDRSTRLEN (9 /*strlen("AF_INETX ")*/ + INET6_ADDRSTRLEN + 6 /*strlen(" 65535")*/ + 1)
+
+struct server_local {
+ struct sockaddr_in46 addr;
+ char addrstr[ADDRSTRLEN];
+ struct sockaddr_in46 peer;
+ char peerstr[ADDRSTRLEN];
+ struct uring_task task;
+ struct list_head list;
+};
+
+/* FIXME: This can be a plain sockaddr_in46 */
+struct server_remote {
+ struct sockaddr_in46 addr;
+ char addrstr[ADDRSTRLEN];
+ struct list_head list;
+};
+
+struct server_proxy {
+ struct sockaddr_in46 client;
+ char clientstr[ADDRSTRLEN];
+ struct sockaddr_in46 server;
+ char serverstr[ADDRSTRLEN];
+ struct uring_task task;
+ char buf[4096];
+ size_t len;
+ struct list_head list;
+};
+
+void
+server_refdump(struct server *server)
+{
+ struct server_local *local;
+ struct server_proxy *proxy;
+
+ uring_task_refdump(&server->task);
+ list_for_each_entry(local, &server->locals, list)
+ uring_task_refdump(&local->task);
+ list_for_each_entry(proxy, &server->proxys, list)
+ uring_task_refdump(&proxy->task);
+}
+
+static void
+server_free(struct uring_task *task)
+{
+ struct server *scfg = container_of(task, struct server, task);
+
+ fprintf(stderr, "Freeing scfg %s\n", scfg->name);
+ list_del(&scfg->list);
+ free(scfg->pretty_name);
+ free(scfg->name);
+ free(scfg);
+}
+
+void
+server_delete(struct cfg *cfg, struct server *scfg)
+{
+ struct server_local *local, *ltmp;
+ struct server_remote *remote, *rtmp;
+
+ fprintf(stderr, "Removing server cfg: %s\n", scfg->name);
+
+ list_for_each_entry_safe(remote, rtmp, &scfg->remotes, list) {
+ list_del(&remote->list);
+ free(remote);
+ }
+
+ list_for_each_entry_safe(local, ltmp, &scfg->locals, list) {
+ uring_cancel(cfg, &local->task);
+ }
+
+ uring_task_put(cfg, &scfg->task);
+}
+
+void
+server_delete_by_name(struct cfg *cfg, const char *name)
+{
+ struct server *scfg;
+
+ if (!cfg || !name || name[0] == '\0')
+ return;
+
+ list_for_each_entry(scfg, &cfg->servers, list) {
+ if (!strcmp(scfg->name, name)) {
+ server_delete(cfg, scfg);
+ return;
+ }
+ }
+}
+
+static char *
+server_print_addr(struct sockaddr_in46 *addr, char *buf, size_t buflen)
+{
+ char abuf[ADDRSTRLEN];
+
+ switch (addr->storage.ss_family) {
+ case AF_INET:
+ snprintf(buf, buflen, "AF_INET4 %s %u",
+ inet_ntop(addr->in4.sin_family, &addr->in4.sin_addr, abuf, sizeof(abuf)),
+ (unsigned)ntohs(addr->in4.sin_port));
+ break;
+ case AF_INET6:
+ snprintf(buf, buflen, "AF_INET6 %s %u",
+ inet_ntop(addr->in6.sin6_family, &addr->in6.sin6_addr, abuf, sizeof(abuf)),
+ (unsigned)ntohs(addr->in6.sin6_port));
+ break;
+ default:
+ snprintf(buf, buflen, "AF_UNKNOWN");
+ break;
+ }
+
+ return buf;
+}
+
+static void
+server_dump(struct server *scfg)
+{
+ struct server_local *local;
+ struct server_remote *remote;
+
+ fprintf(stderr, "Dumping server %s\n", scfg->name);
+ switch (scfg->type) {
+ case SERVER_TYPE_ANNOUNCE:
+ fprintf(stderr, " * Type: announce\n");
+ break;
+ case SERVER_TYPE_PROXY:
+ fprintf(stderr, " * Type: proxy\n");
+ break;
+ default:
+ fprintf(stderr, " * Type: unknown\n");
+ break;
+ }
+ fprintf(stderr, " * Pretty name: %s\n", scfg->pretty_name ? scfg->pretty_name : "<undefined>");
+ fprintf(stderr, " * Announce port: %" PRIu16 "\n", scfg->announce_port);
+ fprintf(stderr, " * Listening ports:\n");
+ list_for_each_entry(local, &scfg->locals, list)
+ fprintf(stderr, " * %s\n", local->addrstr);
+ fprintf(stderr, " * Remote ports:\n");
+ list_for_each_entry(remote, &scfg->remotes, list)
+ fprintf(stderr, " * %s\n", remote->addrstr);
+}
+
+/*
+struct server_active_proxy {
+ struct sockaddr_in46 client;
+ char clientstr[ADDRSTRLEN];
+ struct sockaddr_in46 server;
+ char serverstr[ADDRSTRLEN]
+ struct uring_task task;
+ char buf[4096];
+ size_t len;
+ struct list_head list;
+};
+*/
+
+static void
+server_proxy_free(struct uring_task *task)
+{
+ struct server_proxy *proxy = container_of(task, struct server_proxy, task);
+
+ list_del(&proxy->list);
+ free(proxy);
+}
+
+static void
+server_proxy_connected(struct cfg *cfg, struct uring_task *task, int res)
+{
+ //struct server_proxy *proxy = container_of(task, struct server_proxy, task);
+
+ fprintf(stderr, "%s: connected %i\n", __func__, res);
+ return;
+}
+
+static void
+server_local_free(struct uring_task *task)
+{
+ struct server_local *local = container_of(task, struct server_local, task);
+
+ fprintf(stderr, "%s called: task 0x%p\n", __func__, task);
+
+ list_del(&local->list);
+ free(local);
+}
+
+static void
+server_local_accept(struct cfg *cfg, struct uring_task *task, int res)
+{
+ struct server_local *local = container_of(task, struct server_local, task);
+ struct server *scfg = container_of(task->parent, struct server, task);
+ struct server_proxy *proxy;
+ struct server_remote *remote;
+ int sfd;
+
+ fprintf(stderr, "%s called: task 0x%p and res %i\n", __func__, task, res);
+ fprintf(stderr, "%s called: scfg name is %s\n", __func__, scfg->name);
+
+ if (task->dead) {
+ fprintf(stderr, "Task dead!\n");
+ uring_task_put(cfg, task);
+ return;
+ }
+
+ if (res < 0) {
+ fprintf(stderr, "%s: result was %i\n", __func__, res);
+ goto out;
+ }
+
+ server_print_addr(&local->peer, local->peerstr, sizeof(local->peerstr));
+ fprintf(stderr, "%s: incoming proxy connection: %s -> %s\n", scfg->name, local->peerstr, local->addrstr);
+
+ if (list_empty(&scfg->remotes)) {
+ error("scfg->remotes empty!\n");
+ goto out;
+ }
+
+ proxy = zmalloc(sizeof(*proxy));
+ if (!proxy) {
+ perror("malloc");
+ uring_close(cfg, NULL, res, NULL);
+ goto out;
+ }
+
+ remote = list_first_entry(&scfg->remotes, struct server_remote, list);
+ fprintf(stderr, "%s: attempting proxy connection to %s (len %u)\n", scfg->name, remote->addrstr, remote->addr.addrlen);
+
+ sfd = socket(remote->addr.storage.ss_family, SOCK_STREAM, 0);
+ if (sfd < 0) {
+ perror("socket");
+ uring_close(cfg, NULL, res, NULL);
+ goto out;
+ }
+
+ proxy->client = local->peer;
+ memcpy(proxy->clientstr, local->peerstr, sizeof(proxy->clientstr));
+ uring_task_init(&proxy->task, "proxy", &scfg->task, server_proxy_free);
+ uring_task_set_fd(&proxy->task, sfd);
+ list_add(&proxy->list, &scfg->proxys);
+ uring_connect(cfg, &proxy->task, &remote->addr, server_proxy_connected);
+
+out:
+ uring_accept(cfg, &local->task, &local->peer, server_local_accept);
+}
+
+static bool
+server_local_open(struct cfg *cfg, struct server *scfg, struct server_local *local)
+{
+ int sfd;
+ int enable = 1;
+ int r;
+
+ sfd = socket(local->addr.storage.ss_family, SOCK_STREAM, 0);
+ if (sfd < 0) {
+ perror("socket");
+ goto out;
+ }
+
+ if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) < 0) {
+ perror("setsockopt");
+ goto out;
+ }
+
+ r = bind(sfd, (struct sockaddr *)&local->addr.storage, local->addr.addrlen);
+ if (r < 0) {
+ perror("bind");
+ goto out;
+ }
+
+ r = listen(sfd, 100);
+ if (r < 0) {
+ perror("listen");
+ goto out;
+ }
+
+ uring_task_set_fd(&local->task, sfd);
+ uring_accept(cfg, &local->task, &local->peer, server_local_accept);
+ fprintf(stderr, "** Opened listening socket: %s\n", local->addrstr);
+ return true;
+
+out:
+ if (sfd >= 0)
+ close(sfd);
+ return false;
+}
+
+bool
+server_commit(struct cfg *cfg, struct server *scfg)
+{
+ struct server_local *local;
+
+ /* FIXME: config, dont reread config if server running, make sure fd is available before this is called */
+ /* FIXME: verify correct cfg */
+ server_dump(scfg);
+
+ list_for_each_entry(local, &scfg->locals, list) {
+ server_local_open(cfg, scfg, local);
+ }
+ return true;
+}
+
+bool
+server_add_remote(struct cfg *cfg, struct server *scfg, struct sockaddr_in46 *addr)
+{
+ struct server_remote *remote;
+
+ if (!scfg || !addr)
+ return false;
+
+ remote = zmalloc(sizeof(*remote));
+ if (!remote)
+ return false;
+
+ remote->addr.storage = addr->storage;
+ remote->addr.addrlen = addr->addrlen;
+ server_print_addr(&remote->addr, remote->addrstr, sizeof(remote->addrstr));
+ list_add(&remote->list, &scfg->remotes);
+ return true;
+}
+
+bool
+server_add_local(struct cfg *cfg, struct server *scfg, struct sockaddr_in46 *addr)
+{
+ struct server_local *local;
+
+ if (!scfg || !addr)
+ return false;
+
+ local = zmalloc(sizeof(*local));
+ if (!local)
+ return false;
+
+ local->addr.storage = addr->storage;
+ local->addr.addrlen = addr->addrlen;
+ uring_task_init(&local->task, "local", &scfg->task, server_local_free);
+ server_print_addr(&local->addr, local->addrstr, sizeof(local->addrstr));
+ fprintf(stderr, "Adding local: %s\n", local->addrstr);
+ list_add(&local->list, &scfg->locals);
+ return true;
+}
+
+bool
+server_set_port(struct cfg *cfg, struct server *scfg, uint16_t port)
+{
+ if (!scfg || scfg->announce_port != 0)
+ return false;
+
+ scfg->announce_port = htons(port);
+ return true;
+}
+
+bool
+server_set_type(struct cfg *cfg, struct server *scfg, enum server_type type)
+{
+ if (!scfg || scfg->type != SERVER_TYPE_UNDEFINED)
+ return false;
+
+ switch (type) {
+ case SERVER_TYPE_ANNOUNCE:
+ scfg->type = SERVER_TYPE_ANNOUNCE;
+ break;
+ case SERVER_TYPE_PROXY:
+ scfg->type = SERVER_TYPE_PROXY;
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+bool
+server_set_pretty_name(struct cfg *cfg, struct server *scfg, const char *pretty_name)
+{
+ if (!pretty_name || pretty_name[0] == '\0' || !scfg || scfg->pretty_name)
+ return false;
+
+ scfg->pretty_name = strdup(pretty_name);
+ if (!scfg->pretty_name)
+ return false;
+
+ return true;
+}
+
+struct server *
+server_new(struct cfg *cfg, const char *name)
+{
+ struct server *scfg;
+
+ list_for_each_entry(scfg, &cfg->servers, list) {
+ if (strcmp(name, scfg->name))
+ continue;
+ debug(2, "Server already exists: %s\n", name);
+ return scfg;
+ }
+
+ debug(2, "Would add server cfg: %s\n", name);
+ scfg = zmalloc(sizeof(*scfg));
+ if (!scfg) {
+ error("malloc");
+ return NULL;
+ }
+
+ scfg->type = SERVER_TYPE_UNDEFINED;
+ scfg->name = strdup(name);
+ scfg->running = false;
+ uring_task_init(&scfg->task, "scfg", &cfg->task, server_free);
+ list_init(&scfg->remotes);
+ list_init(&scfg->locals);
+ list_init(&scfg->proxys);
+ list_add(&scfg->list, &cfg->servers);
+
+ return scfg;
+}