summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Härdeman <david@hardeman.nu>2020-06-19 19:11:48 +0200
committerDavid Härdeman <david@hardeman.nu>2020-06-19 19:11:48 +0200
commit91a7ca50f3f8a2c7bb01113fa3849cb5e153a70f (patch)
tree18c5b7c76f4ec3069e9033a1c222eefd2c945da6
parent445647adc4475c0b8264ce8b6c97d748eec69e7b (diff)
Add support for async DNS
-rw-r--r--announce.c1
-rw-r--r--cfgdir.c174
-rw-r--r--config.c267
-rw-r--r--config.h38
-rw-r--r--main.c86
-rw-r--r--main.h1
-rw-r--r--meson.build1
-rw-r--r--rcon.c1
-rw-r--r--server.c31
-rw-r--r--server.h2
-rw-r--r--uring.c1
-rw-r--r--utils.h12
12 files changed, 459 insertions, 156 deletions
diff --git a/announce.c b/announce.c
index e957ad2..b90d983 100644
--- a/announce.c
+++ b/announce.c
@@ -7,7 +7,6 @@
#include "main.h"
#include "uring.h"
-#include "config.h"
#include "announce.h"
#include "server.h"
diff --git a/cfgdir.c b/cfgdir.c
index 436fe13..d6c48e1 100644
--- a/cfgdir.c
+++ b/cfgdir.c
@@ -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))
diff --git a/config.c b/config.c
index aece8b1..199ba0b 100644
--- a/config.c
+++ b/config.c
@@ -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;
diff --git a/config.h b/config.h
index 1868217..4e43e7b 100644
--- a/config.h
+++ b/config.h
@@ -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);
diff --git a/main.c b/main.c
index e748ff6..b2accc1 100644
--- a/main.c
+++ b/main.c
@@ -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);
diff --git a/main.h b/main.h
index d985c9a..a47eaa6 100644
--- a/main.h
+++ b/main.h
@@ -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])
diff --git a/rcon.c b/rcon.c
index bc8c681..33fcdb7 100644
--- a/rcon.c
+++ b/rcon.c
@@ -11,7 +11,6 @@
#include "main.h"
#include "uring.h"
-#include "config.h"
#include "server.h"
#include "rcon.h"
diff --git a/server.c b/server.c
index 5025f18..690c0ca 100644
--- a/server.c
+++ b/server.c
@@ -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);
diff --git a/server.h b/server.h
index a9d5a51..6f09705 100644
--- a/server.h
+++ b/server.h
@@ -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;
diff --git a/uring.c b/uring.c
index b7450d6..cc2b02d 100644
--- a/uring.c
+++ b/uring.c
@@ -7,7 +7,6 @@
#include "main.h"
#include "uring.h"
-#include "config.h"
struct uring_ev {
struct io_uring uring;
diff --git a/utils.h b/utils.h
index ccd4b6d..f87159e 100644
--- a/utils.h
+++ b/utils.h
@@ -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;