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