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;
178 iodns_process_queries_start:
179 IOSYNCHRONIZE(iodns_sync);
180 for(iodns = iodnsquery_first; iodns; iodns = next_iodns) {
181 next_iodns = iodns->next;
183 if(!(iodns->flags & IODNSFLAG_RUNNING))
185 if((iodns->flags & IODNSFLAG_PROCESSING))
187 iodns->flags |= IODNSFLAG_PROCESSING;
189 IODESYNCHRONIZE(iodns_sync);
191 querystate = IODNSEVENT_FAILED;
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))) {
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 = 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;
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);
217 querystate = IODNSEVENT_SUCCESS;
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;
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);
235 querystate = IODNSEVENT_SUCCESS;
241 freeaddrinfo(allres);
243 iolog_trigger(IOLOG_WARNING, "getaddrinfo returned error code: %d", ret);
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;
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);
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);
264 querystate = IODNSEVENT_SUCCESS;
266 iolog_trigger(IOLOG_WARNING, "getnameinfo returned error code: %d", ret);
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;
277 iodns->flags &= ~(IODNSFLAG_PROCESSING | IODNSFLAG_RUNNING);
278 IODESYNCHRONIZE(iodns_sync);
279 iodns_event_callback(iodns, querystate);
280 goto iodns_process_queries_start;
284 struct IODNSEngine dnsengine_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,