summaryrefslogtreecommitdiff
path: root/config.c
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 /config.c
parent445647adc4475c0b8264ce8b6c97d748eec69e7b (diff)
Add support for async DNS
Diffstat (limited to 'config.c')
-rw-r--r--config.c267
1 files changed, 189 insertions, 78 deletions
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;