fd5cf48bd8a0bcc152e66eabcb5dfc3ed7b79bd1
[NextIRCd.git] / src / IOHandler / IODNSEngine_cares.c
1 /* IODNSEngine_cares.c - IOMultiplexer
2  * Copyright (C) 2014  Philipp Kreil (pk910)
3  * 
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License 
15  * along with this program. If not, see <http://www.gnu.org/licenses/>. 
16  */
17 #define _IOHandler_internals
18 #include "IOInternal.h"
19 #include "IOHandler.h"
20 #include "IODNSLookup.h"
21 #include "IOLog.h"
22 #include "IOSockets.h"
23 #include "IOTimer.h"
24
25 #ifdef HAVE_ARES_H
26 #include <ares.h>
27 #include <string.h>
28 #include <sys/time.h>
29 #ifdef WIN32
30 #define _WIN32_WINNT 0x501
31 #include <windows.h>
32 #include <winsock2.h>
33 #elif defined HAVE_SYS_SELECT_H
34 #include <sys/select.h>
35 #include <netdb.h>
36 #include <arpa/inet.h>
37 #endif
38
39 #include "compat/inet.h"
40
41 struct dnsengine_cares_socket {
42         struct _IOSocket *iosock;
43         int want_read : 1;
44         int want_write : 1;
45 };
46
47 struct dnsengine_cares_query {
48         int query_count;
49         int query_successful;
50         struct _IODNSQuery *iodns;
51 };
52
53 static IOTIMER_CALLBACK(dnsengine_cares_timer_callback);
54
55 static ares_channel dnsengine_cares_channel;
56 static struct dnsengine_cares_socket dnsengine_cares_sockets[ARES_GETSOCK_MAXNUM];
57 static struct IOTimerDescriptor *dnsengine_cares_timer = NULL;
58
59 static int dnsengine_cares_init() {
60         int res;
61         
62         // zero dnsengine_cares_sockets array
63         memset(dnsengine_cares_sockets, 0, sizeof(*dnsengine_cares_sockets) * ARES_GETSOCK_MAXNUM);
64         
65         // initialize cares
66         if((res = ares_init(&dnsengine_cares_channel)) != ARES_SUCCESS) {
67                 iolog_trigger(IOLOG_ERROR, "Failed to initialize c-ares in %s:%d", __FILE__, __LINE__);
68         return 0;
69     }
70     return 1;
71 }
72
73 static void dnsengine_cares_update_sockets() {
74         int ares_socks[ARES_GETSOCK_MAXNUM];
75         memset(ares_socks, 0, sizeof(*ares_socks) * ARES_GETSOCK_MAXNUM);
76         int sockreqs = ares_getsock(dnsengine_cares_channel, ares_socks, ARES_GETSOCK_MAXNUM);
77         int i, j, sockid, newsock, updatesock;
78         struct _IOSocket *iosock;
79         
80         //unregister "old" sockets
81         for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
82                 if(!dnsengine_cares_sockets[i].iosock)
83                         continue;
84                 
85                 //search matching ares_socks
86                 sockid = -1;
87                 for(j = 0; j < ARES_GETSOCK_MAXNUM; j++) {
88                         if(dnsengine_cares_sockets[i].iosock->fd == ares_socks[j]) {
89                                 sockid = j;
90                                 break;
91                         }
92                 }
93                 if(sockid == -1) {
94                         //unregister socket
95                         _free_socket(dnsengine_cares_sockets[i].iosock);
96                         dnsengine_cares_sockets[i].iosock = NULL;
97                 }
98         }
99         
100         //register new / update existing sockets
101         for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
102                 if(!ares_socks[i])
103                         break;
104                 
105                 //search matching dnsengine_cares_socket
106                 sockid = -1;
107                 for(j = 0; j < ARES_GETSOCK_MAXNUM; j++) {
108                         if(dnsengine_cares_sockets[j].iosock && dnsengine_cares_sockets[j].iosock->fd == ares_socks[i]) {
109                                 sockid = j;
110                                 break;
111                         }
112                 }
113                 
114                 if(sockid == -1) {
115                         //append new socket
116                         for(j = 0; j < ARES_GETSOCK_MAXNUM; j++) {
117                                 if(!dnsengine_cares_sockets[j].iosock) {
118                                         sockid = j;
119                                         break;
120                                 }
121                         }
122                         if(sockid == -1) {
123                                 iolog_trigger(IOLOG_ERROR, "Error in dnsengine_cares_update_sockets: could not find free dnsengine_cares_socket in %s:%d", __FILE__, __LINE__);
124                                 continue;
125                         }
126                         iosock = _create_socket();
127                         if(!iosock)
128                                 continue;
129                         
130                         //set up iosock
131                         iosock->socket_flags |= IOSOCKETFLAG_PARENT_DNSENGINE | IOSOCKETFLAG_OVERRIDE_WANT_RW;
132                         iosock->fd = ares_socks[i];
133                         dnsengine_cares_sockets[sockid].iosock = iosock;
134                         dnsengine_cares_sockets[sockid].want_read = 0;
135                         dnsengine_cares_sockets[sockid].want_write = 0;
136                         
137                         newsock = 1;
138                 } else
139                         newsock = 0;
140                 
141                 updatesock = 0;
142                 if(dnsengine_cares_sockets[sockid].want_read ^ ARES_GETSOCK_READABLE(sockreqs, i)) {
143                         if(ARES_GETSOCK_READABLE(sockreqs, i)) {
144                                 dnsengine_cares_sockets[sockid].iosock->socket_flags |= IOSOCKETFLAG_OVERRIDE_WANT_R;
145                                 dnsengine_cares_sockets[sockid].want_read = 1;
146                         } else {
147                                 dnsengine_cares_sockets[sockid].iosock->socket_flags &= ~IOSOCKETFLAG_OVERRIDE_WANT_R;
148                                 dnsengine_cares_sockets[sockid].want_read = 0;
149                         }
150                         updatesock = 1;
151                 }
152                 if(dnsengine_cares_sockets[sockid].want_write ^ ARES_GETSOCK_WRITABLE(sockreqs, i)) {
153                         if(ARES_GETSOCK_WRITABLE(sockreqs, i)) {
154                                 dnsengine_cares_sockets[sockid].iosock->socket_flags |= IOSOCKETFLAG_OVERRIDE_WANT_W;
155                                 dnsengine_cares_sockets[sockid].want_write = 1;
156                         } else {
157                                 dnsengine_cares_sockets[sockid].iosock->socket_flags &= ~IOSOCKETFLAG_OVERRIDE_WANT_W;
158                                 dnsengine_cares_sockets[sockid].want_write = 0;
159                         }
160                         updatesock = 1;
161                 }
162                 if(updatesock || newsock) {
163                         if(newsock)
164                                 iosocket_activate(dnsengine_cares_sockets[sockid].iosock);
165                         else
166                                 iosocket_update(dnsengine_cares_sockets[sockid].iosock);
167                 }
168         }
169 }
170
171 static void dnsengine_cares_update_timeout() {
172         struct timeval timeout, now;
173         timeout.tv_sec = 60;
174         timeout.tv_usec = 0;
175         ares_timeout(dnsengine_cares_channel, &timeout, &timeout);
176         
177         gettimeofday(&now, NULL);
178         timeout.tv_sec += now.tv_sec;
179         timeout.tv_usec += now.tv_usec;
180         if(timeout.tv_usec > 1000000) {
181                 timeout.tv_sec += 1;
182                 timeout.tv_usec -= 1000000;
183         }
184         
185         if(dnsengine_cares_timer)
186                 iotimer_set_timeout(dnsengine_cares_timer, &timeout);
187         else {
188                 dnsengine_cares_timer = iotimer_create(&timeout);
189                 iotimer_set_callback(dnsengine_cares_timer, dnsengine_cares_timer_callback);
190                 iotimer_start(dnsengine_cares_timer);
191         }
192 }
193
194 static IOTIMER_CALLBACK(dnsengine_cares_timer_callback) {
195         dnsengine_cares_timer = NULL;
196         ares_process_fd(dnsengine_cares_channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
197         dnsengine_cares_update_timeout();
198         dnsengine_cares_update_sockets();
199 }
200
201 static void dnsengine_cares_socket_callback(struct _IOSocket *iosock, int wantread, int wantwrite) {
202         int socketfd = iosock->fd;
203         ares_process_fd(dnsengine_cares_channel, (wantread ? socketfd : ARES_SOCKET_BAD), (wantread ? socketfd : ARES_SOCKET_BAD));
204         dnsengine_cares_update_timeout();
205         dnsengine_cares_update_sockets();
206 }
207
208 static void dnsengine_cares_stop() {
209         if(dnsengine_cares_timer)
210                 iotimer_destroy(dnsengine_cares_timer);
211 }
212
213
214 static void dnsengine_cares_callback(void *arg, int status, int timeouts, struct hostent *host) {
215         struct dnsengine_cares_query *query = arg;
216         struct _IODNSQuery *iodns = query->iodns;
217         query->query_count--;
218         if(iodns) {
219                 if(!(iodns->flags & IODNSFLAG_RUNNING)) {
220                         // query stopped
221                         query->iodns = NULL;
222                         iodns = NULL;
223                         iodns_free_result(iodns->result);
224                         _free_dnsquery(iodns);
225                 }
226                 if(iodns && status == ARES_SUCCESS) {
227                         if((iodns->type & IODNS_FORWARD)) {
228                                 char **h_addr;
229                                 for(h_addr = host->h_addr_list; *h_addr; h_addr++) {
230                                         struct IODNSResult *dnsresult = malloc(sizeof(*dnsresult));
231                                         if(!dnsresult) {
232                                                 iolog_trigger(IOLOG_ERROR, "Failed to allocate memory for IODNSResult in %s:%d", __FILE__, __LINE__);
233                                                 goto dnsengine_cares_callback_finally;
234                                         }
235                                         
236                                         int sockaddrlen;
237                                         if(host->h_addrtype == AF_INET) {
238                                                 dnsresult->type = IODNS_RECORD_A;
239                                                 sockaddrlen = sizeof(struct sockaddr_in);
240                                         } else {
241                                                 dnsresult->type = IODNS_RECORD_AAAA;
242                                                 sockaddrlen = sizeof(struct sockaddr_in6);
243                                         }
244                                         dnsresult->result.addr.addresslen = sockaddrlen;
245                                         dnsresult->result.addr.address = malloc(sockaddrlen);
246                                         if(!dnsresult->result.addr.address) {
247                                                 iolog_trigger(IOLOG_ERROR, "Failed to allocate memory for sockaddr in %s:%d", __FILE__, __LINE__);
248                                                 goto dnsengine_cares_callback_finally;
249                                         }
250                                         void *target = (host->h_addrtype == AF_INET ? ((void *) &((struct sockaddr_in *)dnsresult->result.addr.address)->sin_addr) : ((void *) &((struct sockaddr_in6 *)dnsresult->result.addr.address)->sin6_addr));
251                                         memcpy(target, *h_addr, host->h_length);
252                                         
253                                         if(host->h_addrtype == AF_INET) {
254                                                 char str[INET_ADDRSTRLEN];
255                                                 inet_ntop( AF_INET, &((struct sockaddr_in *)dnsresult->result.addr.address)->sin_addr, str, INET_ADDRSTRLEN );
256                                                 iolog_trigger(IOLOG_DEBUG, "Resolved %s to (A): %s", iodns->request.host, str);
257                                         } else {
258                                                 char str[INET6_ADDRSTRLEN];
259                                                 inet_ntop( AF_INET6, &((struct sockaddr_in6 *)dnsresult->result.addr.address)->sin6_addr, str, INET6_ADDRSTRLEN );
260                                                 iolog_trigger(IOLOG_DEBUG, "Resolved %s to (AAAA): %s", iodns->request.host, str);
261                                         }
262                                         
263                                         dnsresult->next = iodns->result;
264                                         iodns->result = dnsresult;
265                                 }
266                                 
267                         } else if((iodns->type & IODNS_REVERSE)) {
268                                 struct IODNSResult *dnsresult = malloc(sizeof(*dnsresult));
269                                 if(!dnsresult) {
270                                         iolog_trigger(IOLOG_ERROR, "Failed to allocate memory for IODNSResult in %s:%d", __FILE__, __LINE__);
271                                         goto dnsengine_cares_callback_finally;
272                                 }
273                                 
274                                 dnsresult->type = IODNS_RECORD_PTR;
275                                 dnsresult->result.host = strdup(host->h_name);
276                                 if(!dnsresult->result.host) {
277                                         iolog_trigger(IOLOG_ERROR, "Failed to duplicate h_name string for IODNSResult in %s:%d", __FILE__, __LINE__);
278                                         goto dnsengine_cares_callback_finally;
279                                 }
280                                 
281                                 dnsresult->next = iodns->result;
282                                 iodns->result = dnsresult;
283                         }
284                         
285                         query->query_successful++;
286                 }
287         }
288         dnsengine_cares_callback_finally:
289         if(query->query_count <= 0) {
290                 if(iodns) {
291                         iodns->flags &= ~(IODNSFLAG_PROCESSING | IODNSFLAG_RUNNING);
292                         iodns_event_callback(iodns, (query->query_successful ? IODNSEVENT_SUCCESS : IODNSEVENT_FAILED));
293                 }
294                 free(query);
295         }
296 }
297
298 static void dnsengine_cares_add(struct _IODNSQuery *iodns) {
299         struct dnsengine_cares_query *query = malloc(sizeof(*query));
300         if(!query) {
301                 iolog_trigger(IOLOG_ERROR, "Failed to allocate memory for dnsengine_cares_query in %s:%d", __FILE__, __LINE__);
302                 iodns_event_callback(iodns, IODNSEVENT_FAILED);
303                 return;
304         }
305         iodns->query = query;
306         query->query_count = 0;
307         query->query_successful = 0;
308         query->iodns = iodns;
309         iodns->flags |= IODNSFLAG_PROCESSING;
310         if((iodns->type & IODNS_FORWARD)) {
311                 if((iodns->type & IODNS_RECORD_A)) {
312                         query->query_count++;
313                         ares_gethostbyname(dnsengine_cares_channel, iodns->request.host, AF_INET, dnsengine_cares_callback, query);
314                 }
315                 if((iodns->type & IODNS_RECORD_AAAA)) {
316                         query->query_count++;
317                         ares_gethostbyname(dnsengine_cares_channel, iodns->request.host, AF_INET6, dnsengine_cares_callback, query);
318                 }
319         } else if((iodns->type & IODNS_REVERSE)) {
320                 query->query_count++;
321                 struct sockaddr *addr = iodns->request.addr.address;
322                 if(addr->sa_family == AF_INET) {
323                         struct sockaddr_in *addr4 = (struct sockaddr_in *) iodns->request.addr.address;
324                         ares_gethostbyaddr(dnsengine_cares_channel, &addr4->sin_addr, sizeof(addr4->sin_addr), addr->sa_family, dnsengine_cares_callback, query);
325                 } else {
326                         struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)iodns->request.addr.address;
327                         ares_gethostbyaddr(dnsengine_cares_channel, &addr6->sin6_addr, sizeof(addr6->sin6_addr), addr->sa_family, dnsengine_cares_callback, query);
328                 }
329         }
330         dnsengine_cares_update_timeout();
331         dnsengine_cares_update_sockets();
332 }
333
334 static void dnsengine_cares_remove(struct _IODNSQuery *iodns) {
335         /* empty */
336 }
337
338 static void dnsengine_cares_loop() {
339     /* empty */
340 }
341
342 struct IODNSEngine dnsengine_cares = {
343     .name = "c-ares",
344     .init = dnsengine_cares_init,
345         .stop = dnsengine_cares_stop,
346     .add = dnsengine_cares_add,
347     .remove = dnsengine_cares_remove,
348     .loop = dnsengine_cares_loop,
349         .socket_callback = dnsengine_cares_socket_callback,
350 };
351
352 #else
353
354 struct IODNSEngine dnsengine_cares = {
355     .name = "c-ares",
356     .init = NULL,
357         .stop = NULL,
358     .add = NULL,
359     .remove = NULL,
360     .loop = NULL,
361         .socket_callback = NULL,
362 };
363
364 #endif