1 /* IODNSEngine_cares.c - IOMultiplexer
2 * Copyright (C) 2014 Philipp Kreil (pk910)
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.
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.
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/>.
17 #define _IOHandler_internals
18 #include "IOInternal.h"
19 #include "IOHandler.h"
20 #include "IODNSLookup.h"
22 #include "IOSockets.h"
30 #define _WIN32_WINNT 0x501
33 #elif defined HAVE_SYS_SELECT_H
34 #include <sys/select.h>
36 #include <arpa/inet.h>
39 #include "compat/inet.h"
41 struct dnsengine_cares_socket {
42 struct _IOSocket *iosock;
47 struct dnsengine_cares_query {
50 struct _IODNSQuery *iodns;
53 static IOTIMER_CALLBACK(dnsengine_cares_timer_callback);
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;
59 static int dnsengine_cares_init() {
62 // zero dnsengine_cares_sockets array
63 memset(dnsengine_cares_sockets, 0, sizeof(*dnsengine_cares_sockets) * ARES_GETSOCK_MAXNUM);
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__);
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;
80 //unregister "old" sockets
81 for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
82 if(!dnsengine_cares_sockets[i].iosock)
85 //search matching ares_socks
87 for(j = 0; j < ARES_GETSOCK_MAXNUM; j++) {
88 if(dnsengine_cares_sockets[i].iosock->fd == ares_socks[j]) {
95 _free_socket(dnsengine_cares_sockets[i].iosock);
96 dnsengine_cares_sockets[i].iosock = NULL;
100 //register new / update existing sockets
101 for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
105 //search matching dnsengine_cares_socket
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]) {
116 for(j = 0; j < ARES_GETSOCK_MAXNUM; j++) {
117 if(!dnsengine_cares_sockets[j].iosock) {
123 iolog_trigger(IOLOG_ERROR, "Error in dnsengine_cares_update_sockets: could not find free dnsengine_cares_socket in %s:%d", __FILE__, __LINE__);
126 iosock = _create_socket();
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;
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;
147 dnsengine_cares_sockets[sockid].iosock->socket_flags &= ~IOSOCKETFLAG_OVERRIDE_WANT_R;
148 dnsengine_cares_sockets[sockid].want_read = 0;
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;
157 dnsengine_cares_sockets[sockid].iosock->socket_flags &= ~IOSOCKETFLAG_OVERRIDE_WANT_W;
158 dnsengine_cares_sockets[sockid].want_write = 0;
162 if(updatesock || newsock) {
164 iosocket_activate(dnsengine_cares_sockets[sockid].iosock);
166 iosocket_update(dnsengine_cares_sockets[sockid].iosock);
171 static void dnsengine_cares_update_timeout() {
172 struct timeval timeout, now;
175 ares_timeout(dnsengine_cares_channel, &timeout, &timeout);
177 gettimeofday(&now, NULL);
178 timeout.tv_sec += now.tv_sec;
179 timeout.tv_usec += now.tv_usec;
180 if(timeout.tv_usec > 1000000) {
182 timeout.tv_usec -= 1000000;
185 if(dnsengine_cares_timer)
186 iotimer_set_timeout(dnsengine_cares_timer, &timeout);
188 dnsengine_cares_timer = iotimer_create(&timeout);
189 iotimer_set_callback(dnsengine_cares_timer, dnsengine_cares_timer_callback);
190 iotimer_start(dnsengine_cares_timer);
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();
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();
208 static void dnsengine_cares_stop() {
209 if(dnsengine_cares_timer)
210 iotimer_destroy(dnsengine_cares_timer);
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--;
219 if(!(iodns->flags & IODNSFLAG_RUNNING)) {
223 iodns_free_result(iodns->result);
224 _free_dnsquery(iodns);
226 if(iodns && status == ARES_SUCCESS) {
227 if((iodns->type & IODNS_FORWARD)) {
229 for(h_addr = host->h_addr_list; *h_addr; h_addr++) {
230 struct IODNSResult *dnsresult = malloc(sizeof(*dnsresult));
232 iolog_trigger(IOLOG_ERROR, "Failed to allocate memory for IODNSResult in %s:%d", __FILE__, __LINE__);
233 goto dnsengine_cares_callback_finally;
237 if(host->h_addrtype == AF_INET) {
238 dnsresult->type = IODNS_RECORD_A;
239 sockaddrlen = sizeof(struct sockaddr_in);
241 dnsresult->type = IODNS_RECORD_AAAA;
242 sockaddrlen = sizeof(struct sockaddr_in6);
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;
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);
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);
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);
264 dnsresult->next = iodns->result;
265 iodns->result = dnsresult;
268 } else if((iodns->type & IODNS_REVERSE)) {
269 struct IODNSResult *dnsresult = malloc(sizeof(*dnsresult));
271 iolog_trigger(IOLOG_ERROR, "Failed to allocate memory for IODNSResult in %s:%d", __FILE__, __LINE__);
272 goto dnsengine_cares_callback_finally;
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;
282 dnsresult->next = iodns->result;
283 iodns->result = dnsresult;
286 query->query_successful++;
289 dnsengine_cares_callback_finally:
290 if(query->query_count <= 0) {
292 iodns->flags &= ~(IODNSFLAG_PROCESSING | IODNSFLAG_RUNNING);
293 iodns_event_callback(iodns, (query->query_successful ? IODNSEVENT_SUCCESS : IODNSEVENT_FAILED));
299 static void dnsengine_cares_add(struct _IODNSQuery *iodns) {
300 struct dnsengine_cares_query *query = malloc(sizeof(*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);
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);
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);
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);
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);
331 dnsengine_cares_update_timeout();
332 dnsengine_cares_update_sockets();
335 static void dnsengine_cares_remove(struct _IODNSQuery *iodns) {
339 static void dnsengine_cares_loop() {
343 struct IODNSEngine dnsengine_cares = {
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,
355 struct IODNSEngine dnsengine_cares = {
362 .socket_callback = NULL,