2708 lines
83 KiB
C
2708 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
|
|
|
|
#ifdef _WIN32
|
|
# include <Evil.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 (!ECORE_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 (!ECORE_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;
|
|
}
|