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