svn update: 48958 (latest:48959)
[framework/uifw/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;
44 };
45
46 static ares_channel info_channel;
47 static int info_init = 0;
48 static Eina_List *info_fds = NULL;
49 static int active = 0;
50 static Ecore_Timer *tm = NULL;
51 static fd_set info_readers, info_writers;
52
53 static void _ecore_con_info_ares_nameinfo(Ecore_Con_CAres *arg, int status, int timeouts, char *node, char *service);
54 static void _ecore_con_info_ares_host_cb(Ecore_Con_CAres *arg, int status, int timeouts, struct hostent *hostent);
55 static int _ecore_con_info_cares_fd_cb(void *data, Ecore_Fd_Handler *fd_handler);
56 static int _ecore_con_info_cares_timeout_cb(void *data);
57 static void _ecore_con_info_cares_clean(void);
58
59 int
60 ecore_con_info_init(void)
61 {
62    if (info_init == 0)
63      {
64         if (ares_library_init(ARES_LIB_INIT_ALL) != 0)
65           return 0;
66         if (ares_init(&info_channel) != ARES_SUCCESS)
67           {
68              ares_library_cleanup();
69              return 0;
70           }
71      }
72
73    info_init++;
74    return info_init;
75 }
76
77 int
78 ecore_con_info_shutdown(void)
79 {
80    info_init--;
81    if (info_init == 0)
82      {
83         /* Cancel all ongoing request */
84         ares_cancel(info_channel);
85         ares_destroy(info_channel);
86
87         /* Destroy FD handler here. */
88         /* Shutdown ares */
89         ares_library_cleanup();
90      }
91    return info_init;
92 }
93
94 int
95 ecore_con_info_tcp_connect(Ecore_Con_Server *svr,
96                            Ecore_Con_Info_Cb done_cb,
97                            void *data)
98 {
99    struct addrinfo hints;
100
101    memset(&hints, 0, sizeof(struct addrinfo));
102    hints.ai_family = AF_UNSPEC;
103    hints.ai_socktype = SOCK_STREAM;
104    hints.ai_flags = AI_CANONNAME;
105    hints.ai_protocol = IPPROTO_TCP;
106    hints.ai_canonname = NULL;
107    hints.ai_next = NULL;
108    hints.ai_addr = NULL;
109
110    return ecore_con_info_get(svr, done_cb, data, &hints);
111 }
112
113 int
114 ecore_con_info_tcp_listen(Ecore_Con_Server *svr,
115                           Ecore_Con_Info_Cb done_cb,
116                           void *data)
117 {
118    struct addrinfo hints;
119
120    memset(&hints, 0, sizeof(struct addrinfo));
121    hints.ai_family = AF_UNSPEC;
122    hints.ai_socktype = SOCK_STREAM;
123    hints.ai_flags = AI_PASSIVE;
124    hints.ai_protocol = IPPROTO_TCP;
125    hints.ai_canonname = NULL;
126    hints.ai_next = NULL;
127    hints.ai_addr = NULL;
128
129    return ecore_con_info_get(svr, done_cb, data, &hints);
130 }
131
132 int
133 ecore_con_info_udp_connect(Ecore_Con_Server *svr,
134                            Ecore_Con_Info_Cb done_cb,
135                            void *data)
136 {
137    struct addrinfo hints;
138
139    memset(&hints, 0, sizeof(struct addrinfo));
140    hints.ai_family = AF_UNSPEC;
141    hints.ai_socktype = SOCK_DGRAM;
142    hints.ai_flags = AI_CANONNAME;
143    hints.ai_protocol = IPPROTO_UDP;
144    hints.ai_canonname = NULL;
145    hints.ai_next = NULL;
146    hints.ai_addr = NULL;
147
148    return ecore_con_info_get(svr, done_cb, data, &hints);
149 }
150
151 int
152 ecore_con_info_udp_listen(Ecore_Con_Server *svr,
153                           Ecore_Con_Info_Cb done_cb,
154                           void *data)
155 {
156    struct addrinfo hints;
157
158    memset(&hints, 0, sizeof(struct addrinfo));
159    hints.ai_family = AF_UNSPEC;
160    hints.ai_socktype = SOCK_DGRAM;
161    hints.ai_flags = AI_PASSIVE;
162    hints.ai_protocol = IPPROTO_UDP;
163    hints.ai_canonname = NULL;
164    hints.ai_next = NULL;
165    hints.ai_addr = NULL;
166
167    return ecore_con_info_get(svr, done_cb, data, &hints);
168 }
169
170 int
171 ecore_con_info_mcast_listen(Ecore_Con_Server *svr,
172                            Ecore_Con_Info_Cb done_cb,
173                            void *data)
174 {
175    struct addrinfo hints;
176
177    memset(&hints, 0, sizeof(struct addrinfo));
178    hints.ai_family = AF_UNSPEC;
179    hints.ai_socktype = SOCK_DGRAM;
180    hints.ai_flags = 0;
181    hints.ai_protocol = IPPROTO_UDP;
182    hints.ai_canonname = NULL;
183    hints.ai_next = NULL;
184    hints.ai_addr = NULL;
185
186    return ecore_con_info_get(svr, done_cb, data, &hints);
187 }
188
189 EAPI int
190 ecore_con_info_get(Ecore_Con_Server *svr,
191                    Ecore_Con_Info_Cb done_cb,
192                    void *data,
193                    struct addrinfo *hints)
194 {
195    Ecore_Con_CAres *cares;
196    int ai_family = AF_UNSPEC;
197
198    cares = calloc(1, sizeof (Ecore_Con_CAres));
199    if (!cares) return 0;
200
201    cares->svr = svr;
202    cares->done_cb = done_cb;
203    cares->data = data;
204
205    if (hints)
206      {
207         ai_family = hints->ai_family;
208         memcpy(&cares->hints, hints, sizeof (struct addrinfo));
209      }
210
211    if (inet_pton(AF_INET, svr->name, &cares->addr.v4) == 1)
212      {
213         cares->byaddr = EINA_TRUE;
214         ares_gethostbyaddr(info_channel, &cares->addr.v4, sizeof (cares->addr.v4), AF_INET, (ares_host_callback) _ecore_con_info_ares_host_cb, cares);
215      }
216    else if (inet_pton(AF_INET6, svr->name, &cares->addr.v6) == 1)
217      {
218         cares->byaddr = EINA_TRUE;
219         ares_gethostbyaddr(info_channel, &cares->addr.v6, sizeof (cares->addr.v6), AF_INET6, (ares_host_callback) _ecore_con_info_ares_host_cb, cares);
220      }
221    else
222      {
223         cares->byaddr = EINA_FALSE;
224         ares_gethostbyname(info_channel, svr->name, ai_family, (ares_host_callback) _ecore_con_info_ares_host_cb, cares);
225      }
226
227    _ecore_con_info_cares_clean();
228
229    return 1;
230 }
231
232 static int
233 _ecore_con_info_fds_search(const Ecore_Con_FD *fd1, const Ecore_Con_FD *fd2)
234 {
235    return fd1->fd - fd2->fd;
236 }
237
238 static Eina_Bool
239 _ecore_con_info_fds_lookup(int fd)
240 {
241    Ecore_Con_FD fdl;
242    Ecore_Con_FD *search;
243
244    fdl.fd = fd;
245
246    search = eina_list_search_unsorted(info_fds, (Eina_Compare_Cb) _ecore_con_info_fds_search, &fdl);
247
248    if (search)
249      {
250         search->active = active;
251         return EINA_TRUE;
252      }
253    return EINA_FALSE;
254 }
255
256 static void
257 _ecore_con_info_cares_clean(void)
258 {
259    fd_set readers, writers;
260    Eina_List *l, *l_next;
261    Ecore_Con_FD *ecf;
262    int nfds;
263    int i;
264
265    FD_ZERO(&readers);
266    FD_ZERO(&writers);
267    nfds = ares_fds(info_channel, &readers, &writers);
268
269    active++;
270    for (i = 0; i < nfds; ++i)
271      {
272         int flags = 0;
273
274         if (FD_ISSET(i, &readers)) flags |= ECORE_FD_READ;
275         if (FD_ISSET(i, &writers)) flags |= ECORE_FD_WRITE;
276
277         if (flags)
278           {
279              if (!_ecore_con_info_fds_lookup(i))
280                {
281                   ecf = malloc(sizeof (Ecore_Con_FD));
282                   if (ecf)
283                     {
284                        ecf->fd = i;
285                        ecf->active = active;
286                        ecf->handler = ecore_main_fd_handler_add(i, ECORE_FD_WRITE | ECORE_FD_READ,
287                                                                 _ecore_con_info_cares_fd_cb,
288                                                                 NULL, NULL, NULL);
289                        info_fds = eina_list_append(info_fds, ecf);
290                     }
291                }
292           }
293      }
294
295    info_readers = readers;
296    info_writers = writers;
297
298    EINA_LIST_FOREACH_SAFE(info_fds, l, l_next, ecf)
299      {
300         if (ecf->active != active)
301           {
302              ecore_main_fd_handler_del(ecf->handler);
303              free(ecf);
304              info_fds = eina_list_remove_list(info_fds, l);
305           }
306      }
307
308    if (!info_fds)
309      {
310         if (tm) ecore_timer_del(tm);
311         tm = NULL;
312      }
313    else
314      {
315         struct timeval tv;
316
317         ares_timeout(info_channel, NULL, &tv);
318
319         if (tm)
320           ecore_timer_delay(tm, tv.tv_sec);
321         else
322           tm = ecore_timer_add((double) tv.tv_sec, _ecore_con_info_cares_timeout_cb, NULL);
323      }
324 }
325
326 static int
327 _ecore_con_info_cares_timeout_cb(void *data)
328 {
329    ares_process(info_channel, &info_readers, &info_writers);
330    _ecore_con_info_cares_clean();
331
332    return 1;
333 }
334
335 static int
336 _ecore_con_info_cares_fd_cb(void *data, Ecore_Fd_Handler *fd_handler)
337 {
338    ares_process(info_channel, &info_readers, &info_writers);
339    _ecore_con_info_cares_clean();
340
341    return 1;
342 }
343
344 static void
345 _ecore_con_info_ares_host_cb(Ecore_Con_CAres *arg, int status, int timeouts, struct hostent *hostent)
346 {
347    struct sockaddr *addr;
348    int addrlen;
349    int length = 0;
350
351    /* Found something ? */
352    switch (status)
353      {
354       case ARES_SUCCESS:
355          if (hostent->h_addr_list[0] == NULL)
356            {
357               fprintf(stderr, "No IP found\n");
358               goto on_error;
359            }
360
361          switch (hostent->h_addrtype)
362            {
363             case AF_INET:
364               {
365                  struct sockaddr_in *addri;
366
367                  addrlen = sizeof (struct sockaddr_in);
368                  addri = malloc(addrlen);
369
370                  if (!addri)
371                    {
372                       fprintf(stderr, "Not enough memory\n");
373                       goto on_error;
374                    }
375
376                  addri->sin_family = AF_INET;
377                  addri->sin_port = htons(arg->svr->port);
378
379                  memcpy(&addri->sin_addr.s_addr, arg->byaddr ? &arg->addr.v4 : (struct in_addr*)hostent->h_addr_list[0], sizeof (struct in_addr));
380
381                  addr = (struct sockaddr*) addri;
382                  break;
383               }
384             case AF_INET6:
385               {
386                  struct sockaddr_in6 *addri6;
387
388                  addrlen = sizeof (struct sockaddr_in6);
389                  addri6 = malloc(addrlen);
390
391                  if (!addri6)
392                    {
393                       fprintf(stderr, "Not enough memory\n");
394                       goto on_error;
395                    }
396
397                  addri6->sin6_family = AF_INET6;
398                  addri6->sin6_port = htons(arg->svr->port);
399                  addri6->sin6_flowinfo = 0;
400                  addri6->sin6_scope_id = 0;
401
402                  memcpy(&addri6->sin6_addr.s6_addr, arg->byaddr ? &arg->addr.v6 : (struct in6_addr*)hostent->h_addr_list[0], sizeof (struct in6_addr));
403
404                  addr = (struct sockaddr*) addri6;
405                  break;
406               }
407             default:
408                fprintf(stderr, "Unknown addrtype %i\n", hostent->h_addrtype);
409                goto on_error;
410            }
411
412          if (hostent->h_name)
413            length = strlen(hostent->h_name) + 1;
414
415          arg->result = malloc(sizeof (Ecore_Con_Info) + length);
416          if (!arg->result)
417            {
418               fprintf(stderr, "Not enough memory\n");
419               free(addr);
420               goto on_error;
421            }
422
423          /* FIXME: What to do when hint is not set ? */
424          arg->result->info.ai_flags = arg->hints.ai_flags;
425          arg->result->info.ai_socktype = arg->hints.ai_socktype;
426          arg->result->info.ai_protocol = arg->hints.ai_protocol;
427
428          arg->result->info.ai_family = hostent->h_addrtype;
429          arg->result->info.ai_addrlen = addrlen;
430          arg->result->info.ai_addr = addr;
431          arg->result->info.ai_canonname = (char*) (arg->result + 1);
432
433          strcpy(arg->result->info.ai_canonname, hostent->h_name);
434
435          arg->result->info.ai_next = NULL;
436
437          ares_getnameinfo(info_channel, addr, addrlen,
438                           ARES_NI_NUMERICSERV | ARES_NI_NUMERICHOST | ARES_NI_LOOKUPSERVICE | ARES_NI_LOOKUPHOST,
439                           (ares_nameinfo_callback) _ecore_con_info_ares_nameinfo, arg);
440          break;
441       case ARES_ENOTIMP: /* unknown family */
442       case ARES_EBADNAME: /* not a valid internet address */
443       case ARES_ENOTFOUND: /* address notfound */
444       case ARES_ENOMEM: /* not enough memory */
445       case ARES_EDESTRUCTION: /* request canceled, shuting down */
446          goto on_error;
447       default:
448          fprintf(stderr, "Unknown status returned by c-ares: %i assuming error\n", status);
449          goto on_error;
450      }
451
452    return ;
453
454  on_error:
455    arg->done_cb(arg->data, NULL);
456    free(arg);
457 }
458
459 static void
460 _ecore_con_info_ares_nameinfo(Ecore_Con_CAres *arg, int status, int timeouts, char *node, char *service)
461 {
462    switch (status)
463      {
464       case ARES_SUCCESS:
465          if (node) strcpy(arg->result->ip, node);
466          else *arg->result->ip = '\0';
467          if (service) strcpy(arg->result->service, service);
468          else *arg->result->service = '\0';
469
470          arg->done_cb(arg->data, arg->result);
471          break;
472       case ARES_ENOTIMP:
473       case ARES_ENOTFOUND:
474       case ARES_ENOMEM:
475       case ARES_EDESTRUCTION:
476       case ARES_EBADFLAGS:
477          arg->done_cb(arg->data, NULL);
478          break;
479      }
480
481    free(arg->result->info.ai_addr);
482    free(arg->result);
483    free(arg);
484 }