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