1 /* IODNSEngine_default.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"
21 #include "IODNSLookup.h"
24 #define _WIN32_WINNT 0x501
29 #include <sys/types.h>
30 #include <sys/socket.h>
32 #include <arpa/inet.h>
34 #include "compat/inet.h"
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;
46 static pthread_cond_t iodns_cond;
47 static pthread_mutex_t iodns_sync, iodns_sync2;
49 static int iodns_loop_blocking = 0;
51 static void iodns_process_queries();
53 #ifdef IODNS_USE_THREADS
54 static void *dnsengine_worker_main(void *arg) {
55 struct _IODNSQuery *query;
57 IOSYNCHRONIZE(iodns_sync);
58 if(iodns_threads_wanted < iodns_threads_running) {
59 iodns_threads_running--;
63 for(query = iodnsquery_first; query; query = query->next) {
64 if((query->flags & IODNSFLAG_RUNNING))
67 IODESYNCHRONIZE(iodns_sync);
69 pthread_cond_wait(&iodns_cond, &iodns_sync2);
71 if(iodns_threads_wanted < iodns_threads_running) {
72 iodns_threads_running--;
76 iodns_process_queries();
81 static int dnsengine_default_start_worker() {
82 if(iodns_threads_wanted >= IODNS_MAX_THREAD-1)
85 for(i = 0; i < IODNS_MAX_THREAD; i++) {
89 if(i >= IODNS_MAX_THREAD)
91 iodns_thread[i] = malloc(sizeof(**iodns_thread));
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);
100 iodns_threads_running++;
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);
112 if(!dnsengine_default_start_worker()) {
113 iodns_loop_blocking = 1;
114 iodns_threads_running = 0;
117 iodns_loop_blocking = 1;
122 static void dnsengine_default_stop() {
123 #ifdef IODNS_USE_THREADS
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;
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);
149 for(iodns = iodnsquery_first; iodns; iodns = iodns->next) {
150 if(!(iodns->flags & IODNSFLAG_RUNNING))
152 if((iodns->flags & IODNSFLAG_PROCESSING))
156 if(querycount / iodns_threads_wanted > IODNS_INC_THREAD_BY_LOAD) {
157 dnsengine_default_start_worker();
163 static void dnsengine_default_remove(struct _IODNSQuery *iodns) {
167 static void dnsengine_default_loop() {
168 if(iodns_loop_blocking)
169 iodns_process_queries();
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 iodns_process_queries_start:
178 IOSYNCHRONIZE(iodns_sync);
179 for(iodns = iodnsquery_first; iodns; iodns = next_iodns) {
180 next_iodns = iodns->next;
182 if(!(iodns->flags & IODNSFLAG_RUNNING))
184 if((iodns->flags & IODNSFLAG_PROCESSING))
186 iodns->flags |= IODNSFLAG_PROCESSING;
188 IODESYNCHRONIZE(iodns_sync);
190 querystate = IODNSEVENT_FAILED;
192 if((iodns->type & IODNS_FORWARD)) {
193 memset (&hints, 0, sizeof (hints));
194 hints.ai_family = PF_UNSPEC;
195 hints.ai_socktype = SOCK_STREAM;
196 hints.ai_flags |= AI_CANONNAME;
198 if (!(ret = getaddrinfo(iodns->request.host, NULL, &hints, &allres))) {
201 switch (res->ai_family) {
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 = malloc(dnsresult->result.addr.addresslen);
208 memcpy(dnsresult->result.addr.address, res->ai_addr, dnsresult->result.addr.addresslen);
209 dnsresult->next = iodns->result;
210 iodns->result = dnsresult;
212 char str[INET_ADDRSTRLEN];
213 inet_ntop( AF_INET, &((struct sockaddr_in *)dnsresult->result.addr.address)->sin_addr, str, INET_ADDRSTRLEN );
214 iolog_trigger(IOLOG_DEBUG, "Resolved %s to (A): %s", iodns->request.host, str);
216 querystate = IODNSEVENT_SUCCESS;
220 if((iodns->type & IODNS_RECORD_AAAA)) {
221 dnsresult = malloc(sizeof(*dnsresult));
222 dnsresult->type = IODNS_RECORD_AAAA;
223 dnsresult->result.addr.addresslen = res->ai_addrlen;
224 dnsresult->result.addr.address = calloc(dnsresult->result.addr.addresslen, 1);
225 memcpy(dnsresult->result.addr.address, res->ai_addr, dnsresult->result.addr.addresslen);
226 dnsresult->next = iodns->result;
227 iodns->result = dnsresult;
229 char str[INET6_ADDRSTRLEN];
230 inet_ntop( AF_INET6, &((struct sockaddr_in6 *)dnsresult->result.addr.address)->sin6_addr, str, INET6_ADDRSTRLEN );
231 iolog_trigger(IOLOG_DEBUG, "Resolved %s to (AAAA): %s", iodns->request.host, str);
233 querystate = IODNSEVENT_SUCCESS;
239 freeaddrinfo(allres);
241 iolog_trigger(IOLOG_WARNING, "getaddrinfo returned error code: %d", ret);
244 IOSYNCHRONIZE(iodns_sync);
245 if(!(iodns->flags & IODNSFLAG_RUNNING)) {
246 iodns_free_result(iodns->result);
247 _free_dnsquery(iodns);
248 IODESYNCHRONIZE(iodns_sync);
249 goto iodns_process_queries_start;
251 iodns->flags &= ~(IODNSFLAG_PROCESSING | IODNSFLAG_RUNNING);
252 IODESYNCHRONIZE(iodns_sync);
253 iodns_event_callback(iodns, querystate);
254 goto iodns_process_queries_start;
258 struct IODNSEngine dnsengine_default = {
260 .init = dnsengine_default_init,
261 .stop = dnsengine_default_stop,
262 .add = dnsengine_default_add,
263 .remove = dnsengine_default_remove,
264 .loop = dnsengine_default_loop,
265 .socket_callback = NULL,