[IOMultiplexerV2] dev snapshot
[NextIRCd.git] / src / 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 "IODNSHandler.h"
21
22 #ifdef HAVE_PTHREAD_H
23 static pthread_t *iodns_thread;
24 static int iodns_thread_running = 1;
25
26 static pthread_cond_t iodns_cond;
27 static pthread_mutex_t iodns_sync2;
28 #endif
29 static int iodns_loop_blocking = 0;
30
31 static void iodns_process_queries();
32
33 static void dnsengine_worker_main(void *arg) {
34         struct _IODNSQuery *query;
35         while(iodns_thread_running) {
36                 IOSYNCHRONIZE(iodns_sync);
37                 for(query = iodnsquery_first; query; query = query->next) {
38                         if((query->flags & IODNSFLAG_RUNNING))
39                                 break;
40                 }
41                 IODESYNCHRONIZE(iodns_sync);
42                 if(!query)
43                         pthread_cond_wait(&iodns_cond, &iodns_sync2);
44                 
45                 if(iodns_thread_running)
46                         iodns_process_queries();
47         }
48 }
49
50 static int dnsengine_default_init() {
51         #ifdef HAVE_PTHREAD_H
52         /* create worker thread */
53         pthread_cond_init(&iodns_cond, NULL);
54         IOTHREAD_MUTEX_INIT(iodns_sync2);
55         
56         iodns_thread_running = 1;
57         
58         int thread_err;
59         thread_err = pthread_create(&iodns_thread, NULL, dnsengine_worker_main, NULL);
60         if(thread_err) {
61                 iolog_trigger(IOLOG_ERROR, "could not create pthread in %s:%d (Returned: %i)", __FILE__, __LINE__, thread_err);
62                 iodns_loop_blocking = 1;
63                 iodns_thread = NULL;
64                 iodns_thread_running = 0;
65         }
66         #else
67         iodns_loop_blocking = 1;
68         #endif
69     return 1;
70 }
71
72 static void dnsengine_default_stop() {
73         #ifdef HAVE_PTHREAD_H
74         if(iodns_thread_running) {
75                 iodns_thread_running = 0;
76                 IOSYNCHRONIZE(iodns_sync2);
77                 pthread_cond_signal(&iodns_cond);
78                 IODESYNCHRONIZE(iodns_sync2);
79                 pthread_join(iodns_thread, NULL);
80         }
81         #endif
82 }
83
84 static void dnsengine_default_add(struct _IODNSQuery *iodns) {
85     #ifdef HAVE_PTHREAD_H
86         if(iodns_thread_running) {
87                 IOSYNCHRONIZE(iodns_sync2);
88                 pthread_cond_signal(&iodns_cond);
89                 IODESYNCHRONIZE(iodns_sync2);
90         }
91         #endif
92 }
93
94 static void dnsengine_default_remove(struct _IODNSQuery *iodns) {
95     /* unused */
96 }
97
98 static void dnsengine_default_loop() {
99     if(iodns_loop_blocking)
100                 iodns_process_queries();
101 }
102
103 static void iodns_process_queries() {
104         enum IODNSEventType querystate;
105     struct addrinfo hints, *res, *next_res;
106     struct _IODNSQuery *iodns, *next_iodns;
107     struct IODNSResult *dnsresult;
108         iodns_process_queries_start:
109         IOSYNCHRONIZE(iodns_sync);
110     for(iodns = first_dnsquery; iodns; iodns = next_iodns) {
111         next_iodns = iodns->next;
112                 
113                 if(!(iodns->flags & IODNSFLAG_RUNNING))
114                         continue;
115                 if((iodns->flags & IODNSFLAG_PROCESSING))
116                         continue;
117                 
118                 IODESYNCHRONIZE(iodns_sync);
119                 
120         querystate = IODNSEVENT_FAILED;
121         
122         if((iodns->type & IODNS_FORWARD)) {
123             memset (&hints, 0, sizeof (hints));
124             hints.ai_family = PF_UNSPEC;
125             hints.ai_socktype = SOCK_STREAM;
126             hints.ai_flags |= AI_CANONNAME;
127             if (!getaddrinfo(iodns->request.host, NULL, &hints, &res)) {
128                 while (res) {
129                     switch (res->ai_family) {
130                     case AF_INET:
131                         if((iodns->type & IODNS_RECORD_A)) {
132                             dnsresult = malloc(sizeof(*dnsresult));
133                             dnsresult->type = IODNS_RECORD_A;
134                             dnsresult->result.addr.addresslen = res->ai_addrlen;
135                             dnsresult->result.addr.address = malloc(dnsresult->addresslen);
136                             memcpy(dnsresult->address, res->ai_addr, dnsresult->addresslen);
137                             dnsresult->next = iodns->result;
138                             iodns->result = dnsresult;
139                             querystate = IODNSEVENT_SUCCESS;
140                         }
141                         break;
142                     case AF_INET6:
143                         if((iodns->type & IODNS_RECORD_AAAA)) {
144                             dnsresult = malloc(sizeof(*dnsresult));
145                             dnsresult->type = IODNS_RECORD_AAAA;
146                             dnsresult->result.addr.addresslen = res->ai_addrlen;
147                             dnsresult->result.addr.address = malloc(dnsresult->addresslen);
148                             memcpy(dnsresult->address, res->ai_addr, dnsresult->addresslen);
149                             dnsresult->next = iodns->result;
150                             iodns->result = dnsresult;
151                             querystate = IODNSEVENT_SUCCESS;
152                         }
153                         break;
154                     }
155                     next_res = res->ai_next;
156                     freeaddrinfo(res);
157                     res = next_res;
158                 }
159             }
160         }
161                 IOSYNCHRONIZE(iodns_sync);
162                 if(!(iodns->flags & IODNSFLAG_RUNNING)) {
163                         iodns_free_result(iodns->result);
164                         _free_dnsquery(iodns);
165                         IODESYNCHRONIZE(iodns_sync);
166                         goto iodns_process_queries_start;
167                 }
168                 iodns->flags &= ~(IODNSFLAG_PROCESSING | IODNSFLAG_RUNNING);
169                 IODESYNCHRONIZE(iodns_sync);
170                 iodns_event_callback(iodns, querystate);
171                 goto iodns_process_queries_start;
172     }
173 }
174
175 struct IODNSEngine dnsengine_default = {
176     .name = "default",
177     .init = dnsengine_default_init,
178         .stop = dnsengine_default_stop,
179     .add = dnsengine_default_add,
180     .remove = dnsengine_default_remove,
181     .loop = dnsengine_default_loop,
182 };