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