efl/src/lib/ecore_con/ecore_con_legacy.c

2704 lines
83 KiB
C

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#define EFL_NET_SOCKET_SSL_PROTECTED
#include "Ecore.h"
#include "Efl_Net.h"
#include "ecore_private.h"
#include "Ecore_Con.h"
#include "ecore_con_private.h"
#define ECORE_CON_TYPE 0x0f
#define ECORE_CON_SSL 0xf0
#define ECORE_CON_SUPER_SSL 0xf00
/* This file exists solely to provide ABI compatibility */
struct _Ecore_Con_Server
{
ECORE_MAGIC;
Eo *dialer;
Eo *server;
struct {
Eina_Future *job;
Eina_Binbuf *pending_send; /* until job is fulfilled, no dialer exists,
* this binbuf allows immediate
* ecore_con_server_send() in that situation */
Eo *clients_ctx;
Eina_List *certs;
Eina_List *privkeys;
Eina_List *crls;
Eina_List *cafiles;
Eina_Stringshare *verify_name;
Eina_Bool verify;
Eina_Bool verify_basic;
Eina_Bool upgrading;
Ecore_Con_Type upgrade_type;
} ssl;
Eina_List *clients;
Eina_List *event_count;
const void *data;
Eina_Stringshare *name;
Eina_Stringshare *ip;
size_t pending_write;
double start_time;
double timeout;
Ecore_Con_Type type;
int port;
Eina_Bool want_mcast;
Eina_Bool is_dialer;
Eina_Bool connecting;
Eina_Bool delete_me;
};
struct _Ecore_Con_Client
{
ECORE_MAGIC;
Eo *socket;
Ecore_Con_Server *svr;
Eina_List *event_count;
const void *data;
Eina_Stringshare *ip;
struct {
Eina_Future *job;
Eo *ctx;
Eina_Bool upgrading;
} ssl;
size_t pending_write;
double start_time;
int port;
Eina_Bool delete_me;
};
typedef struct _Ecore_Con_Lookup_Ctx {
Ecore_Thread *thread;
Ecore_Con_Dns_Cb cb;
const void *data;
} Ecore_Con_Lookup_Ctx;
/* allows delete_me to be true */
#define ECORE_CON_SERVER_CHECK_RELAXED_RETURN(svr, ...) \
do \
{ \
if (!svr) return __VA_ARGS__; \
if (!EINA_MAGIC_CHECK(svr, ECORE_MAGIC_CON_SERVER)) \
{ \
ECORE_MAGIC_FAIL(svr, ECORE_MAGIC_CON_SERVER, __FUNCTION__); \
return __VA_ARGS__; \
} \
} \
while (0)
#define ECORE_CON_SERVER_CHECK_RETURN(svr, ...) \
do \
{ \
ECORE_CON_SERVER_CHECK_RELAXED_RETURN(svr, __VA_ARGS__) ; \
EINA_SAFETY_ON_TRUE_RETURN_VAL(svr->delete_me, __VA_ARGS__); \
} \
while (0)
#define ECORE_CON_CLIENT_CHECK_RELAXED_RETURN(cl, ...) \
do \
{ \
if (!cl) return __VA_ARGS__; \
if (!EINA_MAGIC_CHECK(cl, ECORE_MAGIC_CON_CLIENT)) \
{ \
ECORE_MAGIC_FAIL(cl, ECORE_MAGIC_CON_CLIENT, __FUNCTION__); \
return __VA_ARGS__; \
} \
} \
while (0)
#define ECORE_CON_CLIENT_CHECK_RETURN(cl, ...) \
do \
{ \
ECORE_CON_CLIENT_CHECK_RELAXED_RETURN(cl, __VA_ARGS__) ; \
EINA_SAFETY_ON_TRUE_RETURN_VAL(cl->delete_me, __VA_ARGS__); \
} \
while (0)
/* from ecore_con_alloc.c */
#define GENERIC_ALLOC_FREE_HEADER(TYPE, Type) \
TYPE *Type##_alloc(void); \
void Type##_free(TYPE *e);
GENERIC_ALLOC_FREE_HEADER(Ecore_Con_Event_Client_Add, ecore_con_event_client_add);
GENERIC_ALLOC_FREE_HEADER(Ecore_Con_Event_Client_Del, ecore_con_event_client_del);
GENERIC_ALLOC_FREE_HEADER(Ecore_Con_Event_Server_Add, ecore_con_event_server_add);
GENERIC_ALLOC_FREE_HEADER(Ecore_Con_Event_Server_Del, ecore_con_event_server_del);
GENERIC_ALLOC_FREE_HEADER(Ecore_Con_Event_Server_Write, ecore_con_event_server_write);
GENERIC_ALLOC_FREE_HEADER(Ecore_Con_Event_Server_Data, ecore_con_event_server_data);
GENERIC_ALLOC_FREE_HEADER(Ecore_Con_Event_Server_Error, ecore_con_event_server_error);
GENERIC_ALLOC_FREE_HEADER(Ecore_Con_Event_Server_Upgrade, ecore_con_event_server_upgrade);
GENERIC_ALLOC_FREE_HEADER(Ecore_Con_Event_Proxy_Bind, ecore_con_event_proxy_bind);
GENERIC_ALLOC_FREE_HEADER(Ecore_Con_Event_Client_Write, ecore_con_event_client_write);
GENERIC_ALLOC_FREE_HEADER(Ecore_Con_Event_Client_Data, ecore_con_event_client_data);
GENERIC_ALLOC_FREE_HEADER(Ecore_Con_Event_Client_Error, ecore_con_event_client_error);
GENERIC_ALLOC_FREE_HEADER(Ecore_Con_Event_Client_Upgrade, ecore_con_event_client_upgrade);
#undef GENERIC_ALLOC_FREE_HEADER
/* shared */
EAPI int ECORE_CON_EVENT_SERVER_DEL = 0;
EAPI int ECORE_CON_EVENT_SERVER_ERROR = 0;
/* ecore_con_server_add() */
EAPI int ECORE_CON_EVENT_CLIENT_ADD = 0;
EAPI int ECORE_CON_EVENT_CLIENT_DEL = 0;
/* ecore_con_server_connect() */
EAPI int ECORE_CON_EVENT_SERVER_ADD = 0;
EAPI int ECORE_CON_EVENT_SERVER_DATA = 0;
EAPI int ECORE_CON_EVENT_SERVER_WRITE = 0;
EAPI int ECORE_CON_EVENT_PROXY_BIND = 0; /* we're not supporting proxy bind, keep only for ABI */
EAPI int ECORE_CON_EVENT_SERVER_UPGRADE = 0;
/* for each client from ECORE_CON_EVENT_CLIENT_ADD */
EAPI int ECORE_CON_EVENT_CLIENT_DATA = 0;
EAPI int ECORE_CON_EVENT_CLIENT_WRITE = 0;
EAPI int ECORE_CON_EVENT_CLIENT_ERROR = 0;
EAPI int ECORE_CON_EVENT_CLIENT_UPGRADE = 0;
static Eina_List *_servers = NULL;
static Eina_List *_ecore_con_lookups = NULL;
static int _ecore_con_event_count = 0;
Ecore_Con_Socks *_ecore_con_proxy_once = NULL;
Ecore_Con_Socks *_ecore_con_proxy_global = NULL;
void
ecore_con_legacy_init(void)
{
ECORE_CON_EVENT_CLIENT_ADD = ecore_event_type_new();
ECORE_CON_EVENT_CLIENT_DEL = ecore_event_type_new();
ECORE_CON_EVENT_SERVER_ADD = ecore_event_type_new();
ECORE_CON_EVENT_SERVER_DEL = ecore_event_type_new();
ECORE_CON_EVENT_CLIENT_DATA = ecore_event_type_new();
ECORE_CON_EVENT_SERVER_DATA = ecore_event_type_new();
ECORE_CON_EVENT_CLIENT_WRITE = ecore_event_type_new();
ECORE_CON_EVENT_SERVER_WRITE = ecore_event_type_new();
ECORE_CON_EVENT_CLIENT_ERROR = ecore_event_type_new();
ECORE_CON_EVENT_SERVER_ERROR = ecore_event_type_new();
ECORE_CON_EVENT_PROXY_BIND = ecore_event_type_new();
ECORE_CON_EVENT_SERVER_UPGRADE = ecore_event_type_new();
ECORE_CON_EVENT_CLIENT_UPGRADE = ecore_event_type_new();
eina_magic_string_set(ECORE_MAGIC_CON_SERVER, "Ecore_Con_Server");
eina_magic_string_set(ECORE_MAGIC_CON_CLIENT, "Ecore_Con_Client");
eina_magic_string_set(ECORE_MAGIC_CON_URL, "Ecore_Con_Url");
ecore_con_socks_init();
}
static void _ecore_con_server_free(Ecore_Con_Server *svr);
void
ecore_con_legacy_shutdown(void)
{
Eina_List *l, *l2;
Ecore_Con_Server *svr;
Ecore_Con_Lookup_Ctx *lookup_ctx;
EINA_LIST_FOREACH_SAFE(_servers, l, l2, svr)
{
Ecore_Con_Event_Server_Add *ev;
if (!svr) continue;
svr->delete_me = EINA_TRUE;
INF("svr %p is dead", svr);
/* some pointer hacks here to prevent double frees if people are being stupid */
EINA_LIST_FREE(svr->event_count, ev)
ev->server = NULL;
_ecore_con_server_free(svr);
}
EINA_LIST_FREE(_ecore_con_lookups, lookup_ctx)
ecore_thread_cancel(lookup_ctx->thread);
ecore_event_type_flush(ECORE_CON_EVENT_CLIENT_ADD,
ECORE_CON_EVENT_CLIENT_DEL,
ECORE_CON_EVENT_SERVER_ADD,
ECORE_CON_EVENT_SERVER_DEL,
ECORE_CON_EVENT_CLIENT_DATA,
ECORE_CON_EVENT_SERVER_DATA,
ECORE_CON_EVENT_CLIENT_WRITE,
ECORE_CON_EVENT_SERVER_WRITE,
ECORE_CON_EVENT_CLIENT_ERROR,
ECORE_CON_EVENT_SERVER_ERROR,
ECORE_CON_EVENT_PROXY_BIND,
ECORE_CON_EVENT_SERVER_UPGRADE,
ECORE_CON_EVENT_CLIENT_UPGRADE);
ecore_con_socks_shutdown();
if (!_ecore_con_event_count) ecore_con_mempool_shutdown();
else WRN("%d events still exist, leaking ecore_con mempools", _ecore_con_event_count);
}
/**
* @addtogroup Ecore_Con_Client_Group Ecore Connection Client Functions
*
* Functions that operate on Ecore connection client objects, these
* are announced using ECORE_CON_EVENT_CLIENT_ADD by servers created
* with ecore_con_server_add().
*
* @{
*/
static Efl_Callback_Array_Item *_ecore_con_client_socket_cbs(void);
static Efl_Callback_Array_Item *_ecore_con_client_socket_ssl_cbs(void);
static void
_ecore_con_client_socket_close(Ecore_Con_Client *cl)
{
if (!cl->socket) return;
if (!efl_io_closer_closed_get(cl->socket))
efl_io_closer_close(cl->socket);
/* socket may remain alive due other references, we don't own it */
efl_event_callback_array_del(cl->socket, _ecore_con_client_socket_cbs(), cl);
}
static void
_ecore_con_client_free(Ecore_Con_Client *cl)
{
Ecore_Con_Server *svr = cl->svr;
cl->delete_me = EINA_TRUE;
cl->svr = NULL;
if (svr)
svr->clients = eina_list_remove(svr->clients, cl);
_ecore_con_client_socket_close(cl);
if (cl->socket)
{
Eo *parent, *inner_socket = efl_io_buffered_stream_inner_io_get(cl->socket);
if (efl_isa(inner_socket, EFL_NET_SOCKET_SSL_CLASS))
efl_event_callback_array_del(inner_socket, _ecore_con_client_socket_ssl_cbs(), cl);
parent = efl_parent_get(cl->socket);
if (parent && svr && (parent != svr->server))
efl_del(cl->socket); /* we own it */
else
efl_unref(cl->socket);
cl->socket = NULL;
}
if (cl->ssl.job) eina_future_cancel(cl->ssl.job);
if (cl->ssl.ctx)
{
efl_unref(cl->ssl.ctx);
cl->ssl.ctx = NULL;
}
if (cl->event_count) return;
if (svr && (!svr->event_count) && (svr->delete_me))
_ecore_con_server_free(svr);
cl->data = NULL;
eina_stringshare_replace(&cl->ip, NULL);
EINA_MAGIC_SET(cl, ECORE_MAGIC_NONE);
free(cl);
}
/* BEGIN: post of Ecore_Event for ecore_con_server_connect() ************/
static void
_ecore_con_free_event_client_add(void *data EINA_UNUSED, void *event)
{
Ecore_Con_Event_Client_Add *ev = event;
if (ev->client)
{
Ecore_Con_Client *cl = ev->client;
cl->event_count = eina_list_remove(cl->event_count, ev);
if ((!cl->event_count) && (cl->delete_me))
_ecore_con_client_free(cl);
}
ecore_con_event_client_add_free(ev);
_ecore_con_event_count--;
}
static void
_ecore_con_post_event_client_add(Ecore_Con_Client *cl)
{
Ecore_Con_Event_Client_Add *ev = ecore_con_event_client_add_alloc();
EINA_SAFETY_ON_NULL_GOTO(ev, error);
ev->client = cl;
cl->event_count = eina_list_append(cl->event_count, ev);
ecore_event_add(ECORE_CON_EVENT_CLIENT_ADD, ev, _ecore_con_free_event_client_add, NULL);
_ecore_con_event_count++;
return;
error:
_ecore_con_client_free(cl);
}
static void
_ecore_con_free_event_client_del(void *data EINA_UNUSED, void *event)
{
Ecore_Con_Event_Client_Del *ev = event;
if (ev->client)
{
Ecore_Con_Client *cl = ev->client;
cl->event_count = eina_list_remove(cl->event_count, ev);
if (!cl->event_count)
_ecore_con_client_free(cl);
else
cl->delete_me = EINA_TRUE;
}
ecore_con_event_client_del_free(ev);
_ecore_con_event_count--;
}
static Eina_Bool
_ecore_con_post_event_client_del(Ecore_Con_Client *cl)
{
Ecore_Con_Event_Client_Del *ev = ecore_con_event_client_del_alloc();
EINA_SAFETY_ON_NULL_GOTO(ev, error);
ev->client = cl;
cl->event_count = eina_list_append(cl->event_count, ev);
/* legacy compatibility: test suite expects NULL pointer for IP */
eina_stringshare_replace(&cl->ip, NULL);
ecore_event_add(ECORE_CON_EVENT_CLIENT_DEL, ev, _ecore_con_free_event_client_del, NULL);
_ecore_con_event_count++;
return EINA_TRUE;
error:
_ecore_con_client_free(cl);
return EINA_FALSE;
}
static void
_ecore_con_free_event_client_data(void *data EINA_UNUSED, void *event)
{
Ecore_Con_Event_Client_Data *ev = event;
if (ev->client)
{
Ecore_Con_Client *cl = ev->client;
cl->event_count = eina_list_remove(cl->event_count, ev);
if ((!cl->event_count) && (cl->delete_me))
_ecore_con_client_free(cl);
}
free(ev->data);
ecore_con_event_client_data_free(ev);
_ecore_con_event_count--;
}
static void
_ecore_con_post_event_client_data(Ecore_Con_Client *cl, Eina_Rw_Slice slice)
{
Ecore_Con_Event_Client_Data *ev = ecore_con_event_client_data_alloc();
EINA_SAFETY_ON_NULL_GOTO(ev, error);
ev->client = cl;
ev->data = slice.mem;
ev->size = slice.len;
cl->event_count = eina_list_append(cl->event_count, ev);
ecore_event_add(ECORE_CON_EVENT_CLIENT_DATA, ev, _ecore_con_free_event_client_data, NULL);
_ecore_con_event_count++;
return;
error:
free(slice.mem);
}
static void
_ecore_con_free_event_client_write(void *data EINA_UNUSED, void *event)
{
Ecore_Con_Event_Client_Write *ev = event;
if (ev->client)
{
Ecore_Con_Client *cl = ev->client;
cl->event_count = eina_list_remove(cl->event_count, ev);
if ((!cl->event_count) && (cl->delete_me))
_ecore_con_client_free(cl);
}
ecore_con_event_client_write_free(ev);
_ecore_con_event_count--;
}
static void
_ecore_con_post_event_client_write(Ecore_Con_Client *cl, size_t size)
{
Ecore_Con_Event_Client_Write *ev = ecore_con_event_client_write_alloc();
EINA_SAFETY_ON_NULL_RETURN(ev);
ev->client = cl;
ev->size = size;
cl->event_count = eina_list_append(cl->event_count, ev);
ecore_event_add(ECORE_CON_EVENT_CLIENT_WRITE, ev, _ecore_con_free_event_client_write, NULL);
_ecore_con_event_count++;
}
static void
_ecore_con_free_event_client_error(void *data EINA_UNUSED, void *event)
{
Ecore_Con_Event_Client_Error *ev = event;
if (ev->client)
{
Ecore_Con_Client *cl = ev->client;
cl->event_count = eina_list_remove(cl->event_count, ev);
if ((!cl->event_count) && (cl->delete_me))
_ecore_con_client_free(cl);
}
free(ev->error);
ecore_con_event_client_error_free(ev);
_ecore_con_event_count--;
}
static Eina_Bool
_ecore_con_post_event_client_error(Ecore_Con_Client *cl, const char *err)
{
Ecore_Con_Event_Client_Error *ev = ecore_con_event_client_error_alloc();
EINA_SAFETY_ON_NULL_GOTO(ev, error);
INF("cl=%p error from %s: %s", cl, efl_net_socket_address_remote_get(cl->socket), err);
ev->client = cl;
ev->error = err ? strdup(err) : NULL;
cl->event_count = eina_list_append(cl->event_count, ev);
ecore_event_add(ECORE_CON_EVENT_CLIENT_ERROR, ev, _ecore_con_free_event_client_error, NULL);
_ecore_con_event_count++;
return EINA_TRUE;
error:
_ecore_con_client_free(cl);
return EINA_FALSE;
}
static void
_ecore_con_free_event_client_upgrade(void *data EINA_UNUSED, void *event)
{
Ecore_Con_Event_Client_Upgrade *ev = event;
if (ev->client)
{
Ecore_Con_Client *cl = ev->client;
cl->event_count = eina_list_remove(cl->event_count, ev);
if ((!cl->event_count) && (cl->delete_me))
_ecore_con_client_free(cl);
}
ecore_con_event_client_upgrade_free(ev);
_ecore_con_event_count--;
}
static void
_ecore_con_post_event_client_upgrade(Ecore_Con_Client *cl)
{
Ecore_Con_Event_Client_Upgrade *ev = ecore_con_event_client_upgrade_alloc();
EINA_SAFETY_ON_NULL_GOTO(ev, error);
ev->client = cl;
cl->event_count = eina_list_append(cl->event_count, ev);
ecore_event_add(ECORE_CON_EVENT_CLIENT_UPGRADE, ev, _ecore_con_free_event_client_upgrade, NULL);
_ecore_con_event_count++;
return;
error:
_ecore_con_client_free(cl);
}
/* END: post of Ecore_Event for ecore_con_server_add() ******************/
static void
_ecore_con_client_socket_progress(void *data, const Efl_Event *event EINA_UNUSED)
{
Ecore_Con_Client *cl = data;
size_t now = efl_io_buffered_stream_pending_write_get(cl->socket);
if (cl->delete_me) return;
if (cl->ssl.upgrading) return;
if (cl->pending_write == now) return;
EINA_SAFETY_ON_TRUE_GOTO(cl->pending_write < now, end); /* forgot to update! */
_ecore_con_post_event_client_write(cl, cl->pending_write - now);
end:
cl->pending_write = now;
}
static void
_ecore_con_client_socket_slice_changed(void *data, const Efl_Event *event EINA_UNUSED)
{
Ecore_Con_Client *cl = data;
Eina_Slice ro_slice;
Eina_Rw_Slice rw_slice;
if (cl->delete_me) return;
if (cl->ssl.upgrading) return;
ro_slice = efl_io_buffered_stream_slice_get(cl->socket);
if (ro_slice.len == 0) return;
rw_slice = eina_slice_dup(ro_slice);
efl_io_buffered_stream_clear(cl->socket);
_ecore_con_post_event_client_data(cl, rw_slice);
}
static void
_ecore_con_client_socket_read_finished(void *data, const Efl_Event *event EINA_UNUSED)
{
Ecore_Con_Client *cl = data;
DBG("client %p socket %p read finished, queue EOS", cl, cl->socket);
if (!efl_io_closer_closed_get(cl->socket))
efl_io_buffered_stream_eos_mark(cl->socket);
}
static void
_ecore_con_client_socket_finished(void *data, const Efl_Event *event EINA_UNUSED)
{
Ecore_Con_Client *cl = data;
DBG("client %p socket %p finished", cl, cl->socket);
_ecore_con_client_socket_close(cl);
_ecore_con_post_event_client_del(cl);
}
static void
_ecore_con_client_socket_error(void *data, const Efl_Event *event)
{
Ecore_Con_Client *cl = data;
Eina_Error *perr = event->info;
if (cl->delete_me) return;
WRN("error client %s: %s", efl_net_socket_address_remote_get(cl->socket), eina_error_msg_get(*perr));
_ecore_con_client_socket_close(cl);
_ecore_con_post_event_client_error(cl, eina_error_msg_get(*perr));
}
EFL_CALLBACKS_ARRAY_DEFINE(_ecore_con_client_socket_cbs,
{ EFL_IO_BUFFERED_STREAM_EVENT_PROGRESS, _ecore_con_client_socket_progress },
{ EFL_IO_BUFFERED_STREAM_EVENT_SLICE_CHANGED, _ecore_con_client_socket_slice_changed },
{ EFL_IO_BUFFERED_STREAM_EVENT_READ_FINISHED, _ecore_con_client_socket_read_finished },
{ EFL_IO_BUFFERED_STREAM_EVENT_FINISHED, _ecore_con_client_socket_finished },
{ EFL_IO_BUFFERED_STREAM_EVENT_ERROR, _ecore_con_client_socket_error });
static Ecore_Con_Client *
ecore_con_client_add(Ecore_Con_Server *svr, Eo *socket)
{
Ecore_Con_Client *cl;
Eo *inner_socket;
cl = calloc(1, sizeof(Ecore_Con_Client));
EINA_SAFETY_ON_NULL_RETURN_VAL(cl, NULL);
cl->socket = efl_ref(socket);
cl->svr = svr;
cl->start_time = ecore_time_get();
cl->port = -1;
inner_socket = efl_io_buffered_stream_inner_io_get(socket);
if (efl_isa(inner_socket, EFL_NET_SOCKET_TCP_CLASS) ||
efl_isa(inner_socket, EFL_NET_SOCKET_SSL_CLASS) ||
efl_isa(inner_socket, EFL_NET_SERVER_UDP_CLIENT_CLASS))
{
const char *addr = efl_net_socket_address_remote_get(inner_socket);
if (addr)
{
const char *p;
if (addr[0] != '[') p = strchr(addr, ':');
else
{
addr++;
p = strchr(addr, ']');
}
if (p)
{
const char *portstr = p;
if (portstr[0] == ']') portstr++;
if (portstr[0] == ':') portstr++; /* not else if! */
cl->ip = eina_stringshare_add_length(addr, p - addr);
cl->port = atoi(portstr);
}
}
}
else
{
/* legacy compatibility */
cl->ip = eina_stringshare_add("0.0.0.0");
cl->port = -1;
}
efl_event_callback_array_add(cl->socket, _ecore_con_client_socket_cbs(), cl);
if (efl_isa(inner_socket, EFL_NET_SOCKET_SSL_CLASS))
efl_event_callback_array_add(inner_socket, _ecore_con_client_socket_ssl_cbs(), cl);
ECORE_MAGIC_SET(cl, ECORE_MAGIC_CON_CLIENT);
svr->clients = eina_list_append(svr->clients, cl);
return cl;
}
EAPI int
ecore_con_client_send(Ecore_Con_Client *cl, const void *data, int size)
{
Eina_Error err;
Eina_Slice slice = { .mem = data, .len = size };
ECORE_CON_CLIENT_CHECK_RETURN(cl, 0);
EINA_SAFETY_ON_NULL_RETURN_VAL(data, 0);
EINA_SAFETY_ON_TRUE_RETURN_VAL(size < 1, 0);
EINA_SAFETY_ON_NULL_RETURN_VAL(cl->socket, 0);
err = efl_io_writer_write(cl->socket, &slice, NULL);
if (err)
{
ERR("cl=%p could not send data=%p, size=%d: %s", cl, data, size, eina_error_msg_get(err));
return 0;
}
cl->pending_write = efl_io_buffered_stream_pending_write_get(cl->socket);
return slice.len;
}
EAPI Eina_Bool
ecore_con_client_connected_get(const Ecore_Con_Client *cl)
{
ECORE_CON_CLIENT_CHECK_RETURN(cl, EINA_FALSE);
return !efl_io_closer_closed_get(cl->socket);
}
EAPI void
ecore_con_client_timeout_set(Ecore_Con_Client *cl, double timeout)
{
ECORE_CON_CLIENT_CHECK_RETURN(cl);
efl_io_buffered_stream_timeout_inactivity_set(cl->socket, timeout);
}
EAPI double
ecore_con_client_timeout_get(const Ecore_Con_Client *cl)
{
ECORE_CON_CLIENT_CHECK_RETURN(cl, -1.0);
return efl_io_buffered_stream_timeout_inactivity_get(cl->socket);
}
EAPI void *
ecore_con_client_del(Ecore_Con_Client *cl)
{
const void *data;
ECORE_CON_CLIENT_CHECK_RELAXED_RETURN(cl, NULL);
data = cl->data;
_ecore_con_client_free(cl);
return (void *)data;
}
EAPI void
ecore_con_client_data_set(Ecore_Con_Client *cl,
const void *data)
{
ECORE_CON_CLIENT_CHECK_RELAXED_RETURN(cl);
cl->data = data;
}
EAPI void *
ecore_con_client_data_get(Ecore_Con_Client *cl)
{
ECORE_CON_CLIENT_CHECK_RELAXED_RETURN(cl, NULL);
return (void *)cl->data;
}
EAPI const char *
ecore_con_client_ip_get(const Ecore_Con_Client *cl)
{
ECORE_CON_CLIENT_CHECK_RELAXED_RETURN(cl, NULL);
return cl->ip;
}
EAPI int
ecore_con_client_port_get(const Ecore_Con_Client *cl)
{
ECORE_CON_CLIENT_CHECK_RELAXED_RETURN(cl, -1);
return cl->port;
}
EAPI Ecore_Con_Server *
ecore_con_client_server_get(const Ecore_Con_Client *cl)
{
ECORE_CON_CLIENT_CHECK_RELAXED_RETURN(cl, NULL);
return cl->svr;
}
EAPI double
ecore_con_client_uptime_get(const Ecore_Con_Client *cl)
{
ECORE_CON_CLIENT_CHECK_RELAXED_RETURN(cl, 0.0);
return ecore_time_get() - cl->start_time;
}
EAPI void
ecore_con_client_flush(Ecore_Con_Client *cl)
{
ECORE_CON_CLIENT_CHECK_RETURN(cl);
Eo *inner_socket;
if (!cl->socket) return;
efl_io_buffered_stream_flush(cl->socket, EINA_FALSE, EINA_TRUE);
inner_socket = efl_io_buffered_stream_inner_io_get(cl->socket);
if (!efl_isa(inner_socket, EFL_NET_SOCKET_TCP_CLASS)) return;
if (!efl_net_socket_tcp_cork_get(inner_socket)) return;
efl_net_socket_tcp_cork_set(inner_socket, EINA_FALSE);
efl_net_socket_tcp_cork_set(inner_socket, EINA_TRUE);
}
EAPI int
ecore_con_client_fd_get(const Ecore_Con_Client *cl)
{
ECORE_CON_CLIENT_CHECK_RETURN(cl, SOCKET_TO_LOOP_FD(INVALID_SOCKET));
if (cl->socket)
{
Eo *inner_socket = efl_io_buffered_stream_inner_io_get(cl->socket);
if (efl_isa(inner_socket, EFL_LOOP_FD_CLASS))
{
return efl_loop_fd_get(inner_socket);
}
else
{
if (efl_isa(inner_socket, EFL_NET_SOCKET_SSL_CLASS))
{
Eo* adopted_socket = NULL;
if (efl_net_socket_ssl_adopted_get(inner_socket, &adopted_socket, NULL))
if (efl_isa(adopted_socket, EFL_LOOP_FD_CLASS))
return efl_loop_fd_get(adopted_socket);
}
}
}
return SOCKET_TO_LOOP_FD(INVALID_SOCKET);
}
static void
_ecore_con_client_socket_ssl_ready(void *data, const Efl_Event *event EINA_UNUSED)
{
Ecore_Con_Client *cl = data;
cl->ssl.upgrading = EINA_FALSE;
INF("cl=%p upgraded to SSL at %s", cl, efl_net_socket_address_remote_get(cl->socket));
_ecore_con_post_event_client_upgrade(cl);
}
static void
_ecore_con_client_socket_ssl_error(void *data, const Efl_Event *event)
{
Ecore_Con_Client *cl = data;
Eina_Error *perr = event->info;
if (cl->delete_me) return;
WRN("SSL error client %s: %s", efl_net_socket_address_remote_get(cl->socket), eina_error_msg_get(*perr));
_ecore_con_client_socket_close(cl);
_ecore_con_post_event_client_error(cl, eina_error_msg_get(*perr));
}
EFL_CALLBACKS_ARRAY_DEFINE(_ecore_con_client_socket_ssl_cbs,
{ EFL_NET_SOCKET_SSL_EVENT_SSL_READY, _ecore_con_client_socket_ssl_ready },
{ EFL_NET_SOCKET_SSL_EVENT_SSL_ERROR, _ecore_con_client_socket_ssl_error });
static Eina_Value
_ecore_con_client_ssl_upgrade_job(void *data, const Eina_Value v,
const Eina_Future *dead EINA_UNUSED)
{
Ecore_Con_Client *cl = data;
Eo *loop = efl_main_loop_get();
Eo *inner_socket;
Eo *socket;
Eo *tcp_socket;
//Canceled
if (v.type == EINA_VALUE_TYPE_ERROR) return v;
tcp_socket = cl->socket;
cl->socket = NULL; /* take it, will be wrapped */
inner_socket = efl_add(EFL_NET_SOCKET_SSL_CLASS, loop,
efl_net_socket_ssl_adopt(efl_added, tcp_socket, cl->ssl.ctx));
EINA_SAFETY_ON_NULL_GOTO(inner_socket, error_inner_socket);
/* do not reparent tcp_socket! it's owned by server */
efl_unref(tcp_socket); /* inner_socket keeps it */
socket = efl_add(EFL_NET_SOCKET_SIMPLE_CLASS, loop,
efl_io_buffered_stream_inner_io_set(efl_added, inner_socket));
EINA_SAFETY_ON_NULL_GOTO(socket, error_socket);
efl_parent_set(inner_socket, socket);
cl->socket = socket;
efl_io_closer_close_on_exec_set(socket, EINA_TRUE);
efl_io_closer_close_on_invalidate_set(socket, EINA_TRUE);
efl_event_callback_array_del(tcp_socket, _ecore_con_client_socket_cbs(), cl);
efl_event_callback_array_add(socket, _ecore_con_client_socket_cbs(), cl);
efl_event_callback_array_add(inner_socket, _ecore_con_client_socket_ssl_cbs(), cl);
DBG("socket=%p upgraded to SSL with inner_socket=%p, ssl_ctx=%p, tcp_socket=%p. Start handshake...",
cl->socket,
efl_io_buffered_stream_inner_io_get(cl->socket),
cl->ssl.ctx,
tcp_socket);
return v;
error_socket:
efl_del(inner_socket);
error_inner_socket:
cl->socket = tcp_socket; /* put it back */
if (_ecore_con_post_event_client_error(cl, "Couldn't finish SSL setup"))
_ecore_con_post_event_client_del(cl);
return v;
}
static Eo * _ecore_con_server_ssl_ctx_create(const Ecore_Con_Server *svr);
static void
_ecore_con_server_job_schedule(Ecore_Con_Server *svr, Eo *loop,
Eina_Future_Cb cb)
{
eina_future_then(efl_loop_job(loop), cb, svr, &svr->ssl.job);
}
EAPI Eina_Bool
ecore_con_ssl_client_upgrade(Ecore_Con_Client *cl, Ecore_Con_Type compl_type)
{
Ecore_Con_Server *svr;
Eo *ssl_ctx;
double start;
ECORE_CON_CLIENT_CHECK_RETURN(cl, EINA_FALSE);
EINA_SAFETY_ON_TRUE_RETURN_VAL((compl_type & ECORE_CON_SSL) == 0, EINA_FALSE);
EINA_SAFETY_ON_TRUE_RETURN_VAL(cl->ssl.upgrading, EINA_FALSE);
EINA_SAFETY_ON_FALSE_RETURN_VAL(efl_isa(efl_io_buffered_stream_inner_io_get(cl->socket), EFL_NET_SOCKET_TCP_CLASS), EINA_FALSE);
svr = cl->svr;
EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_isa(efl_net_server_simple_inner_server_get(svr->server), EFL_NET_SERVER_SSL_CLASS), EINA_FALSE); /* cannot upgrade if already SSL server! */
start = ecore_time_get();
while (efl_io_buffered_stream_pending_write_get(cl->socket) && ((ecore_time_get() - start) <= ecore_animator_frametime_get()))
ecore_con_client_flush(cl);
if (efl_io_buffered_stream_pending_write_get(cl->socket))
{
ERR("cl=%p still pending send %zd bytes! Flush client before upgrading to SSL!",
cl, efl_io_buffered_stream_pending_write_get(cl->socket));
return EINA_FALSE;
}
if ((!svr->ssl.upgrade_type) || (!svr->ssl.clients_ctx))
{
svr->ssl.upgrade_type = compl_type;
svr->ssl.clients_ctx = _ecore_con_server_ssl_ctx_create(svr);
EINA_SAFETY_ON_NULL_RETURN_VAL(svr->ssl.clients_ctx, EINA_FALSE);
}
if (svr->ssl.upgrade_type == compl_type)
ssl_ctx = efl_ref(svr->ssl.clients_ctx);
else
ssl_ctx = _ecore_con_server_ssl_ctx_create(svr);
EINA_SAFETY_ON_NULL_RETURN_VAL(ssl_ctx, EINA_FALSE);
cl->ssl.upgrading = EINA_TRUE;
cl->ssl.ctx = ssl_ctx;
eina_future_then(efl_loop_job(efl_loop_get(cl->socket)),
_ecore_con_client_ssl_upgrade_job, cl, &cl->ssl.job);
DBG("cl=%p SSL upgrading from %#x to type=%#x", cl, svr->type, compl_type);
return EINA_TRUE;
}
/**
* @}
*/
Eina_Bool
ecore_con_server_check(const Ecore_Con_Server *svr)
{
ECORE_CON_SERVER_CHECK_RETURN(svr, EINA_FALSE);
return EINA_TRUE;
}
static Ecore_Con_Server *
_ecore_con_server_new(Eina_Bool is_dialer, Ecore_Con_Type type, const char *name, int port, const void *data)
{
EINA_SAFETY_ON_TRUE_RETURN_VAL(!name || (!name[0]), NULL);
Ecore_Con_Server *svr = calloc(1, sizeof(Ecore_Con_Server));
EINA_SAFETY_ON_NULL_RETURN_VAL(svr, NULL);
svr->start_time = ecore_time_get();
svr->name = eina_stringshare_add(name);
svr->port = port;
svr->data = data;
svr->type = type;
svr->is_dialer = is_dialer;
EINA_MAGIC_SET(svr, ECORE_MAGIC_CON_SERVER);
_servers = eina_list_append(_servers, svr);
return svr;
}
static void
_ecore_con_server_dialer_close(Ecore_Con_Server *svr)
{
svr->connecting = EINA_FALSE;
if (!svr->dialer) return;
if (!efl_io_closer_closed_get(svr->dialer))
efl_io_closer_close(svr->dialer);
}
static void
_ecore_con_server_free(Ecore_Con_Server *svr)
{
const char *str;
svr->delete_me = EINA_TRUE;
_servers = eina_list_remove(_servers, svr);
while (svr->clients)
ecore_con_client_del(svr->clients->data);
_ecore_con_server_dialer_close(svr);
if (svr->ssl.clients_ctx)
{
// This is always created with efl_add_ref
efl_parent_set(svr->ssl.clients_ctx, NULL);
efl_unref(svr->ssl.clients_ctx);
svr->ssl.clients_ctx = NULL;
}
if (svr->dialer)
{
efl_del(svr->dialer);
svr->dialer = NULL;
}
if (svr->server)
{
efl_del(svr->server);
svr->server = NULL;
}
if (svr->ssl.job) eina_future_cancel(svr->ssl.job);
if (svr->ssl.pending_send)
{
eina_binbuf_free(svr->ssl.pending_send);
svr->ssl.pending_send = NULL;
}
if (svr->event_count) return;
EINA_LIST_FREE(svr->ssl.certs, str) eina_stringshare_del(str);
EINA_LIST_FREE(svr->ssl.privkeys, str) eina_stringshare_del(str);
EINA_LIST_FREE(svr->ssl.crls, str) eina_stringshare_del(str);
EINA_LIST_FREE(svr->ssl.cafiles, str) eina_stringshare_del(str);
eina_stringshare_replace(&svr->ssl.verify_name, NULL);
svr->data = NULL;
eina_stringshare_replace(&svr->name, NULL);
eina_stringshare_replace(&svr->ip, NULL);
EINA_MAGIC_SET(svr, ECORE_MAGIC_NONE);
free(svr);
}
/* BEGIN: post of shared Ecore_Event ***********************************/
static void
_ecore_con_free_event_server_del(void *data EINA_UNUSED, void *event)
{
Ecore_Con_Event_Server_Del *ev = event;
if (ev->server)
{
Ecore_Con_Server *svr = ev->server;
svr->event_count = eina_list_remove(svr->event_count, ev);
if ((!svr->event_count) && (svr->delete_me))
_ecore_con_server_free(svr);
}
ecore_con_event_server_del_free(ev);
_ecore_con_event_count--;
}
static Eina_Bool
_ecore_con_post_event_server_del(Ecore_Con_Server *svr)
{
Ecore_Con_Event_Server_Del *ev = ecore_con_event_server_del_alloc();
EINA_SAFETY_ON_NULL_GOTO(ev, error);
if (svr->dialer)
INF("svr=%p disconnected from %s (%s)", svr, efl_net_dialer_address_dial_get(svr->dialer), efl_net_socket_address_remote_get(svr->dialer));
else if (svr->server)
INF("svr=%p stopped serving at %s", svr, efl_net_server_address_get(svr->server));
else
INF("svr=%p name=%s, port=%d %s destroyed before SSL was configured", svr, svr->name, svr->port, svr->is_dialer ? "dialer" : "server");
if (svr->connecting)
{
DBG("svr=%p was still connecting to %s (%s), ignore ECORE_CON_EVENT_SERVER_DEL", svr, efl_net_dialer_address_dial_get(svr->dialer), efl_net_socket_address_remote_get(svr->dialer));
return EINA_TRUE;
}
ev->server = svr;
svr->event_count = eina_list_append(svr->event_count, ev);
ecore_event_add(ECORE_CON_EVENT_SERVER_DEL, ev, _ecore_con_free_event_server_del, NULL);
_ecore_con_event_count++;
return EINA_TRUE;
error:
_ecore_con_server_free(svr);
return EINA_FALSE;
}
static void
_ecore_con_free_event_server_error(void *data EINA_UNUSED, void *event)
{
Ecore_Con_Event_Server_Error *ev = event;
if (ev->server)
{
Ecore_Con_Server *svr = ev->server;
svr->event_count = eina_list_remove(svr->event_count, ev);
if ((!svr->event_count) && (svr->delete_me))
_ecore_con_server_free(svr);
}
free(ev->error);
ecore_con_event_server_error_free(ev);
_ecore_con_event_count--;
}
static Eina_Bool
_ecore_con_post_event_server_error(Ecore_Con_Server *svr, const char *err)
{
Ecore_Con_Event_Server_Error *ev = ecore_con_event_server_error_alloc();
EINA_SAFETY_ON_NULL_GOTO(ev, error);
if (svr->dialer)
INF("svr=%p error from %s (%s): %s", svr, efl_net_dialer_address_dial_get(svr->dialer), efl_net_socket_address_remote_get(svr->dialer), err);
else if (svr->server)
INF("svr=%p error at %s: %s", svr, efl_net_server_address_get(svr->server), err);
else
INF("svr=%p name=%s, port=%d %s error before SSL was configured: %s", svr, svr->name, svr->port, svr->is_dialer ? "dialer" : "server", err);
ev->server = svr;
ev->error = err ? strdup(err) : NULL;
svr->event_count = eina_list_append(svr->event_count, ev);
ecore_event_add(ECORE_CON_EVENT_SERVER_ERROR, ev, _ecore_con_free_event_server_error, NULL);
_ecore_con_event_count++;
return EINA_TRUE;
error:
_ecore_con_server_free(svr);
return EINA_FALSE;
}
/* END: post of shared Ecore_Event ***********************************/
/* BEGIN: post of Ecore_Event for ecore_con_server_connect() ************/
static void
_ecore_con_free_event_server_add(void *data EINA_UNUSED, void *event)
{
Ecore_Con_Event_Server_Add *ev = event;
if (ev->server)
{
Ecore_Con_Server *svr = ev->server;
svr->event_count = eina_list_remove(svr->event_count, ev);
if ((!svr->event_count) && (svr->delete_me))
_ecore_con_server_free(svr);
}
ecore_con_event_server_add_free(ev);
_ecore_con_event_count--;
}
static void
_ecore_con_post_event_server_add(Ecore_Con_Server *svr)
{
Ecore_Con_Event_Server_Add *ev = ecore_con_event_server_add_alloc();
EINA_SAFETY_ON_NULL_GOTO(ev, error);
ev->server = svr;
svr->event_count = eina_list_append(svr->event_count, ev);
ecore_event_add(ECORE_CON_EVENT_SERVER_ADD, ev, _ecore_con_free_event_server_add, NULL);
_ecore_con_event_count++;
return;
error:
_ecore_con_server_free(svr);
}
static void
_ecore_con_free_event_server_data(void *data EINA_UNUSED, void *event)
{
Ecore_Con_Event_Server_Data *ev = event;
if (ev->server)
{
Ecore_Con_Server *svr = ev->server;
svr->event_count = eina_list_remove(svr->event_count, ev);
if ((!svr->event_count) && (svr->delete_me))
_ecore_con_server_free(svr);
}
free(ev->data);
ecore_con_event_server_data_free(ev);
_ecore_con_event_count--;
}
static void
_ecore_con_post_event_server_data(Ecore_Con_Server *svr, Eina_Rw_Slice slice)
{
Ecore_Con_Event_Server_Data *ev = ecore_con_event_server_data_alloc();
EINA_SAFETY_ON_NULL_GOTO(ev, error);
ev->server = svr;
ev->data = slice.mem;
ev->size = slice.len;
svr->event_count = eina_list_append(svr->event_count, ev);
ecore_event_add(ECORE_CON_EVENT_SERVER_DATA, ev, _ecore_con_free_event_server_data, NULL);
_ecore_con_event_count++;
return;
error:
free(slice.mem);
}
static void
_ecore_con_free_event_server_write(void *data EINA_UNUSED, void *event)
{
Ecore_Con_Event_Server_Write *ev = event;
if (ev->server)
{
Ecore_Con_Server *svr = ev->server;
svr->event_count = eina_list_remove(svr->event_count, ev);
if ((!svr->event_count) && (svr->delete_me))
_ecore_con_server_free(svr);
}
ecore_con_event_server_write_free(ev);
_ecore_con_event_count--;
}
static void
_ecore_con_post_event_server_write(Ecore_Con_Server *svr, size_t size)
{
Ecore_Con_Event_Server_Write *ev = ecore_con_event_server_write_alloc();
EINA_SAFETY_ON_NULL_RETURN(ev);
ev->server = svr;
ev->size = size;
svr->event_count = eina_list_append(svr->event_count, ev);
ecore_event_add(ECORE_CON_EVENT_SERVER_WRITE, ev, _ecore_con_free_event_server_write, NULL);
_ecore_con_event_count++;
}
static void
_ecore_con_free_event_server_upgrade(void *data EINA_UNUSED, void *event)
{
Ecore_Con_Event_Server_Upgrade *ev = event;
if (ev->server)
{
Ecore_Con_Server *svr = ev->server;
svr->event_count = eina_list_remove(svr->event_count, ev);
if ((!svr->event_count) && (svr->delete_me))
_ecore_con_server_free(svr);
}
ecore_con_event_server_upgrade_free(ev);
_ecore_con_event_count--;
}
static void
_ecore_con_post_event_server_upgrade(Ecore_Con_Server *svr)
{
Ecore_Con_Event_Server_Upgrade *ev = ecore_con_event_server_upgrade_alloc();
EINA_SAFETY_ON_NULL_GOTO(ev, error);
ev->server = svr;
svr->event_count = eina_list_append(svr->event_count, ev);
ecore_event_add(ECORE_CON_EVENT_SERVER_UPGRADE, ev, _ecore_con_free_event_server_upgrade, NULL);
_ecore_con_event_count++;
return;
error:
_ecore_con_server_free(svr);
}
/* END: post of Ecore_Event for ecore_con_server_connect() **************/
static void
_ecore_con_server_dialer_slice_changed(void *data, const Efl_Event *event EINA_UNUSED)
{
Ecore_Con_Server *svr = data;
Eina_Slice ro_slice;
Eina_Rw_Slice rw_slice;
if (svr->delete_me) return;
if (svr->ssl.upgrading) return;
ro_slice = efl_io_buffered_stream_slice_get(svr->dialer);
if (ro_slice.len == 0) return;
rw_slice = eina_slice_dup(ro_slice);
efl_io_buffered_stream_clear(svr->dialer);
_ecore_con_post_event_server_data(svr, rw_slice);
}
static void
_ecore_con_server_dialer_progress(void *data, const Efl_Event *event EINA_UNUSED)
{
Ecore_Con_Server *svr = data;
size_t now;
if (svr->delete_me) return;
if (svr->ssl.upgrading) return;
now = efl_io_buffered_stream_pending_write_get(svr->dialer);
if (svr->pending_write == now) return;
EINA_SAFETY_ON_TRUE_GOTO(svr->pending_write < now, end); /* forgot to update! */
_ecore_con_post_event_server_write(svr, svr->pending_write - now);
end:
svr->pending_write = now;
}
static void
_ecore_con_server_dialer_read_finished(void *data, const Efl_Event *event EINA_UNUSED)
{
Ecore_Con_Server *svr = data;
if (svr->delete_me) return;
if (!efl_io_closer_closed_get(svr->dialer))
efl_io_buffered_stream_eos_mark(svr->dialer);
}
static void
_ecore_con_server_dialer_finished(void *data, const Efl_Event *event EINA_UNUSED)
{
Ecore_Con_Server *svr = data;
if (svr->delete_me) return;
_ecore_con_post_event_server_del(svr);
}
static void
_ecore_con_server_dialer_error(void *data, const Efl_Event *event)
{
Ecore_Con_Server *svr = data;
Eina_Error *perr = event->info;
static int nested = EINA_FALSE;
if (svr->delete_me) return;
if (nested) return;
nested = EINA_TRUE;
WRN("error reaching server %s: %s", efl_net_dialer_address_dial_get(svr->dialer), eina_error_msg_get(*perr));
if (_ecore_con_post_event_server_error(svr, eina_error_msg_get(*perr)))
_ecore_con_server_dialer_close(svr);
nested = EINA_FALSE;
}
static void
_ecore_con_server_dialer_resolved(void *data, const Efl_Event *event EINA_UNUSED)
{
Ecore_Con_Server *svr = data;
Eo *inner_dialer;
if (svr->delete_me) return;
inner_dialer = efl_io_buffered_stream_inner_io_get(svr->dialer);
if (efl_isa(inner_dialer, EFL_NET_DIALER_TCP_CLASS) ||
efl_isa(inner_dialer, EFL_NET_DIALER_UDP_CLASS) ||
efl_isa(inner_dialer, EFL_NET_DIALER_SSL_CLASS))
{
const char *p, *ip = efl_net_socket_address_remote_get(svr->dialer);
EINA_SAFETY_ON_NULL_RETURN(ip);
if (ip[0] != '[') p = strchr(ip, ':');
else
{
ip++;
p = strchr(ip, ']');
}
EINA_SAFETY_ON_NULL_RETURN(p);
eina_stringshare_del(svr->ip);
svr->ip = eina_stringshare_add_length(ip, p - ip);
}
}
static void
_ecore_con_server_dialer_connected(void *data, const Efl_Event *event EINA_UNUSED)
{
Ecore_Con_Server *svr = data;
if (svr->delete_me) return;
if (svr->ssl.upgrading)
{
svr->ssl.upgrading = EINA_FALSE;
INF("svr=%p upgraded to SSL at %s (%s)", svr, efl_net_dialer_address_dial_get(svr->dialer), efl_net_socket_address_remote_get(svr->dialer));
if (svr->ssl.pending_send)
{
Eina_Slice slice = eina_binbuf_slice_get(svr->ssl.pending_send);
ecore_con_server_send(svr, slice.mem, slice.len);
eina_binbuf_free(svr->ssl.pending_send);
svr->ssl.pending_send = NULL;
}
_ecore_con_post_event_server_upgrade(svr);
return;
}
svr->connecting = EINA_FALSE;
svr->start_time = ecore_time_get();
INF("svr=%p connected to %s (%s)", svr, efl_net_dialer_address_dial_get(svr->dialer), efl_net_socket_address_remote_get(svr->dialer));
_ecore_con_post_event_server_add(svr);
}
EFL_CALLBACKS_ARRAY_DEFINE(_ecore_con_server_dialer_cbs,
{ EFL_IO_BUFFERED_STREAM_EVENT_PROGRESS, _ecore_con_server_dialer_progress },
{ EFL_IO_BUFFERED_STREAM_EVENT_SLICE_CHANGED, _ecore_con_server_dialer_slice_changed },
{ EFL_IO_BUFFERED_STREAM_EVENT_READ_FINISHED, _ecore_con_server_dialer_read_finished },
{ EFL_IO_BUFFERED_STREAM_EVENT_FINISHED, _ecore_con_server_dialer_finished },
{ EFL_IO_BUFFERED_STREAM_EVENT_ERROR, _ecore_con_server_dialer_error },
{ EFL_NET_DIALER_EVENT_DIALER_ERROR, _ecore_con_server_dialer_error },
{ EFL_NET_DIALER_EVENT_DIALER_RESOLVED, _ecore_con_server_dialer_resolved },
{ EFL_NET_DIALER_EVENT_DIALER_CONNECTED, _ecore_con_server_dialer_connected });
static void
_ecore_con_server_server_client_add(void *data, const Efl_Event *event)
{
Ecore_Con_Server *svr = data;
Ecore_Con_Client *cl;
Eo *socket = event->info;
INF("svr=%p address=%s got client %p (%s) address=%s",
svr, efl_net_server_address_get(svr->server),
socket,
efl_class_name_get(efl_io_buffered_stream_inner_io_get(socket)),
efl_net_socket_address_remote_get(socket));
cl = ecore_con_client_add(svr, socket);
EINA_SAFETY_ON_NULL_RETURN(cl);
_ecore_con_post_event_client_add(cl);
}
static void
_ecore_con_server_server_serving(void *data, const Efl_Event *event EINA_UNUSED)
{
Ecore_Con_Server *svr = data;
Eo *inner_server;
const char *address = efl_net_server_address_get(svr->server);
const char *p;
char *ip;
EINA_SAFETY_ON_NULL_RETURN(address);
INF("svr=%p ready at %s", svr, address);
if (!svr->want_mcast) return;
inner_server = efl_net_server_simple_inner_server_get(svr->server);
if (address[0] != '[') p = strchr(address, ':');
else
{
address++;
p = strchr(address, ']');
}
EINA_SAFETY_ON_NULL_RETURN(p);
ip = malloc(p - address + 1);
EINA_SAFETY_ON_NULL_RETURN(ip);
memcpy(ip, address, p - address);
ip[p - address] = '\0';
DBG("svr=%p join multicast group: %s", svr, ip);
efl_net_server_udp_multicast_join(inner_server, ip);
free(ip);
}
static void
_ecore_con_server_server_error(void *data, const Efl_Event *event)
{
Ecore_Con_Server *svr = data;
Eina_Error *perr = event->info;
WRN("svr=%p address=%s error: %s",
svr, efl_net_server_address_get(svr->server),
eina_error_msg_get(*perr));
_ecore_con_post_event_server_error(svr, eina_error_msg_get(*perr));
}
EFL_CALLBACKS_ARRAY_DEFINE(_ecore_con_server_server_cbs,
{ EFL_NET_SERVER_EVENT_CLIENT_ADD, _ecore_con_server_server_client_add },
{ EFL_NET_SERVER_EVENT_SERVING, _ecore_con_server_server_serving },
{ EFL_NET_SERVER_EVENT_SERVER_ERROR, _ecore_con_server_server_error });
/**
* @addtogroup Ecore_Con_Server_Group Ecore Connection Server Functions
*
* Functions that operate on Ecore server objects.
*
* @{
*/
static Eina_Bool
_ecore_con_server_server_set(Ecore_Con_Server *svr, Eo *server)
{
char address[4096] = "";
Eo *inner_server = efl_net_server_simple_inner_server_get(server);
Ecore_Con_Type type = svr->type & ECORE_CON_TYPE;
Eina_Error err;
svr->server = server;
efl_event_callback_array_add(server, _ecore_con_server_server_cbs(), svr);
if (efl_isa(inner_server, EFL_NET_SERVER_TCP_CLASS) ||
efl_isa(inner_server, EFL_NET_SERVER_UDP_CLASS) ||
efl_isa(inner_server, EFL_NET_SERVER_SSL_CLASS))
{
if (strchr(svr->name, ':'))
snprintf(address, sizeof(address), "[%s]:%d", svr->name, svr->port);
else
snprintf(address, sizeof(address), "%s:%d", svr->name, svr->port);
}
else if (type == ECORE_CON_LOCAL_ABSTRACT)
snprintf(address, sizeof(address), "abstract:%s", svr->name);
else if ((type == ECORE_CON_LOCAL_USER) ||
(type == ECORE_CON_LOCAL_SYSTEM))
{
char *path = ecore_con_local_path_new(type == ECORE_CON_LOCAL_SYSTEM, svr->name, svr->port);
if (!path)
{
ERR("could not create local path for name='%s', port=%d", svr->name, svr->port);
return EINA_FALSE;
}
else
{
eina_strlcpy(address, path, sizeof(address));
free(path);
}
}
if (efl_isa(inner_server, EFL_NET_SERVER_FD_CLASS))
{
efl_net_server_fd_reuse_address_set(inner_server, EINA_TRUE);
efl_net_server_fd_reuse_port_set(inner_server, EINA_TRUE);
}
if (efl_isa(inner_server, EFL_NET_SERVER_TCP_CLASS))
{
/* old ecore_con did not map ipv4 to ipv6... */
efl_net_server_ip_ipv6_only_set(inner_server, EINA_TRUE);
}
else if (efl_isa(inner_server, EFL_NET_SERVER_UDP_CLASS))
{
/* old ecore_con did not map ipv4 to ipv6... */
efl_net_server_ip_ipv6_only_set(inner_server, EINA_TRUE);
svr->want_mcast = type == ECORE_CON_REMOTE_MCAST;
}
else if (efl_isa(inner_server, EFL_NET_SERVER_SSL_CLASS))
{
/* old ecore_con did not map ipv4 to ipv6... */
efl_net_server_ip_ipv6_only_set(inner_server, EINA_TRUE);
}
#ifdef EFL_NET_SERVER_UNIX_CLASS
else if (efl_isa(inner_server, EFL_NET_SERVER_UNIX_CLASS))
{
efl_net_server_unix_leading_directories_create_set(inner_server, EINA_TRUE, (type == ECORE_CON_LOCAL_SYSTEM) ? 0755 : 0700);
}
#endif
if (svr->type & ECORE_CON_SOCKET_ACTIVATE)
{
if (efl_isa(inner_server, EFL_NET_SERVER_FD_CLASS))
efl_net_server_fd_socket_activate(inner_server, address);
else
{
ERR("svr=%p (%s): not able to socket-activate this type!", svr, efl_class_name_get(inner_server));
goto serve;
}
if (efl_net_server_serving_get(inner_server))
{
DBG("svr=%p (%s) was socket activated as %s",
svr, efl_class_name_get(inner_server), address);
return EINA_TRUE;
}
else
ERR("svr=%p (%s): not able to socket-activate as %s. Try to serve...", svr, efl_class_name_get(inner_server), address);
}
serve:
err = efl_net_server_serve(svr->server, address);
if (err)
{
WRN("Could not serve at address=%s using class=%s",
address, efl_class_name_get(inner_server));
return EINA_FALSE;
}
DBG("svr=%p server=%p (%s) address=%s",
svr, svr->server, efl_class_name_get(inner_server),
efl_net_server_address_get(svr->server));
return EINA_TRUE;
}
static Eo *
_ecore_con_server_ssl_ctx_create(const Ecore_Con_Server *svr)
{
Efl_Net_Ssl_Cipher cipher = EFL_NET_SSL_CIPHER_AUTO;
Ecore_Con_Type ssl_type = svr->ssl.upgrade_type & ECORE_CON_SSL;
if (ssl_type & ECORE_CON_USE_MIXED)
cipher = EFL_NET_SSL_CIPHER_AUTO;
else if (ssl_type & ECORE_CON_USE_TLS)
cipher = EFL_NET_SSL_CIPHER_TLSV1;
else if (ssl_type & ECORE_CON_USE_SSL3)
{
ERR("SSLv3 is unsupported!");
return NULL;
}
else if (ssl_type & ECORE_CON_USE_SSL2)
{
ERR("SSLv2 is unsupported!");
return NULL;
}
/* legacy compatibility: server never verified peer, only dialer did */
return efl_add_ref(EFL_NET_SSL_CONTEXT_CLASS, efl_main_loop_get(),
efl_net_ssl_context_certificates_set(efl_added, eina_list_iterator_new(svr->ssl.certs)),
efl_net_ssl_context_private_keys_set(efl_added, eina_list_iterator_new(svr->ssl.privkeys)),
efl_net_ssl_context_certificate_revocation_lists_set(efl_added, eina_list_iterator_new(svr->ssl.crls)),
efl_net_ssl_context_certificate_authorities_set(efl_added, eina_list_iterator_new(svr->ssl.cafiles)),
efl_net_ssl_context_default_paths_load_set(efl_added, EINA_FALSE), /* old API didn't load default paths */
efl_net_ssl_context_setup(efl_added, cipher, EINA_FALSE));
}
static Eina_Value
_ecore_con_server_server_ssl_job(void *data, const Eina_Value v,
const Eina_Future *dead EINA_UNUSED)
{
Ecore_Con_Server *svr = data;
Eo *loop = efl_main_loop_get();
Eo *ssl_ctx;
Eo *inner_server;
Eo *server;
if (v.type == EINA_VALUE_TYPE_ERROR) return v;
ssl_ctx = _ecore_con_server_ssl_ctx_create(svr);
EINA_SAFETY_ON_NULL_GOTO(ssl_ctx, error_ssl_ctx);
inner_server = efl_add(EFL_NET_SERVER_SSL_CLASS, loop,
efl_net_server_ssl_context_set(efl_added, ssl_ctx));
EINA_SAFETY_ON_NULL_GOTO(inner_server, error_inner_server);
server = efl_add(EFL_NET_SERVER_SIMPLE_CLASS, loop,
efl_net_server_simple_inner_server_set(efl_added, inner_server));
EINA_SAFETY_ON_NULL_GOTO(server, error_server);
efl_parent_set(inner_server, server);
efl_unref(ssl_ctx); /* inner_server keeps it */
if (!_ecore_con_server_server_set(svr, server))
goto error_serve;
DBG("server=%p configured with inner_server=%p, ssl_ctx=%p",
svr->server,
efl_net_server_simple_inner_server_get(svr->server),
efl_net_server_ssl_context_get(efl_net_server_simple_inner_server_get(svr->server)));
return v;
error_serve:
if (_ecore_con_post_event_server_error(svr, "Couldn't serve using SSL"))
_ecore_con_post_event_server_del(svr);
return v;
error_server:
efl_del(inner_server);
error_inner_server:
efl_del(ssl_ctx);
error_ssl_ctx:
if (_ecore_con_post_event_server_error(svr, "Couldn't finish SSL setup"))
_ecore_con_post_event_server_del(svr);
return v;
}
/**
* @example ecore_con_server_example.c
* Shows how to write a simple server using the Ecore_Con library
* using ecore_con_server_add()
*/
EAPI Ecore_Con_Server *
ecore_con_server_add(Ecore_Con_Type compl_type,
const char *name,
int port,
const void *data)
{
const Efl_Class *cls = NULL;
Ecore_Con_Server *svr;
Ecore_Con_Type type;
Eo *server;
Eo *loop;
EINA_SAFETY_ON_NULL_RETURN_VAL(name, NULL);
type = compl_type & ECORE_CON_TYPE;
/* The allowable port number is an unsigned 16-bit integer for remote connection, so 1-65535, 0 is reserved */
if (((type == ECORE_CON_REMOTE_TCP) || (type == ECORE_CON_REMOTE_NODELAY) || (type == ECORE_CON_REMOTE_CORK) ||
(type == ECORE_CON_REMOTE_UDP) || (type == ECORE_CON_REMOTE_MCAST)) &&
((port < 0) || (port > 65535)))
{
ERR("Port %i invalid (0 <= port <= 65535)", port);
return NULL;
}
loop = efl_main_loop_get();
EINA_SAFETY_ON_NULL_RETURN_VAL(loop, NULL);
svr = _ecore_con_server_new(EINA_FALSE, compl_type, name, port, data);
EINA_SAFETY_ON_NULL_RETURN_VAL(svr, NULL);
switch (type)
{
case ECORE_CON_LOCAL_USER:
case ECORE_CON_LOCAL_SYSTEM:
case ECORE_CON_LOCAL_ABSTRACT:
#ifdef EFL_NET_SERVER_UNIX_CLASS
cls = EFL_NET_SERVER_UNIX_CLASS;
#elif defined(EFL_NET_SERVER_WINDOWS_CLASS)
cls = EFL_NET_SERVER_WINDOWS_CLASS;
#else
ERR("Your platform doesn't support Efl_Net_Server-compatible local communication");
// TODO: maybe write to a file and use TCP
#endif
break;
case ECORE_CON_REMOTE_TCP:
case ECORE_CON_REMOTE_NODELAY:
case ECORE_CON_REMOTE_CORK:
cls = EFL_NET_SERVER_TCP_CLASS;
break;
case ECORE_CON_REMOTE_UDP:
case ECORE_CON_REMOTE_MCAST:
cls = EFL_NET_SERVER_UDP_CLASS;
break;
default:
ERR("Unsupported type=%#x & %#x = %#x", compl_type, ECORE_CON_TYPE, type);
}
EINA_SAFETY_ON_NULL_GOTO(cls, error);
if (compl_type & ECORE_CON_SSL)
{
if (cls != EFL_NET_SERVER_TCP_CLASS)
ERR("SSL can only be used with TCP types, got %s, forcing TCP", efl_class_name_get(cls));
/* efl_net_ssl_context must be created prior to the object as
* it's immutable once created. However the previous
* Ecore_Con_Server API returned a handle and then configured
* it, like in runtime, but in practice it's only effective
* before the server starts.
*
* Then do not create the SSL server right away, instead do it
* from a job, let the user configure SSL before the job
* finishes.
*
* EINA_SAFETY_ON_NULL_RETURN(svr->ssl.job) will alert users
* trying to configure after the job expires.
*
* We can do that since the documentation says:
*
* > Call this function on a server object before main loop
* > has started to enable verification of certificates
* > against loaded certificates.
*/
svr->ssl.upgrade_type = compl_type;
_ecore_con_server_job_schedule(svr, loop, _ecore_con_server_server_ssl_job);
return svr;
}
server = efl_add(EFL_NET_SERVER_SIMPLE_CLASS, loop,
efl_net_server_simple_inner_class_set(efl_added, cls));
EINA_SAFETY_ON_NULL_GOTO(server, error);
if (!_ecore_con_server_server_set(svr, server))
goto error;
return svr;
error:
_ecore_con_server_free(svr);
return NULL;
}
/**
* @brief convert @a socks to proxy URL string and apply using efl_net_dialer_proxy_set().
*
* @param svr the server created by ecore_con_server_connect().
* @param socks the socks handle created by
* ecore_con_socks4_remote_add() or ecore_con_socks5_remote_add().
*
* @internal
*/
static void
_ecore_con_server_proxy_apply(Ecore_Con_Server *svr, const Ecore_Con_Socks *socks)
{
char str[4096], port[16] = "";
const char *protocol;
const char *user = "";
const char *userpass_sep = "";
const char *pass = "";
const char *auth_sep = "";
if (socks->version == 4)
{
Ecore_Con_Socks_v4 *v4 = (Ecore_Con_Socks_v4 *)socks;
if (v4->lookup) protocol = "socks4a";
else protocol = "socks4";
if (v4->port > 0)
snprintf(port, sizeof(port), ":%d", v4->port);
if (v4->username)
{
user = v4->username;
auth_sep = "@";
}
}
else if (socks->version == 5)
{
Ecore_Con_Socks_v5 *v5 = (Ecore_Con_Socks_v5 *)socks;
if (socks->lookup) protocol = "socks5h";
else protocol = "socks5";
if (v5->port > 0)
snprintf(port, sizeof(port), ":%d", v5->port);
if (v5->username)
{
user = v5->username;
auth_sep = "@";
if (v5->password)
{
pass = v5->password;
userpass_sep = ":";
}
}
}
else
{
ERR("unsupported socks->version=%d", socks->version);
return;
}
snprintf(str, sizeof(str),
"%s://%s%s%s%s%s%s",
protocol,
pass, userpass_sep, user, auth_sep,
socks->ip, port);
DBG("using proxy url='%s' for server=%s, port=%d",
str, svr->name, svr->port);
efl_net_dialer_proxy_set(svr->dialer, str);
if (socks->bind)
ERR("proxy bind is not supported! Ecore_Con_Socks=%p %s", socks, str);
}
static Eina_Bool
_ecore_con_server_dialer_set(Ecore_Con_Server *svr, Eo *dialer)
{
char address[4096] = "";
Eo *inner_dialer = efl_io_buffered_stream_inner_io_get(dialer);
Ecore_Con_Type type = svr->type & ECORE_CON_TYPE;
Eina_Error err;
svr->dialer = dialer;
efl_io_closer_close_on_exec_set(dialer, EINA_TRUE);
efl_io_closer_close_on_invalidate_set(dialer, EINA_TRUE);
efl_io_buffered_stream_timeout_inactivity_set(dialer, svr->timeout);
efl_event_callback_array_add(dialer, _ecore_con_server_dialer_cbs(), svr);
if (efl_isa(inner_dialer, EFL_NET_DIALER_TCP_CLASS))
{
efl_net_socket_tcp_no_delay_set(inner_dialer, !!(type & ECORE_CON_REMOTE_NODELAY));
efl_net_socket_tcp_cork_set(inner_dialer, !!(type & ECORE_CON_REMOTE_CORK));
}
else if (efl_isa(inner_dialer, EFL_NET_DIALER_SSL_CLASS))
{
efl_net_dialer_ssl_no_delay_set(inner_dialer, !!(type & ECORE_CON_REMOTE_NODELAY));
}
if (efl_isa(inner_dialer, EFL_NET_DIALER_TCP_CLASS) ||
efl_isa(inner_dialer, EFL_NET_DIALER_UDP_CLASS) ||
efl_isa(inner_dialer, EFL_NET_DIALER_SSL_CLASS))
{
if (strchr(svr->name, ':'))
snprintf(address, sizeof(address), "[%s]:%d", svr->name, svr->port);
else
snprintf(address, sizeof(address), "%s:%d", svr->name, svr->port);
}
else if (type == ECORE_CON_LOCAL_ABSTRACT)
snprintf(address, sizeof(address), "abstract:%s", svr->name);
else if ((type == ECORE_CON_LOCAL_USER) ||
(type == ECORE_CON_LOCAL_SYSTEM))
{
char *path = ecore_con_local_path_new(type == ECORE_CON_LOCAL_SYSTEM, svr->name, svr->port);
#ifdef EFL_NET_DIALER_UNIX_CLASS
struct stat st;
#endif
if (!path)
{
ERR("could not create local path for name='%s', port=%d", svr->name, svr->port);
return EINA_FALSE;
}
else
{
eina_strlcpy(address, path, sizeof(address));
free(path);
}
#ifdef EFL_NET_DIALER_UNIX_CLASS
if ((stat(address, &st) != 0)
#ifdef S_ISSOCK
|| (!S_ISSOCK(st.st_mode))
#endif
)
{
DBG("%s is not a socket", address);
return EINA_FALSE;
}
#endif
}
if ((svr->type & ECORE_CON_NO_PROXY) == ECORE_CON_NO_PROXY)
{
DBG("svr=%p not using any proxy for dialer %p (%s)",
svr, svr->dialer, efl_class_name_get(inner_dialer));
efl_net_dialer_proxy_set(svr->dialer, "");
}
else if (type > ECORE_CON_LOCAL_ABSTRACT)
{
if (_ecore_con_proxy_once)
{
_ecore_con_server_proxy_apply(svr, _ecore_con_proxy_once);
_ecore_con_proxy_once = NULL;
}
else if (_ecore_con_proxy_global)
_ecore_con_server_proxy_apply(svr, _ecore_con_proxy_global);
}
svr->connecting = EINA_TRUE;
err = efl_net_dialer_dial(svr->dialer, address);
if (err)
{
WRN("Could not connect to address=%s using class=%s",
address, efl_class_name_get(inner_dialer));
return EINA_FALSE;
}
DBG("svr=%p dialer=%p (%s) address=%s",
svr, svr->dialer, efl_class_name_get(inner_dialer),
efl_net_dialer_address_dial_get(svr->dialer));
return EINA_TRUE;
}
static Eina_Value
_ecore_con_server_dialer_ssl_job(void *data, const Eina_Value v,
const Eina_Future *dead EINA_UNUSED)
{
Ecore_Con_Server *svr = data;
Eo *loop = efl_main_loop_get();
Eo *ssl_ctx;
Eo *inner_dialer;
Eo *dialer;
Efl_Net_Ssl_Cipher cipher = EFL_NET_SSL_CIPHER_AUTO;
Efl_Net_Ssl_Verify_Mode verify_mode = EFL_NET_SSL_VERIFY_MODE_NONE; /* was the default */
Ecore_Con_Type ssl_type = svr->ssl.upgrade_type & ECORE_CON_SSL;
if (ssl_type & ECORE_CON_USE_MIXED)
cipher = EFL_NET_SSL_CIPHER_AUTO;
else if (ssl_type & ECORE_CON_USE_TLS)
cipher = EFL_NET_SSL_CIPHER_TLSV1;
else if (ssl_type & ECORE_CON_USE_SSL3)
{
ERR("SSLv3 is unsupported!");
goto error_ssl_ctx;
}
else if (ssl_type & ECORE_CON_USE_SSL2)
{
ERR("SSLv2 is unsupported!");
goto error_ssl_ctx;
}
if (svr->ssl.verify)
verify_mode = EFL_NET_SSL_VERIFY_MODE_REQUIRED;
ssl_ctx = efl_add(EFL_NET_SSL_CONTEXT_CLASS, efl_main_loop_get(),
efl_net_ssl_context_certificates_set(efl_added, eina_list_iterator_new(svr->ssl.certs)),
efl_net_ssl_context_private_keys_set(efl_added, eina_list_iterator_new(svr->ssl.privkeys)),
efl_net_ssl_context_certificate_revocation_lists_set(efl_added, eina_list_iterator_new(svr->ssl.crls)),
efl_net_ssl_context_certificate_authorities_set(efl_added, eina_list_iterator_new(svr->ssl.cafiles)),
efl_net_ssl_context_verify_mode_set(efl_added, verify_mode),
efl_net_ssl_context_hostname_set(efl_added, svr->ssl.verify_name ? svr->ssl.verify_name : svr->name),
efl_net_ssl_context_hostname_verify_set(efl_added, svr->ssl.verify_basic),
efl_net_ssl_context_default_paths_load_set(efl_added, EINA_FALSE), /* old API didn't load default paths */
efl_net_ssl_context_setup(efl_added, cipher, EINA_TRUE));
EINA_SAFETY_ON_NULL_GOTO(ssl_ctx, error_ssl_ctx);
inner_dialer = efl_add(EFL_NET_DIALER_SSL_CLASS, loop,
efl_net_dialer_ssl_context_set(efl_added, ssl_ctx));
EINA_SAFETY_ON_NULL_GOTO(inner_dialer, error_inner_dialer);
dialer = efl_add(EFL_NET_DIALER_SIMPLE_CLASS, loop,
efl_io_buffered_stream_inner_io_set(efl_added, inner_dialer));
EINA_SAFETY_ON_NULL_GOTO(dialer, error_dialer);
efl_parent_set(inner_dialer, dialer);
efl_parent_set(ssl_ctx, inner_dialer);
if (!_ecore_con_server_dialer_set(svr, dialer))
goto error_dial;
DBG("dialer=%p configured with inner_dialer=%p, ssl_ctx=%p",
svr->dialer,
efl_io_buffered_stream_inner_io_get(svr->dialer),
efl_net_dialer_ssl_context_get(efl_io_buffered_stream_inner_io_get(svr->dialer)));
if (svr->ssl.pending_send)
{
/* if ecore_con_server_send() was called before the job completed
* then we queued data there, flush to dialer.
* See https://phab.enlightenment.org/T5339
*/
Eina_Slice slice = eina_binbuf_slice_get(svr->ssl.pending_send);
ecore_con_server_send(svr, slice.mem, slice.len);
eina_binbuf_free(svr->ssl.pending_send);
svr->ssl.pending_send = NULL;
}
return v;
error_dial:
if (_ecore_con_post_event_server_error(svr, "Couldn't dial using SSL"))
_ecore_con_post_event_server_del(svr);
return v;
error_dialer:
efl_del(inner_dialer);
error_inner_dialer:
efl_del(ssl_ctx);
error_ssl_ctx:
if (_ecore_con_post_event_server_error(svr, "Couldn't finish SSL setup"))
_ecore_con_post_event_server_del(svr);
return v;
}
static Eina_Value
_ecore_con_server_dialer_ssl_upgrade_job(void *data, const Eina_Value v,
const Eina_Future *dead EINA_UNUSED)
{
Ecore_Con_Server *svr = data;
Eo *loop = efl_main_loop_get();
Eo *ssl_ctx;
Eo *inner_dialer;
Eo *dialer;
Eo *tcp_dialer;
Efl_Net_Ssl_Cipher cipher = EFL_NET_SSL_CIPHER_AUTO;
Efl_Net_Ssl_Verify_Mode verify_mode = EFL_NET_SSL_VERIFY_MODE_NONE; /* was the default */
Ecore_Con_Type ssl_type = svr->ssl.upgrade_type & ECORE_CON_SSL;
//Canceled
if (v.type == EINA_VALUE_TYPE_ERROR) return v;
if (ssl_type & ECORE_CON_USE_MIXED)
cipher = EFL_NET_SSL_CIPHER_AUTO;
else if (ssl_type & ECORE_CON_USE_TLS)
cipher = EFL_NET_SSL_CIPHER_TLSV1;
else if (ssl_type & ECORE_CON_USE_SSL3)
{
ERR("SSLv3 is unsupported!");
goto error_ssl_ctx;
}
else if (ssl_type & ECORE_CON_USE_SSL2)
{
ERR("SSLv2 is unsupported!");
goto error_ssl_ctx;
}
if (svr->ssl.verify)
verify_mode = EFL_NET_SSL_VERIFY_MODE_REQUIRED;
ssl_ctx = efl_add_ref(EFL_NET_SSL_CONTEXT_CLASS, efl_main_loop_get(),
efl_net_ssl_context_certificates_set(efl_added, eina_list_iterator_new(svr->ssl.certs)),
efl_net_ssl_context_private_keys_set(efl_added, eina_list_iterator_new(svr->ssl.privkeys)),
efl_net_ssl_context_certificate_revocation_lists_set(efl_added, eina_list_iterator_new(svr->ssl.crls)),
efl_net_ssl_context_certificate_authorities_set(efl_added, eina_list_iterator_new(svr->ssl.cafiles)),
efl_net_ssl_context_verify_mode_set(efl_added, verify_mode),
efl_net_ssl_context_hostname_set(efl_added, svr->ssl.verify_name ? svr->ssl.verify_name : svr->name),
efl_net_ssl_context_hostname_verify_set(efl_added, svr->ssl.verify_basic),
efl_net_ssl_context_default_paths_load_set(efl_added, EINA_FALSE), /* old API didn't load default paths */
efl_net_ssl_context_setup(efl_added, cipher, EINA_TRUE));
EINA_SAFETY_ON_NULL_GOTO(ssl_ctx, error_ssl_ctx);
tcp_dialer = svr->dialer;
svr->dialer = NULL; /* take it, will be wrapped */
inner_dialer = efl_add(EFL_NET_DIALER_SSL_CLASS, loop,
efl_net_socket_ssl_adopt(efl_added, tcp_dialer, ssl_ctx));
EINA_SAFETY_ON_NULL_GOTO(inner_dialer, error_inner_dialer);
efl_parent_set(tcp_dialer, inner_dialer);
dialer = efl_add(EFL_NET_DIALER_SIMPLE_CLASS, loop,
efl_io_buffered_stream_inner_io_set(efl_added, inner_dialer));
EINA_SAFETY_ON_NULL_GOTO(dialer, error_dialer);
efl_parent_set(inner_dialer, dialer);
efl_unref(ssl_ctx); /* inner_dialer keeps it */
svr->dialer = dialer;
efl_io_closer_close_on_exec_set(dialer, EINA_TRUE);
efl_io_closer_close_on_invalidate_set(dialer, EINA_TRUE);
efl_event_callback_array_del(tcp_dialer, _ecore_con_server_dialer_cbs(), svr);
efl_event_callback_array_add(dialer, _ecore_con_server_dialer_cbs(), svr);
DBG("dialer=%p upgraded to SSL with inner_dialer=%p, ssl_ctx=%p, tcp_dialer=%p. Start handshake...",
svr->dialer,
efl_io_buffered_stream_inner_io_get(svr->dialer),
efl_net_dialer_ssl_context_get(efl_io_buffered_stream_inner_io_get(svr->dialer)),
tcp_dialer);
return v;
error_dialer:
efl_del(inner_dialer);
error_inner_dialer:
svr->dialer = tcp_dialer; /* put it back */
efl_del(ssl_ctx);
error_ssl_ctx:
if (_ecore_con_post_event_server_error(svr, "Couldn't finish SSL setup"))
_ecore_con_post_event_server_del(svr);
return v;
}
/**
* @example ecore_con_client_example.c
*
* Shows how to write a simple client (dialer that connects to the
* example server using ecore_con_server_connect().
*/
EAPI Ecore_Con_Server *
ecore_con_server_connect(Ecore_Con_Type compl_type,
const char *name,
int port,
const void *data)
{
const Efl_Class *cls = NULL;
Ecore_Con_Server *svr;
Ecore_Con_Type type;
Eo *dialer;
Eo *loop;
EINA_SAFETY_ON_NULL_RETURN_VAL(name, NULL);
type = compl_type & ECORE_CON_TYPE;
loop = efl_main_loop_get();
EINA_SAFETY_ON_NULL_RETURN_VAL(loop, NULL);
svr = _ecore_con_server_new(EINA_TRUE, compl_type, name, port, data);
EINA_SAFETY_ON_NULL_RETURN_VAL(svr, NULL);
switch (type)
{
case ECORE_CON_LOCAL_USER:
case ECORE_CON_LOCAL_SYSTEM:
case ECORE_CON_LOCAL_ABSTRACT:
#ifdef EFL_NET_DIALER_UNIX_CLASS
cls = EFL_NET_DIALER_UNIX_CLASS;
#elif defined(EFL_NET_DIALER_WINDOWS_CLASS)
cls = EFL_NET_DIALER_WINDOWS_CLASS;
#else
ERR("Your platform doesn't support Efl_Net_Dialer-compatible local communication");
// TODO: maybe write to a file and use TCP
#endif
break;
case ECORE_CON_REMOTE_TCP:
case ECORE_CON_REMOTE_NODELAY:
case ECORE_CON_REMOTE_CORK:
cls = EFL_NET_DIALER_TCP_CLASS;
break;
case ECORE_CON_REMOTE_UDP:
case ECORE_CON_REMOTE_BROADCAST:
cls = EFL_NET_DIALER_UDP_CLASS;
break;
default:
ERR("Unsupported type=%#x & %#x = %#x", compl_type, ECORE_CON_TYPE, type);
}
EINA_SAFETY_ON_NULL_GOTO(cls, error);
if (compl_type & ECORE_CON_SSL)
{
if (cls != EFL_NET_DIALER_TCP_CLASS)
ERR("SSL can only be used with TCP types, got %s, forcing TCP", efl_class_name_get(cls));
/* efl_net_ssl_context must be created prior to the object as
* it's immutable once created. However the previous
* Ecore_Con_Server API returned a handle and then configured
* it, like in runtime, but in practice it's only effective
* before the connection happens.
*
* Then do not create the SSL dialer right away, instead do it
* from a job, let the user configure SSL before the job
* finishes.
*
* EINA_SAFETY_ON_NULL_RETURN(svr->ssl.job) will alert users
* trying to configure after the job expires.
*
* We can do that since the documentation says:
*
* > Call this function on a server object before main loop
* > has started to enable verification of certificates
* > against loaded certificates.
*/
_ecore_con_server_job_schedule(svr, loop, _ecore_con_server_dialer_ssl_job);
return svr;
}
dialer = efl_add(EFL_NET_DIALER_SIMPLE_CLASS, loop,
efl_net_dialer_simple_inner_class_set(efl_added, cls));
EINA_SAFETY_ON_NULL_GOTO(dialer, error);
if (!_ecore_con_server_dialer_set(svr, dialer))
goto error;
return svr;
error:
_ecore_con_server_free(svr);
return NULL;
}
EAPI const char *
ecore_con_server_name_get(const Ecore_Con_Server *svr)
{
ECORE_CON_SERVER_CHECK_RELAXED_RETURN(svr, NULL);
return svr->name;
}
EAPI void
ecore_con_server_client_limit_set(Ecore_Con_Server *svr,
int client_limit,
char reject_excess_clients)
{
ECORE_CON_SERVER_CHECK_RETURN(svr);
if (!svr->server) return;
efl_net_server_clients_limit_set(svr->server, client_limit, reject_excess_clients);
}
EAPI const Eina_List *
ecore_con_server_clients_get(const Ecore_Con_Server *svr)
{
ECORE_CON_SERVER_CHECK_RETURN(svr, NULL);
return svr->clients;
}
EAPI void
ecore_con_server_timeout_set(Ecore_Con_Server *svr, double timeout)
{
ECORE_CON_SERVER_CHECK_RETURN(svr);
svr->timeout = timeout; /* used for new clients */
if (!svr->dialer) return;
efl_io_buffered_stream_timeout_inactivity_set(svr->dialer, timeout);
}
EAPI double
ecore_con_server_timeout_get(const Ecore_Con_Server *svr)
{
ECORE_CON_SERVER_CHECK_RETURN(svr, -1.0);
return svr->timeout;
}
EAPI void *
ecore_con_server_del(Ecore_Con_Server *svr)
{
const void *data;
ECORE_CON_SERVER_CHECK_RELAXED_RETURN(svr, NULL);
data = svr->data;
_ecore_con_server_free(svr);
return (void *)data;
}
EAPI void *
ecore_con_server_data_get(Ecore_Con_Server *svr)
{
ECORE_CON_SERVER_CHECK_RELAXED_RETURN(svr, NULL);
return (void *)svr->data;
}
EAPI void *
ecore_con_server_data_set(Ecore_Con_Server *svr,
void *data)
{
const void *old;
ECORE_CON_SERVER_CHECK_RELAXED_RETURN(svr, NULL);
old = svr->data;
svr->data = data;
return (void *)old;
}
EAPI Eina_Bool
ecore_con_server_connected_get(const Ecore_Con_Server *svr)
{
ECORE_CON_SERVER_CHECK_RETURN(svr, EINA_FALSE);
if (svr->is_dialer)
{
if (svr->dialer)
return efl_net_dialer_connected_get(svr->dialer);
return EINA_FALSE; /* still setting up SSL */
}
return EINA_TRUE;
}
EAPI int
ecore_con_server_port_get(const Ecore_Con_Server *svr)
{
ECORE_CON_SERVER_CHECK_RELAXED_RETURN(svr, -1);
return svr->port;
}
EAPI int
ecore_con_server_send(Ecore_Con_Server *svr, const void *data, int size)
{
Eina_Error err;
Eina_Slice slice = { .mem = data, .len = size };
ECORE_CON_SERVER_CHECK_RETURN(svr, 0);
EINA_SAFETY_ON_NULL_RETURN_VAL(data, 0);
EINA_SAFETY_ON_TRUE_RETURN_VAL(size < 1, 0);
/* while upgrading or no dialer (due SSL dialer being created on
* the next mainloop iteration from a job as described from
* ecore_con_server_connect()), queue pending data and send as soon
* as the dialer is assigned.
*
* This allows immediate usage of ecore_con_server_send() after
* ecore_con_server_connect() as the old API did and needed by
* https://phab.enlightenment.org/T5339
*/
if ((svr->ssl.upgrading) || (!svr->dialer))
{
Eina_Bool r;
if (!svr->ssl.pending_send)
{
svr->ssl.pending_send = eina_binbuf_new();
EINA_SAFETY_ON_NULL_RETURN_VAL(svr->ssl.pending_send, 0);
}
r = eina_binbuf_append_length(svr->ssl.pending_send, data, size);
EINA_SAFETY_ON_FALSE_RETURN_VAL(r, 0);
return size;
}
err = efl_io_writer_write(svr->dialer, &slice, NULL);
if (err)
{
ERR("svr=%p could not send data=%p, size=%d: %s",
svr, data, size, eina_error_msg_get(err));
return 0;
}
svr->pending_write = efl_io_buffered_stream_pending_write_get(svr->dialer);
return slice.len;
}
EAPI const char *
ecore_con_server_ip_get(const Ecore_Con_Server *svr)
{
ECORE_CON_SERVER_CHECK_RELAXED_RETURN(svr, NULL);
return svr->ip;
}
EAPI double
ecore_con_server_uptime_get(const Ecore_Con_Server *svr)
{
ECORE_CON_SERVER_CHECK_RELAXED_RETURN(svr, 0.0);
return ecore_time_get() - svr->start_time;
}
EAPI void
ecore_con_server_flush(Ecore_Con_Server *svr)
{
ECORE_CON_SERVER_CHECK_RETURN(svr);
Eo *inner_dialer;
if (!svr->dialer) return;
while (!efl_io_closer_closed_get(svr->dialer) &&
!efl_net_dialer_connected_get(svr->dialer))
ecore_main_loop_iterate();
efl_io_buffered_stream_flush(svr->dialer, EINA_FALSE, EINA_TRUE);
inner_dialer = efl_io_buffered_stream_inner_io_get(svr->dialer);
if (!efl_isa(inner_dialer, EFL_NET_SOCKET_TCP_CLASS)) return;
if (!efl_net_socket_tcp_cork_get(inner_dialer)) return;
efl_net_socket_tcp_cork_set(inner_dialer, EINA_FALSE);
efl_net_socket_tcp_cork_set(inner_dialer, EINA_TRUE);
}
EAPI int
ecore_con_server_fd_get(const Ecore_Con_Server *svr)
{
ECORE_CON_SERVER_CHECK_RETURN(svr, SOCKET_TO_LOOP_FD(INVALID_SOCKET));
if (svr->dialer)
{
Eo *inner_dialer = efl_io_buffered_stream_inner_io_get(svr->dialer);
if (efl_isa(inner_dialer, EFL_LOOP_FD_CLASS))
{
return efl_loop_fd_get(inner_dialer);
}
else
{
if (efl_isa(inner_dialer, EFL_NET_DIALER_SSL_CLASS))
{
Eo* adopted_dialer = NULL;
if (efl_net_socket_ssl_adopted_get(inner_dialer, &adopted_dialer, NULL))
if (efl_isa(adopted_dialer, EFL_LOOP_FD_CLASS))
return efl_loop_fd_get(adopted_dialer);
}
}
return SOCKET_TO_LOOP_FD(INVALID_SOCKET);
}
if (svr->server)
{
Eo *inner_server = efl_net_server_simple_inner_server_get(svr->server);
if (efl_isa(inner_server, EFL_LOOP_FD_CLASS))
return efl_loop_fd_get(inner_server);
return SOCKET_TO_LOOP_FD(INVALID_SOCKET);
}
return SOCKET_TO_LOOP_FD(INVALID_SOCKET);
}
EAPI Eina_Bool
ecore_con_ssl_server_cert_add(Ecore_Con_Server *svr, const char *cert)
{
ECORE_CON_SERVER_CHECK_RETURN(svr, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(cert, EINA_FALSE);
if (!svr->server) /* SSL adds a job to allow setup */
EINA_SAFETY_ON_NULL_RETURN_VAL(svr->ssl.job, EINA_FALSE);
else
EINA_SAFETY_ON_TRUE_RETURN_VAL(svr->ssl.clients_ctx != NULL, EINA_FALSE);
svr->ssl.certs = eina_list_append(svr->ssl.certs, eina_stringshare_add(cert));
return EINA_TRUE;
}
EAPI Eina_Bool
ecore_con_ssl_server_privkey_add(Ecore_Con_Server *svr, const char *privkey)
{
ECORE_CON_SERVER_CHECK_RETURN(svr, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(privkey, EINA_FALSE);
if (!svr->server) /* SSL adds a job to allow setup */
EINA_SAFETY_ON_NULL_RETURN_VAL(svr->ssl.job, EINA_FALSE);
else
EINA_SAFETY_ON_TRUE_RETURN_VAL(svr->ssl.clients_ctx != NULL, EINA_FALSE);
svr->ssl.privkeys = eina_list_append(svr->ssl.privkeys, eina_stringshare_add(privkey));
return EINA_TRUE;
}
EAPI Eina_Bool
ecore_con_ssl_server_crl_add(Ecore_Con_Server *svr, const char *crl)
{
ECORE_CON_SERVER_CHECK_RETURN(svr, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(crl, EINA_FALSE);
if (!svr->server) /* SSL adds a job to allow setup */
EINA_SAFETY_ON_NULL_RETURN_VAL(svr->ssl.job, EINA_FALSE);
else
EINA_SAFETY_ON_TRUE_RETURN_VAL(svr->ssl.clients_ctx != NULL, EINA_FALSE);
svr->ssl.crls = eina_list_append(svr->ssl.crls, eina_stringshare_add(crl));
return EINA_TRUE;
}
EAPI Eina_Bool
ecore_con_ssl_server_cafile_add(Ecore_Con_Server *svr, const char *cafile)
{
ECORE_CON_SERVER_CHECK_RETURN(svr, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(cafile, EINA_FALSE);
if (!svr->server) /* SSL adds a job to allow setup */
EINA_SAFETY_ON_NULL_RETURN_VAL(svr->ssl.job, EINA_FALSE);
else
EINA_SAFETY_ON_TRUE_RETURN_VAL(svr->ssl.clients_ctx != NULL, EINA_FALSE);
svr->ssl.cafiles = eina_list_append(svr->ssl.cafiles, eina_stringshare_add(cafile));
return EINA_TRUE;
}
EAPI void
ecore_con_ssl_server_verify(Ecore_Con_Server *svr)
{
ECORE_CON_SERVER_CHECK_RETURN(svr);
if (!svr->server) /* SSL adds a job to allow setup */
EINA_SAFETY_ON_NULL_RETURN(svr->ssl.job);
else
EINA_SAFETY_ON_TRUE_RETURN(svr->ssl.clients_ctx != NULL);
if (!svr->is_dialer)
{
/* legacy compatible + print a warning */
WRN("svr=%p created with ecore_con_server_add()", svr);
return;
}
svr->ssl.verify = EINA_TRUE;
}
EAPI void
ecore_con_ssl_server_verify_basic(Ecore_Con_Server *svr)
{
ECORE_CON_SERVER_CHECK_RETURN(svr);
EINA_SAFETY_ON_NULL_RETURN(svr->ssl.job);
if (!svr->server) /* SSL adds a job to allow setup */
EINA_SAFETY_ON_NULL_RETURN(svr->ssl.job);
else
EINA_SAFETY_ON_TRUE_RETURN(svr->ssl.clients_ctx != NULL);
if (!svr->is_dialer)
{
/* legacy compatible + print a warning */
WRN("svr=%p created with ecore_con_server_add()", svr);
return;
}
svr->ssl.verify_basic = EINA_TRUE;
}
EAPI void
ecore_con_ssl_server_verify_name_set(Ecore_Con_Server *svr, const char *name)
{
ECORE_CON_SERVER_CHECK_RETURN(svr);
EINA_SAFETY_ON_NULL_RETURN(name);
if (!svr->server) /* SSL adds a job to allow setup */
EINA_SAFETY_ON_NULL_RETURN(svr->ssl.job);
else
EINA_SAFETY_ON_TRUE_RETURN(svr->ssl.clients_ctx != NULL);
eina_stringshare_replace(&svr->ssl.verify_name, name);
}
EAPI const char *
ecore_con_ssl_server_verify_name_get(Ecore_Con_Server *svr)
{
ECORE_CON_SERVER_CHECK_RETURN(svr, NULL);
return svr->ssl.verify_name ? svr->ssl.verify_name : svr->name;
}
EAPI Eina_Bool
ecore_con_ssl_server_upgrade(Ecore_Con_Server *svr, Ecore_Con_Type compl_type)
{
double start;
ECORE_CON_SERVER_CHECK_RETURN(svr, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(svr->dialer, EINA_FALSE);
EINA_SAFETY_ON_TRUE_RETURN_VAL(svr->server != NULL, EINA_FALSE);
EINA_SAFETY_ON_TRUE_RETURN_VAL(svr->ssl.upgrading, EINA_FALSE);
EINA_SAFETY_ON_TRUE_RETURN_VAL((compl_type & ECORE_CON_SSL) == 0, EINA_FALSE);
EINA_SAFETY_ON_FALSE_RETURN_VAL(efl_isa(efl_io_buffered_stream_inner_io_get(svr->dialer), EFL_NET_DIALER_TCP_CLASS), EINA_FALSE);
start = ecore_time_get();
while (efl_io_buffered_stream_pending_write_get(svr->dialer) && ((ecore_time_get() - start) <= ecore_animator_frametime_get()))
ecore_con_server_flush(svr);
if (efl_io_buffered_stream_pending_write_get(svr->dialer))
{
ERR("svr=%p still pending send %zd bytes! Flush server before upgrading to SSL!",
svr, efl_io_buffered_stream_pending_write_get(svr->dialer));
return EINA_FALSE;
}
svr->ssl.upgrading = EINA_TRUE;
svr->ssl.upgrade_type = compl_type;
_ecore_con_server_job_schedule(svr, efl_loop_get(svr->dialer),
_ecore_con_server_dialer_ssl_upgrade_job);
DBG("svr=%p SSL upgrading from %#x to type=%#x", svr, svr->type, compl_type);
return EINA_TRUE;
}
/**
* @}
*/
static void
_ecore_con_lookup_done_cb(void *data, const char *host, const char *port EINA_UNUSED, const struct addrinfo *hints EINA_UNUSED, struct addrinfo *result, int gai_error)
{
Ecore_Con_Lookup_Ctx *ctx = data;
ctx->thread = NULL;
if (gai_error)
WRN("Failed to lookup host='%s': %s", host, gai_strerror(gai_error));
else if (result)
{
char ip[INET6_ADDRSTRLEN];
const void *mem;
if (result->ai_family == AF_INET)
{
const struct sockaddr_in *a = (const struct sockaddr_in *)result->ai_addr;
mem = &a->sin_addr;
}
else if (result->ai_family == AF_INET6)
{
const struct sockaddr_in6 *a = (const struct sockaddr_in6 *)result->ai_addr;
mem = &a->sin6_addr;
}
else
{
ERR("unexpected result->ai_family=%d", result->ai_family);
goto end;
}
if (!inet_ntop(result->ai_family, mem, ip, sizeof(ip)))
{
ERR("could not convert IP to string: %s", eina_error_msg_get(errno));
goto end;
}
ctx->cb(result->ai_canonname, ip, result->ai_addr, result->ai_addrlen, (void *)ctx->data);
}
end:
freeaddrinfo(result);
_ecore_con_lookups = eina_list_remove(_ecore_con_lookups, ctx);
free(ctx);
}
/*
* NOTE: this function has numerous problems:
* - not able to specify family (IPv4 or IPv6 or both).
* - callback reports a single result
* - doesn't return a handle to cancel (likely to access memory after free)
* - doesn't report errors
*/
EAPI Eina_Bool
ecore_con_lookup(const char *name, Ecore_Con_Dns_Cb done_cb, const void *data)
{
Ecore_Con_Lookup_Ctx *ctx;
struct addrinfo hints = {
.ai_family = AF_UNSPEC,
.ai_flags = AI_ADDRCONFIG | AI_V4MAPPED | AI_CANONNAME,
};
EINA_SAFETY_ON_TRUE_RETURN_VAL(!name || (!name[0]), EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(done_cb, EINA_FALSE);
ctx = malloc(sizeof(Ecore_Con_Lookup_Ctx));
EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, EINA_FALSE);
ctx->cb = done_cb;
ctx->data = data;
ctx->thread = efl_net_ip_resolve_async_new(name, "0", &hints, _ecore_con_lookup_done_cb, ctx);
EINA_SAFETY_ON_NULL_GOTO(ctx->thread, error);
_ecore_con_lookups = eina_list_append(_ecore_con_lookups, ctx);
return EINA_TRUE;
error:
free(ctx);
return EINA_FALSE;
}