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"
27 #define _WIN32_WINNT 0x501
32 #include <sys/types.h>
33 #include <sys/socket.h>
35 #include <arpa/inet.h>
37 #include "compat/inet.h"
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;
49 static pthread_cond_t iodns_cond;
50 static pthread_mutex_t iodns_sync, iodns_sync2;
52 static int iodns_loop_blocking = 0;
54 static void iodns_process_queries();
56 #ifdef IODNS_USE_THREADS
57 static void *dnsengine_worker_main(void *arg) {
58 struct _IODNSQuery *query;
60 IOSYNCHRONIZE(iodns_sync);
61 if(iodns_threads_wanted < iodns_threads_running) {
62 iodns_threads_running--;
66 for(query = iodnsquery_first; query; query = query->next) {
67 if((query->flags & IODNSFLAG_RUNNING))
70 IODESYNCHRONIZE(iodns_sync);
72 pthread_cond_wait(&iodns_cond, &iodns_sync2);
74 if(iodns_threads_wanted < iodns_threads_running) {
75 iodns_threads_running--;
79 iodns_process_queries();
84 static int dnsengine_default_start_worker() {
85 if(iodns_threads_wanted >= IODNS_MAX_THREAD-1)
88 for(i = 0; i < IODNS_MAX_THREAD; i++) {
92 if(i >= IODNS_MAX_THREAD)
94 iodns_thread[i] = malloc(sizeof(**iodns_thread));
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);
103 iodns_threads_running++;
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);
115 if(!dnsengine_default_start_worker()) {
116 iodns_loop_blocking = 1;
117 iodns_threads_running = 0;
120 iodns_loop_blocking = 1;
125 static void dnsengine_default_stop() {
126 #ifdef IODNS_USE_THREADS
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;
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);
152 for(iodns = iodnsquery_first; iodns; iodns = iodns->next) {
153 if(!(iodns->flags & IODNSFLAG_RUNNING))
155 if((iodns->flags & IODNSFLAG_PROCESSING))
159 if(querycount / iodns_threads_wanted > IODNS_INC_THREAD_BY_LOAD) {
160 dnsengine_default_start_worker();
166 static void dnsengine_default_remove(struct _IODNSQuery *iodns) {
170 static void dnsengine_default_loop() {
171 if(iodns_loop_blocking)
172 iodns_process_queries();
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;
181 iodns_process_queries_start:
182 IOSYNCHRONIZE(iodns_sync);
183 for(iodns = iodnsquery_first; iodns; iodns = next_iodns) {
184 next_iodns = iodns->next;
186 if(!(iodns->flags & IODNSFLAG_RUNNING))
188 if((iodns->flags & IODNSFLAG_PROCESSING))
190 iodns->flags |= IODNSFLAG_PROCESSING;
192 IODESYNCHRONIZE(iodns_sync);
194 querystate = IODNSEVENT_FAILED;
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))) {
204 switch (res->ai_family) {
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;
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);
220 querystate = IODNSEVENT_SUCCESS;
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;
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);
238 querystate = IODNSEVENT_SUCCESS;
244 freeaddrinfo(allres);
246 iolog_trigger(IOLOG_WARNING, "getaddrinfo returned error code: %d", ret);
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;
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);
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);
267 querystate = IODNSEVENT_SUCCESS;
269 iolog_trigger(IOLOG_WARNING, "getnameinfo returned error code: %d", ret);
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;
280 iodns->flags &= ~(IODNSFLAG_PROCESSING | IODNSFLAG_RUNNING);
281 IODESYNCHRONIZE(iodns_sync);
282 iodns_event_callback(iodns, querystate);
283 goto iodns_process_queries_start;
287 struct IODNSEngine dnsengine_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,