Removed the logging facility from citserver, use syslog instead
[citadel.git] / citadel / modules / c-ares-dns / serv_c-ares-dns.c
1 /*
2  * Copyright (c) 1998-2009 by the citadel.org team
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, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  *
18  *  Inspired by NodeJS.org; thanks for the MX-Parser ;-)
19  */
20
21 #include "sysdep.h"
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <stdio.h>
25 #include <termios.h>
26 #include <fcntl.h>
27 #include <signal.h>
28 #include <pwd.h>
29 #include <errno.h>
30 #include <sys/types.h>
31 #include <syslog.h>
32
33 #if TIME_WITH_SYS_TIME
34 # include <sys/time.h>
35 # include <time.h>
36 #else
37 # if HAVE_SYS_TIME_H
38 #  include <sys/time.h>
39 # else
40 #  include <time.h>
41 # endif
42 #endif
43 #include <sys/wait.h>
44 #include <ctype.h>
45 #include <string.h>
46 #include <limits.h>
47 #include <sys/socket.h>
48 #include <netinet/in.h>
49 #include <arpa/inet.h>
50
51 #include <libcitadel.h>
52 #include "citadel.h"
53 #include "server.h"
54 #include "citserver.h"
55 #include "support.h"
56
57 #include "ctdl_module.h"
58 #include "event_client.h"
59
60
61 extern struct ev_loop *event_base;
62
63 void SockStateCb(void *data, int sock, int read, int write);
64
65
66 static void HostByAddrCb(void *data,
67                          int status,
68                          int timeouts,
69                          struct hostent *hostent) 
70 {
71         AsyncIO *IO = data;
72         IO->DNSQuery->DNSStatus = status;
73         if  (status != ARES_SUCCESS) {
74 //              ResolveError(*cb, status);
75                 return;
76         }
77         IO->DNSQuery->Data = hostent;
78 /// TODO: howto free this??
79 }
80
81 static void ParseAnswerA(AsyncIO *IO, unsigned char* abuf, int alen) 
82 {
83         struct hostent* host;
84
85         if (IO->DNSQuery->VParsedDNSReply != NULL)
86                 IO->DNSQuery->DNSReplyFree(IO->DNSQuery->VParsedDNSReply);
87         IO->DNSQuery->VParsedDNSReply = NULL;
88
89         IO->DNSQuery->DNSStatus = ares_parse_a_reply(abuf, alen, &host, NULL, NULL);
90         if (IO->DNSQuery->DNSStatus != ARES_SUCCESS) {
91 //    ResolveError(arg->js_cb, status);
92                 return;
93         }
94         IO->DNSQuery->VParsedDNSReply = host;
95         IO->DNSQuery->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
96 }
97
98
99 static void ParseAnswerAAAA(AsyncIO *IO, unsigned char* abuf, int alen) 
100 {
101         struct hostent* host;
102
103         if (IO->DNSQuery->VParsedDNSReply != NULL)
104                 IO->DNSQuery->DNSReplyFree(IO->DNSQuery->VParsedDNSReply);
105         IO->DNSQuery->VParsedDNSReply = NULL;
106
107         IO->DNSQuery->DNSStatus = ares_parse_aaaa_reply(abuf, alen, &host, NULL, NULL);
108         if (IO->DNSQuery->DNSStatus != ARES_SUCCESS) {
109 //    ResolveError(arg->js_cb, status);
110                 return;
111         }
112         IO->DNSQuery->VParsedDNSReply = host;
113         IO->DNSQuery->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
114 }
115
116
117 static void ParseAnswerCNAME(AsyncIO *IO, unsigned char* abuf, int alen) 
118 {
119         struct hostent* host;
120
121         if (IO->DNSQuery->VParsedDNSReply != NULL)
122                 IO->DNSQuery->DNSReplyFree(IO->DNSQuery->VParsedDNSReply);
123         IO->DNSQuery->VParsedDNSReply = NULL;
124
125         IO->DNSQuery->DNSStatus = ares_parse_a_reply(abuf, alen, &host, NULL, NULL);
126         if (IO->DNSQuery->DNSStatus != ARES_SUCCESS) {
127 //    ResolveError(arg->js_cb, status);
128                 return;
129         }
130
131         // a CNAME lookup always returns a single record but
132         IO->DNSQuery->VParsedDNSReply = host;
133         IO->DNSQuery->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
134 }
135
136
137 static void ParseAnswerMX(AsyncIO *IO, unsigned char* abuf, int alen) 
138 {
139         struct ares_mx_reply *mx_out;
140
141         if (IO->DNSQuery->VParsedDNSReply != NULL)
142                 IO->DNSQuery->DNSReplyFree(IO->DNSQuery->VParsedDNSReply);
143         IO->DNSQuery->VParsedDNSReply = NULL;
144
145         IO->DNSQuery->DNSStatus = ares_parse_mx_reply(abuf, alen, &mx_out);
146         if (IO->DNSQuery->DNSStatus != ARES_SUCCESS) {
147 //    ResolveError(arg->js_cb, status);
148                 return;
149         }
150
151         IO->DNSQuery->VParsedDNSReply = mx_out;
152         IO->DNSQuery->DNSReplyFree = (FreeDNSReply) ares_free_data;
153 }
154
155
156 static void ParseAnswerNS(AsyncIO *IO, unsigned char* abuf, int alen) 
157 {
158         struct hostent* host;
159
160         if (IO->DNSQuery->VParsedDNSReply != NULL)
161                 IO->DNSQuery->DNSReplyFree(IO->DNSQuery->VParsedDNSReply);
162         IO->DNSQuery->VParsedDNSReply = NULL;
163
164         IO->DNSQuery->DNSStatus = ares_parse_ns_reply(abuf, alen, &host);
165         if (IO->DNSQuery->DNSStatus != ARES_SUCCESS) {
166 //    ResolveError(arg->js_cb, status);
167                 return;
168         }
169         IO->DNSQuery->VParsedDNSReply = host;
170         IO->DNSQuery->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
171 }
172
173
174 static void ParseAnswerSRV(AsyncIO *IO, unsigned char* abuf, int alen) 
175 {
176         struct ares_srv_reply *srv_out;
177
178         if (IO->DNSQuery->VParsedDNSReply != NULL)
179                 IO->DNSQuery->DNSReplyFree(IO->DNSQuery->VParsedDNSReply);
180         IO->DNSQuery->VParsedDNSReply = NULL;
181
182         IO->DNSQuery->DNSStatus = ares_parse_srv_reply(abuf, alen, &srv_out);
183         if (IO->DNSQuery->DNSStatus != ARES_SUCCESS) {
184 //    ResolveError(arg->js_cb, status);
185                 return;
186         }
187
188         IO->DNSQuery->VParsedDNSReply = srv_out;
189         IO->DNSQuery->DNSReplyFree = (FreeDNSReply) ares_free_data;
190 }
191
192
193 static void ParseAnswerTXT(AsyncIO *IO, unsigned char* abuf, int alen) 
194 {
195         struct ares_txt_reply *txt_out;
196
197         if (IO->DNSQuery->VParsedDNSReply != NULL)
198                 IO->DNSQuery->DNSReplyFree(IO->DNSQuery->VParsedDNSReply);
199         IO->DNSQuery->VParsedDNSReply = NULL;
200
201         IO->DNSQuery->DNSStatus = ares_parse_txt_reply(abuf, alen, &txt_out);
202         if (IO->DNSQuery->DNSStatus != ARES_SUCCESS) {
203 //    ResolveError(arg->js_cb, status);
204                 return;
205         }
206         IO->DNSQuery->VParsedDNSReply = txt_out;
207         IO->DNSQuery->DNSReplyFree = (FreeDNSReply) ares_free_data;
208 }
209
210 void QueryCb(void *arg,
211              int status,
212              int timeouts,
213              unsigned char* abuf,
214              int alen) 
215 {
216         AsyncIO *IO = arg;
217
218         IO->DNSQuery->DNSStatus = status;
219         if (status == ARES_SUCCESS)
220                 IO->DNSQuery->DNS_CB(arg, abuf, alen);
221         else
222                 IO->DNSQuery->DNSStatus = status;
223 ///     ev_io_stop(event_base, &IO->DNSQuery->dns_io_event);
224         
225         ev_idle_init(&IO->unwind_stack,
226                      IO_postdns_callback);
227         IO->unwind_stack.data = IO;
228         ev_idle_start(event_base, &IO->unwind_stack);
229         syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
230 }
231
232 void QueryCbDone(AsyncIO *IO)
233 {
234         ev_idle_stop(event_base, &IO->unwind_stack);
235 }
236
237
238 void InitC_ares_dns(AsyncIO *IO)
239 {
240         int optmask = 0;
241         if (IO->DNSChannel == NULL) {
242                 optmask |= ARES_OPT_SOCK_STATE_CB;
243                 IO->DNSOptions.sock_state_cb = SockStateCb;
244                 IO->DNSOptions.sock_state_cb_data = IO;
245                 ares_init_options(&IO->DNSChannel, &IO->DNSOptions, optmask);
246         }
247 }
248
249 void QueueGetHostByNameDone(void *Ctx, 
250                             int status,
251                             int timeouts,
252                             struct hostent *hostent)
253 {
254         AsyncIO *IO = (AsyncIO *) Ctx;
255
256         IO->DNSQuery->DNSStatus = status;
257         IO->DNSQuery->VParsedDNSReply = hostent;
258         IO->DNSQuery->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
259
260         ev_idle_init(&IO->unwind_stack,
261                      IO_postdns_callback);
262         IO->unwind_stack.data = IO;
263         ev_idle_start(event_base, &IO->unwind_stack);
264         syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
265 }
266
267 void QueueGetHostByName(AsyncIO *IO, const char *Hostname, DNSQueryParts *QueryParts, IO_CallBack PostDNS)
268 {
269         IO->DNSQuery = QueryParts;
270         IO->DNSQuery->PostDNS = PostDNS;
271
272         InitC_ares_dns(IO);
273
274         ares_gethostbyname(IO->DNSChannel,
275                            Hostname,   
276                            AF_INET6, /* it falls back to ipv4 in doubt... */
277                            QueueGetHostByNameDone,
278                            IO);
279 //get_one_mx_host_ip_done);
280 }
281 int QueueQuery(ns_type Type, const char *name, AsyncIO *IO, DNSQueryParts *QueryParts, IO_CallBack PostDNS)
282 {
283         int length, family;
284         char address_b[sizeof(struct in6_addr)];
285
286         IO->DNSQuery = QueryParts;
287         IO->DNSQuery->PostDNS = PostDNS;
288
289         InitC_ares_dns(IO);
290
291         switch(Type) {
292         case ns_t_a:
293                 IO->DNSQuery->DNS_CB = ParseAnswerA;
294                 break;
295
296         case ns_t_aaaa:
297                 IO->DNSQuery->DNS_CB = ParseAnswerAAAA;
298                 break;
299
300         case ns_t_mx:
301                 IO->DNSQuery->DNS_CB = ParseAnswerMX;
302                 break;
303
304         case ns_t_ns:
305                 IO->DNSQuery->DNS_CB = ParseAnswerNS;
306                 break;
307
308         case ns_t_txt:
309                 IO->DNSQuery->DNS_CB = ParseAnswerTXT;
310                 break;
311
312         case ns_t_srv:
313                 IO->DNSQuery->DNS_CB = ParseAnswerSRV;
314                 break;
315
316         case ns_t_cname:
317                 IO->DNSQuery->DNS_CB = ParseAnswerCNAME;
318                 break;
319
320         case ns_t_ptr:
321
322
323                 if (inet_pton(AF_INET, name, &address_b) == 1) {
324                         length = sizeof(struct in_addr);
325                         family = AF_INET;
326                 } else if (inet_pton(AF_INET6, name, &address_b) == 1) {
327                         length = sizeof(struct in6_addr);
328                         family = AF_INET6;
329                 } else {
330                         return -1;
331                 }
332
333                 ares_gethostbyaddr(IO->DNSChannel, address_b, length, family, HostByAddrCb, IO);
334
335                 return 1;
336
337         default:
338                 return 0;
339         }
340         ares_query(IO->DNSChannel, name, ns_c_in, Type, QueryCb, IO);
341         return 1;
342 }
343
344
345
346
347
348 /*****************************************************************************
349  *                   libevent / c-ares integration                           *
350  *****************************************************************************/
351 static void DNS_send_callback(struct ev_loop *loop, ev_io *watcher, int revents)
352 {
353         AsyncIO *IO = watcher->data;
354         
355         ares_process_fd(IO->DNSChannel, ARES_SOCKET_BAD, IO->dns_send_event.fd);
356 }
357 static void DNS_recv_callback(struct ev_loop *loop, ev_io *watcher, int revents)
358 {
359         AsyncIO *IO = watcher->data;
360         
361         ares_process_fd(IO->DNSChannel, IO->dns_recv_event.fd, ARES_SOCKET_BAD);
362 }
363
364 void SockStateCb(void *data, int sock, int read, int write) 
365 {
366 /*
367         struct timeval tvbuf, maxtv, *ret;
368         
369         int64_t time = 10;
370 */
371         AsyncIO *IO = data;
372 /* already inside of the event queue. */        
373
374         if (read) {
375                 if ((IO->dns_recv_event.fd != sock) &&
376                     (IO->dns_recv_event.fd != 0)) {
377                         ev_io_stop(event_base, &IO->dns_recv_event);
378                 }
379                 IO->dns_recv_event.fd = sock;
380                 ev_io_init(&IO->dns_recv_event, DNS_recv_callback, IO->dns_recv_event.fd, EV_READ);
381                 IO->dns_recv_event.data = IO;
382                 ev_io_start(event_base, &IO->dns_recv_event);
383         } 
384         if (write) {
385                 if ((IO->dns_send_event.fd != sock) &&
386                     (IO->dns_send_event.fd != 0)) {
387                         ev_io_stop(event_base, &IO->dns_send_event);
388                 }
389                 IO->dns_send_event.fd = sock;
390                 ev_io_init(&IO->dns_send_event, DNS_send_callback, IO->dns_send_event.fd, EV_WRITE);
391                 IO->dns_send_event.data = IO;
392                 ev_io_start(event_base, &IO->dns_send_event);
393         }
394 /*
395
396                 ev_io_start(event_base, &IO->dns_io_event);
397         
398                 maxtv.tv_sec = time/1000;
399                 maxtv.tv_usec = (time % 1000) * 1000;
400                 
401                 ret = ares_timeout(IO->DNSChannel, &maxtv, &tvbuf);
402         }
403 */
404         if ((read == 0) && (write == 0)) {
405                 ev_io_stop(event_base, &IO->dns_recv_event);
406                 ev_io_stop(event_base, &IO->dns_send_event);
407         }
408 }
409
410 CTDL_MODULE_INIT(c_ares_client)
411 {
412         if (!threading)
413         {
414                 int r = ares_library_init(ARES_LIB_INIT_ALL);
415                 if (0 != r) {
416                         // TODO
417                         // ThrowException(Exception::Error(String::New(ares_strerror(r))));
418 ////                    assert(r == 0);
419                 }
420         }
421         return "c-ares";
422 }