diff options
author | David Härdeman <david@hardeman.nu> | 2020-06-19 19:11:48 +0200 |
---|---|---|
committer | David Härdeman <david@hardeman.nu> | 2020-06-19 19:11:48 +0200 |
commit | 91a7ca50f3f8a2c7bb01113fa3849cb5e153a70f (patch) | |
tree | 18c5b7c76f4ec3069e9033a1c222eefd2c945da6 | |
parent | 445647adc4475c0b8264ce8b6c97d748eec69e7b (diff) |
Add support for async DNS
-rw-r--r-- | announce.c | 1 | ||||
-rw-r--r-- | cfgdir.c | 174 | ||||
-rw-r--r-- | config.c | 267 | ||||
-rw-r--r-- | config.h | 38 | ||||
-rw-r--r-- | main.c | 86 | ||||
-rw-r--r-- | main.h | 1 | ||||
-rw-r--r-- | meson.build | 1 | ||||
-rw-r--r-- | rcon.c | 1 | ||||
-rw-r--r-- | server.c | 31 | ||||
-rw-r--r-- | server.h | 2 | ||||
-rw-r--r-- | uring.c | 1 | ||||
-rw-r--r-- | utils.h | 12 |
12 files changed, 459 insertions, 156 deletions
@@ -7,7 +7,6 @@ #include "main.h" #include "uring.h" -#include "config.h" #include "announce.h" #include "server.h" @@ -1,3 +1,4 @@ +#define _GNU_SOURCE #include <stdio.h> #include <ctype.h> #include <string.h> @@ -5,9 +6,6 @@ #include <sys/inotify.h> #include <dirent.h> #include <errno.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <netdb.h> #include <unistd.h> #include <arpa/inet.h> #include <inttypes.h> @@ -18,6 +16,98 @@ #include "config.h" #include "server.h" +static void +scfg_dns_cb(struct dns_async *dns, bool (*server_cb)(struct cfg *, struct server *, struct saddr *)) +{ + struct server *scfg; + struct cfg *cfg; + struct sockaddr_in *in4; + struct sockaddr_in6 *in6; + struct saddr *saddr; + struct addrinfo *results = NULL, *ai; + int r; + + if (!dns || !dns->priv || !server_cb) { + error("invalid arguments\n"); + return; + } + + scfg = dns->priv; + cfg = scfg->cfg; + + debug(DBG_DNS, "called, dns: %p, name: %s, scfg: %p, scfg->name: %s\n", + dns, dns->name, scfg, scfg->name); + + r = gai_error(&dns->gcb); + if (r == EAI_INPROGRESS) { + /* This shouldn't happen, assume we'll get called again */ + error("called with request in progress\n"); + return; + } else if (r == EAI_CANCELED) { + /* The server must be in the process of going away */ + goto out; + } else if (r < 0) { + error("DNS lookup of %s:%s failed: %s\n", + dns->name, dns->port, gai_strerror(r)); + goto out; + } + + results = dns->gcb.ar_result; + + for (ai = results; ai; ai = ai->ai_next) { + saddr = zmalloc(sizeof(*saddr)); + if (!saddr) { + error("DNS lookup of %s:%s failed: %m\n", dns->name, dns->port); + goto out; + } + + switch (ai->ai_family) { + case AF_INET: + in4 = (struct sockaddr_in *)ai->ai_addr; + saddr_set_ipv4(saddr, in4->sin_addr.s_addr, in4->sin_port); + server_cb(cfg, scfg, saddr); + break; + + case AF_INET6: + in6 = (struct sockaddr_in6 *)ai->ai_addr; + saddr_set_ipv6(saddr, &in6->sin6_addr, in6->sin6_port); + server_cb(cfg, scfg, saddr); + break; + + default: + error("getaddrinfo(%s:%s): unknown address family (%i)\n", + dns->name, dns->port, ai->ai_family); + xfree(saddr); + break; + } + } + +out: + freeaddrinfo(results); + list_del(&dns->list); + xfree(dns); + uring_task_put(cfg, &scfg->task); + server_commit(cfg, scfg); +} + +static void +scfg_local_dns_cb(struct dns_async *dns) +{ + scfg_dns_cb(dns, server_add_local); +} + +static void +scfg_remote_dns_cb(struct dns_async *dns) +{ + scfg_dns_cb(dns, server_add_remote); +} + +static void +scfg_rcon_dns_cb(struct dns_async *dns) +{ + scfg_dns_cb(dns, server_add_rcon); +} + enum scfg_keys { SCFG_KEY_INVALID = 0, SCFG_KEY_TYPE, @@ -51,11 +141,11 @@ struct cfg_key_value_map scfg_key_map[] = { }, { .key_name = "local", .key_value = SCFG_KEY_LOCAL, - .value_type = CFG_VAL_TYPE_ADDRS, + .value_type = CFG_VAL_TYPE_ASYNC_ADDRS, }, { .key_name = "remote", .key_value = SCFG_KEY_REMOTE, - .value_type = CFG_VAL_TYPE_ADDRS, + .value_type = CFG_VAL_TYPE_ASYNC_ADDRS, }, { .key_name = "idle_timeout", .key_value = SCFG_KEY_IDLE_TIMEOUT, @@ -79,7 +169,7 @@ struct cfg_key_value_map scfg_key_map[] = { }, { .key_name = "rcon", .key_value = SCFG_KEY_RCON, - .value_type = CFG_VAL_TYPE_ADDRS, + .value_type = CFG_VAL_TYPE_ASYNC_ADDRS, }, { .key_name = "rcon_password", .key_value = SCFG_KEY_RCON_PASSWORD, @@ -95,6 +185,40 @@ struct cfg_key_value_map scfg_key_map[] = { } }; +static bool +handle_dns(struct cfg *cfg, struct server *scfg, const char *type, + struct cfg_value *value, dns_callback_t *async_cb, + bool (*sync_cb)(struct cfg *, struct server *, struct saddr *)) +{ + struct saddr *saddr, *tmp; + struct dns_async *dns; + + switch (value->type) { + case CFG_VAL_TYPE_ADDRS: + debug(DBG_DNS, "%s: got immediate addrs\n", type); + + list_for_each_entry_safe(saddr, tmp, &value->saddrs, list) { + list_del(&saddr->list); + sync_cb(cfg, scfg, saddr); + } + return true; + + case CFG_VAL_TYPE_ASYNC_ADDRS: + debug(DBG_DNS, "%s: doing async lookup of DNS record: %p\n", + type, value->dns_async); + + dns = value->dns_async; + dns->callback = async_cb; + dns->priv = scfg; + list_add(&dns->list, &scfg->dnslookups); + uring_task_get(cfg, &scfg->task); + return true; + + default: + return false; + } +} + static void scfg_parse(struct cfg *cfg, struct server *scfg) { @@ -106,7 +230,7 @@ scfg_parse(struct cfg *cfg, struct server *scfg) while (true) { int key; const char *keyname; - union cfg_value value; + struct cfg_value value; if (!config_parse_line(cfg, scfg->name, &pos, scfg_key_map, &key, &keyname, &value)) @@ -139,25 +263,17 @@ scfg_parse(struct cfg *cfg, struct server *scfg) return; break; - case SCFG_KEY_LOCAL: { - struct saddr *saddr, *tmp; - - list_for_each_entry_safe(saddr, tmp, &value.saddrs, list) { - list_del(&saddr->list); - server_add_local(cfg, scfg, saddr); - } + case SCFG_KEY_LOCAL: + if (!handle_dns(cfg, scfg, "local", &value, + scfg_local_dns_cb, server_add_local)) + return; break; - } - - case SCFG_KEY_REMOTE: { - struct saddr *saddr, *tmp; - list_for_each_entry_safe(saddr, tmp, &value.saddrs, list) { - list_del(&saddr->list); - server_add_remote(cfg, scfg, saddr); - } + case SCFG_KEY_REMOTE: + if (!handle_dns(cfg, scfg, "remote", &value, + scfg_remote_dns_cb, server_add_remote)) + return; break; - } case SCFG_KEY_IDLE_TIMEOUT: if (!server_set_idle_timeout(cfg, scfg, value.uint16)) @@ -197,15 +313,11 @@ scfg_parse(struct cfg *cfg, struct server *scfg) return; break; - case SCFG_KEY_RCON: { - struct saddr *saddr, *tmp; - - list_for_each_entry_safe(saddr, tmp, &value.saddrs, list) { - list_del(&saddr->list); - server_add_rcon(cfg, scfg, saddr); - } + case SCFG_KEY_RCON: + if (!handle_dns(cfg, scfg, "rcon", &value, + scfg_rcon_dns_cb, server_add_rcon)) + return; break; - } case SCFG_KEY_RCON_PASSWORD: if (!server_set_rcon_password(cfg, scfg, value.str)) @@ -1,11 +1,9 @@ +#define _GNU_SOURCE #include <stdio.h> #include <ctype.h> #include <string.h> #include <stdbool.h> #include <errno.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <netdb.h> #include <unistd.h> #include <arpa/inet.h> #include <inttypes.h> @@ -22,7 +20,7 @@ eat_whitespace_and_comments(char **pos) while (isspace(**pos)) (*pos)++; - if (**pos != '#') { + if (**pos != '#') return; while (**pos != '\r' && **pos != '\n' && **pos != '\0') @@ -60,15 +58,119 @@ get_line(char **pos) } static bool -strtosockaddrs(const char *str, struct list_head *list) +dnslookup(const char *name, uint16_t port, struct cfg_value *rvalue, bool async) { - char *tmp; - uint16_t port; + struct sockaddr_in *in4; + struct sockaddr_in6 *in6; + struct dns_async tmp; + struct dns_async *dns; + int mode = async ? GAI_NOWAIT : GAI_WAIT; + struct addrinfo *results = NULL, *ai; + struct saddr *saddr = NULL; + bool rv = false; int r; + + if (port < 1 || strlen(name) >= sizeof(dns->name)) + goto out; + + if (async) { + rvalue->type = CFG_VAL_TYPE_ASYNC_ADDRS; + rvalue->dns_async = NULL; + dns = zmalloc(sizeof(*dns)); + if (!dns) { + error("async DNS lookup of %s failed: %m\n", name); + goto out; + } + debug(DBG_DNS, "doing async DNS lookup of %s: %p\n", name, dns); + } else { + memset(&tmp, 0, sizeof(tmp)); + dns = &tmp; + debug(DBG_DNS, "doing sync DNS lookup of %s\n", name); + } + + sprintf(dns->name, "%s", name); + sprintf(dns->port, "%" PRIu16, port); + + dns->req.ai_family = AF_UNSPEC; + dns->req.ai_socktype = SOCK_STREAM; + dns->req.ai_protocol = 0; + dns->req.ai_flags = AI_NUMERICSERV; + + dns->sev.sigev_notify = SIGEV_SIGNAL; + dns->sev.sigev_signo = SIGUSR1; + dns->sev.sigev_value.sival_ptr = dns; + + dns->gcb.ar_name = dns->name; + dns->gcb.ar_service = dns->port; + dns->gcb.ar_request = &dns->req; + + struct gaicb *gcbs[] = { &dns->gcb }; + + r = getaddrinfo_a(mode, gcbs, ARRAY_SIZE(gcbs), &dns->sev); + if (r != 0) { + error("getaddrinfo(%s:%" PRIu16 "): %s\n", name, port, gai_strerror(r)); + goto out; + } + + if (async) { + rvalue->dns_async = dns; + rv = true; + goto out; + } + + results = dns->gcb.ar_result; + + for (ai = results; ai; ai = ai->ai_next) { + saddr = zmalloc(sizeof(*saddr)); + if (!saddr) { + error("sync DNS lookup of %s failed: %m\n", name); + goto out; + } + + switch (ai->ai_family) { + case AF_INET: + in4 = (struct sockaddr_in *)ai->ai_addr; + saddr_set_ipv4(saddr, in4->sin_addr.s_addr, in4->sin_port); + error("addrstr: %s\n", saddr->addrstr); + list_add(&saddr->list, &rvalue->saddrs); + break; + + case AF_INET6: + in6 = (struct sockaddr_in6 *)ai->ai_addr; + saddr_set_ipv6(saddr, &in6->sin6_addr, in6->sin6_port); + error("addrstr: %s\n", saddr->addrstr); + list_add(&saddr->list, &rvalue->saddrs); + break; + + default: + error("getaddrinfo(%s:%s): unknown address family (%i)\n", + dns->name, dns->port, ai->ai_family); + xfree(saddr); + break; + } + } + + rv = true; + +out: + freeaddrinfo(results); + return rv; +} + +static bool +strtosockaddrs(const char *str, struct cfg_value *rvalue, bool async) +{ struct saddr *saddr; + uint16_t port; + char *tmp; + struct list_head *list; + unsigned naddrs = 0; - if (!str || *str == '\0' || !list) + if (!str || *str == '\0' || !rvalue) return false; + + rvalue->type = CFG_VAL_TYPE_ADDRS; + list = &rvalue->saddrs; list_init(list); if (*str == '[') { @@ -102,6 +204,7 @@ strtosockaddrs(const char *str, struct list_head *list) goto error; saddr_set_ipv6(saddr, NULL, htons(port)); + naddrs++; } else if (*str == '*') { /* IPv4, *:p */ @@ -121,6 +224,7 @@ strtosockaddrs(const char *str, struct list_head *list) saddr_set_ipv4(saddr, INADDR_ANY, htons(port)); list_add(&saddr->list, list); + naddrs++; } else if ((tmp = strchr(str, ':'))) { /* IPv4, a.b.c.d:p or IPv4/6 hostname:p */ @@ -136,63 +240,17 @@ strtosockaddrs(const char *str, struct list_head *list) goto error; if (inet_pton(AF_INET, str, &saddr->in4.sin_addr) > 0) { - debug(DBG_CFG, "got an IPv4:port (%s)\n", str); + debug(DBG_CFG, "got an IPv4:port (%s:%" PRIu16 ")\n", str, port); saddr_set_ipv4(saddr, saddr->in4.sin_addr.s_addr, htons(port)); list_add(&saddr->list, list); + naddrs++; goto success; - } else { - xfree(saddr); - } + } - struct addrinfo hints = { - .ai_family = AF_UNSPEC, - .ai_socktype = SOCK_STREAM, - .ai_protocol = 0, - .ai_flags = 0, - }; - struct addrinfo *results, *ai; - - /* FIXME: This is completely synchronous but getaddrinfo_a is not very ergonomic */ - r = getaddrinfo(str, tmp, &hints, &results); - if (r != 0) { - error("getaddrinfo(%s): %s\n", str, gai_strerror(r)); + xfree(saddr); + debug(DBG_CFG, "maybe got a hostname:port (%s:%" PRIu16 ")\n", str, port); + if (!dnslookup(str, port, rvalue, async)) goto error; - } - - debug(DBG_CFG, "got a hostname:port (%s)\n", str); - for (ai = results; ai; ai = ai->ai_next) { - saddr = zmalloc(sizeof(*saddr)); - if (!saddr) { - freeaddrinfo(results); - goto error; - } - - switch (ai->ai_family) { - case AF_INET: { - struct sockaddr_in *in4 = (struct sockaddr_in *)ai->ai_addr; - - saddr_set_ipv4(saddr, in4->sin_addr.s_addr, htons(port)); - list_add(&saddr->list, list); - break; - } - - case AF_INET6: { - struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)ai->ai_addr; - - saddr_set_ipv6(saddr, &in6->sin6_addr, in6->sin6_port); - list_add(&saddr->list, list); - break; - } - - default: - error("getaddrinfo(%s): unknown address family (%i)\n", - str, ai->ai_family); - xfree(saddr); - break; - } - } - - freeaddrinfo(results); } else if (strtou16_strict(tmp, &port) == 0) { /* Port */ @@ -204,6 +262,7 @@ strtosockaddrs(const char *str, struct list_head *list) saddr_set_ipv6(saddr, &in6addr_any, htons(port)); list_add(&saddr->list, list); + naddrs++; saddr = zmalloc(sizeof(*saddr)); if (!saddr) @@ -211,6 +270,7 @@ strtosockaddrs(const char *str, struct list_head *list) saddr_set_ipv4(saddr, INADDR_ANY, htons(port)); list_add(&saddr->list, list); + naddrs++; } else { /* Unknown */ @@ -219,22 +279,28 @@ strtosockaddrs(const char *str, struct list_head *list) } success: - if (list_empty(list)) { - error("empty address list!?\n"); - return false; - } else { - int i = 0; + switch (rvalue->type) { + case CFG_VAL_TYPE_ADDRS: + if (list_empty(list) || naddrs == 0) { + error("empty address list!?\n"); + return false; + } - list_for_each_entry(saddr, list, list) - i++; + debug(DBG_CFG, "parsed to %u addresses\n", naddrs); + return true; - debug(DBG_CFG, "parsed to %i addresses\n", i); - } + case CFG_VAL_TYPE_ASYNC_ADDRS: + debug(DBG_CFG, "looking up address asynchronously\n"); + return true; - return true; + default: + error("invalid rvalue type\n"); + rvalue->type = CFG_VAL_TYPE_INVALID; + break; + } error: - if (!list_empty(list)) { + if (rvalue->type == CFG_VAL_TYPE_ADDRS && !list_empty(list)) { struct saddr *tmp; list_for_each_entry_safe(saddr, tmp, list, list) { @@ -249,7 +315,7 @@ error: bool config_parse_line(struct cfg *cfg, const char *filename, char **buf, struct cfg_key_value_map *kvmap, int *rkey, - const char **rkeyname, union cfg_value *rvalue) + const char **rkeyname, struct cfg_value *rvalue) { char *line, *tmp, *key; int i; @@ -301,6 +367,7 @@ config_parse_line(struct cfg *cfg, const char *filename, char **buf, switch (kvmap[i].value_type) { case CFG_VAL_TYPE_STRING: + rvalue->type = CFG_VAL_TYPE_STRING; rvalue->str = tmp; break; @@ -309,26 +376,61 @@ config_parse_line(struct cfg *cfg, const char *filename, char **buf, if (strtou16_strict(tmp, &v) < 0) goto error; + + rvalue->type = CFG_VAL_TYPE_UINT16; rvalue->uint16 = v; break; } - case CFG_VAL_TYPE_ADDRS: { - if (!strtosockaddrs(tmp, &rvalue->saddrs)) + case CFG_VAL_TYPE_ADDRS: + if (!strtosockaddrs(tmp, rvalue, false)) + goto error; + + if (rvalue->type != CFG_VAL_TYPE_ADDRS) { + error("invalid type returned from strtosockaddrs\n"); goto error; + } + if (list_empty(&rvalue->saddrs)) { error("empty address list\n"); goto error; } break; - } + + case CFG_VAL_TYPE_ASYNC_ADDRS: + if (!strtosockaddrs(tmp, rvalue, true)) + goto error; + + switch (rvalue->type) { + case CFG_VAL_TYPE_ADDRS: + if (list_empty(&rvalue->saddrs)) { + error("empty address list\n"); + goto error; + } + break; + + case CFG_VAL_TYPE_ASYNC_ADDRS: + if (!rvalue->dns_async) { + error("dns_async not set\n"); + goto error; + } + break; + + default: + error("invalid type returned from strtosockaddrs\n"); + goto error; + } + + break; case CFG_VAL_TYPE_BOOL: - if (!strcasecmp(tmp, "yes")) + if (!strcasecmp(tmp, "yes") || !strcasecmp(tmp, "true")) { + rvalue->type = CFG_VAL_TYPE_BOOL; rvalue->boolean = true; - else if (!strcasecmp(tmp, "no")) + } else if (!strcasecmp(tmp, "no") || !strcasecmp(tmp, "false")) { + rvalue->type = CFG_VAL_TYPE_BOOL; rvalue->boolean = false; - else { + } else { error("invalid boolean value (%s)\n", tmp); goto error; } @@ -340,6 +442,14 @@ config_parse_line(struct cfg *cfg, const char *filename, char **buf, goto error; } + /* sanity check */ + if ((rvalue->type != kvmap[i].value_type) && + ((kvmap[i].value_type != CFG_VAL_TYPE_ASYNC_ADDRS) && + (rvalue->type != CFG_VAL_TYPE_ADDRS))) { + error("rvalue->type != kvmap->type\n"); + goto error; + } + *rkey = kvmap[i].key_value; *rkeyname = kvmap[i].key_name; return true; @@ -348,6 +458,7 @@ config_parse_line(struct cfg *cfg, const char *filename, char **buf, error: /* FIXME: the line is already mangled here, a line number would be nice */ error("%s: invalid config line: %s\n", filename, line); + rvalue->type = CFG_VAL_TYPE_INVALID; *rkey = 0; *rkeyname = NULL; return true; @@ -1,31 +1,57 @@ #ifndef fooconfighfoo #define fooconfighfoo +#define _GNU_SOURCE +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include <signal.h> + enum cfg_value_type { CFG_VAL_TYPE_INVALID, CFG_VAL_TYPE_STRING, CFG_VAL_TYPE_UINT16, CFG_VAL_TYPE_ADDRS, + CFG_VAL_TYPE_ASYNC_ADDRS, CFG_VAL_TYPE_BOOL, }; +struct dns_async; + +typedef void (dns_callback_t)(struct dns_async *); + +struct dns_async { + char name[FQDN_STR_LEN + 1]; + char port[PORT_STR_LEN + 1]; + struct addrinfo req; + struct gaicb gcb; + struct sigevent sev; + dns_callback_t *callback; + void *priv; + struct list_head list; +}; + struct cfg_key_value_map { const char *key_name; int key_value; enum cfg_value_type value_type; }; -union cfg_value { - const char *str; - uint16_t uint16; - struct list_head saddrs; - bool boolean; +struct cfg_value { + enum cfg_value_type type; + union { + const char *str; + uint16_t uint16; + struct list_head saddrs; + struct dns_async *dns_async; + bool boolean; + }; }; bool config_parse_line(struct cfg *cfg, const char *filename, char **buf, struct cfg_key_value_map *kvmap, int *rkey, const char **rkeyname, - union cfg_value *rvalue); + struct cfg_value *rvalue); bool config_parse_header(struct cfg *cfg, const char *filename, const char *title, char **buf); @@ -255,7 +255,7 @@ cfg_read(struct cfg *cfg) while (true) { int key; const char *keyname; - union cfg_value value; + struct cfg_value value; if (!config_parse_line(cfg, path, &pos, mcfg_key_map, &key, &keyname, &value)) @@ -276,6 +276,7 @@ cfg_read(struct cfg *cfg) cfg->igmp_iface = xstrdup(value.str); if (!cfg->igmp_iface) perrordie("xstrdup"); + break; case MCFG_KEY_INVALID: @@ -291,40 +292,46 @@ const struct { } debug_category_str[] = { { .name = "config", - .val = DBG_CFG + .val = DBG_CFG, },{ .name = "refcount", - .val = DBG_REF + .val = DBG_REF, },{ .name = "malloc", - .val = DBG_MALLOC + .val = DBG_MALLOC, },{ .name = "announce", - .val = DBG_ANN + .val = DBG_ANN, },{ - .name = "signals", - .val = DBG_SIG + .name = "signal", + .val = DBG_SIG, },{ .name = "uring", - .val = DBG_UR + .val = DBG_UR, },{ .name = "server", - .val = DBG_SRV + .val = DBG_SRV, },{ .name = "proxy", - .val = DBG_PROXY + .val = DBG_PROXY, },{ .name = "rcon", - .val = DBG_RCON + .val = DBG_RCON, },{ .name = "idle", - .val = DBG_IDLE + .val = DBG_IDLE, },{ .name = "igmp", - .val = DBG_IGMP + .val = DBG_IGMP, + },{ + .name = "systemd", + .val = DBG_SYSD, + },{ + .name = "dns", + .val = DBG_DNS, },{ .name = NULL, - .val = 0 + .val = 0, } }; @@ -561,24 +568,19 @@ dump_tree(struct cfg *cfg) debug(DBG_REF, "\n\n\n\n"); } +static struct dns_async *hack_dns = NULL; + static void signalfd_read(struct cfg *cfg, struct uring_task *task, int res) { struct signalfd_ev *sev = container_of(task, struct signalfd_ev, task); struct server *server, *stmp; - static int count = 0; if (task->dead) { debug(DBG_SIG, "task dead\n"); return; } - count++; - if (count > 5) { - error("Max signal count exceeded, force quitting\n"); - exit(EXIT_FAILURE); - } - if (res != sizeof(sev->buf)) die("error in signalfd (%i)", res); @@ -586,7 +588,7 @@ signalfd_read(struct cfg *cfg, struct uring_task *task, int res) verbose("got a signal to quit\n"); sd_notifyf(0, "STOPPING=1\nSTATUS=Received signal, exiting"); exit(EXIT_SUCCESS); - } else { + } else if (sev->buf < 10000) { verbose("got a signal to dump tree\n"); sd_notifyf(0, "STOPPING=1\nSTATUS=Received signal, exiting"); dump_tree(cfg); @@ -598,25 +600,45 @@ signalfd_read(struct cfg *cfg, struct uring_task *task, int res) server_delete(cfg, server); uring_delete(cfg); return; + } else { + debug(DBG_DNS, "DNS lookup complete, dns: %p, dns->cb: %p\n", + hack_dns, + hack_dns ? hack_dns->callback : NULL); + + if (!hack_dns || !hack_dns->callback) { + error("DNS callback not set\n"); + goto out; + } + + if (hack_dns && hack_dns->callback) + hack_dns->callback(hack_dns); } +out: uring_read(cfg, &sev->task, &sev->buf, sizeof(sev->buf), signalfd_read); } static int hack_efd = -1; static void -hack_handler(int signum) +hack_handler(int signum, siginfo_t *info, void *ucontext) { uint64_t val; - static int count = 0; switch (signum) { + case SIGUSR1: + debug(DBG_SIG, "Got a SIGUSR1\n"); + if (info->si_code != SI_ASYNCNL || info->si_signo != SIGUSR1 || !info->si_ptr) { + debug(DBG_SIG, "unexpected values in siginfo\n"); + return; + } + debug(DBG_SIG, "SIGUSR1 struct dns_async: %p\n", info->si_ptr); + hack_dns = info->si_ptr; + val = 10000; + break; case SIGINT: debug(DBG_SIG, "Got a SIGINT\n"); val = 1000; - if (count > 3) - dump_tree(cfghack); break; case SIGHUP: debug(DBG_SIG, "Got a SIGHUP\n"); @@ -632,13 +654,7 @@ hack_handler(int signum) break; } - if (count > 5) { - error("Max signal count exceeded, force quitting\n"); - exit(EXIT_FAILURE); - } - write(hack_efd, &val, sizeof(val)); - count++; } static void @@ -664,14 +680,16 @@ signalfd_init(struct cfg *cfg) struct sigaction action; sigfillset(&action.sa_mask); - action.sa_handler = hack_handler; - action.sa_flags = 0; + action.sa_sigaction = hack_handler; + action.sa_flags = SA_SIGINFO; sigaction(SIGINT, &action, NULL); sigaction(SIGHUP, &action, NULL); sigaction(SIGTERM, &action, NULL); + sigaction(SIGUSR1, &action, NULL); action.sa_handler = SIG_IGN; + action.sa_flags = 0; sigaction(SIGPIPE, &action, NULL); sfd = eventfd(0, EFD_CLOEXEC); @@ -28,6 +28,7 @@ enum debug_lvl { DBG_IDLE = (0x1 << 12), DBG_IGMP = (0x1 << 13), DBG_SYSD = (0x1 << 14), + DBG_DNS = (0x1 << 15), }; static inline bool diff --git a/meson.build b/meson.build index 2425c45..74da814 100644 --- a/meson.build +++ b/meson.build @@ -22,6 +22,7 @@ executable('ctest', 'ctest.c') executable('stest', 'stest.c') executable('mcproxy', mcproxy_sources, + link_args: [ '-lanl' ], dependencies : [liburing, libsystemd, libcapng]) @@ -11,7 +11,6 @@ #include "main.h" #include "uring.h" -#include "config.h" #include "server.h" #include "rcon.h" @@ -19,6 +19,7 @@ #include "server.h" #include "proxy.h" #include "utils.h" +#include "config.h" #include "idle.h" #include "rcon.h" #include "systemd.h" @@ -85,6 +86,7 @@ server_delete(struct cfg *cfg, struct server *scfg) struct saddr *remote; struct saddr *rcon; struct saddr *tmp; + struct dns_async *dns, *dtmp; verbose("Removing server %s\n", scfg->name); @@ -107,6 +109,9 @@ server_delete(struct cfg *cfg, struct server *scfg) xfree(remote); } + list_for_each_entry_safe(dns, dtmp, &scfg->dnslookups, list) + gai_cancel(&dns->gcb); + uring_poll_cancel(cfg, &scfg->exec_task); uring_task_put(cfg, &scfg->exec_task); uring_task_destroy(cfg, &scfg->task); @@ -429,18 +434,32 @@ server_commit(struct cfg *cfg, struct server *scfg) return false; } + if (!list_empty(&scfg->dnslookups)) { + debug(DBG_SRV, "called with pending DNS requests\n"); + return true; + } + /* FIXME: running? */ if (scfg->stop_method == SERVER_STOP_METHOD_RCON && - (list_empty(&scfg->rcons) || !scfg->rcon_password)) { - error("%s: rcon stop method but missing rcon password\n", scfg->name); + list_empty(&scfg->rcons)) { + error("%s: rcon stop method missing rcon address\n", + scfg->name); + return false; + } + + if (scfg->stop_method == SERVER_STOP_METHOD_RCON && + !scfg->rcon_password) { + error("%s: rcon stop method missing rcon password\n", + scfg->name); return false; } if ((scfg->start_method == SERVER_START_METHOD_SYSTEMD || scfg->stop_method == SERVER_STOP_METHOD_SYSTEMD) && !scfg->systemd_service) { - error("%s: systemd start/stop method but missing systemd service\n", scfg->name); + error("%s: systemd start/stop method missing systemd service\n", + scfg->name); return false; } @@ -558,6 +577,7 @@ server_commit(struct cfg *cfg, struct server *scfg) systemd_service_running(cfg, scfg); } + debug(DBG_SRV, "success\n"); return true; } @@ -567,6 +587,7 @@ server_add_remote(struct cfg *cfg, struct server *scfg, struct saddr *remote) if (!scfg || !remote) return false; + debug(DBG_SRV, "adding remote: %s\n", remote->addrstr); list_add(&remote->list, &scfg->remotes); return true; } @@ -590,6 +611,7 @@ server_add_local(struct cfg *cfg, struct server *scfg, struct saddr *saddr) local->local = *saddr; uring_task_init(&local->task, "local", &scfg->task, server_local_free); + debug(DBG_SRV, "adding local: %s\n", saddr->addrstr); list_add(&local->list, &scfg->locals); xfree(saddr); return true; @@ -601,6 +623,7 @@ server_add_rcon(struct cfg *cfg, struct server *scfg, struct saddr *rcon) if (!scfg || !rcon) return false; + debug(DBG_SRV, "adding rcon: %s\n", rcon->addrstr); list_add(&rcon->list, &scfg->rcons); return true; } @@ -740,6 +763,7 @@ server_new(struct cfg *cfg, const char *name) return NULL; } + scfg->cfg = cfg; scfg->type = SERVER_TYPE_UNDEFINED; scfg->name = xstrdup(name); scfg->running = false; @@ -752,6 +776,7 @@ server_new(struct cfg *cfg, const char *name) list_init(&scfg->locals); list_init(&scfg->proxys); list_init(&scfg->rcons); + list_init(&scfg->dnslookups); scfg->idle_timeout = 0; list_add(&scfg->list, &cfg->servers); @@ -29,7 +29,9 @@ struct server { struct list_head remotes; struct list_head proxys; struct list_head rcons; + struct list_head dnslookups; bool running; + struct cfg *cfg; enum server_stop_method stop_method; enum server_start_method start_method; @@ -7,7 +7,6 @@ #include "main.h" #include "uring.h" -#include "config.h" struct uring_ev { struct io_uring uring; @@ -25,7 +25,17 @@ void __xfree(const char *fn, int line, void *ptr); void debug_resource_usage(); -#define ADDRSTRLEN (9 /*strlen("AF_INETX ")*/ + INET6_ADDRSTRLEN + 6 /*strlen(" 65535")*/ + 1) +/* Length of longest DNS name = 253 + trailing dot */ +#define FQDN_STR_LEN 254 + +/* Length of longest port string = strlen("65535") */ +#define PORT_STR_LEN 5 + +/* Length of longest address family string = strlen("AF_INETX") */ +#define AF_STR_LEN 8 + +/* Length of longest addrstr, format = "AF_INETX <IPADDR> <PORT> */ +#define ADDRSTRLEN (AF_STR_LEN + 1 + INET6_ADDRSTRLEN + 1 + PORT_STR_LEN + 1) struct saddr { union { struct sockaddr_storage storage; |