Move c-ares related data into its own Sub-Struct
[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 #ifdef DEBUG_CARES
73         EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
74 #endif
75         ev_timer_stop (event_base, &IO->DNS.timeout);
76
77         IO->DNS.Query->DNSStatus = status;
78         if  (status != ARES_SUCCESS) {
79                 StrBufPlain(IO->ErrMsg, ares_strerror(status), -1);
80                 return;
81         }
82         IO->DNS.Query->Data = hostent;
83 /// TODO: howto free this??
84 }
85
86 static void ParseAnswerA(AsyncIO *IO, unsigned char* abuf, int alen) 
87 {
88         struct hostent* host;
89 #ifdef DEBUG_CARES
90         EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
91 #endif
92
93         if (IO->DNS.Query->VParsedDNSReply != NULL)
94                 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
95         IO->DNS.Query->VParsedDNSReply = NULL;
96
97         IO->DNS.Query->DNSStatus = ares_parse_a_reply(abuf, alen, &host, NULL, NULL);
98         if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
99                 StrBufPlain(IO->ErrMsg, ares_strerror(IO->DNS.Query->DNSStatus), -1);
100                 return;
101         }
102         IO->DNS.Query->VParsedDNSReply = host;
103         IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
104 }
105
106
107 static void ParseAnswerAAAA(AsyncIO *IO, unsigned char* abuf, int alen) 
108 {
109         struct hostent* host;
110 #ifdef DEBUG_CARES
111         EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
112 #endif
113
114         if (IO->DNS.Query->VParsedDNSReply != NULL)
115                 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
116         IO->DNS.Query->VParsedDNSReply = NULL;
117
118         IO->DNS.Query->DNSStatus = ares_parse_aaaa_reply(abuf, alen, &host, NULL, NULL);
119         if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
120                 StrBufPlain(IO->ErrMsg, ares_strerror(IO->DNS.Query->DNSStatus), -1);
121                 return;
122         }
123         IO->DNS.Query->VParsedDNSReply = host;
124         IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
125 }
126
127
128 static void ParseAnswerCNAME(AsyncIO *IO, unsigned char* abuf, int alen) 
129 {
130         struct hostent* host;
131
132 #ifdef DEBUG_CARES
133         EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
134 #endif
135
136         if (IO->DNS.Query->VParsedDNSReply != NULL)
137                 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
138         IO->DNS.Query->VParsedDNSReply = NULL;
139
140         IO->DNS.Query->DNSStatus = ares_parse_a_reply(abuf, alen, &host, NULL, NULL);
141         if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
142                 StrBufPlain(IO->ErrMsg, ares_strerror(IO->DNS.Query->DNSStatus), -1);
143                 return;
144         }
145
146         // a CNAME lookup always returns a single record but
147         IO->DNS.Query->VParsedDNSReply = host;
148         IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
149 }
150
151
152 static void ParseAnswerMX(AsyncIO *IO, unsigned char* abuf, int alen) 
153 {
154         struct ares_mx_reply *mx_out;
155 #ifdef DEBUG_CARES
156         EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
157 #endif
158
159         if (IO->DNS.Query->VParsedDNSReply != NULL)
160                 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
161         IO->DNS.Query->VParsedDNSReply = NULL;
162
163         IO->DNS.Query->DNSStatus = ares_parse_mx_reply(abuf, alen, &mx_out);
164         if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
165                 StrBufPlain(IO->ErrMsg, ares_strerror(IO->DNS.Query->DNSStatus), -1);
166                 return;
167         }
168
169         IO->DNS.Query->VParsedDNSReply = mx_out;
170         IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_data;
171 }
172
173
174 static void ParseAnswerNS(AsyncIO *IO, unsigned char* abuf, int alen) 
175 {
176         struct hostent* host;
177 #ifdef DEBUG_CARES
178         EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
179 #endif
180
181         if (IO->DNS.Query->VParsedDNSReply != NULL)
182                 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
183         IO->DNS.Query->VParsedDNSReply = NULL;
184
185         IO->DNS.Query->DNSStatus = ares_parse_ns_reply(abuf, alen, &host);
186         if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
187                 StrBufPlain(IO->ErrMsg, ares_strerror(IO->DNS.Query->DNSStatus), -1);
188                 return;
189         }
190         IO->DNS.Query->VParsedDNSReply = host;
191         IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
192 }
193
194
195 static void ParseAnswerSRV(AsyncIO *IO, unsigned char* abuf, int alen) 
196 {
197         struct ares_srv_reply *srv_out;
198 #ifdef DEBUG_CARES
199         EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
200 #endif
201
202         if (IO->DNS.Query->VParsedDNSReply != NULL)
203                 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
204         IO->DNS.Query->VParsedDNSReply = NULL;
205
206         IO->DNS.Query->DNSStatus = ares_parse_srv_reply(abuf, alen, &srv_out);
207         if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
208                 StrBufPlain(IO->ErrMsg, ares_strerror(IO->DNS.Query->DNSStatus), -1);
209                 return;
210         }
211
212         IO->DNS.Query->VParsedDNSReply = srv_out;
213         IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_data;
214 }
215
216
217 static void ParseAnswerTXT(AsyncIO *IO, unsigned char* abuf, int alen) 
218 {
219         struct ares_txt_reply *txt_out;
220 #ifdef DEBUG_CARES
221         EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
222 #endif
223
224         if (IO->DNS.Query->VParsedDNSReply != NULL)
225                 IO->DNS.Query->DNSReplyFree(IO->DNS.Query->VParsedDNSReply);
226         IO->DNS.Query->VParsedDNSReply = NULL;
227
228         IO->DNS.Query->DNSStatus = ares_parse_txt_reply(abuf, alen, &txt_out);
229         if (IO->DNS.Query->DNSStatus != ARES_SUCCESS) {
230                 StrBufPlain(IO->ErrMsg, ares_strerror(IO->DNS.Query->DNSStatus), -1);
231                 return;
232         }
233         IO->DNS.Query->VParsedDNSReply = txt_out;
234         IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_data;
235 }
236
237 void QueryCb(void *arg,
238              int status,
239              int timeouts,
240              unsigned char* abuf,
241              int alen) 
242 {
243         AsyncIO *IO = arg;
244 #ifdef DEBUG_CARES
245         EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
246 #endif
247         ev_timer_stop (event_base, &IO->DNS.timeout);
248
249         IO->DNS.Query->DNSStatus = status;
250         if (status == ARES_SUCCESS)
251                 IO->DNS.Query->DNS_CB(arg, abuf, alen);
252         else {
253                 EV_syslog(LOG_DEBUG, "C-ARES: Failed by: %s error %s\n",
254                           __FUNCTION__,
255                           ares_strerror(status));
256                 StrBufPlain(IO->ErrMsg, ares_strerror(status), -1);
257                 IO->DNS.Query->DNSStatus = status;
258         }
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 }
265
266 void QueryCbDone(AsyncIO *IO)
267 {
268 #ifdef DEBUG_CARES
269         EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
270 #endif
271
272         ev_idle_stop(event_base, &IO->unwind_stack);
273 }
274
275
276 void InitC_ares_dns(AsyncIO *IO)
277 {
278         int optmask = 0;
279 #ifdef DEBUG_CARES
280         EV_syslog(LOG_DEBUG, "C-ARES: %s %p\n", __FUNCTION__, IO->DNS.Channel);
281 #endif
282
283         if (IO->DNS.Channel == NULL) {
284                 optmask |= ARES_OPT_SOCK_STATE_CB;
285                 IO->DNS.Options.sock_state_cb = SockStateCb;
286                 IO->DNS.Options.sock_state_cb_data = IO;
287                 ares_init_options(&IO->DNS.Channel, &IO->DNS.Options, optmask);
288         }
289         IO->DNS.Query->DNSStatus = 0;
290 }
291
292 static void
293 DNStimeouttrigger_callback(struct ev_loop *loop, ev_timer *watcher, int revents)
294 {
295         AsyncIO *IO = watcher->data;
296         struct timeval tv, MaxTV;
297         struct timeval *NextTV;
298
299         memset(&MaxTV, 0, sizeof(MaxTV));
300         memset(&tv, 0, sizeof(tv));
301         MaxTV.tv_sec = 30;
302         NextTV = ares_timeout(IO->DNS.Channel, &MaxTV, &tv);
303
304         if ((NextTV->tv_sec != MaxTV.tv_sec) ||
305             (NextTV->tv_usec != MaxTV.tv_usec))
306         {
307                 fd_set readers, writers;
308 #ifdef DEBUG_CARES
309                 EV_syslog(LOG_DEBUG, "C-ARES: %s Timeout!\n", __FUNCTION__);
310 #endif
311                 FD_ZERO(&readers);
312                 FD_ZERO(&writers);
313                 ares_fds(IO->DNS.Channel, &readers, &writers);
314                 ares_process(IO->DNS.Channel, &readers, &writers);
315         }
316 }
317
318 void QueueGetHostByNameDone(void *Ctx, 
319                             int status,
320                             int timeouts,
321                             struct hostent *hostent)
322 {
323         AsyncIO *IO = (AsyncIO *) Ctx;
324 #ifdef DEBUG_CARES
325         EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
326 #endif
327
328         IO->DNS.Query->DNSStatus = status;
329         IO->DNS.Query->VParsedDNSReply = hostent;
330         IO->DNS.Query->DNSReplyFree = (FreeDNSReply) ares_free_hostent;
331
332         ev_idle_init(&IO->unwind_stack,
333                      IO_postdns_callback);
334         IO->unwind_stack.data = IO;
335         ev_idle_start(event_base, &IO->unwind_stack);
336 }
337
338 void QueueGetHostByName(AsyncIO *IO, const char *Hostname, DNSQueryParts *QueryParts, IO_CallBack PostDNS)
339 {
340 #ifdef DEBUG_CARES
341         EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
342         IO->DNS.SourcePort = 0;
343 #endif
344
345         IO->DNS.Query = QueryParts;
346         IO->DNS.Query->PostDNS = PostDNS;
347
348         InitC_ares_dns(IO);
349
350         ev_timer_init(&IO->DNS.timeout, DNStimeouttrigger_callback, 10, 1);
351         IO->DNS.timeout.data = IO;
352         ares_gethostbyname(IO->DNS.Channel,
353                            Hostname,   
354                            AF_INET6, /* it falls back to ipv4 in doubt... */
355                            QueueGetHostByNameDone,
356                            IO);
357         ev_timer_start(event_base, &IO->DNS.timeout);
358
359 }
360 int QueueQuery(ns_type Type, const char *name, AsyncIO *IO, DNSQueryParts *QueryParts, IO_CallBack PostDNS)
361 {
362         int length, family;
363         char address_b[sizeof(struct in6_addr)];
364
365 #ifdef DEBUG_CARES
366         IO->DNS.SourcePort = 0;
367 #endif
368
369         IO->DNS.Query = QueryParts;
370         IO->DNS.Query->PostDNS = PostDNS;
371
372         InitC_ares_dns(IO);
373
374         ev_timer_init(&IO->DNS.timeout, DNStimeouttrigger_callback, 10, 1);
375         IO->DNS.timeout.data = IO;
376
377         switch(Type) {
378         case ns_t_a:
379                 IO->DNS.Query->DNS_CB = ParseAnswerA;
380                 break;
381
382         case ns_t_aaaa:
383                 IO->DNS.Query->DNS_CB = ParseAnswerAAAA;
384                 break;
385
386         case ns_t_mx:
387                 IO->DNS.Query->DNS_CB = ParseAnswerMX;
388                 break;
389
390         case ns_t_ns:
391                 IO->DNS.Query->DNS_CB = ParseAnswerNS;
392                 break;
393
394         case ns_t_txt:
395                 IO->DNS.Query->DNS_CB = ParseAnswerTXT;
396                 break;
397
398         case ns_t_srv:
399                 IO->DNS.Query->DNS_CB = ParseAnswerSRV;
400                 break;
401
402         case ns_t_cname:
403                 IO->DNS.Query->DNS_CB = ParseAnswerCNAME;
404                 break;
405
406         case ns_t_ptr:
407
408
409                 if (inet_pton(AF_INET, name, &address_b) == 1) {
410                         length = sizeof(struct in_addr);
411                         family = AF_INET;
412                 } else if (inet_pton(AF_INET6, name, &address_b) == 1) {
413                         length = sizeof(struct in6_addr);
414                         family = AF_INET6;
415                 } else {
416                         return -1;
417                 }
418
419                 ares_gethostbyaddr(IO->DNS.Channel, address_b, length, family, HostByAddrCb, IO);
420                 ev_timer_start(event_base, &IO->DNS.timeout);
421 #ifdef DEBUG_CARES
422                 EV_syslog(LOG_DEBUG, "C-ARES: %s X1\n", __FUNCTION__);
423 #endif
424                 return 1;
425
426         default:
427 #ifdef DEBUG_CARES
428                 EV_syslog(LOG_DEBUG, "C-ARES: %sX2\n", __FUNCTION__);
429 #endif
430                 return 0;
431         }
432 #ifdef DEBUG_CARES
433         EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
434 #endif
435         ares_query(IO->DNS.Channel, name, ns_c_in, Type, QueryCb, IO);
436         ev_timer_start(event_base, &IO->DNS.timeout);
437         return 1;
438 }
439
440
441
442
443
444 /*****************************************************************************
445  *                      libev / c-ares integration                           *
446  *****************************************************************************/
447 static void DNS_send_callback(struct ev_loop *loop, ev_io *watcher, int revents)
448 {
449         AsyncIO *IO = watcher->data;
450         
451 #ifdef DEBUG_CARES
452         EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
453 #endif
454
455         ares_process_fd(IO->DNS.Channel, ARES_SOCKET_BAD, IO->DNS.send_event.fd);
456 }
457 static void DNS_recv_callback(struct ev_loop *loop, ev_io *watcher, int revents)
458 {
459         AsyncIO *IO = watcher->data;
460         
461 #ifdef DEBUG_CARES
462         EV_syslog(LOG_DEBUG, "C-ARES: %s\n", __FUNCTION__);
463 #endif
464
465         ares_process_fd(IO->DNS.Channel, IO->DNS.recv_event.fd, ARES_SOCKET_BAD);
466 }
467
468 void SockStateCb(void *data, int sock, int read, int write) 
469 {
470         AsyncIO *IO = data;
471 /* already inside of the event queue. */
472 #ifdef DEBUG_CARES
473 {
474         struct sockaddr_in sin = {};
475         socklen_t slen;
476         slen = sizeof(sin);     
477         if ((IO->DNS.SourcePort == 0) && 
478             (getsockname(sock, &sin, &slen) == 0))
479         {
480                 IO->DNS.SourcePort = ntohs(sin.sin_port);
481         }
482         EV_syslog(LOG_DEBUG, "C-ARES: %s %d|%d Sock %d port %hu\n",
483                   __FUNCTION__,
484                   read,
485                   write,
486                   sock,
487                   IO->DNS.SourcePort);
488 }
489 #endif
490
491         if (read) {
492                 if ((IO->DNS.recv_event.fd != sock) &&
493                     (IO->DNS.recv_event.fd != 0)) {
494                         ev_io_stop(event_base, &IO->DNS.recv_event);
495                 }
496                 IO->DNS.recv_event.fd = sock;
497                 ev_io_init(&IO->DNS.recv_event, DNS_recv_callback, IO->DNS.recv_event.fd, EV_READ);
498                 IO->DNS.recv_event.data = IO;
499                 ev_io_start(event_base, &IO->DNS.recv_event);
500         } 
501         if (write) {
502                 if ((IO->DNS.send_event.fd != sock) &&
503                     (IO->DNS.send_event.fd != 0)) {
504                         ev_io_stop(event_base, &IO->DNS.send_event);
505                 }
506                 IO->DNS.send_event.fd = sock;
507                 ev_io_init(&IO->DNS.send_event, DNS_send_callback, IO->DNS.send_event.fd, EV_WRITE);
508                 IO->DNS.send_event.data = IO;
509                 ev_io_start(event_base, &IO->DNS.send_event);
510         }
511         if ((read == 0) && (write == 0)) {
512                 ev_io_stop(event_base, &IO->DNS.recv_event);
513                 ev_io_stop(event_base, &IO->DNS.send_event);
514         }
515 }
516
517 CTDL_MODULE_INIT(c_ares_client)
518 {
519         if (!threading)
520         {
521                 int r = ares_library_init(ARES_LIB_INIT_ALL);
522                 if (0 != r) {
523                         // TODO
524                         // ThrowException(Exception::Error(String::New(ares_strerror(r))));
525 ////                    assert(r == 0);
526                 }
527         }
528         return "c-ares";
529 }