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 --- config.c | 267 ++++++++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 189 insertions(+), 78 deletions(-) (limited to 'config.c') 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 #include #include #include #include -#include -#include -#include #include #include #include @@ -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; -- cgit v1.2.3