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