* ecore_con: Add an alternative to getaddrinfo/fork by using c-ares.

For more information http://c-ares.haxx.se/


SVN revision: 44170
This commit is contained in:
Cedric BAIL 2009-12-03 10:26:40 +00:00
parent 96b82e825b
commit 7de1f120c2
4 changed files with 546 additions and 3 deletions

View File

@ -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

View File

@ -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
])

View File

@ -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@

View File

@ -0,0 +1,484 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#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 <string.h>
#include <arpa/inet.h>
#include <ares.h>
#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);
}