From 91a7ca50f3f8a2c7bb01113fa3849cb5e153a70f Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Fri, 19 Jun 2020 19:11:48 +0200 Subject: Add support for async DNS --- cfgdir.c | 174 +++++++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 143 insertions(+), 31 deletions(-) (limited to 'cfgdir.c') 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 #include #include @@ -5,9 +6,6 @@ #include #include #include -#include -#include -#include #include #include #include @@ -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)) -- cgit v1.2.3