#ifdef HAVE_CONFIG_H # include #endif #ifdef HAVE_ARPA_INET_H # include #endif #ifdef HAVE_SYS_SOCKET_H # include #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; }