#include #include #include #include #include #include "main.h" #include "uring.h" #include "server.h" #include "proxy.h" #include "utils.h" #include "idle.h" struct server_local { struct sockaddr_in46 addr; char addrstr[ADDRSTRLEN]; struct sockaddr_in46 peer; struct uring_task task; struct list_head list; }; static bool set_property(struct cfg *cfg, struct server *scfg, char **property, const char *value) { if (!cfg || !scfg || empty_str(value) || *property) return false; *property = strdup(value); if (!*property) { perror("strdup"); return false; } return true; } 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) proxy_refdump(proxy); if (server->idle) idle_refdump(server->idle); } 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->start_exec); free(scfg->stop_exec); free(scfg->name); free(scfg); } void server_delete(struct cfg *cfg, struct server *scfg) { struct server_local *local, *ltmp; struct sockaddr_in46 *remote, *rtmp; fprintf(stderr, "Removing server cfg: %s\n", scfg->name); idle_delete(cfg, scfg); 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 void server_dump(struct server *scfg) { struct server_local *local; struct sockaddr_in46 *remote; char abuf[ADDRSTRLEN]; 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", sockaddr_to_str(remote, abuf, sizeof(abuf))); } 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; char abuf[ADDRSTRLEN]; 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; } sockaddr_to_str(&local->peer, abuf, sizeof(abuf)); fprintf(stderr, "%s: incoming proxy connection: %s -> %s\n", scfg->name, abuf, local->addrstr); if (list_empty(&scfg->remotes)) { error("scfg->remotes empty!\n"); uring_close(cfg, NULL, res, NULL); goto out; } proxy = proxy_new(cfg, scfg, &local->peer, res); if (!proxy) uring_close(cfg, NULL, res, NULL); 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; uint16_t port; if (!scfg || !scfg->name) return false; if (!list_empty(&scfg->proxys)) return false; /* FIXME: running? */ switch (scfg->type) { case SERVER_TYPE_ANNOUNCE: if (scfg->announce_port < 1) return false; if (scfg->idle_timeout > 0 && scfg->stop_method == SERVER_STOP_METHOD_UNDEFINED) return false; if (scfg->start_method != SERVER_START_METHOD_UNDEFINED) return false; if (!list_empty(&scfg->locals)) return false; if (!list_empty(&scfg->remotes)) return false; break; case SERVER_TYPE_PROXY: if (scfg->announce_port >= 1) return false; if (scfg->idle_timeout > 0 && scfg->stop_method == SERVER_STOP_METHOD_UNDEFINED) return false; if (list_empty(&scfg->locals)) return false; if (list_empty(&scfg->remotes)) return false; list_for_each_entry(local, &scfg->locals, list) { port = sockaddr_port(&local->addr); if (port == 0) return false; if (scfg->announce_port < 1) scfg->announce_port = port; if (scfg->announce_port != port) { fprintf(stderr, "Multiple announce ports!?\n"); return false; } } if (scfg->announce_port < 1) return false; break; default: return false; } if (!scfg->pretty_name) { char *suffix; suffix = strrchr(scfg->name, '.'); if (!suffix || suffix == scfg->name) return false; scfg->pretty_name = strndup(scfg->name, suffix - scfg->name); if (!scfg->pretty_name) return false; } /* FIXME: config, dont reread config if server running, make sure fd is available before this is called */ server_dump(scfg); list_for_each_entry(local, &scfg->locals, list) { server_local_open(cfg, scfg, local); } idle_init(cfg, scfg); return true; } bool server_add_remote(struct cfg *cfg, struct server *scfg, struct sockaddr_in46 *addr) { if (!scfg || !addr) return false; list_add(&addr->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); sockaddr_to_str(&local->addr, local->addrstr, sizeof(local->addrstr)); fprintf(stderr, "Adding local: %s\n", local->addrstr); list_add(&local->list, &scfg->locals); free(addr); return true; } bool server_set_stop_method(struct cfg *cfg, struct server *scfg, enum server_stop_method stop_method) { if (scfg->stop_method != SERVER_STOP_METHOD_UNDEFINED || stop_method == SERVER_STOP_METHOD_UNDEFINED) return false; scfg->stop_method = stop_method; return true; } bool server_set_start_method(struct cfg *cfg, struct server *scfg, enum server_start_method start_method) { if (scfg->start_method != SERVER_START_METHOD_UNDEFINED || start_method == SERVER_START_METHOD_UNDEFINED) return false; scfg->start_method = start_method; return true; } bool server_set_stop_exec(struct cfg *cfg, struct server *scfg, const char *cmd) { return set_property(cfg, scfg, &scfg->stop_exec, cmd); } bool server_set_start_exec(struct cfg *cfg, struct server *scfg, const char *cmd) { return set_property(cfg, scfg, &scfg->start_exec, cmd); } bool server_set_idle_timeout(struct cfg *cfg, struct server *scfg, uint16_t timeout) { if (!scfg || scfg->idle_timeout != 0) return false; scfg->idle_timeout = timeout; 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) { return set_property(cfg, scfg, &scfg->pretty_name, pretty_name); } 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; scfg->stop_method = SERVER_STOP_METHOD_UNDEFINED; scfg->start_method = SERVER_START_METHOD_UNDEFINED; uring_task_init(&scfg->task, "scfg", uring_parent(cfg), server_free); list_init(&scfg->remotes); list_init(&scfg->locals); list_init(&scfg->proxys); memset(&scfg->mcast_iov, 0, sizeof(scfg->mcast_iov)); scfg->mcast_iov.iov_base = scfg->mcast_buf; memset(&scfg->mcast_msg, 0, sizeof(scfg->mcast_msg)); scfg->mcast_msg.msg_iov = &scfg->mcast_iov; scfg->mcast_msg.msg_iovlen = 1; scfg->idle_timeout = 0; list_add(&scfg->list, &cfg->servers); return scfg; }