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