[IOMultiplexerV2] started to implement c-ares backend
[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 #endif
36
37 struct dnsengine_cares_socket {
38         struct _IOSocket *iosock;
39         int want_read : 1;
40         int want_write : 1;
41 };
42
43 static IOTIMER_CALLBACK(dnsengine_cares_timer_callback);
44
45 static ares_channel dnsengine_cares_channel;
46 static struct dnsengine_cares_socket dnsengine_cares_sockets[ARES_GETSOCK_MAXNUM];
47 static struct IOTimerDescriptor *dnsengine_cares_timer = NULL;
48
49 static int dnsengine_cares_init() {
50         int res;
51         
52         // zero dnsengine_cares_sockets array
53         memset(dnsengine_cares_sockets, 0, sizeof(*dnsengine_cares_sockets) * ARES_GETSOCK_MAXNUM);
54         
55         // initialize cares
56         if((res = ares_init(&dnsengine_cares_channel)) != ARES_SUCCESS) {
57                 iolog_trigger(IOLOG_ERROR, "Failed to initialize c-ares in %s:%d", __FILE__, __LINE__);
58         return 0;
59     }
60     return 0; /* backend not completed */
61 }
62
63 static void dnsengine_cares_update_sockets() {
64         int ares_socks[ARES_GETSOCK_MAXNUM];
65         memset(ares_socks, 0, sizeof(*ares_socks) * ARES_GETSOCK_MAXNUM);
66         int sockreqs = ares_getsock(dnsengine_cares_channel, ares_socks, ARES_GETSOCK_MAXNUM);
67         int i, j, sockid, newsock, updatesock;
68         struct _IOSocket *iosock;
69         
70         //unregister "old" sockets
71         for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
72                 if(!dnsengine_cares_sockets[i].iosock)
73                         continue;
74                 
75                 //search matching ares_socks
76                 sockid = -1;
77                 for(j = 0; j < ARES_GETSOCK_MAXNUM; j++) {
78                         if(dnsengine_cares_sockets[i].iosock->fd == ares_socks[j]) {
79                                 sockid = j;
80                                 break;
81                         }
82                 }
83                 if(sockid == -1) {
84                         //unregister socket
85                         _free_socket(dnsengine_cares_sockets[i].iosock);
86                         dnsengine_cares_sockets[i].iosock = NULL;
87                 }
88         }
89         
90         //register new / update existing sockets
91         for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
92                 if(!ares_socks[i])
93                         break;
94                 
95                 //search matching dnsengine_cares_socket
96                 sockid = -1;
97                 for(j = 0; j < ARES_GETSOCK_MAXNUM; j++) {
98                         if(dnsengine_cares_sockets[j].iosock && dnsengine_cares_sockets[j].iosock->fd == ares_socks[i]) {
99                                 sockid = j;
100                                 break;
101                         }
102                 }
103                 
104                 if(sockid == -1) {
105                         //append new socket
106                         for(j = 0; j < ARES_GETSOCK_MAXNUM; j++) {
107                                 if(!dnsengine_cares_sockets[j].iosock) {
108                                         sockid = j;
109                                         break;
110                                 }
111                         }
112                         if(sockid == -1) {
113                                 iolog_trigger(IOLOG_ERROR, "Error in dnsengine_cares_update_sockets: could not find free dnsengine_cares_socket in %s:%d", __FILE__, __LINE__);
114                                 continue;
115                         }
116                         iosock = _create_socket();
117                         if(!iosock)
118                                 continue;
119                         
120                         //set up iosock
121                         iosock->socket_flags |= IOSOCKETFLAG_PARENT_DNSENGINE | IOSOCKETFLAG_OVERRIDE_WANT_RW;
122                         iosock->fd = ares_socks[i];
123                         dnsengine_cares_sockets[sockid].want_read = 0;
124                         dnsengine_cares_sockets[sockid].want_write = 0;
125                         
126                         newsock = 1;
127                 } else
128                         newsock = 0;
129                 
130                 updatesock = 0;
131                 if(dnsengine_cares_sockets[sockid].want_read ^ ARES_GETSOCK_READABLE(sockreqs, i)) {
132                         if(ARES_GETSOCK_READABLE(sockreqs, i)) {
133                                 dnsengine_cares_sockets[sockid].iosock->socket_flags |= IOSOCKETFLAG_OVERRIDE_WANT_R;
134                                 dnsengine_cares_sockets[sockid].want_read = 1;
135                         } else {
136                                 dnsengine_cares_sockets[sockid].iosock->socket_flags &= ~IOSOCKETFLAG_OVERRIDE_WANT_R;
137                                 dnsengine_cares_sockets[sockid].want_read = 0;
138                         }
139                         updatesock = 1;
140                 }
141                 if(dnsengine_cares_sockets[sockid].want_write ^ ARES_GETSOCK_WRITABLE(sockreqs, i)) {
142                         if(ARES_GETSOCK_WRITABLE(sockreqs, i)) {
143                                 dnsengine_cares_sockets[sockid].iosock->socket_flags |= IOSOCKETFLAG_OVERRIDE_WANT_W;
144                                 dnsengine_cares_sockets[sockid].want_write = 1;
145                         } else {
146                                 dnsengine_cares_sockets[sockid].iosock->socket_flags &= ~IOSOCKETFLAG_OVERRIDE_WANT_W;
147                                 dnsengine_cares_sockets[sockid].want_write = 0;
148                         }
149                         updatesock = 1;
150                 }
151                 if(updatesock || newsock) {
152                         if(newsock)
153                                 iosocket_activate(dnsengine_cares_sockets[sockid].iosock);
154                         else
155                                 iosocket_update(dnsengine_cares_sockets[sockid].iosock);
156                 }
157         }
158 }
159
160 static void dnsengine_cares_update_timeout() {
161         struct timeval timeout, now;
162         timeout.tv_sec = 60;
163         timeout.tv_usec = 0;
164         ares_timeout(dnsengine_cares_channel, &timeout, &timeout);
165         
166         gettimeofday(&now, NULL);
167         timeout.tv_sec += now.tv_sec;
168         timeout.tv_usec += now.tv_usec;
169         if(timeout.tv_usec > 1000000) {
170                 timeout.tv_sec += 1;
171                 timeout.tv_usec -= 1000000;
172         }
173         
174         if(dnsengine_cares_timer)
175                 iotimer_set_timeout(dnsengine_cares_timer, &timeout);
176         else {
177                 dnsengine_cares_timer = iotimer_create(&timeout);
178                 iotimer_set_callback(dnsengine_cares_timer, dnsengine_cares_timer_callback);
179                 iotimer_start(dnsengine_cares_timer);
180         }
181 }
182
183 static IOTIMER_CALLBACK(dnsengine_cares_timer_callback) {
184         dnsengine_cares_timer = NULL;
185         ares_process_fd(dnsengine_cares_channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
186         dnsengine_cares_update_timeout();
187         dnsengine_cares_update_sockets();
188 }
189
190 static void dnsengine_cares_socket_callback(struct _IOSocket *iosock, int wantread, int wantwrite) {
191         int socketfd = iosock->fd;
192         ares_process_fd(dnsengine_cares_channel, (wantread ? socketfd : ARES_SOCKET_BAD), (wantread ? socketfd : ARES_SOCKET_BAD));
193         dnsengine_cares_update_timeout();
194         dnsengine_cares_update_sockets();
195 }
196
197 static void dnsengine_cares_stop() {
198         if(dnsengine_cares_timer)
199                 iotimer_destroy(dnsengine_cares_timer);
200 }
201
202
203
204 static void dnsengine_cares_add(struct _IODNSQuery *iodns) {
205     /* TODO */
206 }
207
208 static void dnsengine_cares_remove(struct _IODNSQuery *iodns) {
209     /* TODO */
210 }
211
212 static void dnsengine_cares_loop() {
213     /* empty */
214 }
215
216 struct IODNSEngine dnsengine_cares = {
217     .name = "c-ares",
218     .init = dnsengine_cares_init,
219         .stop = dnsengine_cares_stop,
220     .add = dnsengine_cares_add,
221     .remove = dnsengine_cares_remove,
222     .loop = dnsengine_cares_loop,
223         .socket_callback = dnsengine_cares_socket_callback,
224 };
225
226 #else
227
228 struct IODNSEngine dnsengine_cares = {
229     .name = "c-ares",
230     .init = NULL,
231         .stop = NULL,
232     .add = NULL,
233     .remove = NULL,
234     .loop = NULL,
235         .socket_callback = NULL,
236 };
237
238 #endif