From db66484c4300f5f0e857eff01d15fd3593002a79 Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Fri, 5 Jun 2020 14:09:18 +0200 Subject: Initial commit --- server.c | 423 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 423 insertions(+) create mode 100644 server.c (limited to 'server.c') 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 +#include +#include +#include +#include + +#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 : ""); + 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; +} -- cgit v1.2.3