Initialize Tizen 2.3
[framework/uifw/ecore.git] / wearable / src / lib / ecore_con / ecore_con_ares.c
1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif
4
5 /*
6  * This version of ecore_con_info use c-ares to provide asynchronous dns lookup.
7  *
8  * Note: It doesn't fork nor does it use libc getaddrinfo.
9  * http://c-ares.haxx.se/docs.html
10  */
11
12 #include <string.h>
13 #include <sys/types.h>
14
15 #ifdef HAVE_NETINET_IN_H
16 # include <netinet/in.h>
17 #endif
18
19 #ifdef HAVE_ARPA_INET_H
20 # include <arpa/inet.h>
21 #endif
22
23 #include <ares.h>
24
25 #include "Ecore.h"
26 #include "Ecore_Con.h"
27 #include "ecore_con_private.h"
28
29 typedef struct _Ecore_Con_FD    Ecore_Con_FD;
30 typedef struct _Ecore_Con_CAres Ecore_Con_CAres;
31
32 struct _Ecore_Con_FD
33 {
34    Ecore_Fd_Handler *handler;
35    Ecore_Timer      *timer;
36    int               fd;
37 };
38
39 struct _Ecore_Con_CAres
40 {
41    Ecore_Con_Server *svr;
42    Ecore_Con_Info_Cb done_cb;
43    void             *data;
44    struct addrinfo   hints;
45    Ecore_Con_Info   *result;
46
47    union {
48       struct in_addr  v4;
49 #ifdef HAVE_IPV6
50       struct in6_addr v6;
51 #endif
52    } addr;
53
54    Eina_Bool byaddr : 1;
55    Eina_Bool isv6 : 1;
56 };
57
58 static ares_channel info_channel;
59 static int info_init = 0;
60 static Eina_List *info_fds = NULL;
61
62 static void _ecore_con_info_ares_nameinfo(Ecore_Con_CAres *arg,
63                                           int              status,
64                                           int              timeouts,
65                                           char            *node,
66                                           char            *service);
67 static void _ecore_con_info_ares_host_cb(Ecore_Con_CAres *arg,
68                                          int              status,
69                                          int              timeouts,
70                                          struct hostent  *hostent);
71 static Eina_Bool _ecore_con_info_cares_fd_cb(Ecore_Con_FD     *ecf,
72                             Ecore_Fd_Handler *fd_handler);
73 static Eina_Bool _ecore_con_info_cares_timeout_cb(void *data);
74
75 static void
76 _ecore_con_info_cares_state_cb(void *data,
77                                ares_socket_t fd,
78                                int readable,
79                                int writable);
80 static int
81 _ecore_con_info_fds_search(const Ecore_Con_FD *fd1,
82                            const Ecore_Con_FD *fd2);
83
84 int
85 ecore_con_info_init(void)
86 {
87    struct ares_options opts;
88
89    if (!info_init)
90      {
91         if (ares_library_init(ARES_LIB_INIT_ALL))
92           return 0;
93
94         opts.lookups = "fb"; /* hosts file then dns */
95         opts.sock_state_cb = _ecore_con_info_cares_state_cb;
96
97         if (ares_init_options(&info_channel, &opts,
98             ARES_OPT_LOOKUPS | ARES_OPT_SOCK_STATE_CB) != ARES_SUCCESS)
99           {
100              ares_library_cleanup();
101              return 0;
102           }
103      }
104
105    info_init++;
106    return info_init;
107 }
108
109 int
110 ecore_con_info_shutdown(void)
111 {
112    info_init--;
113    if (info_init == 0)
114      {
115         /* Cancel all ongoing request */
116          ares_cancel(info_channel);
117          ares_destroy(info_channel);
118
119          /* Shutdown ares */
120          ares_library_cleanup();
121      }
122
123    return info_init;
124 }
125
126 int
127 ecore_con_info_tcp_connect(Ecore_Con_Server *svr,
128                            Ecore_Con_Info_Cb done_cb,
129                            void             *data)
130 {
131    struct addrinfo hints;
132
133    memset(&hints, 0, sizeof(struct addrinfo));
134 #ifdef HAVE_IPV6
135    hints.ai_family = AF_INET6;
136 #else
137    hints.ai_family = AF_INET;
138 #endif
139    hints.ai_socktype = SOCK_STREAM;
140    hints.ai_flags = AI_CANONNAME;
141    hints.ai_protocol = IPPROTO_TCP;
142    hints.ai_canonname = NULL;
143    hints.ai_next = NULL;
144    hints.ai_addr = NULL;
145
146    return ecore_con_info_get(svr, done_cb, data, &hints);
147 }
148
149 int
150 ecore_con_info_tcp_listen(Ecore_Con_Server *svr,
151                           Ecore_Con_Info_Cb done_cb,
152                           void             *data)
153 {
154    struct addrinfo hints;
155
156    memset(&hints, 0, sizeof(struct addrinfo));
157 #ifdef HAVE_IPV6
158    hints.ai_family = AF_INET6;
159 #else
160    hints.ai_family = AF_INET;
161 #endif
162    hints.ai_socktype = SOCK_STREAM;
163    hints.ai_flags = AI_PASSIVE;
164    hints.ai_protocol = IPPROTO_TCP;
165    hints.ai_canonname = NULL;
166    hints.ai_next = NULL;
167    hints.ai_addr = NULL;
168
169    return ecore_con_info_get(svr, done_cb, data, &hints);
170 }
171
172 int
173 ecore_con_info_udp_connect(Ecore_Con_Server *svr,
174                            Ecore_Con_Info_Cb done_cb,
175                            void             *data)
176 {
177    struct addrinfo hints;
178
179    memset(&hints, 0, sizeof(struct addrinfo));
180 #ifdef HAVE_IPV6
181    hints.ai_family = AF_INET6;
182 #else
183    hints.ai_family = AF_INET;
184 #endif
185    hints.ai_socktype = SOCK_DGRAM;
186    hints.ai_flags = AI_CANONNAME;
187    hints.ai_protocol = IPPROTO_UDP;
188    hints.ai_canonname = NULL;
189    hints.ai_next = NULL;
190    hints.ai_addr = NULL;
191
192    return ecore_con_info_get(svr, done_cb, data, &hints);
193 }
194
195 int
196 ecore_con_info_udp_listen(Ecore_Con_Server *svr,
197                           Ecore_Con_Info_Cb done_cb,
198                           void             *data)
199 {
200    struct addrinfo hints;
201
202    memset(&hints, 0, sizeof(struct addrinfo));
203 #ifdef HAVE_IPV6
204    hints.ai_family = AF_INET6;
205 #else
206    hints.ai_family = AF_INET;
207 #endif
208    hints.ai_socktype = SOCK_DGRAM;
209    hints.ai_flags = AI_PASSIVE;
210    hints.ai_protocol = IPPROTO_UDP;
211    hints.ai_canonname = NULL;
212    hints.ai_next = NULL;
213    hints.ai_addr = NULL;
214
215    return ecore_con_info_get(svr, done_cb, data, &hints);
216 }
217
218 int
219 ecore_con_info_mcast_listen(Ecore_Con_Server *svr,
220                             Ecore_Con_Info_Cb done_cb,
221                             void             *data)
222 {
223    struct addrinfo hints;
224
225    memset(&hints, 0, sizeof(struct addrinfo));
226 #ifdef HAVE_IPV6
227    hints.ai_family = AF_INET6;
228 #else
229    hints.ai_family = AF_INET;
230 #endif
231    hints.ai_socktype = SOCK_DGRAM;
232    hints.ai_flags = 0;
233    hints.ai_protocol = IPPROTO_UDP;
234    hints.ai_canonname = NULL;
235    hints.ai_next = NULL;
236    hints.ai_addr = NULL;
237
238    return ecore_con_info_get(svr, done_cb, data, &hints);
239 }
240
241 static Eina_Bool
242 _ecore_con_info_ares_getnameinfo(Ecore_Con_CAres *arg,
243                                  int              addrtype,
244                                  const char      *name,
245                                  struct sockaddr *addr,
246                                  int              addrlen)
247 {
248    int length = 0;
249
250    if (name)
251      length = strlen(name) + 1;
252    else
253      length = 1;
254
255    arg->result = malloc(sizeof(Ecore_Con_Info) + length);
256    if (!arg->result)
257      return EINA_FALSE;
258
259    /* FIXME: What to do when hint is not set ? */
260    arg->result->info.ai_flags = arg->hints.ai_flags;
261    arg->result->info.ai_socktype = arg->hints.ai_socktype;
262    arg->result->info.ai_protocol = arg->hints.ai_protocol;
263
264    arg->result->info.ai_family = addrtype;
265    arg->result->info.ai_addrlen = addrlen;
266    arg->result->info.ai_addr = addr;
267    arg->result->info.ai_canonname = (char *)(arg->result + 1);
268
269    if (!name)
270      *arg->result->info.ai_canonname = '\0';
271    else
272      strcpy(arg->result->info.ai_canonname, name);
273
274    arg->result->info.ai_next = NULL;
275
276    ares_getnameinfo(
277      info_channel, addr, addrlen,
278      ARES_NI_NUMERICSERV | ARES_NI_NUMERICHOST |
279      ARES_NI_LOOKUPSERVICE | ARES_NI_LOOKUPHOST,
280      (ares_nameinfo_callback)_ecore_con_info_ares_nameinfo, arg);
281
282    return EINA_TRUE;
283 }
284
285 EAPI int
286 ecore_con_info_get(Ecore_Con_Server *svr,
287                    Ecore_Con_Info_Cb done_cb,
288                    void             *data,
289                    struct addrinfo  *hints)
290 {
291    Ecore_Con_CAres *cares;
292 #ifdef HAVE_IPV6
293    int ai_family = AF_INET6;
294 #else
295    int ai_family = AF_INET;
296 #endif
297
298    cares = calloc(1, sizeof(Ecore_Con_CAres));
299    if (!cares)
300      return 0;
301
302    cares->svr = svr;
303    cares->done_cb = done_cb;
304    cares->data = data;
305
306    if (hints)
307      {
308         ai_family = hints->ai_family;
309         memcpy(&cares->hints, hints, sizeof(struct addrinfo));
310      }
311
312    if (inet_pton(AF_INET, svr->ecs ? svr->ecs->ip : svr->name, &cares->addr.v4) == 1)
313      {
314         cares->byaddr = EINA_TRUE;
315         cares->isv6 = EINA_FALSE;
316         ares_gethostbyaddr(info_channel, &cares->addr.v4,
317                            sizeof(cares->addr.v4),
318                            AF_INET,
319                            (ares_host_callback)_ecore_con_info_ares_host_cb,
320                            cares);
321      }
322 #ifdef HAVE_IPV6
323    else if (inet_pton(AF_INET6, svr->ecs ? svr->ecs->ip : svr->name, &cares->addr.v6) == 1)
324      {
325         cares->byaddr = EINA_TRUE;
326         cares->isv6 = EINA_TRUE;
327         ares_gethostbyaddr(info_channel, &cares->addr.v6,
328                            sizeof(cares->addr.v6),
329                            AF_INET6,
330                            (ares_host_callback)_ecore_con_info_ares_host_cb,
331                            cares);
332      }
333 #endif
334    else
335      {
336         cares->byaddr = EINA_FALSE;
337         ares_gethostbyname(info_channel, svr->ecs ? svr->ecs->ip : svr->name, ai_family,
338                            (ares_host_callback)_ecore_con_info_ares_host_cb,
339                            cares);
340      }
341
342    svr->infos = eina_list_append(svr->infos, cares);
343    return 1;
344 }
345
346 void
347 ecore_con_info_data_clear(void *info)
348 {
349    Ecore_Con_CAres *cares = info;
350    if (cares) cares->data = NULL;
351 }
352
353 static Eina_Bool
354 _ecore_con_info_cares_timeout_cb(void *data __UNUSED__)
355 {
356    ares_process_fd(info_channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
357    return ECORE_CALLBACK_RENEW;
358 }
359
360 static Eina_Bool
361 _ecore_con_info_cares_fd_cb(Ecore_Con_FD     *ecf,
362                             Ecore_Fd_Handler *fd_handler)
363 {
364    ares_socket_t read_fd, write_fd;
365
366    read_fd = write_fd = ARES_SOCKET_BAD;
367
368    if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_READ))
369      read_fd = ecf->fd;
370    if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_WRITE))
371      write_fd = ecf->fd;
372
373    ares_process_fd(info_channel, read_fd, write_fd);
374
375    return ECORE_CALLBACK_RENEW;
376 }
377
378 static int
379 _ecore_con_info_fds_search(const Ecore_Con_FD *fd1,
380                            const Ecore_Con_FD *fd2)
381 {
382    return fd1->fd - fd2->fd;
383 }
384
385 static void
386 _ecore_con_info_cares_state_cb(void *data __UNUSED__,
387                                ares_socket_t fd,
388                                int readable,
389                                int writable)
390 {
391    int flags = 0;
392    Ecore_Con_FD *search = NULL, *ecf = NULL;
393
394    search = eina_list_search_unsorted(info_fds,
395             (Eina_Compare_Cb)_ecore_con_info_fds_search, &ecf);
396
397    if (!(readable | writable))
398      {
399         ares_process_fd(info_channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
400         if (search)
401           {
402              info_fds = eina_list_remove(info_fds, search);
403              ecore_timer_del(search->timer);
404              ecore_main_fd_handler_del(search->handler);
405              free(search);
406           }
407         return;
408      }
409
410    if (!search)
411      {
412         search = malloc(sizeof(Ecore_Con_FD));
413         EINA_SAFETY_ON_NULL_RETURN(search);
414
415         search->fd = fd;
416         search->handler = ecore_main_fd_handler_add(fd, ECORE_FD_WRITE | ECORE_FD_READ,
417             (Ecore_Fd_Cb)_ecore_con_info_cares_fd_cb, search, NULL, NULL);
418         /* c-ares default timeout is 5 seconds */
419         search->timer = ecore_timer_add(5, _ecore_con_info_cares_timeout_cb, NULL);
420         info_fds = eina_list_append(info_fds, search);
421      }
422
423    if (readable) flags |= ECORE_FD_READ;
424    if (writable) flags |= ECORE_FD_WRITE;
425    ecore_main_fd_handler_active_set(search->handler, flags);
426 }
427
428 static void
429 _ecore_con_info_ares_host_cb(Ecore_Con_CAres *arg,
430                              int              status,
431                              int              timeouts  __UNUSED__,
432                              struct hostent  *hostent)
433 {
434    struct sockaddr *addr;
435    int addrlen;
436
437    /* Found something ? */
438    switch (status)
439      {
440       case ARES_SUCCESS:
441         if (!hostent->h_addr_list[0])
442           {
443              ERR("No IP found");
444              goto on_error;
445           }
446
447         switch (hostent->h_addrtype)
448           {
449            case AF_INET:
450            {
451               struct sockaddr_in *addri;
452
453               addrlen = sizeof(struct sockaddr_in);
454               addri = malloc(addrlen);
455
456               if (!addri)
457                 goto on_mem_error;
458
459               addri->sin_family = AF_INET;
460               addri->sin_port = htons(arg->svr->ecs ? arg->svr->ecs->port : arg->svr->port);
461
462               memcpy(&addri->sin_addr.s_addr,
463                      hostent->h_addr_list[0], sizeof(struct in_addr));
464
465               addr = (struct sockaddr *)addri;
466               break;
467            }
468 #ifdef HAVE_IPV6
469            case AF_INET6:
470            {
471               struct sockaddr_in6 *addri6;
472
473               addrlen = sizeof(struct sockaddr_in6);
474               addri6 = malloc(addrlen);
475
476               if (!addri6)
477                 goto on_mem_error;
478
479               addri6->sin6_family = AF_INET6;
480               addri6->sin6_port = htons(arg->svr->ecs ? arg->svr->ecs->port : arg->svr->port);
481               addri6->sin6_flowinfo = 0;
482               addri6->sin6_scope_id = 0;
483
484               memcpy(&addri6->sin6_addr.s6_addr,
485                      hostent->h_addr_list[0], sizeof(struct in6_addr));
486
487               addr = (struct sockaddr *)addri6;
488               break;
489            }
490 #endif
491            default:
492              ERR("Unknown addrtype %i", hostent->h_addrtype);
493              goto on_error;
494           }
495
496         if (!_ecore_con_info_ares_getnameinfo(arg, hostent->h_addrtype,
497                                               hostent->h_name,
498                                               addr, addrlen))
499           goto on_error;
500
501         break;
502
503       case ARES_ENOTFOUND: /* address notfound */
504         if (arg->byaddr)
505           {
506 #ifdef HAVE_IPV6
507              /* This happen when host doesn't have a reverse. */
508               if (arg->isv6)
509                 {
510                    struct sockaddr_in6 *addri6;
511
512                    addrlen = sizeof(struct sockaddr_in6);
513                    addri6 = malloc(addrlen);
514
515                    if (!addri6)
516                      goto on_mem_error;
517
518                    addri6->sin6_family = AF_INET6;
519                    addri6->sin6_port = htons(arg->svr->ecs ? arg->svr->ecs->port : arg->svr->port);
520                    addri6->sin6_flowinfo = 0;
521                    addri6->sin6_scope_id = 0;
522
523                    memcpy(&addri6->sin6_addr.s6_addr,
524                           &arg->addr.v6, sizeof(struct in6_addr));
525
526                    addr = (struct sockaddr *)addri6;
527                 }
528               else
529 #endif
530                 {
531                    struct sockaddr_in *addri;
532
533                    addrlen = sizeof(struct sockaddr_in);
534                    addri = malloc(addrlen);
535
536                    if (!addri)
537                      goto on_mem_error;
538
539                    addri->sin_family = AF_INET;
540                    addri->sin_port = htons(arg->svr->ecs ? arg->svr->ecs->port : arg->svr->port);
541
542                    memcpy(&addri->sin_addr.s_addr,
543                           &arg->addr.v4, sizeof(struct in_addr));
544
545                    addr = (struct sockaddr *)addri;
546                 }
547
548               if (!_ecore_con_info_ares_getnameinfo(arg,
549 #ifdef HAVE_IPV6
550                                                     arg->isv6 ? AF_INET6 :
551 #endif
552                                                     AF_INET,
553                                                     NULL, addr,
554                                                     addrlen))
555                 goto on_error;
556
557               break;
558           }
559
560       case ARES_ENOTIMP: /* unknown family */
561       case ARES_EBADNAME: /* not a valid internet address */
562       case ARES_ENOMEM: /* not enough memory */
563       case ARES_EDESTRUCTION: /* request canceled, shuting down */
564       case ARES_ENODATA: /* no data returned */
565       case ARES_ECONNREFUSED: /* connection refused */
566       case ARES_ETIMEOUT: /* connection timed out */
567         ecore_con_event_server_error(arg->svr, ares_strerror(status));
568         goto on_error;
569
570       default:
571         ERR("Unknown status returned by c-ares: %i assuming error", status);
572         ecore_con_event_server_error(arg->svr, ares_strerror(status));
573         goto on_error;
574      }
575
576    return;
577
578 on_mem_error:
579    ERR("Not enough memory");
580
581 on_error:
582    if (arg->data)
583      {
584         ecore_con_server_infos_del(arg->data, arg);
585         arg->done_cb(arg->data, NULL);
586      }
587    free(arg);
588 }
589
590 static void
591 _ecore_con_info_ares_nameinfo(Ecore_Con_CAres *arg,
592                               int              status,
593                               int              timeouts __UNUSED__,
594                               char            *node,
595                               char            *service)
596 {
597    switch (status)
598      {
599       case ARES_SUCCESS:
600         if (node)
601           strcpy(arg->result->ip, node);
602         else
603           *arg->result->ip = '\0';
604
605         if (service)
606           strcpy(arg->result->service, service);
607         else
608           *arg->result->service = '\0';
609
610         if (arg->data) arg->done_cb(arg->data, arg->result);
611         break;
612
613       case ARES_ENOTIMP:
614       case ARES_ENOTFOUND:
615       case ARES_ENOMEM:
616       case ARES_EDESTRUCTION:
617       case ARES_EBADFLAGS:
618         ecore_con_event_server_error(arg->svr, ares_strerror(status));
619         if (arg->data) arg->done_cb(arg->data, NULL);
620         break;
621      }
622
623    free(arg->result->info.ai_addr);
624    free(arg->result);
625    if (arg->data) ecore_con_server_infos_del(arg->data, arg);
626    free(arg);
627 }
628