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