48082df0da24ed2e09b43360e6f34d0e101f5dc8
[framework/uifw/ecore.git] / src / lib / ecore_con / ecore_con_dns.c
1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif
4
5 /*
6  * This version of ecore_con_info uses dns.c to provide asynchronous dns lookup.
7  *
8  * dns.c is written by William Ahern:
9  * http://25thandclement.com/~william/projects/dns.c.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 "dns.h"
24
25 #include "Ecore.h"
26 #include "Ecore_Con.h"
27 #include "ecore_con_private.h"
28
29 typedef struct dns_addrinfo dns_addrinfo;
30 typedef struct dns_resolv_conf dns_resolv_conf;
31 typedef struct dns_resolver dns_resolver;
32 typedef struct dns_hosts       dns_hosts;
33
34 typedef struct _Ecore_Con_DNS Ecore_Con_DNS;
35
36 struct _Ecore_Con_DNS
37 {
38    Ecore_Con_Server *svr;
39    Ecore_Con_Info_Cb done_cb;
40    void             *data;
41    dns_addrinfo     *ai;
42    dns_resolver     *resolv;
43    struct addrinfo   hints;
44    Ecore_Fd_Handler *fdh;
45    Ecore_Timer      *timer;
46 };
47
48 static int _ecore_con_dns_init = 0;
49 static dns_resolv_conf *resconf = NULL;
50 static dns_hosts *hosts = NULL;
51
52 static void
53 _ecore_con_dns_free(Ecore_Con_DNS *dns)
54 {
55    if (dns->svr->infos) dns->svr->infos = eina_list_remove(dns->svr->infos, dns);
56    if (dns->timer) ecore_timer_del(dns->timer);
57    if (dns->fdh) ecore_main_fd_handler_del(dns->fdh);
58    dns_res_close(dns->resolv);
59    free(dns);
60 }
61
62 static Eina_Bool
63 _dns_addrinfo_get(Ecore_Con_DNS *dns, const char *addr, int port)
64 {
65    int error = 0;
66    char service[NI_MAXSERV];
67
68    snprintf(service, sizeof(service), "%d", port);
69    dns->ai = dns_ai_open(addr, service, DNS_T_A, (const struct addrinfo *)&dns->hints, dns->resolv, &error);
70    return error;
71 }
72
73 static int
74 _ecore_con_dns_check(Ecore_Con_DNS *dns)
75 {
76    struct addrinfo *ent = NULL;
77    int error = 0;
78  
79    error = dns_ai_nextent(&ent, dns->ai);
80
81    switch (error)
82      {
83       case 0:
84         break;
85       case EAGAIN:
86         return 1;
87       default:
88         ERR("resolve failed: %s", dns_strerror(error));
89         goto error;
90      }
91
92    {
93       Ecore_Con_Info result = {0, .ip = {0}, .service = {0}};
94 #if 0
95       char pretty[512];
96       dns_ai_print(pretty, sizeof(pretty), ent, dns->ai);
97       printf("%s\n", pretty);
98 #endif
99       result.size = 0;
100       dns_inet_ntop(dns_sa_family(ent->ai_addr), dns_sa_addr(dns_sa_family(ent->ai_addr), ent->ai_addr), result.ip, sizeof(result.ip));
101       snprintf(result.service, sizeof(result.service), "%u", ntohs(*dns_sa_port(dns_sa_family(ent->ai_addr), ent->ai_addr)));
102       memcpy(&result.info, ent, sizeof(result.info));
103       if (dns->fdh) ecore_main_fd_handler_del(dns->fdh);
104       dns->fdh = NULL;
105       dns->done_cb(dns->data, &result);
106       free(ent);
107       _ecore_con_dns_free(dns);
108    }
109
110    return 0;
111 error:
112    dns->done_cb(dns->data, NULL);
113    _ecore_con_dns_free(dns);
114    return -1;
115 }
116
117 static Eina_Bool
118 _dns_fd_cb(Ecore_Con_DNS *dns, Ecore_Fd_Handler *fdh)
119 {
120    if (_ecore_con_dns_check(dns) == 1)
121      ecore_main_fd_handler_active_set(fdh, dns_ai_events(dns->ai));
122    return ECORE_CALLBACK_RENEW;
123 }
124
125 static Eina_Bool
126 _dns_timer_cb(Ecore_Con_DNS *dns)
127 {
128    dns->done_cb(dns->data, NULL);
129    _ecore_con_dns_free(dns);
130    dns->timer = NULL;
131    return EINA_FALSE;
132 }
133
134 int
135 ecore_con_info_init(void)
136 {
137    int err;
138    if (_ecore_con_dns_init) return ++_ecore_con_dns_init;
139
140    resconf = dns_resconf_local(&err);
141    if (!resconf)
142      {
143         ERR("resconf_open: %s", dns_strerror(err));
144         return 0;
145      }
146    hosts = dns_hosts_local(&err);
147    if (!hosts)
148      {
149         ERR("hosts_open: %s", dns_strerror(err));
150         dns_resconf_close(resconf);
151         resconf = NULL;
152         return 0;
153      }
154    /* this is super slow don't do it */
155    //resconf->options.recurse = 1;
156    return ++_ecore_con_dns_init;
157 }
158
159 int
160 ecore_con_info_shutdown(void)
161 {
162    if (!_ecore_con_dns_init) return 0;
163    if (--_ecore_con_dns_init) return _ecore_con_dns_init;
164    dns_resconf_close(resconf);
165    resconf = NULL;
166    dns_hosts_close(hosts);
167    hosts = NULL;
168    return 0;
169 }
170
171 void
172 ecore_con_info_data_clear(void *info)
173 {
174    Ecore_Con_DNS *dns = info;
175    if (dns) dns->data = NULL;
176 }
177
178 int
179 ecore_con_info_tcp_connect(Ecore_Con_Server *svr,
180                            Ecore_Con_Info_Cb done_cb,
181                            void *data)
182 {
183    struct addrinfo hints;
184
185    memset(&hints, 0, sizeof(struct addrinfo));
186 #ifdef HAVE_IPV6
187    hints.ai_family = AF_INET6;
188 #else
189    hints.ai_family = AF_INET;
190 #endif
191    hints.ai_socktype = SOCK_STREAM;
192    hints.ai_flags = AI_CANONNAME;
193    hints.ai_protocol = IPPROTO_TCP;
194
195    return ecore_con_info_get(svr, done_cb, data, &hints);
196 }
197
198 int
199 ecore_con_info_tcp_listen(Ecore_Con_Server *svr,
200                           Ecore_Con_Info_Cb done_cb,
201                           void *data)
202 {
203    struct addrinfo hints;
204
205    memset(&hints, 0, sizeof(struct addrinfo));
206 #ifdef HAVE_IPV6
207    hints.ai_family = AF_INET6;
208 #else
209    hints.ai_family = AF_INET;
210 #endif
211    hints.ai_socktype = SOCK_STREAM;
212    hints.ai_flags = AI_PASSIVE;
213    hints.ai_protocol = IPPROTO_TCP;
214
215    return ecore_con_info_get(svr, done_cb, data, &hints);
216 }
217
218 int
219 ecore_con_info_udp_connect(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 = AI_CANONNAME;
233    hints.ai_protocol = IPPROTO_UDP;
234
235    return ecore_con_info_get(svr, done_cb, data, &hints);
236 }
237
238 int
239 ecore_con_info_udp_listen(Ecore_Con_Server *svr,
240                           Ecore_Con_Info_Cb done_cb,
241                           void *data)
242 {
243    struct addrinfo hints;
244
245    memset(&hints, 0, sizeof(struct addrinfo));
246 #ifdef HAVE_IPV6
247    hints.ai_family = AF_INET6;
248 #else
249    hints.ai_family = AF_INET;
250 #endif
251    hints.ai_socktype = SOCK_DGRAM;
252    hints.ai_flags = AI_PASSIVE;
253    hints.ai_protocol = IPPROTO_UDP;
254
255    return ecore_con_info_get(svr, done_cb, data, &hints);
256 }
257
258 int
259 ecore_con_info_mcast_listen(Ecore_Con_Server *svr,
260                             Ecore_Con_Info_Cb done_cb,
261                             void *data)
262 {
263    struct addrinfo hints;
264
265    memset(&hints, 0, sizeof(struct addrinfo));
266 #ifdef HAVE_IPV6
267    hints.ai_family = AF_INET6;
268 #else
269    hints.ai_family = AF_INET;
270 #endif
271    hints.ai_socktype = SOCK_DGRAM;
272    hints.ai_protocol = IPPROTO_UDP;
273
274    return ecore_con_info_get(svr, done_cb, data, &hints);
275 }
276
277 EAPI int
278 ecore_con_info_get(Ecore_Con_Server *svr,
279                    Ecore_Con_Info_Cb done_cb,
280                    void *data,
281                    struct addrinfo *hints)
282 {
283    Ecore_Con_DNS *dns;
284    int error = 0;
285
286    dns = calloc(1, sizeof(Ecore_Con_DNS));
287    if (!dns) return 0;
288
289    dns->svr = svr;
290    dns->done_cb = done_cb;
291    dns->data = data;
292
293    if (hints)
294      memcpy(&dns->hints, hints, sizeof(struct addrinfo));
295
296    if (!(dns->resolv = dns_res_open(resconf, hosts, dns_hints_mortal(dns_hints_local(resconf, &error)), NULL, dns_opts(), &error)))
297      {
298         ERR("res_open: %s", dns_strerror(error));
299         goto reserr;
300
301      }
302
303    error = _dns_addrinfo_get(dns, svr->ecs ? svr->ecs->ip : svr->name, dns->svr->ecs ? dns->svr->ecs->port : dns->svr->port);
304    if (error && (error != EAGAIN))
305      {
306         ERR("resolver: %s", dns_strerror(error));
307         goto seterr;
308      }
309
310    switch (_ecore_con_dns_check(dns))
311      {
312       case 0:
313         break;
314       case 1:
315         dns->fdh = ecore_main_fd_handler_add(dns_ai_pollfd(dns->ai), dns_ai_events(dns->ai), (Ecore_Fd_Cb)_dns_fd_cb, dns, NULL, NULL);
316         svr->infos = eina_list_append(svr->infos, dns);
317         dns->timer = ecore_timer_add(5.0, (Ecore_Task_Cb)_dns_timer_cb, dns);
318         break;
319       default:
320         return 0;
321      }
322
323    return 1;
324 seterr:
325    if (dns->resolv) dns_res_close(dns->resolv);
326 reserr:
327    free(dns);
328    return 0;
329 }
330