diff --git a/legacy/ecore/configure.ac b/legacy/ecore/configure.ac index 872c4b635f..e8d8dcf149 100644 --- a/legacy/ecore/configure.ac +++ b/legacy/ecore/configure.ac @@ -82,6 +82,7 @@ want_curl="no" want_abstract_sockets="no" want_gnutls="no" want_openssl="no" +want_cares="no" want_cipher="no" want_signature="no" want_poll="yes" @@ -797,6 +798,7 @@ ECORE_CHECK_MODULE([Con], [${want_ecore_con}]) have_curl="no" have_gnutls="no" have_openssl="no" +have_cares="no" if test "x${have_ecore_con}" = "xyes" ; then ECORE_CHECK_CURL([${want_curl}], @@ -825,6 +827,15 @@ if test "x${have_ecore_con}" = "xyes" ; then # depends on ecore_con anyway. fi fi + + ECORE_CHECK_CARES([${want_cares}], + [ + have_cares="yes" + requirements_ecore_con="libcares ${requirements_ecore_con}" + ], [ + have_cares="no" + ]) + fi # ecore_ipc @@ -1193,6 +1204,7 @@ fi echo " GnuTLS.....................: $have_gnutls" echo " CURL.......................: $have_curl" echo " Abstract Sockets...........: $want_abstract_sockets" + echo " c-ares.....................: $have_cares" fi echo " Ecore_Ipc....................: $have_ecore_ipc" if test "x$have_ecore_ipc" = "xyes" ; then diff --git a/legacy/ecore/m4/ecore_check_options.m4 b/legacy/ecore/m4/ecore_check_options.m4 index d39d6f7e6b..e11ca43b73 100644 --- a/legacy/ecore/m4/ecore_check_options.m4 +++ b/legacy/ecore/m4/ecore_check_options.m4 @@ -272,3 +272,45 @@ else m4_default([$3], [:]) fi ]) + +dnl use: ECORE_CHECK_CARES(default-enabled[, ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +AC_DEFUN([ECORE_CHECK_CARES], +[ +_cares_requirement="" +_ecore_want_cares=$1 +_ecore_have_cares="no" +CARES_LIBS="" +CARES_CFLAGS="" + +AC_ARG_ENABLE(cares, + [AC_HELP_STRING([--disable-cares], [disable cares support])], + [ + if test "x${enableval}" = "xyes" ; then + _ecore_want_cares="yes" + else + _ecore_want_cares="no" + fi + ]) + +if test "x${_ecore_want_cares}" = "xyes" -o "x${_ecore_want_cares}" = "xauto" ; then + PKG_CHECK_MODULES([CARES], [libcares >= 1.6.1], + [ + AC_DEFINE(HAVE_CARES, 1, [Build Ecore_Con_Info with c-ares support]) + _ecore_have_cares="yes" + _cares_requirement="libcares" + ], [ + _ecore_have_cares="no" + ]) +fi + +AM_CONDITIONAL(HAVE_CARES, test "x$_ecore_have_cares" = "xyes") + +AC_SUBST(CARES_LIBS) +AC_SUBST(CARES_CFLAGS) + +if test "x$_ecore_have_cares" = "xyes" ; then + m4_default([$2], [:]) +else + m4_default([$3], [:]) +fi +]) diff --git a/legacy/ecore/src/lib/ecore_con/Makefile.am b/legacy/ecore/src/lib/ecore_con/Makefile.am index 3477ac6aa5..69e6684741 100644 --- a/legacy/ecore/src/lib/ecore_con/Makefile.am +++ b/legacy/ecore/src/lib/ecore_con/Makefile.am @@ -5,7 +5,7 @@ AM_CPPFLAGS = \ -I$(top_builddir)/src/lib/ecore_con \ -I$(top_srcdir)/src/lib/ecore \ -I$(top_srcdir)/src/lib/ecore_con \ -@SSL_CFLAGS@ @CURL_CFLAGS@ @EINA_CFLAGS@ @TLS_CFLAGS@ +@SSL_CFLAGS@ @CURL_CFLAGS@ @EINA_CFLAGS@ @TLS_CFLAGS@ @CARES_CFLAGS@ if BUILD_ECORE_CON @@ -16,13 +16,18 @@ Ecore_Con.h libecore_con_la_SOURCES = \ ecore_con.c \ ecore_con_dns.c \ -ecore_con_info.c \ ecore_con_ssl.c \ ecore_con_url.c +if HAVE_CARES +libecore_con_la_SOURCES += ecore_con_ares.c +else +libecore_con_la_SOURCES += ecore_con_info.c +endif + libecore_con_la_LIBADD = \ $(top_builddir)/src/lib/ecore/libecore.la \ -@SSL_LIBS@ @CURL_LIBS@ @EINA_LIBS@ @TLS_LIBS@ +@SSL_LIBS@ @CURL_LIBS@ @EINA_LIBS@ @TLS_LIBS@ @CARES_LIBS@ libecore_con_la_LDFLAGS = -no-undefined -version-info @version_info@ @ecore_con_release_info@ diff --git a/legacy/ecore/src/lib/ecore_con/ecore_con_ares.c b/legacy/ecore/src/lib/ecore_con/ecore_con_ares.c new file mode 100644 index 0000000000..0a660ef4a0 --- /dev/null +++ b/legacy/ecore/src/lib/ecore_con/ecore_con_ares.c @@ -0,0 +1,484 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +/* + * This version of ecore_con_info use c-ares to provide asynchronous dns lookup. + * + * Note: It doesn't fork nor does it use libc getaddrinfo. + * http://c-ares.haxx.se/docs.html + */ + +#include +#include +#include + +#include "Ecore.h" +#include "Ecore_Con.h" +#include "ecore_con_private.h" + +typedef struct _Ecore_Con_FD Ecore_Con_FD; +typedef struct _Ecore_Con_CAres Ecore_Con_CAres; + +struct _Ecore_Con_FD +{ + Ecore_Fd_Handler *handler; + int active; + int fd; +}; + +struct _Ecore_Con_CAres +{ + Ecore_Con_Server *svr; + Ecore_Con_Info_Cb done_cb; + void *data; + struct addrinfo hints; + Ecore_Con_Info *result; + + union { + struct in_addr v4; + struct in6_addr v6; + } addr; + + Eina_Bool byaddr; +}; + +static ares_channel info_channel; +static int info_init = 0; +static Eina_List *info_fds = NULL; +static int active = 0; +static Ecore_Timer *tm = NULL; +static fd_set info_readers, info_writers; + +static void _ecore_con_info_ares_nameinfo(Ecore_Con_CAres *arg, int status, int timeouts, char *node, char *service); +static void _ecore_con_info_ares_host_cb(Ecore_Con_CAres *arg, int status, int timeouts, struct hostent *hostent); +static int _ecore_con_info_cares_fd_cb(void *data, Ecore_Fd_Handler *fd_handler); +static int _ecore_con_info_cares_timeout_cb(void *data); +static void _ecore_con_info_cares_clean(void); + +EAPI int +ecore_con_info_init(void) +{ + if (info_init == 0) + { + if (ares_library_init(ARES_LIB_INIT_ALL) != 0) + return 0; + if (ares_init(&info_channel) != ARES_SUCCESS) + { + ares_library_cleanup(); + return 0; + } + } + + info_init++; + return info_init; +} + +EAPI int +ecore_con_info_shutdown(void) +{ + info_init--; + if (info_init == 0) + { + /* Cancel all ongoing request */ + ares_cancel(info_channel); + ares_destroy(info_channel); + + /* Destroy FD handler here. */ + /* Shutdown ares */ + ares_library_cleanup(); + } + return info_init; +} + +int +ecore_con_info_tcp_connect(Ecore_Con_Server *svr, + Ecore_Con_Info_Cb done_cb, + void *data) +{ + struct addrinfo hints; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_CANONNAME; + hints.ai_protocol = IPPROTO_TCP; + hints.ai_canonname = NULL; + hints.ai_next = NULL; + hints.ai_addr = NULL; + + return ecore_con_info_get(svr, done_cb, data, &hints); +} + +int +ecore_con_info_tcp_listen(Ecore_Con_Server *svr, + Ecore_Con_Info_Cb done_cb, + void *data) +{ + struct addrinfo hints; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + hints.ai_protocol = IPPROTO_TCP; + hints.ai_canonname = NULL; + hints.ai_next = NULL; + hints.ai_addr = NULL; + + return ecore_con_info_get(svr, done_cb, data, &hints); +} + +int +ecore_con_info_udp_connect(Ecore_Con_Server *svr, + Ecore_Con_Info_Cb done_cb, + void *data) +{ + struct addrinfo hints; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_flags = AI_CANONNAME; + hints.ai_protocol = IPPROTO_UDP; + hints.ai_canonname = NULL; + hints.ai_next = NULL; + hints.ai_addr = NULL; + + return ecore_con_info_get(svr, done_cb, data, &hints); +} + +int +ecore_con_info_udp_listen(Ecore_Con_Server *svr, + Ecore_Con_Info_Cb done_cb, + void *data) +{ + struct addrinfo hints; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_flags = AI_PASSIVE; + hints.ai_protocol = IPPROTO_UDP; + hints.ai_canonname = NULL; + hints.ai_next = NULL; + hints.ai_addr = NULL; + + return ecore_con_info_get(svr, done_cb, data, &hints); +} + +int +ecore_con_info_mcast_listen(Ecore_Con_Server *svr, + Ecore_Con_Info_Cb done_cb, + void *data) +{ + struct addrinfo hints; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_flags = 0; + hints.ai_protocol = IPPROTO_UDP; + hints.ai_canonname = NULL; + hints.ai_next = NULL; + hints.ai_addr = NULL; + + return ecore_con_info_get(svr, done_cb, data, &hints); +} + +EAPI int +ecore_con_info_get(Ecore_Con_Server *svr, + Ecore_Con_Info_Cb done_cb, + void *data, + struct addrinfo *hints) +{ + Ecore_Con_CAres *cares; + int ai_family = AF_UNSPEC; + + cares = calloc(1, sizeof (Ecore_Con_CAres)); + if (!cares) return 0; + + cares->svr = svr; + cares->done_cb = done_cb; + cares->data = data; + + if (hints) + { + ai_family = hints->ai_family; + memcpy(&cares->hints, hints, sizeof (struct addrinfo)); + } + + if (inet_pton(AF_INET, svr->name, &cares->addr.v4) == 1) + { + cares->byaddr = EINA_TRUE; + ares_gethostbyaddr(info_channel, &cares->addr.v4, sizeof (cares->addr.v4), AF_INET, (ares_host_callback) _ecore_con_info_ares_host_cb, cares); + } + else if (inet_pton(AF_INET6, svr->name, &cares->addr.v6) == 1) + { + cares->byaddr = EINA_TRUE; + ares_gethostbyaddr(info_channel, &cares->addr.v6, sizeof (cares->addr.v6), AF_INET6, (ares_host_callback) _ecore_con_info_ares_host_cb, cares); + } + else + { + cares->byaddr = EINA_FALSE; + ares_gethostbyname(info_channel, svr->name, ai_family, (ares_host_callback) _ecore_con_info_ares_host_cb, cares); + } + + _ecore_con_info_cares_clean(); + + return 1; +} + +static int +_ecore_con_info_fds_search(const Ecore_Con_FD *fd1, const Ecore_Con_FD *fd2) +{ + return fd1->fd - fd2->fd; +} + +static Eina_Bool +_ecore_con_info_fds_lookup(int fd) +{ + Ecore_Con_FD fdl; + Ecore_Con_FD *search; + + fdl.fd = fd; + + search = eina_list_search_unsorted(info_fds, (Eina_Compare_Cb) _ecore_con_info_fds_search, &fdl); + + if (search) + { + search->active = active; + return EINA_TRUE; + } + return EINA_FALSE; +} + +static void +_ecore_con_info_cares_clean(void) +{ + fd_set readers, writers; + Eina_List *l, *l_next; + Ecore_Con_FD *ecf; + int nfds; + int i; + + FD_ZERO(&readers); + FD_ZERO(&writers); + nfds = ares_fds(info_channel, &readers, &writers); + + active++; + for (i = 0; i < nfds; ++i) + { + int flags = 0; + + if (FD_ISSET(i, &readers)) flags |= ECORE_FD_READ; + if (FD_ISSET(i, &writers)) flags |= ECORE_FD_WRITE; + + if (flags) + { + if (!_ecore_con_info_fds_lookup(i)) + { + ecf = malloc(sizeof (Ecore_Con_FD)); + if (ecf) + { + ecf->fd = i; + ecf->active = active; + ecf->handler = ecore_main_fd_handler_add(i, ECORE_FD_WRITE | ECORE_FD_READ, + _ecore_con_info_cares_fd_cb, + NULL, NULL, NULL); + info_fds = eina_list_append(info_fds, ecf); + } + } + } + } + + info_readers = readers; + info_writers = writers; + + EINA_LIST_FOREACH_SAFE(info_fds, l, l_next, ecf) + { + if (ecf->active != active) + { + ecore_main_fd_handler_del(ecf->handler); + free(ecf); + info_fds = eina_list_remove_list(info_fds, l); + } + } + + if (!info_fds) + { + if (tm) ecore_timer_del(tm); + tm = NULL; + } + else + { + struct timeval tv; + + ares_timeout(info_channel, NULL, &tv); + + if (tm) + ecore_timer_delay(tm, tv.tv_sec); + else + tm = ecore_timer_add((double) tv.tv_sec, _ecore_con_info_cares_timeout_cb, NULL); + } +} + +static int +_ecore_con_info_cares_timeout_cb(void *data) +{ + ares_process(info_channel, &info_readers, &info_writers); + _ecore_con_info_cares_clean(); + + return 1; +} + +static int +_ecore_con_info_cares_fd_cb(void *data, Ecore_Fd_Handler *fd_handler) +{ + ares_process(info_channel, &info_readers, &info_writers); + _ecore_con_info_cares_clean(); + + return 1; +} + +static void +_ecore_con_info_ares_host_cb(Ecore_Con_CAres *arg, int status, int timeouts, struct hostent *hostent) +{ + struct sockaddr *addr; + int addrlen; + int length = 0; + + /* Found something ? */ + switch (status) + { + case ARES_SUCCESS: + if (hostent->h_addr_list[0] == NULL) + { + fprintf(stderr, "No IP found\n"); + goto on_error; + } + + switch (hostent->h_addrtype) + { + case AF_INET: + { + struct sockaddr_in *addri; + + addrlen = sizeof (struct sockaddr_in); + addri = malloc(addrlen); + + if (!addri) + { + fprintf(stderr, "Not enough memory\n"); + goto on_error; + } + + addri->sin_family = AF_INET; + addri->sin_port = htons(arg->svr->port); + + memcpy(&addri->sin_addr.s_addr, arg->byaddr ? &arg->addr.v4 : (struct in_addr*)hostent->h_addr_list[0], sizeof (struct in_addr)); + + addr = (struct sockaddr*) addri; + break; + } + case AF_INET6: + { + struct sockaddr_in6 *addri6; + + addrlen = sizeof (struct sockaddr_in6); + addri6 = malloc(addrlen); + + if (!addri6) + { + fprintf(stderr, "Not enough memory\n"); + goto on_error; + } + + addri6->sin6_family = AF_INET6; + addri6->sin6_port = htons(arg->svr->port); + addri6->sin6_flowinfo = 0; + addri6->sin6_scope_id = 0; + + memcpy(&addri6->sin6_addr.s6_addr, arg->byaddr ? &arg->addr.v6 : (struct in6_addr*)hostent->h_addr_list[0], sizeof (struct in6_addr)); + + addr = (struct sockaddr*) addri6; + break; + } + default: + fprintf(stderr, "Unknown addrtype %i\n", hostent->h_addrtype); + goto on_error; + } + + if (hostent->h_name) + length = strlen(hostent->h_name) + 1; + + arg->result = malloc(sizeof (Ecore_Con_Info) + length); + if (!arg->result) + { + fprintf(stderr, "Not enough memory\n"); + free(addr); + goto on_error; + } + + /* FIXME: What to do when hint is not set ? */ + arg->result->info.ai_flags = arg->hints.ai_flags; + arg->result->info.ai_socktype = arg->hints.ai_socktype; + arg->result->info.ai_protocol = arg->hints.ai_protocol; + + arg->result->info.ai_family = hostent->h_addrtype; + arg->result->info.ai_addrlen = addrlen; + arg->result->info.ai_addr = addr; + arg->result->info.ai_canonname = (char*) (arg->result + 1); + + strcpy(arg->result->info.ai_canonname, hostent->h_name); + + arg->result->info.ai_next = NULL; + + ares_getnameinfo(info_channel, addr, addrlen, + ARES_NI_NUMERICSERV | ARES_NI_NUMERICHOST | ARES_NI_LOOKUPSERVICE | ARES_NI_LOOKUPHOST, + (ares_nameinfo_callback) _ecore_con_info_ares_nameinfo, arg); + break; + case ARES_ENOTIMP: /* unknown family */ + case ARES_EBADNAME: /* not a valid internet address */ + case ARES_ENOTFOUND: /* address notfound */ + case ARES_ENOMEM: /* not enough memory */ + case ARES_EDESTRUCTION: /* request canceled, shuting down */ + goto on_error; + default: + fprintf(stderr, "Unknown status returned by c-ares: %i assuming error\n", status); + goto on_error; + } + + return ; + + on_error: + arg->done_cb(arg->data, NULL); + free(arg); +} + +static void +_ecore_con_info_ares_nameinfo(Ecore_Con_CAres *arg, int status, int timeouts, char *node, char *service) +{ + switch (status) + { + case ARES_SUCCESS: + if (node) strcpy(arg->result->ip, node); + else *arg->result->ip = '\0'; + if (service) strcpy(arg->result->service, service); + else *arg->result->service = '\0'; + + arg->done_cb(arg->data, arg->result); + break; + case ARES_ENOTIMP: + case ARES_ENOTFOUND: + case ARES_ENOMEM: + case ARES_EDESTRUCTION: + case ARES_EBADFLAGS: + arg->done_cb(arg->data, NULL); + break; + } + + free(arg->result->info.ai_addr); + free(arg->result); + free(arg); +}