[IOMultiplexerV2] coding style fixes
[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                                         dnsresult->result.addr.address->sa_family = host->h_addrtype;
254                                         if(host->h_addrtype == AF_INET) {
255                                                 char str[INET_ADDRSTRLEN];
256                                                 inet_ntop( AF_INET, &((struct sockaddr_in *)dnsresult->result.addr.address)->sin_addr, str, INET_ADDRSTRLEN );
257                                                 iolog_trigger(IOLOG_DEBUG, "Resolved %s to (A): %s", iodns->request.host, str);
258                                         } else {
259                                                 char str[INET6_ADDRSTRLEN];
260                                                 inet_ntop( AF_INET6, &((struct sockaddr_in6 *)dnsresult->result.addr.address)->sin6_addr, str, INET6_ADDRSTRLEN );
261                                                 iolog_trigger(IOLOG_DEBUG, "Resolved %s to (AAAA): %s", iodns->request.host, str);
262                                         }
263                                         
264                                         dnsresult->next = iodns->result;
265                                         iodns->result = dnsresult;
266                                 }
267                                 
268                         } else if((iodns->type & IODNS_REVERSE)) {
269                                 struct IODNSResult *dnsresult = malloc(sizeof(*dnsresult));
270                                 if(!dnsresult) {
271                                         iolog_trigger(IOLOG_ERROR, "Failed to allocate memory for IODNSResult in %s:%d", __FILE__, __LINE__);
272                                         goto dnsengine_cares_callback_finally;
273                                 }
274                                 
275                                 dnsresult->type = IODNS_RECORD_PTR;
276                                 dnsresult->result.host = strdup(host->h_name);
277                                 if(!dnsresult->result.host) {
278                                         iolog_trigger(IOLOG_ERROR, "Failed to duplicate h_name string for IODNSResult in %s:%d", __FILE__, __LINE__);
279                                         goto dnsengine_cares_callback_finally;
280                                 }
281                                 
282                                 dnsresult->next = iodns->result;
283                                 iodns->result = dnsresult;
284                         }
285                         
286                         query->query_successful++;
287                 }
288         }
289         dnsengine_cares_callback_finally:
290         if(query->query_count <= 0) {
291                 if(iodns) {
292                         iodns->flags &= ~(IODNSFLAG_PROCESSING | IODNSFLAG_RUNNING);
293                         iodns_event_callback(iodns, (query->query_successful ? IODNSEVENT_SUCCESS : IODNSEVENT_FAILED));
294                 }
295                 free(query);
296         }
297 }
298
299 static void dnsengine_cares_add(struct _IODNSQuery *iodns) {
300         struct dnsengine_cares_query *query = malloc(sizeof(*query));
301         if(!query) {
302                 iolog_trigger(IOLOG_ERROR, "Failed to allocate memory for dnsengine_cares_query in %s:%d", __FILE__, __LINE__);
303                 iodns_event_callback(iodns, IODNSEVENT_FAILED);
304                 return;
305         }
306         iodns->query = query;
307         query->query_count = 0;
308         query->query_successful = 0;
309         query->iodns = iodns;
310         iodns->flags |= IODNSFLAG_PROCESSING;
311         if((iodns->type & IODNS_FORWARD)) {
312                 if((iodns->type & IODNS_RECORD_A)) {
313                         query->query_count++;
314                         ares_gethostbyname(dnsengine_cares_channel, iodns->request.host, AF_INET, dnsengine_cares_callback, query);
315                 }
316                 if((iodns->type & IODNS_RECORD_AAAA)) {
317                         query->query_count++;
318                         ares_gethostbyname(dnsengine_cares_channel, iodns->request.host, AF_INET6, dnsengine_cares_callback, query);
319                 }
320         } else if((iodns->type & IODNS_REVERSE)) {
321                 query->query_count++;
322                 struct sockaddr *addr = iodns->request.addr.address;
323                 if(addr->sa_family == AF_INET) {
324                         struct sockaddr_in *addr4 = (struct sockaddr_in *) iodns->request.addr.address;
325                         ares_gethostbyaddr(dnsengine_cares_channel, &addr4->sin_addr, sizeof(addr4->sin_addr), addr->sa_family, dnsengine_cares_callback, query);
326                 } else {
327                         struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)iodns->request.addr.address;
328                         ares_gethostbyaddr(dnsengine_cares_channel, &addr6->sin6_addr, sizeof(addr6->sin6_addr), addr->sa_family, dnsengine_cares_callback, query);
329                 }
330         }
331         dnsengine_cares_update_timeout();
332         dnsengine_cares_update_sockets();
333 }
334
335 static void dnsengine_cares_remove(struct _IODNSQuery *iodns) {
336         /* empty */
337 }
338
339 static void dnsengine_cares_loop() {
340         /* empty */
341 }
342
343 struct IODNSEngine dnsengine_cares = {
344         .name = "c-ares",
345         .init = dnsengine_cares_init,
346         .stop = dnsengine_cares_stop,
347         .add = dnsengine_cares_add,
348         .remove = dnsengine_cares_remove,
349         .loop = dnsengine_cares_loop,
350         .socket_callback = dnsengine_cares_socket_callback,
351 };
352
353 #else
354
355 struct IODNSEngine dnsengine_cares = {
356         .name = "c-ares",
357         .init = NULL,
358         .stop = NULL,
359         .add = NULL,
360         .remove = NULL,
361         .loop = NULL,
362         .socket_callback = NULL,
363 };
364
365 #endif