[IOMultiplexerV2] dev snapshot
[NextIRCd.git] / src / IODNSLookup.c
1 /* IODNSLookup.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 "IODNSLookup.h"
21 #include "IOLog.h"
22 #include "IOSockets.h"
23
24 #ifdef HAVE_PTHREAD_H
25 pthread_mutex_t iodns_sync;
26 #endif
27
28 struct _IODNSQuery *iodnsquery_first = NULL;
29 struct _IODNSQuery *iodnsquery_last = NULL;
30
31 struct IODNSEngine *dnsengine = NULL;
32
33 static void iodns_init_engine() {
34         if(dnsengine)
35                 return;
36         //try DNS engines
37         if(dnsengine_cares.init && dnsengine_cares.init())
38                 dnsengine = &dnsengine_cares;
39         else if(dnsengine_default.init && dnsengine_default.init())
40                 dnsengine = &dnsengine_default;
41         else {
42                 iohandler_log(IOLOG_FATAL, "found no useable IO DNS engine");
43                 return;
44         }
45         iohandler_log(IOLOG_DEBUG, "using %s IODNS engine", dnsengine->name);
46 }
47
48 void _init_iodns() {
49         IOTHREAD_MUTEX_INIT(iodns_sync);
50         iodns_init_engine();
51 }
52
53 struct _IODNSQuery *_create_dnsquery() {
54         struct _IODNSQuery *query = calloc(1, sizeof(*query));
55         if(!query) {
56                 iolog_trigger(IOLOG_ERROR, "could not allocate memory for _IODNSQuery in %s:%d", __FILE__, __LINE__);
57                 return NULL;
58         }
59         IOSYNCHRONIZE(iodns_sync);
60         if(iodnsquery_last)
61                 iodnsquery_last->next = query;
62         else
63                 iodnsquery_first = query;
64         query->prev = iodnsquery_last;
65         iodnsquery_last = query;
66         IODESYNCHRONIZE(iodns_sync);
67         return query;
68 }
69
70 void _start_dnsquery(struct _IODNSQuery *query) {
71         IOSYNCHRONIZE(iodns_sync);
72         query->flags |= IODNSFLAG_RUNNING;
73         dnsengine->add(query);
74         IODESYNCHRONIZE(iodns_sync);
75 }
76
77 void _free_dnsquery(struct _IODNSQuery *query) {
78         IOSYNCHRONIZE(iodns_sync);
79         if(query->prev)
80                 query->prev->next = query->next;
81         else
82                 iodnsquery_first = query->next;
83         if(query->next)
84                 query->next->prev = query->prev;
85         else
86                 iodnsquery_last = query->prev;
87         IODESYNCHRONIZE(iodns_sync);
88         if((query->type & IODNS_REVERSE) && query->request.addr.address)
89                 free(query->request.addr.address);
90         free(query);
91 }
92
93 void _stop_dnsquery(struct _IODNSQuery *query) {
94         IOSYNCHRONIZE(iodns_sync);
95         if((query->flags & IODNSFLAG_RUNNING)) {
96                 query->flags &= ~IODNSFLAG_RUNNING;
97                 dnsengine->remove(query);
98         }
99         if(!(query->flags & IODNSFLAG_PROCESSING))
100                 _free_dnsquery(query);
101         IODESYNCHRONIZE(iodns_sync);
102 }
103
104 void iodns_event_callback(struct _IODNSQuery *query, enum IODNSEventType state) {
105         if((query->flags & IODNSFLAG_PARENT_PUBLIC)) {
106                 struct IODNSQuery *descriptor = query->parent;
107                 struct IODNSEvent event;
108                 event.type = state;
109                 event.query = descriptor;
110                 event.result = query->result;
111                 
112                 descriptor->parent = NULL;
113                 _stop_dnsquery(query);
114                 
115                 if(descriptor->callback)
116                         descriptor->callback(&event);
117                 
118                 iogc_add(descriptor);
119         } else if((query->flags & IODNSFLAG_PARENT_SOCKET)) {
120                 struct IODNSEvent event;
121                 event.type = state;
122                 event.query = NULL;
123                 event.result = query->result
124                 void *parent = query->parent;
125                 
126                 _stop_dnsquery(query);
127                 iosocket_lookup_callback(parent, &event);
128                 
129         }
130 }
131
132 void iodns_poll() {
133         if(dnsengine)
134                 dnsengine.loop();
135 }
136
137 /* public functions */
138
139 struct IODNSQuery *iodns_getaddrinfo(char *hostname, int records, iodns_callback *callback) {
140         if(!(records & IODNS_FORWARD) || !hostname || !callback)
141                 return NULL;
142         
143         struct IODNSQuery *descriptor = calloc(1, sizeof(*descriptor));
144         if(!descriptor) {
145                 iolog_trigger(IOLOG_ERROR, "could not allocate memory for IODNSQuery in %s:%d", __FILE__, __LINE__);
146                 return NULL;
147         }
148         
149         struct _IODNSQuery *query = _create_dnsquery();
150         if(!query) {
151                 free(descriptor);
152                 return NULL
153         }
154         
155         query->parent = descriptor;
156         query->flags |= IODNSFLAG_PARENT_PUBLIC;
157         descriptor->query = query;
158         
159         query->request.host = strdup(hostname);
160         query->type = (records & IODNS_FORWARD);
161         
162         descriptor->callback = callback;
163         
164         _start_dnsquery(query);
165         return descriptor;
166 }
167
168 struct IODNSQuery *iodns_getnameinfo(const struct sockaddr *addr, socklen_t addrlen, iodns_callback *callback) {
169         if(!addr || !callback)
170                 return NULL;
171         
172         struct IODNSQuery *descriptor = calloc(1, sizeof(*descriptor));
173         if(!descriptor) {
174                 iolog_trigger(IOLOG_ERROR, "could not allocate memory for IODNSQuery in %s:%d", __FILE__, __LINE__);
175                 return NULL;
176         }
177         
178         struct _IODNSQuery *query = _create_dnsquery();
179         if(!query) {
180                 free(descriptor);
181                 return NULL
182         }
183         
184         query->parent = descriptor;
185         query->flags |= IODNSFLAG_PARENT_PUBLIC;
186         descriptor->query = query;
187         
188         query->type = IODNS_RECORD_PTR;
189         query->request.addr.addresslen = addrlen;
190         query->request.addr.address = malloc(addrlen);
191         if(!query->request.addr.address) {
192                 iolog_trigger(IOLOG_ERROR, "could not allocate memory for sockaddr in %s:%d", __FILE__, __LINE__);
193                 _free_dnsquery(query);
194                 free(descriptor);
195                 return NULL;
196         }
197         memcpy(query->request.addr.address, addr, addrlen);
198         
199         descriptor->callback = callback;
200         
201         _start_dnsquery(query);
202         return descriptor;
203 }
204
205 void iodns_abort(struct IODNSQuery *descriptor) {
206         if(!descriptor)
207                 return;
208         
209         struct _IODNSQuery *query = descriptor->query;
210         if(!query) {
211                 iolog_trigger(IOLOG_WARNING, "called iodns_abort for destroyed IODNSQuery in %s:%d", __FILE__, __LINE__);
212                 return;
213         }
214         
215         _stop_dnsquery(query)
216 }
217
218 void iodns_free_result(struct IODNSResult *result) {
219         struct IODNSResult *next;
220         for(;result;result = next) {
221                 next = result->next;
222                 
223                 if((result->type & IODNS_FORWARD)) {
224                         if(result->result.addr.address)
225                                 free(result->result.addr.address);
226                 }
227                 if((result->type & IODNS_REVERSE)) {
228                         if(result->result.host)
229                                 free(result->result.host);
230                 }
231                 free(result);
232         }
233 }
234