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