[IOMultiplexerV2] fixed compiling issue with newer MinGW builds...
[NextIRCd.git] / src / IOHandler / IODNSEngine_default.c
1 /* IODNSEngine_default.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 "IOLog.h"
21 #include "IODNSLookup.h"
22
23 #ifdef WIN32
24 #ifdef _WIN32_WINNT
25 #undef _WIN32_WINNT
26 #endif
27 #define _WIN32_WINNT 0x501
28 #include <winsock2.h>
29 #include <windows.h>
30 #include <ws2tcpip.h>
31 #else
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <netdb.h>
35 #include <arpa/inet.h>
36 #endif
37 #include "compat/inet.h"
38 #include <stdlib.h>
39 #include <string.h>
40
41
42 #ifdef IODNS_USE_THREADS
43 #define IODNS_MAX_THREAD 10
44 #define IODNS_INC_THREAD_BY_LOAD 5 /* add another thread when there are more than IODNS_INC_THREAD_BY_LOAD querys per thread */
45 static pthread_t *iodns_thread[IODNS_MAX_THREAD];
46 static int iodns_threads_wanted = 1;
47 static int iodns_threads_running = 0;
48
49 static pthread_cond_t iodns_cond;
50 static pthread_mutex_t iodns_sync, iodns_sync2;
51 #endif
52 static int iodns_loop_blocking = 0;
53
54 static void iodns_process_queries();
55
56 #ifdef IODNS_USE_THREADS
57 static void *dnsengine_worker_main(void *arg) {
58         struct _IODNSQuery *query;
59         while(1) {
60                 IOSYNCHRONIZE(iodns_sync);
61                 if(iodns_threads_wanted < iodns_threads_running) {
62                         iodns_threads_running--;
63                         break;
64                 }
65                 
66                 for(query = iodnsquery_first; query; query = query->next) {
67                         if((query->flags & IODNSFLAG_RUNNING))
68                                 break;
69                 }
70                 IODESYNCHRONIZE(iodns_sync);
71                 if(!query)
72                         pthread_cond_wait(&iodns_cond, &iodns_sync2);
73                 
74                 if(iodns_threads_wanted < iodns_threads_running) {
75                         iodns_threads_running--;
76                         break;
77                 }
78                 
79                 iodns_process_queries();
80         }
81         return NULL;
82 }
83
84 static int dnsengine_default_start_worker() {
85         if(iodns_threads_wanted >= IODNS_MAX_THREAD-1)
86                 return 0;
87         int i;
88         for(i = 0; i < IODNS_MAX_THREAD; i++) {
89                 if(!iodns_thread[i]) 
90                         break;
91         }
92         if(i >= IODNS_MAX_THREAD)
93                 return 0;
94         iodns_thread[i] = malloc(sizeof(**iodns_thread));
95         if(!iodns_thread[i])
96                 return 0;
97         iodns_threads_wanted++;
98         if(pthread_create(iodns_thread[i], NULL, dnsengine_worker_main, NULL)) {
99                 iodns_threads_wanted--;
100                 iolog_trigger(IOLOG_ERROR, "could not create pthread in %s:%d (Returned: %i)", __FILE__, __LINE__, thread_err);
101                 return 0;
102         }
103         iodns_threads_running++;
104         return 1;
105 }
106 #endif
107
108 static int dnsengine_default_init() {
109         #ifdef IODNS_USE_THREADS
110         /* create worker thread */
111         pthread_cond_init(&iodns_cond, NULL);
112         IOTHREAD_MUTEX_INIT(iodns_sync);
113         IOTHREAD_MUTEX_INIT(iodns_sync2);
114         
115         if(!dnsengine_default_start_worker()) {
116                 iodns_loop_blocking = 1;
117                 iodns_threads_running = 0;
118         }
119         #else
120         iodns_loop_blocking = 1;
121         #endif
122         return 1;
123 }
124
125 static void dnsengine_default_stop() {
126         #ifdef IODNS_USE_THREADS
127         int i;
128         if(iodns_thread_running) {
129                 iodns_threads_wanted = 0;
130                 IOSYNCHRONIZE(iodns_sync2);
131                 pthread_cond_broadcast(&iodns_cond);
132                 IODESYNCHRONIZE(iodns_sync2);
133                 for(i = 0; i < IODNS_MAX_THREAD; i++) {
134                         if(iodns_thread[i]) {
135                                 pthread_join(*iodns_thread[i], NULL);
136                                 free(iodns_thread[i]);
137                                 iodns_thread[i] = NULL;
138                         }
139                 }
140         }
141         #endif
142 }
143
144 static void dnsengine_default_add(struct _IODNSQuery *iodns) {
145         #ifdef IODNS_USE_THREADS
146         if(iodns_thread_running) {
147                 IOSYNCHRONIZE(iodns_sync2);
148                 pthread_cond_signal(&iodns_cond);
149                 IODESYNCHRONIZE(iodns_sync2);
150                 
151                 int querycount = 0;
152                 for(iodns = iodnsquery_first; iodns; iodns = iodns->next) {
153                         if(!(iodns->flags & IODNSFLAG_RUNNING))
154                                 continue;
155                         if((iodns->flags & IODNSFLAG_PROCESSING))
156                                 continue;
157                         querycount++;
158                 }
159                 if(querycount / iodns_threads_wanted > IODNS_INC_THREAD_BY_LOAD) {
160                         dnsengine_default_start_worker();
161                 }
162         }
163         #endif
164 }
165
166 static void dnsengine_default_remove(struct _IODNSQuery *iodns) {
167         /* unused */
168 }
169
170 static void dnsengine_default_loop() {
171         if(iodns_loop_blocking)
172                 iodns_process_queries();
173 }
174
175 static void iodns_process_queries() {
176         enum IODNSEventType querystate;
177         struct addrinfo hints, *res, *allres;
178         struct _IODNSQuery *iodns, *next_iodns;
179         struct IODNSResult *dnsresult;
180         int ret;
181         iodns_process_queries_start:
182         IOSYNCHRONIZE(iodns_sync);
183         for(iodns = iodnsquery_first; iodns; iodns = next_iodns) {
184                 next_iodns = iodns->next;
185                 
186                 if(!(iodns->flags & IODNSFLAG_RUNNING))
187                         continue;
188                 if((iodns->flags & IODNSFLAG_PROCESSING))
189                         continue;
190                 iodns->flags |= IODNSFLAG_PROCESSING;
191                 
192                 IODESYNCHRONIZE(iodns_sync);
193                 
194                 querystate = IODNSEVENT_FAILED;
195                 
196                 if((iodns->type & IODNS_FORWARD)) {
197                         memset (&hints, 0, sizeof (hints));
198                         hints.ai_family = PF_UNSPEC;
199                         hints.ai_socktype = SOCK_STREAM;
200                         hints.ai_flags |= AI_CANONNAME;
201                         if (!(ret = getaddrinfo(iodns->request.host, NULL, &hints, &allres))) {
202                                 res = allres;
203                                 while (res) {
204                                         switch (res->ai_family) {
205                                         case AF_INET:
206                                                 if((iodns->type & IODNS_RECORD_A)) {
207                                                         dnsresult = malloc(sizeof(*dnsresult));
208                                                         dnsresult->type = IODNS_RECORD_A;
209                                                         dnsresult->result.addr.addresslen = res->ai_addrlen;
210                                                         dnsresult->result.addr.address = calloc(dnsresult->result.addr.addresslen, 1);
211                                                         dnsresult->result.addr.address->sa_family = AF_INET;
212                                                         memcpy(dnsresult->result.addr.address, res->ai_addr, dnsresult->result.addr.addresslen);
213                                                         dnsresult->next = iodns->result;
214                                                         iodns->result = dnsresult;
215                                                         
216                                                         char str[INET_ADDRSTRLEN];
217                                                         inet_ntop( AF_INET, &((struct sockaddr_in *)dnsresult->result.addr.address)->sin_addr, str, INET_ADDRSTRLEN );
218                                                         iolog_trigger(IOLOG_DEBUG, "Resolved %s to (A): %s", iodns->request.host, str);
219                                                         
220                                                         querystate = IODNSEVENT_SUCCESS;
221                                                 }
222                                                 break;
223                                         case AF_INET6:
224                                                 if((iodns->type & IODNS_RECORD_AAAA)) {
225                                                         dnsresult = malloc(sizeof(*dnsresult));
226                                                         dnsresult->type = IODNS_RECORD_AAAA;
227                                                         dnsresult->result.addr.addresslen = res->ai_addrlen;
228                                                         dnsresult->result.addr.address = calloc(dnsresult->result.addr.addresslen, 1);
229                                                         dnsresult->result.addr.address->sa_family = AF_INET6;
230                                                         memcpy(dnsresult->result.addr.address, res->ai_addr, dnsresult->result.addr.addresslen);
231                                                         dnsresult->next = iodns->result;
232                                                         iodns->result = dnsresult;
233                                                         
234                                                         char str[INET6_ADDRSTRLEN];
235                                                         inet_ntop( AF_INET6, &((struct sockaddr_in6 *)dnsresult->result.addr.address)->sin6_addr, str, INET6_ADDRSTRLEN );
236                                                         iolog_trigger(IOLOG_DEBUG, "Resolved %s to (AAAA): %s", iodns->request.host, str);
237                                                         
238                                                         querystate = IODNSEVENT_SUCCESS;
239                                                 }
240                                                 break;
241                                         }
242                                         res = res->ai_next;
243                                 }
244                                 freeaddrinfo(allres);
245                         } else {
246                                 iolog_trigger(IOLOG_WARNING, "getaddrinfo returned error code: %d", ret);
247                         }
248                 } else if((iodns->type & IODNS_REVERSE)) {
249                         char hostname[NI_MAXHOST];
250                         if(!(ret = getnameinfo(iodns->request.addr.address, iodns->request.addr.addresslen, hostname, sizeof(hostname), NULL, 0, 0))) {
251                                 dnsresult = malloc(sizeof(*dnsresult));
252                                 dnsresult->type = IODNS_RECORD_PTR;
253                                 dnsresult->result.host = strdup(hostname);
254                                 dnsresult->next = iodns->result;
255                                 iodns->result = dnsresult;
256                                 
257                                 if(iodns->request.addr.address->sa_family == AF_INET) {
258                                         char str[INET_ADDRSTRLEN];
259                                         inet_ntop(AF_INET, &((struct sockaddr_in *)iodns->request.addr.address)->sin_addr, str, INET_ADDRSTRLEN);
260                                         iolog_trigger(IOLOG_DEBUG, "Resolved %s to (PTR): %s", str, hostname);
261                                 } else {
262                                         char str[INET6_ADDRSTRLEN];
263                                         inet_ntop(AF_INET6, &((struct sockaddr_in6 *)iodns->request.addr.address)->sin6_addr, str, INET6_ADDRSTRLEN);
264                                         iolog_trigger(IOLOG_DEBUG, "Resolved %s to (PTR): %s", str, hostname);
265                                 }
266                                 
267                                 querystate = IODNSEVENT_SUCCESS;
268                         } else {
269                                 iolog_trigger(IOLOG_WARNING, "getnameinfo returned error code: %d", ret);
270                         }
271                         
272                 }
273                 IOSYNCHRONIZE(iodns_sync);
274                 if(!(iodns->flags & IODNSFLAG_RUNNING)) {
275                         iodns_free_result(iodns->result);
276                         _free_dnsquery(iodns);
277                         IODESYNCHRONIZE(iodns_sync);
278                         goto iodns_process_queries_start;
279                 }
280                 iodns->flags &= ~(IODNSFLAG_PROCESSING | IODNSFLAG_RUNNING);
281                 IODESYNCHRONIZE(iodns_sync);
282                 iodns_event_callback(iodns, querystate);
283                 goto iodns_process_queries_start;
284         }
285 }
286
287 struct IODNSEngine dnsengine_default = {
288         .name = "default",
289         .init = dnsengine_default_init,
290         .stop = dnsengine_default_stop,
291         .add = dnsengine_default_add,
292         .remove = dnsengine_default_remove,
293         .loop = dnsengine_default_loop,
294         .socket_callback = NULL,
295 };