]> git.meshlink.io Git - meshlink/blob - src/adns.c
Add an asynchronous DNS thread.
[meshlink] / src / adns.c
1 /*
2     dns.c -- hostname resolving functions
3     Copyright (C) 2019 Guus Sliepen <guus@meshlink.io>
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License along
16     with this program; if not, write to the Free Software Foundation, Inc.,
17     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "system.h"
21
22 #include <pthread.h>
23
24 #include "adns.h"
25 #include "logger.h"
26 #include "xalloc.h"
27
28 typedef struct adns_item {
29         adns_cb_t cb;
30         void *data;
31         time_t deadline;
32         struct addrinfo *ai;
33         int err;
34         char *host;
35         char *serv;
36 } adns_item_t;
37
38 static void *adns_loop(void *data) {
39         meshlink_handle_t *mesh = data;
40
41         while(true) {
42                 adns_item_t *item = meshlink_queue_pop_cond(&mesh->adns_queue, &mesh->adns_cond);
43
44                 if(!item) {
45                         break;
46                 }
47
48                 if(time(NULL) < item->deadline) {
49                         logger(mesh, MESHLINK_DEBUG, "Resolving %s port %s", item->host, item->serv);
50                         int result = getaddrinfo(item->host, item->serv, NULL, &item->ai);
51
52                         if(result) {
53                                 item->ai = NULL;
54                                 item->err = errno;
55                         }
56                 } else {
57                         logger(mesh, MESHLINK_WARNING, "Deadline passed for DNS request %s port %s", item->host, item->serv);
58                         item->ai = NULL;
59                         item->err = ETIMEDOUT;
60                 }
61
62                 if(meshlink_queue_push(&mesh->adns_done_queue, item)) {
63                         signal_trigger(&mesh->loop, &mesh->adns_signal);
64                 } else {
65                         free(item->host);
66                         free(item->serv);
67                         free(item);
68                 }
69         }
70
71         return NULL;
72 }
73
74 static void adns_cb_handler(event_loop_t *loop, void *data) {
75         (void)loop;
76         meshlink_handle_t *mesh = data;
77
78         for(adns_item_t *item; (item = meshlink_queue_pop(&mesh->adns_done_queue));) {
79                 item->cb(mesh, item->host, item->serv, item->data, item->ai, item->err);
80                 free(item);
81         }
82 }
83
84 extern void init_adns(meshlink_handle_t *mesh) {
85         signal_add(&mesh->loop, &mesh->adns_signal, adns_cb_handler, mesh, 1);
86         meshlink_queue_init(&mesh->adns_queue);
87         pthread_create(&mesh->adns_thread, NULL, adns_loop, mesh);
88 }
89
90 extern void exit_adns(meshlink_handle_t *mesh) {
91         if(!mesh->adns_signal.cb) {
92                 return;
93         }
94
95         /* Drain the queue of any pending ADNS requests */
96         for(adns_item_t *item; (item = meshlink_queue_pop(&mesh->adns_queue));) {
97                 free(item->host);
98                 free(item->serv);
99                 free(item);
100         }
101
102         /* Signal the ADNS thread to stop */
103         if(!meshlink_queue_push(&mesh->adns_queue, NULL)) {
104                 abort();
105         }
106
107         pthread_cond_signal(&mesh->adns_cond);
108
109         pthread_join(mesh->adns_thread, NULL);
110         meshlink_queue_exit(&mesh->adns_queue);
111         signal_del(&mesh->loop, &mesh->adns_signal);
112 }
113
114 extern void adns_queue(meshlink_handle_t *mesh, char *host, char *serv, adns_cb_t cb, void *data, int timeout) {
115         adns_item_t *item = xmalloc(sizeof(*item));
116         item->cb = cb;
117         item->data = data;
118         item->deadline = time(NULL) + timeout;
119         item->host = host;
120         item->serv = serv;
121
122         logger(mesh, MESHLINK_DEBUG, "Enqueueing DNS request for %s port %s", item->host, item->serv);
123
124         if(!meshlink_queue_push(&mesh->adns_queue, item)) {
125                 abort();
126         }
127
128         pthread_cond_signal(&mesh->adns_cond);
129 }