From e25f5166a3edc98f4a333c98881d4899635f1144 Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Thu, 23 May 2019 23:02:43 +0200 Subject: [PATCH] Add an asynchronous DNS thread. Add a thread dedicated to making DNS lookups. There are two queues, one for pending DNS requests and one for done DNS requests. The async DNS thread reads from the pending request queue, checks for each request if the deadline has not been met yet, and if so calls getaddrinfo(). Once the result is obtained, it adds that to the done request queue and signals the main meshlink thread, which will then call the callback function associated with the DNS request. --- src/Makefile.am | 1 + src/adns.c | 129 ++++++++++++++++++++++++++++++++++++++++ src/adns.h | 31 ++++++++++ src/meshlink.c | 3 + src/meshlink_internal.h | 7 +++ src/meshlink_queue.h | 28 ++++++++- src/net_setup.c | 2 +- src/netutl.c | 2 +- 8 files changed, 199 insertions(+), 4 deletions(-) create mode 100644 src/adns.c create mode 100644 src/adns.h diff --git a/src/Makefile.am b/src/Makefile.am index bfbee514..d855d3a5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -36,6 +36,7 @@ pkginclude_HEADERS = meshlink++.h meshlink.h libmeshlink_la_LDFLAGS = -export-symbols $(srcdir)/meshlink.sym libmeshlink_la_SOURCES = \ + adns.c adns.h \ buffer.c buffer.h \ conf.c conf.h \ connection.c connection.h \ diff --git a/src/adns.c b/src/adns.c new file mode 100644 index 00000000..6e988c01 --- /dev/null +++ b/src/adns.c @@ -0,0 +1,129 @@ +/* + dns.c -- hostname resolving functions + Copyright (C) 2019 Guus Sliepen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "system.h" + +#include + +#include "adns.h" +#include "logger.h" +#include "xalloc.h" + +typedef struct adns_item { + adns_cb_t cb; + void *data; + time_t deadline; + struct addrinfo *ai; + int err; + char *host; + char *serv; +} adns_item_t; + +static void *adns_loop(void *data) { + meshlink_handle_t *mesh = data; + + while(true) { + adns_item_t *item = meshlink_queue_pop_cond(&mesh->adns_queue, &mesh->adns_cond); + + if(!item) { + break; + } + + if(time(NULL) < item->deadline) { + logger(mesh, MESHLINK_DEBUG, "Resolving %s port %s", item->host, item->serv); + int result = getaddrinfo(item->host, item->serv, NULL, &item->ai); + + if(result) { + item->ai = NULL; + item->err = errno; + } + } else { + logger(mesh, MESHLINK_WARNING, "Deadline passed for DNS request %s port %s", item->host, item->serv); + item->ai = NULL; + item->err = ETIMEDOUT; + } + + if(meshlink_queue_push(&mesh->adns_done_queue, item)) { + signal_trigger(&mesh->loop, &mesh->adns_signal); + } else { + free(item->host); + free(item->serv); + free(item); + } + } + + return NULL; +} + +static void adns_cb_handler(event_loop_t *loop, void *data) { + (void)loop; + meshlink_handle_t *mesh = data; + + for(adns_item_t *item; (item = meshlink_queue_pop(&mesh->adns_done_queue));) { + item->cb(mesh, item->host, item->serv, item->data, item->ai, item->err); + free(item); + } +} + +extern void init_adns(meshlink_handle_t *mesh) { + signal_add(&mesh->loop, &mesh->adns_signal, adns_cb_handler, mesh, 1); + meshlink_queue_init(&mesh->adns_queue); + pthread_create(&mesh->adns_thread, NULL, adns_loop, mesh); +} + +extern void exit_adns(meshlink_handle_t *mesh) { + if(!mesh->adns_signal.cb) { + return; + } + + /* Drain the queue of any pending ADNS requests */ + for(adns_item_t *item; (item = meshlink_queue_pop(&mesh->adns_queue));) { + free(item->host); + free(item->serv); + free(item); + } + + /* Signal the ADNS thread to stop */ + if(!meshlink_queue_push(&mesh->adns_queue, NULL)) { + abort(); + } + + pthread_cond_signal(&mesh->adns_cond); + + pthread_join(mesh->adns_thread, NULL); + meshlink_queue_exit(&mesh->adns_queue); + signal_del(&mesh->loop, &mesh->adns_signal); +} + +extern void adns_queue(meshlink_handle_t *mesh, char *host, char *serv, adns_cb_t cb, void *data, int timeout) { + adns_item_t *item = xmalloc(sizeof(*item)); + item->cb = cb; + item->data = data; + item->deadline = time(NULL) + timeout; + item->host = host; + item->serv = serv; + + logger(mesh, MESHLINK_DEBUG, "Enqueueing DNS request for %s port %s", item->host, item->serv); + + if(!meshlink_queue_push(&mesh->adns_queue, item)) { + abort(); + } + + pthread_cond_signal(&mesh->adns_cond); +} diff --git a/src/adns.h b/src/adns.h new file mode 100644 index 00000000..aed15d86 --- /dev/null +++ b/src/adns.h @@ -0,0 +1,31 @@ +#ifndef MESHLINK_ADNS_H +#define MESHLINK_ADNS_H + +/* + adns.h -- header file for adns.c + Copyright (C) 2019 Guus Sliepen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "meshlink_internal.h" + +typedef void (*adns_cb_t)(meshlink_handle_t *mesh, char *host, char *serv, void *data, struct addrinfo *ai, int err); + +extern void init_adns(meshlink_handle_t *mesh); +extern void exit_adns(meshlink_handle_t *mesh); +extern void adns_queue(meshlink_handle_t *mesh, char *host, char *serv, adns_cb_t cb, void *data, int timeout); + +#endif diff --git a/src/meshlink.c b/src/meshlink.c index ef46a952..16f01c28 100644 --- a/src/meshlink.c +++ b/src/meshlink.c @@ -20,6 +20,7 @@ #include "system.h" #include +#include "adns.h" #include "crypto.h" #include "ecdsagen.h" #include "logger.h" @@ -1673,6 +1674,7 @@ bool meshlink_start(meshlink_handle_t *mesh) { } init_outgoings(mesh); + init_adns(mesh); // Start the main thread @@ -1743,6 +1745,7 @@ void meshlink_stop(meshlink_handle_t *mesh) { } } + exit_adns(mesh); exit_outgoings(mesh); // Ensure we are considered unreachable diff --git a/src/meshlink_internal.h b/src/meshlink_internal.h index f324c106..8a632e26 100644 --- a/src/meshlink_internal.h +++ b/src/meshlink_internal.h @@ -193,6 +193,13 @@ struct meshlink_handle { char *catta_servicetype; unsigned int catta_interfaces; + // ADNS + pthread_t adns_thread; + pthread_cond_t adns_cond; + meshlink_queue_t adns_queue; + meshlink_queue_t adns_done_queue; + signal_t adns_signal; + // Proxy configuration, currently not exposed. char *proxyhost; char *proxyport; diff --git a/src/meshlink_queue.h b/src/meshlink_queue.h index d075f1c8..b6e63c67 100644 --- a/src/meshlink_queue.h +++ b/src/meshlink_queue.h @@ -72,7 +72,7 @@ static inline __attribute__((__warn_unused_result__)) bool meshlink_queue_push(m static inline __attribute__((__warn_unused_result__)) void *meshlink_queue_pop(meshlink_queue_t *queue) { meshlink_queue_item_t *item; - void *data; + pthread_mutex_lock(&queue->mutex); if((item = queue->head)) { @@ -84,7 +84,31 @@ static inline __attribute__((__warn_unused_result__)) void *meshlink_queue_pop(m } pthread_mutex_unlock(&queue->mutex); - data = item ? item->data : NULL; + + void *data = item ? item->data : NULL; + free(item); + return data; +} + +static inline __attribute__((__warn_unused_result__)) void *meshlink_queue_pop_cond(meshlink_queue_t *queue, pthread_cond_t *cond) { + meshlink_queue_item_t *item; + + pthread_mutex_lock(&queue->mutex); + + while(!queue->head) { + pthread_cond_wait(cond, &queue->mutex); + } + + item = queue->head; + queue->head = item->next; + + if(!queue->head) { + queue->tail = NULL; + } + + pthread_mutex_unlock(&queue->mutex); + + void *data = item->data; free(item); return data; } diff --git a/src/net_setup.c b/src/net_setup.c index 9cbdeb26..ceaeb0c5 100644 --- a/src/net_setup.c +++ b/src/net_setup.c @@ -422,7 +422,7 @@ static bool add_listen_sockets(meshlink_handle_t *mesh) { .ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM, .ai_protocol = IPPROTO_TCP, - .ai_flags = AI_PASSIVE, + .ai_flags = AI_PASSIVE | AI_NUMERICSERV, }; int err = getaddrinfo(NULL, mesh->myport, &hint, &ai); diff --git a/src/netutl.c b/src/netutl.c index f4ee5aa0..6e728350 100644 --- a/src/netutl.c +++ b/src/netutl.c @@ -57,7 +57,7 @@ sockaddr_t str2sockaddr(const char *address, const char *port) { struct addrinfo hint = { .ai_family = AF_UNSPEC, - .ai_flags = AI_NUMERICHOST, + .ai_flags = NI_NUMERICHOST | NI_NUMERICSERV, .ai_socktype = SOCK_STREAM, }; -- 2.39.5