[IOMultiplexerV2] alpha
[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 #endif
33 #include "compat/inet.h"
34 #include <stdlib.h>
35 #include <string.h>
36
37
38 #ifdef IODNS_USE_THREADS
39 static pthread_t iodns_thread;
40 static int iodns_thread_running = 1;
41
42 static pthread_cond_t iodns_cond;
43 static pthread_mutex_t iodns_sync, iodns_sync2;
44 #endif
45 static int iodns_loop_blocking = 0;
46
47 static void iodns_process_queries();
48
49 #ifdef IODNS_USE_THREADS
50 static void *dnsengine_worker_main(void *arg) {
51         struct _IODNSQuery *query;
52         while(iodns_thread_running) {
53                 IOSYNCHRONIZE(iodns_sync);
54                 for(query = iodnsquery_first; query; query = query->next) {
55                         if((query->flags & IODNSFLAG_RUNNING))
56                                 break;
57                 }
58                 IODESYNCHRONIZE(iodns_sync);
59                 if(!query)
60                         pthread_cond_wait(&iodns_cond, &iodns_sync2);
61                 
62                 if(iodns_thread_running)
63                         iodns_process_queries();
64         }
65         return NULL;
66 }
67 #endif
68
69 static int dnsengine_default_init() {
70         #ifdef IODNS_USE_THREADS
71         /* create worker thread */
72         pthread_cond_init(&iodns_cond, NULL);
73         IOTHREAD_MUTEX_INIT(iodns_sync);
74         IOTHREAD_MUTEX_INIT(iodns_sync2);
75         
76         iodns_thread_running = 1;
77         
78         int thread_err;
79         thread_err = pthread_create(&iodns_thread, NULL, dnsengine_worker_main, NULL);
80         if(thread_err) {
81                 iolog_trigger(IOLOG_ERROR, "could not create pthread in %s:%d (Returned: %i)", __FILE__, __LINE__, thread_err);
82                 iodns_loop_blocking = 1;
83                 iodns_thread_running = 0;
84         }
85         #else
86         iodns_loop_blocking = 1;
87         #endif
88     return 1;
89 }
90
91 static void dnsengine_default_stop() {
92         #ifdef IODNS_USE_THREADS
93         if(iodns_thread_running) {
94                 iodns_thread_running = 0;
95                 IOSYNCHRONIZE(iodns_sync2);
96                 pthread_cond_signal(&iodns_cond);
97                 IODESYNCHRONIZE(iodns_sync2);
98                 pthread_join(iodns_thread, NULL);
99         }
100         #endif
101 }
102
103 static void dnsengine_default_add(struct _IODNSQuery *iodns) {
104     #ifdef IODNS_USE_THREADS
105         if(iodns_thread_running) {
106                 IOSYNCHRONIZE(iodns_sync2);
107                 pthread_cond_signal(&iodns_cond);
108                 IODESYNCHRONIZE(iodns_sync2);
109         }
110         #endif
111 }
112
113 static void dnsengine_default_remove(struct _IODNSQuery *iodns) {
114     /* unused */
115 }
116
117 static void dnsengine_default_loop() {
118     if(iodns_loop_blocking)
119                 iodns_process_queries();
120 }
121
122 static void iodns_process_queries() {
123         enum IODNSEventType querystate;
124     struct addrinfo hints, *res, *allres;
125     struct _IODNSQuery *iodns, *next_iodns;
126     struct IODNSResult *dnsresult;
127         iodns_process_queries_start:
128         IOSYNCHRONIZE(iodns_sync);
129     for(iodns = iodnsquery_first; iodns; iodns = next_iodns) {
130         next_iodns = iodns->next;
131                 
132                 if(!(iodns->flags & IODNSFLAG_RUNNING))
133                         continue;
134                 if((iodns->flags & IODNSFLAG_PROCESSING))
135                         continue;
136                 
137                 IODESYNCHRONIZE(iodns_sync);
138                 
139         querystate = IODNSEVENT_FAILED;
140         
141         if((iodns->type & IODNS_FORWARD)) {
142             memset (&hints, 0, sizeof (hints));
143             hints.ai_family = PF_UNSPEC;
144             hints.ai_socktype = SOCK_STREAM;
145             hints.ai_flags |= AI_CANONNAME;
146                         int ret;
147             if (!(ret = getaddrinfo(iodns->request.host, NULL, &hints, &allres))) {
148                                 res = allres;
149                 while (res) {
150                     switch (res->ai_family) {
151                     case AF_INET:
152                         if((iodns->type & IODNS_RECORD_A)) {
153                             dnsresult = malloc(sizeof(*dnsresult));
154                             dnsresult->type = IODNS_RECORD_A;
155                             dnsresult->result.addr.addresslen = res->ai_addrlen;
156                             dnsresult->result.addr.address = malloc(dnsresult->result.addr.addresslen);
157                             memcpy(dnsresult->result.addr.address, res->ai_addr, dnsresult->result.addr.addresslen);
158                             dnsresult->next = iodns->result;
159                             iodns->result = dnsresult;
160                             
161                             char str[INET_ADDRSTRLEN];
162                                                         inet_ntop( AF_INET, &((struct sockaddr_in *)dnsresult->result.addr.address)->sin_addr, str, INET_ADDRSTRLEN );
163                             iolog_trigger(IOLOG_DEBUG, "Resolved %s to (A): %s", iodns->request.host, str);
164                             
165                             querystate = IODNSEVENT_SUCCESS;
166                         }
167                         break;
168                     case AF_INET6:
169                         if((iodns->type & IODNS_RECORD_AAAA)) {
170                             dnsresult = malloc(sizeof(*dnsresult));
171                             dnsresult->type = IODNS_RECORD_AAAA;
172                             dnsresult->result.addr.addresslen = res->ai_addrlen;
173                             dnsresult->result.addr.address = calloc(dnsresult->result.addr.addresslen, 1);
174                             memcpy(dnsresult->result.addr.address, res->ai_addr, dnsresult->result.addr.addresslen);
175                             dnsresult->next = iodns->result;
176                             iodns->result = dnsresult;
177                             
178                             char str[INET6_ADDRSTRLEN];
179                                                         inet_ntop( AF_INET6, &((struct sockaddr_in6 *)dnsresult->result.addr.address)->sin6_addr, str, INET6_ADDRSTRLEN );
180                             iolog_trigger(IOLOG_DEBUG, "Resolved %s to (AAAA): %s", iodns->request.host, str);
181                             
182                             querystate = IODNSEVENT_SUCCESS;
183                         }
184                         break;
185                     }
186                     res = res->ai_next;
187                 }
188                 freeaddrinfo(allres);
189             } else {
190                                 iolog_trigger(IOLOG_WARNING, "getaddrinfo returned error code: %d", ret);
191                         }
192         }
193                 IOSYNCHRONIZE(iodns_sync);
194                 if(!(iodns->flags & IODNSFLAG_RUNNING)) {
195                         iodns_free_result(iodns->result);
196                         _free_dnsquery(iodns);
197                         IODESYNCHRONIZE(iodns_sync);
198                         goto iodns_process_queries_start;
199                 }
200                 iodns->flags &= ~(IODNSFLAG_PROCESSING | IODNSFLAG_RUNNING);
201                 IODESYNCHRONIZE(iodns_sync);
202                 iodns_event_callback(iodns, querystate);
203                 goto iodns_process_queries_start;
204     }
205 }
206
207 struct IODNSEngine dnsengine_default = {
208     .name = "default",
209     .init = dnsengine_default_init,
210         .stop = dnsengine_default_stop,
211     .add = dnsengine_default_add,
212     .remove = dnsengine_default_remove,
213     .loop = dnsengine_default_loop,
214 };