]> git.meshlink.io Git - meshlink/commitdiff
Add an asynchronous DNS thread.
authorGuus Sliepen <guus@meshlink.io>
Thu, 23 May 2019 21:02:43 +0000 (23:02 +0200)
committerGuus Sliepen <guus@meshlink.io>
Tue, 7 Apr 2020 22:51:51 +0000 (00:51 +0200)
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
src/adns.c [new file with mode: 0644]
src/adns.h [new file with mode: 0644]
src/meshlink.c
src/meshlink_internal.h
src/meshlink_queue.h
src/net_setup.c
src/netutl.c

index bfbee5141ec0136b7f8cceb2395009627d56342d..d855d3a5a0ffc13e683336bedd081aadc751cd5d 100644 (file)
@@ -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 (file)
index 0000000..6e988c0
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+    dns.c -- hostname resolving functions
+    Copyright (C) 2019 Guus Sliepen <guus@meshlink.io>
+
+    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 <pthread.h>
+
+#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 (file)
index 0000000..aed15d8
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef MESHLINK_ADNS_H
+#define MESHLINK_ADNS_H
+
+/*
+    adns.h -- header file for adns.c
+    Copyright (C) 2019 Guus Sliepen <guus@meshlink.io>
+
+    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
index ef46a952ef49343d5ac250c82d581f783147d20a..16f01c281899e044d226b6df073a0385b3f7f0ca 100644 (file)
@@ -20,6 +20,7 @@
 #include "system.h"
 #include <pthread.h>
 
+#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
index f324c10645b556cc20760b3d9227691341e05a0b..8a632e26f0527e676c6cdbf662a05df70fd73ce8 100644 (file)
@@ -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;
index d075f1c8497b887ea17cc0e171c39a6c4fcda7f0..b6e63c67da833ce48f1bfb02caf5ee28e158f89e 100644 (file)
@@ -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;
 }
index 9cbdeb2691409f1c23c7221f0e9bf4af3d5b9b83..ceaeb0c5b1fb9f0502c65e34d23931e44f0221c4 100644 (file)
@@ -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);
index f4ee5aa0f123151400b9628a1a0decd3a177b6a3..6e7283505b5bb7464f1561a7a6054a97e448c283 100644 (file)
@@ -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,
        };