diff --git a/src/Makefile_Ecore_Con.am b/src/Makefile_Ecore_Con.am index 53ea70de24..3e9742e6ab 100644 --- a/src/Makefile_Ecore_Con.am +++ b/src/Makefile_Ecore_Con.am @@ -22,6 +22,8 @@ ecore_con_eolian_files = \ lib/ecore_con/efl_net_server_udp_client.eo \ lib/ecore_con/efl_net_socket_ssl.eo \ lib/ecore_con/efl_net_ssl_context.eo \ + lib/ecore_con/efl_net_dialer_ssl.eo \ + lib/ecore_con/efl_net_server_ssl.eo \ lib/ecore_con/ecore_con_eet_base.eo \ lib/ecore_con/ecore_con_eet_server_obj.eo \ lib/ecore_con/ecore_con_eet_client_obj.eo \ @@ -100,7 +102,9 @@ lib/ecore_con/efl_net_server_tcp.c \ lib/ecore_con/efl_net_server_udp.c \ lib/ecore_con/efl_net_server_udp_client.c \ lib/ecore_con/efl_net_socket_ssl.c \ -lib/ecore_con/efl_net_ssl_context.c +lib/ecore_con/efl_net_ssl_context.c \ +lib/ecore_con/efl_net_dialer_ssl.c \ +lib/ecore_con/efl_net_server_ssl.c EXTRA_DIST2 += lib/ecore_con/ecore_con_legacy.c diff --git a/src/examples/ecore/efl_io_copier_example.c b/src/examples/ecore/efl_io_copier_example.c index 7c84c93f8b..96457d4d6f 100644 --- a/src/examples/ecore/efl_io_copier_example.c +++ b/src/examples/ecore/efl_io_copier_example.c @@ -324,6 +324,10 @@ static const Ecore_Getopt options = { "If set will change the base chunk size used while reading."), ECORE_GETOPT_STORE_DOUBLE('t', "inactivity-timeout", "If greater than zero, specifies the number of seconds without any reads or writes that the copier will be timed out."), + + ECORE_GETOPT_STORE_FALSE(0, "no-ssl-verify", + "Disables SSL verify, to use with self-signed certificates and the likes"), + ECORE_GETOPT_VERSION('V', "version"), ECORE_GETOPT_COPYRIGHT('C', "copyright"), ECORE_GETOPT_LICENSE('L', "license"), @@ -339,6 +343,7 @@ static const Ecore_Getopt options = { #ifndef _WIN32 "unix://path to connect to an AF_UNIX server. For Linux one can create abstract sockets with unix://abstract:name.\n" #endif + "ssl://IP:PORT to connect using TCP+SSL and an IPv4 (A.B.C.D:PORT) or IPv6 ([A:B:C:D::E]:PORT).\n" "", "input-file"), ECORE_GETOPT_STORE_METAVAR_STR(0, NULL, @@ -354,6 +359,7 @@ static const Ecore_Getopt options = { #ifndef _WIN32 "unix://path to connect to an AF_UNIX server. For Linux one can create abstract sockets with unix://abstract:name.\n" #endif + "ssl://IP:PORT to connect using TCP+SSL and an IPv4 (A.B.C.D:PORT) or IPv6 ([A:B:C:D::E]:PORT).\n" "", "output-file"), ECORE_GETOPT_SENTINEL @@ -369,6 +375,7 @@ main(int argc, char **argv) unsigned long buffer_limit = 0; unsigned long read_chunk_size = 0; double timeout = 0.0; + Eina_Bool ssl_verify = EINA_TRUE; Eina_Bool quit_option = EINA_FALSE; Ecore_Getopt_Value values[] = { ECORE_GETOPT_VALUE_STR(line_delimiter), @@ -376,6 +383,8 @@ main(int argc, char **argv) ECORE_GETOPT_VALUE_ULONG(read_chunk_size), ECORE_GETOPT_VALUE_DOUBLE(timeout), + ECORE_GETOPT_VALUE_BOOL(ssl_verify), + /* standard block to provide version, copyright, license and help */ ECORE_GETOPT_VALUE_BOOL(quit_option), /* -V/--version quits */ ECORE_GETOPT_VALUE_BOOL(quit_option), /* -C/--copyright quits */ @@ -572,6 +581,36 @@ main(int argc, char **argv) } } #endif + else if (strncmp(input_fname, "ssl://", strlen("ssl://")) == 0) + { + /* + * Since Efl.Net.Socket implements the required interfaces, + * they can be used here as well. + */ + const char *address = input_fname + strlen("ssl://"); + Eina_Error err; + input = efl_add(EFL_NET_DIALER_SSL_CLASS, ecore_main_loop_get(), + efl_event_callback_array_add(efl_added, input_cbs(), NULL), /* optional */ + efl_event_callback_array_add(efl_added, dialer_cbs(), NULL) /* optional */ + ); + if (!input) + { + fprintf(stderr, "ERROR: could not create SSL Dialer.\n"); + retval = EXIT_FAILURE; + goto end; + } + + efl_net_socket_ssl_verify_mode_set(input, ssl_verify ? EFL_NET_SSL_VERIFY_MODE_REQUIRED : EFL_NET_SSL_VERIFY_MODE_NONE); + efl_net_socket_ssl_hostname_verify_set(input, ssl_verify); + + err = efl_net_dialer_dial(input, address); + if (err) + { + fprintf(stderr, "ERROR: could not SSL dial %s: %s\n", + address, eina_error_msg_get(err)); + goto end_input; + } + } else { /* regular file, open with flags: read-only and close-on-exec */ @@ -787,6 +826,36 @@ main(int argc, char **argv) } } #endif + else if (strncmp(output_fname, "ssl://", strlen("ssl://")) == 0) + { + /* + * Since Efl.Net.Socket implements the required interfaces, + * they can be used here as well. + */ + const char *address = output_fname + strlen("ssl://"); + Eina_Error err; + output = efl_add(EFL_NET_DIALER_SSL_CLASS, ecore_main_loop_get(), + efl_event_callback_array_add(efl_added, output_cbs(), NULL), /* optional */ + efl_event_callback_array_add(efl_added, dialer_cbs(), NULL) /* optional */ + ); + if (!output) + { + fprintf(stderr, "ERROR: could not create SSL Dialer.\n"); + retval = EXIT_FAILURE; + goto end_input; + } + + efl_net_socket_ssl_verify_mode_set(output, ssl_verify ? EFL_NET_SSL_VERIFY_MODE_REQUIRED : EFL_NET_SSL_VERIFY_MODE_NONE); + efl_net_socket_ssl_hostname_verify_set(output, ssl_verify); + + err = efl_net_dialer_dial(output, address); + if (err) + { + fprintf(stderr, "ERROR: could not SSL dial %s: %s\n", + address, eina_error_msg_get(err)); + goto end_output; + } + } else { /* regular file, open with flags: write-only, close-on-exec, diff --git a/src/examples/ecore/efl_net_server_example.c b/src/examples/ecore/efl_net_server_example.c index 98f1615da4..bc9eecf16f 100644 --- a/src/examples/ecore/efl_net_server_example.c +++ b/src/examples/ecore/efl_net_server_example.c @@ -474,12 +474,21 @@ EFL_CALLBACKS_ARRAY_DEFINE(server_cbs, static const char * protocols[] = { "tcp", "udp", + "ssl", #ifndef _WIN32 "unix", #endif NULL }; +static const char *ciphers_strs[] = { + "auto", + "sslv3", + "tlsv1", + "tlsv1.1", + "tlsv1.2", + NULL +}; static const Ecore_Getopt options = { "efl_net_server_example", /* program name */ @@ -518,6 +527,13 @@ static const Ecore_Getopt options = { "Disable multicast loopback."), ECORE_GETOPT_APPEND('M', "udp-multicast-group", "Join a multicast group in the form 'IP@INTERFACE', with optional '@INTERFACE', where INTERFACE is the IP address of the interface to join the multicast.", ECORE_GETOPT_TYPE_STR), + ECORE_GETOPT_CATEGORY("ssl", "SSL options"), + ECORE_GETOPT_CHOICE('c', "ssl-cipher", "Cipher to use, defaults to 'auto'", ciphers_strs), + ECORE_GETOPT_APPEND(0, "ssl-certificate", "certificate path to use.", ECORE_GETOPT_TYPE_STR), + ECORE_GETOPT_APPEND(0, "ssl-private-key", "private key path to use.", ECORE_GETOPT_TYPE_STR), + ECORE_GETOPT_APPEND(0, "ssl-crl", "certificate revogation list to use.", ECORE_GETOPT_TYPE_STR), + ECORE_GETOPT_APPEND(0, "ssl-ca", "certificate authorities path to use.", ECORE_GETOPT_TYPE_STR), + ECORE_GETOPT_CHOICE_METAVAR(0, NULL, "The server protocol.", "protocol", protocols), ECORE_GETOPT_STORE_METAVAR_STR(0, NULL, @@ -543,6 +559,11 @@ main(int argc, char **argv) Eina_Bool ipv6_only = EINA_TRUE; Eina_Bool udp_dont_route = EINA_FALSE; Eina_Bool udp_mcast_loopback = EINA_TRUE; + Eina_List *certificates = NULL; + Eina_List *private_keys = NULL; + Eina_List *crls = NULL; + Eina_List *cas = NULL; + char *cipher_choice = NULL; Eina_Bool quit_option = EINA_FALSE; Ecore_Getopt_Value values[] = { ECORE_GETOPT_VALUE_BOOL(echo), @@ -563,6 +584,13 @@ main(int argc, char **argv) ECORE_GETOPT_VALUE_BOOL(udp_mcast_loopback), ECORE_GETOPT_VALUE_LIST(udp_mcast_groups), + ECORE_GETOPT_VALUE_BOOL(quit_option), /* category: ssl */ + ECORE_GETOPT_VALUE_STR(cipher_choice), + ECORE_GETOPT_VALUE_LIST(certificates), + ECORE_GETOPT_VALUE_LIST(private_keys), + ECORE_GETOPT_VALUE_LIST(crls), + ECORE_GETOPT_VALUE_LIST(cas), + /* positional argument */ ECORE_GETOPT_VALUE_STR(protocol), ECORE_GETOPT_VALUE_STR(address), @@ -603,6 +631,7 @@ main(int argc, char **argv) if (strcmp(protocol, "tcp") == 0) cls = EFL_NET_SERVER_TCP_CLASS; else if (strcmp(protocol, "udp") == 0) cls = EFL_NET_SERVER_UDP_CLASS; + else if (strcmp(protocol, "ssl") == 0) cls = EFL_NET_SERVER_SSL_CLASS; #ifndef _WIN32 else if (strcmp(protocol, "unix") == 0) cls = EFL_NET_SERVER_UNIX_CLASS; #endif @@ -642,6 +671,33 @@ main(int argc, char **argv) EINA_LIST_FOREACH(udp_mcast_groups, lst, str) efl_net_server_udp_multicast_join(server, str); } + else if (cls == EFL_NET_SERVER_SSL_CLASS) + { + Eo *ssl_ctx; + Efl_Net_Ssl_Cipher cipher = EFL_NET_SSL_CIPHER_AUTO; + if (cipher_choice) + { + if (strcmp(cipher_choice, "auto") == 0) + cipher = EFL_NET_SSL_CIPHER_AUTO; + else if (strcmp(cipher_choice, "sslv3") == 0) + cipher = EFL_NET_SSL_CIPHER_SSLV3; + else if (strcmp(cipher_choice, "tlsv1") == 0) + cipher = EFL_NET_SSL_CIPHER_TLSV1; + else if (strcmp(cipher_choice, "tlsv1.1") == 0) + cipher = EFL_NET_SSL_CIPHER_TLSV1_1; + else if (strcmp(cipher_choice, "tlsv1.2") == 0) + cipher = EFL_NET_SSL_CIPHER_TLSV1_2; + } + + ssl_ctx = efl_add(EFL_NET_SSL_CONTEXT_CLASS, NULL, + efl_net_ssl_context_certificates_set(efl_added, eina_list_iterator_new(certificates)), + efl_net_ssl_context_private_keys_set(efl_added, eina_list_iterator_new(private_keys)), + efl_net_ssl_context_certificate_revogation_lists_set(efl_added, eina_list_iterator_new(crls)), + efl_net_ssl_context_certificate_authorities_set(efl_added, eina_list_iterator_new(cas)), + efl_net_ssl_context_setup(efl_added, cipher, EINA_FALSE /* a server! */)); + + efl_net_server_ssl_context_set(server, ssl_ctx); + } #ifndef _WIN32 else if (cls == EFL_NET_SERVER_UNIX_CLASS) { diff --git a/src/lib/ecore_con/Ecore_Con_Eo.h b/src/lib/ecore_con/Ecore_Con_Eo.h index 8e4cf6508b..aaae290640 100644 --- a/src/lib/ecore_con/Ecore_Con_Eo.h +++ b/src/lib/ecore_con/Ecore_Con_Eo.h @@ -37,3 +37,5 @@ #include "efl_net_ssl_context.eo.h" #include "efl_net_socket_ssl.eo.h" +#include "efl_net_dialer_ssl.eo.h" +#include "efl_net_server_ssl.eo.h" diff --git a/src/lib/ecore_con/efl_net_dialer_ssl.c b/src/lib/ecore_con/efl_net_dialer_ssl.c new file mode 100644 index 0000000000..e868374a6c --- /dev/null +++ b/src/lib/ecore_con/efl_net_dialer_ssl.c @@ -0,0 +1,243 @@ +#define EFL_NET_DIALER_SSL_PROTECTED 1 +#define EFL_NET_DIALER_PROTECTED 1 +#define EFL_NET_SOCKET_PROTECTED 1 +#define EFL_IO_READER_PROTECTED 1 + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "Ecore.h" +#include "Ecore_Con.h" +#include "ecore_con_private.h" + +#ifdef HAVE_SYS_SOCKET_H +# include +#endif +#ifdef HAVE_NETINET_SSL_H +# include +#endif +#ifdef HAVE_NETINET_IN_H +# include +#endif +#ifdef HAVE_ARPA_INET_H +# include +#endif +#ifdef HAVE_EVIL +# include +#endif + +#define MY_CLASS EFL_NET_DIALER_SSL_CLASS + +typedef struct _Efl_Net_Dialer_Ssl_Data +{ + Eo *sock; + Eo *ssl_ctx; + Efl_Future *connect_timeout; + Eina_Bool connected; +} Efl_Net_Dialer_Ssl_Data; + +static void +_efl_net_dialer_ssl_ready(void *data EINA_UNUSED, const Efl_Event *event) +{ + Eo *o = event->object; + efl_net_dialer_connected_set(o, EINA_TRUE); +} + +static void +_efl_net_dialer_ssl_error(void *data EINA_UNUSED, const Efl_Event *event) +{ + Eo *o = event->object; + Eina_Error *perr = event->info; + efl_event_callback_call(o, EFL_NET_DIALER_EVENT_ERROR, perr); +} + +EFL_CALLBACKS_ARRAY_DEFINE(_efl_net_dialer_ssl_cbs, + { EFL_NET_SOCKET_SSL_EVENT_SSL_READY, _efl_net_dialer_ssl_ready }, + { EFL_NET_SOCKET_SSL_EVENT_SSL_ERROR, _efl_net_dialer_ssl_error }); + +EOLIAN static Eo* +_efl_net_dialer_ssl_efl_object_constructor(Eo *o, Efl_Net_Dialer_Ssl_Data *pd) +{ + o = efl_constructor(efl_super(o, MY_CLASS)); + if (!o) return NULL; + + pd->sock = efl_add(EFL_NET_DIALER_TCP_CLASS, o); + EINA_SAFETY_ON_NULL_RETURN_VAL(pd->sock, NULL); + + efl_event_callback_array_add(o, _efl_net_dialer_ssl_cbs(), o); + + return o; +} + +EOLIAN static Eo* +_efl_net_dialer_ssl_efl_object_finalize(Eo *o, Efl_Net_Dialer_Ssl_Data *pd) +{ + if (!pd->ssl_ctx) + pd->ssl_ctx = efl_ref(efl_net_ssl_context_default_dialer_get(EFL_NET_SSL_CONTEXT_CLASS)); + + efl_net_socket_ssl_adopt(o, pd->sock, pd->ssl_ctx); + return efl_finalize(efl_super(o, MY_CLASS)); +} + +EOLIAN static void +_efl_net_dialer_ssl_efl_object_destructor(Eo *o, Efl_Net_Dialer_Ssl_Data *pd) +{ + if (efl_io_closer_close_on_destructor_get(o) && + (!efl_io_closer_closed_get(o))) + efl_io_closer_close(o); + + if (pd->sock) + { + efl_del(pd->sock); + pd->sock = NULL; + } + + if (pd->ssl_ctx) + { + efl_unref(pd->ssl_ctx); + pd->ssl_ctx = NULL; + } + + efl_destructor(efl_super(o, MY_CLASS)); +} + +EOLIAN static void +_efl_net_dialer_ssl_ssl_context_set(Eo *o, Efl_Net_Dialer_Ssl_Data *pd, Eo *ssl_ctx) +{ + EINA_SAFETY_ON_TRUE_RETURN(efl_finalized_get(o)); + EINA_SAFETY_ON_FALSE_RETURN(efl_isa(ssl_ctx, EFL_NET_SSL_CONTEXT_CLASS)); + + if (pd->ssl_ctx == ssl_ctx) return; + efl_unref(pd->ssl_ctx); + pd->ssl_ctx = efl_ref(ssl_ctx); +} + +EOLIAN static Eo * +_efl_net_dialer_ssl_ssl_context_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Ssl_Data *pd) +{ + return pd->ssl_ctx; +} + +static void +_efl_net_dialer_ssl_connect_timeout(void *data, const Efl_Event *ev EINA_UNUSED) +{ + Eo *o = data; + Eina_Error err = ETIMEDOUT; + + efl_ref(o); + efl_io_reader_eos_set(o, EINA_TRUE); + efl_event_callback_call(o, EFL_NET_DIALER_EVENT_ERROR, &err); + efl_unref(o); +} + +EOLIAN static Eina_Error +_efl_net_dialer_ssl_efl_net_dialer_dial(Eo *o, Efl_Net_Dialer_Ssl_Data *pd, const char *address) +{ + double timeout; + + EINA_SAFETY_ON_NULL_RETURN_VAL(address, EINVAL); + EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_net_dialer_connected_get(o), EISCONN); + EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_io_closer_closed_get(o), EBADF); + + if (pd->connect_timeout) + efl_future_cancel(pd->connect_timeout); + + timeout = efl_net_dialer_timeout_dial_get(pd->sock); + if (timeout > 0.0) + { + efl_future_use(&pd->connect_timeout, efl_loop_timeout(efl_loop_get(o), timeout, o)); + efl_future_then(pd->connect_timeout, _efl_net_dialer_ssl_connect_timeout, NULL, NULL, o); + efl_future_link(o, pd->connect_timeout); + } + + return efl_net_dialer_dial(pd->sock, address); +} + +EOLIAN static const char * +_efl_net_dialer_ssl_efl_net_dialer_address_dial_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Ssl_Data *pd) +{ + return efl_net_dialer_address_dial_get(pd->sock); +} + +EOLIAN static void +_efl_net_dialer_ssl_efl_net_dialer_proxy_set(Eo *o EINA_UNUSED, Efl_Net_Dialer_Ssl_Data *pd, const char *proxy_url) +{ + efl_net_dialer_proxy_set(pd->sock, proxy_url); +} + +EOLIAN static const char * +_efl_net_dialer_ssl_efl_net_dialer_proxy_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Ssl_Data *pd) +{ + return efl_net_dialer_proxy_get(pd->sock); +} + +EOLIAN static void +_efl_net_dialer_ssl_efl_net_dialer_timeout_dial_set(Eo *o EINA_UNUSED, Efl_Net_Dialer_Ssl_Data *pd, double seconds) +{ + efl_net_dialer_timeout_dial_set(pd->sock, seconds); + + if (pd->connect_timeout) + efl_future_cancel(pd->connect_timeout); + + if ((seconds > 0.0) && (!pd->connected)) + { + efl_future_use(&pd->connect_timeout, efl_loop_timeout(efl_loop_get(o), seconds, o)); + efl_future_then(pd->connect_timeout, _efl_net_dialer_ssl_connect_timeout, NULL, NULL, o); + } +} + +EOLIAN static double +_efl_net_dialer_ssl_efl_net_dialer_timeout_dial_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Ssl_Data *pd) +{ + return efl_net_dialer_timeout_dial_get(pd->sock); +} + +EOLIAN static void +_efl_net_dialer_ssl_efl_net_dialer_connected_set(Eo *o, Efl_Net_Dialer_Ssl_Data *pd, Eina_Bool connected) +{ + if (pd->connect_timeout) + efl_future_cancel(pd->connect_timeout); + if (pd->connected == connected) return; + pd->connected = connected; + if (connected) efl_event_callback_call(o, EFL_NET_DIALER_EVENT_CONNECTED, NULL); +} + +EOLIAN static Eina_Bool +_efl_net_dialer_ssl_efl_net_dialer_connected_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Ssl_Data *pd) +{ + return pd->connected; +} + +EOLIAN static Eina_Error +_efl_net_dialer_ssl_efl_io_closer_close(Eo *o, Efl_Net_Dialer_Ssl_Data *pd EINA_UNUSED) +{ + efl_net_dialer_connected_set(o, EINA_FALSE); + return efl_io_closer_close(efl_super(o, MY_CLASS)); +} + +EOLIAN static Eina_Bool +_efl_net_dialer_ssl_keep_alive_set(Eo *o EINA_UNUSED, Efl_Net_Dialer_Ssl_Data *pd, Eina_Bool keep_alive) +{ + return efl_net_dialer_ssl_keep_alive_set(pd->sock, keep_alive); +} + +EOLIAN static Eina_Bool +_efl_net_dialer_ssl_keep_alive_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Ssl_Data *pd) +{ + return efl_net_dialer_ssl_keep_alive_get(pd->sock); +} + +EOLIAN static Eina_Bool +_efl_net_dialer_ssl_no_delay_set(Eo *o EINA_UNUSED, Efl_Net_Dialer_Ssl_Data *pd, Eina_Bool no_delay) +{ + return efl_net_dialer_ssl_no_delay_set(pd->sock, no_delay); +} + +EOLIAN static Eina_Bool +_efl_net_dialer_ssl_no_delay_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Ssl_Data *pd) +{ + return efl_net_socket_tcp_no_delay_get(pd->sock); +} + +#include "efl_net_dialer_ssl.eo.c" diff --git a/src/lib/ecore_con/efl_net_dialer_ssl.eo b/src/lib/ecore_con/efl_net_dialer_ssl.eo new file mode 100644 index 0000000000..2660c93424 --- /dev/null +++ b/src/lib/ecore_con/efl_net_dialer_ssl.eo @@ -0,0 +1,78 @@ +class Efl.Net.Dialer.Ssl (Efl.Net.Socket.Ssl, Efl.Net.Dialer) { + [[Connects to a remote SSL server using TCP. + + This creates an internal @Efl.Net.Dialer.Tcp and once connected + it will start the SSL handshake. + + If the proxy is NULL (default), then the system proxy will be + used. On UNIX that's the environment variable $socks_proxy (or + '$proxy' or '$all_proxy') is used if the given address doesn't + match $no_proxy patterns. To disable proxy use an empty string. + + If the proxy is NULL (default), then the system proxy will be + used. On UNIX that's the environment variable $socks_proxy (or + '$all_proxy') is used if the given address doesn't match + $no_proxy patterns. To disable proxy use an empty string. If + provided proxy must be one of the protocols: + + - socks5://username:password\@proxyserver:port (SOCKSv5) + - socks5h://username\@proxyserver:port (let socks server to resolve domain) + - socks5://proxyserver:port + - socks5://proxyserver (default port 1080) + - socks4a://proxyserver:port (SOCKSv4 and let socks server to resolve domain) + - socks4://proxyserver:port (SOCKSv4) + + @since 1.19 + ]] + + methods { + @property ssl_context { + [[Defines the SSL context to use for this dialer. + + This specifies a shared context for all clients, with + certificate, private keys, CRL, CAs... + + It must be called before @Efl.Object.finalize! + ]] + values { + ssl_context: Efl.Net.Ssl.Context; + } + } + + @property keep_alive { + [[Controls keep-alive using SO_KEEPALIVE]] + get { } + set { + return: bool (false); [[$true on success]] + } + values { + keep_alive: bool; + } + } + + @property no_delay { + [[Controls TCP's no-delay using TCP_NODELAY]] + get { } + set { + return: bool (false); [[$true on success]] + } + values { + no_delay: bool; + } + } + + /* Note: no cork as it doesn't make sense here for the wrapped connection */ + } + + implements { + Efl.Object.constructor; + Efl.Object.destructor; + Efl.Object.finalize; + Efl.Net.Dialer.dial; + Efl.Net.Dialer.address_dial.get; + Efl.Net.Dialer.connected; + Efl.Net.Dialer.proxy; + Efl.Net.Dialer.timeout_dial; + Efl.Io.Closer.close; + } +} diff --git a/src/lib/ecore_con/efl_net_server_ssl.c b/src/lib/ecore_con/efl_net_server_ssl.c new file mode 100644 index 0000000000..e07ea5918b --- /dev/null +++ b/src/lib/ecore_con/efl_net_server_ssl.c @@ -0,0 +1,211 @@ +#define EFL_NET_SERVER_SSL_PROTECTED 1 +#define EFL_NET_SERVER_FD_PROTECTED 1 +#define EFL_NET_SERVER_PROTECTED 1 +#define EFL_LOOP_FD_PROTECTED 1 + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "Ecore.h" +#include "Ecore_Con.h" +#include "ecore_con_private.h" + +#define MY_CLASS EFL_NET_SERVER_SSL_CLASS + +typedef struct _Efl_Net_Server_Ssl_Data +{ + Eo *server; + Eo *ssl_ctx; +} Efl_Net_Server_Ssl_Data; + +static void +_efl_net_server_ssl_tcp_client_add(void *data, const Efl_Event *event) +{ + Eo *o = data; + Efl_Net_Server_Ssl_Data *pd = efl_data_scope_get(o, MY_CLASS); + Eo *client_ssl; + Eo *client_tcp = event->info; + const char *addr = efl_net_socket_address_remote_get(client_tcp); + + if (!pd->ssl_ctx) + { + ERR("ssl server %p rejecting client %p '%s' since no SSL context was set!", o, client_tcp, addr); + efl_event_callback_call(o, EFL_NET_SERVER_EVENT_CLIENT_REJECTED, (void *)addr); + return; + } + + client_ssl = efl_add(EFL_NET_SOCKET_SSL_CLASS, o, + efl_net_socket_ssl_adopt(efl_added, client_tcp, pd->ssl_ctx)); + if (!client_ssl) + { + ERR("ssl server %p could not wrap client %p '%s' using context=%p", o, client_tcp, addr, pd->ssl_ctx); + efl_event_callback_call(o, EFL_NET_SERVER_EVENT_CLIENT_REJECTED, (void *)addr); + return; + } + + efl_event_callback_call(o, EFL_NET_SERVER_EVENT_CLIENT_ADD, client_ssl); +} + +static void +_efl_net_server_ssl_tcp_client_rejected(void *data, const Efl_Event *event) +{ + Eo *o = data; + char *client = event->info; + efl_event_callback_call(o, EFL_NET_SERVER_EVENT_CLIENT_REJECTED, client); +} + +static void +_efl_net_server_ssl_tcp_error(void *data, const Efl_Event *event) +{ + Eo *o = data; + Eina_Error *perr = event->info; + efl_event_callback_call(o, EFL_NET_SERVER_EVENT_ERROR, perr); +} + +static void +_efl_net_server_ssl_tcp_serving(void *data, const Efl_Event *event EINA_UNUSED) +{ + Eo *o = data; + efl_event_callback_call(o, EFL_NET_SERVER_EVENT_SERVING, NULL); +} + +EFL_CALLBACKS_ARRAY_DEFINE(_efl_net_server_ssl_tcp_cbs, + { EFL_NET_SERVER_EVENT_CLIENT_ADD, _efl_net_server_ssl_tcp_client_add }, + { EFL_NET_SERVER_EVENT_CLIENT_REJECTED, _efl_net_server_ssl_tcp_client_rejected }, + { EFL_NET_SERVER_EVENT_ERROR, _efl_net_server_ssl_tcp_error }, + { EFL_NET_SERVER_EVENT_SERVING, _efl_net_server_ssl_tcp_serving }); + +EOLIAN Efl_Object * +_efl_net_server_ssl_efl_object_constructor(Eo *o, Efl_Net_Server_Ssl_Data *pd) +{ + o = efl_constructor(efl_super(o, MY_CLASS)); + if (!o) return NULL; + + pd->server = efl_add(EFL_NET_SERVER_TCP_CLASS, o, + efl_event_callback_array_add(efl_added, _efl_net_server_ssl_tcp_cbs(), o)); + EINA_SAFETY_ON_NULL_RETURN_VAL(pd->server, NULL); + + return o; +} + +EOLIAN void +_efl_net_server_ssl_efl_object_destructor(Eo *o, Efl_Net_Server_Ssl_Data *pd) +{ + if (pd->server) + { + efl_del(pd->server); + pd->server = NULL; + } + + efl_destructor(efl_super(o, MY_CLASS)); +} + +EOLIAN static void +_efl_net_server_ssl_ssl_context_set(Eo *o EINA_UNUSED, Efl_Net_Server_Ssl_Data *pd, Eo *ssl_ctx) +{ + EINA_SAFETY_ON_FALSE_RETURN(efl_isa(ssl_ctx, EFL_NET_SSL_CONTEXT_CLASS)); + + if (pd->ssl_ctx == ssl_ctx) return; + efl_unref(pd->ssl_ctx); + pd->ssl_ctx = efl_ref(ssl_ctx); +} + +EOLIAN static Eo * +_efl_net_server_ssl_ssl_context_get(Eo *o EINA_UNUSED, Efl_Net_Server_Ssl_Data *pd) +{ + return pd->ssl_ctx; +} + +EOLIAN static Eina_Error +_efl_net_server_ssl_efl_net_server_serve(Eo *o EINA_UNUSED, Efl_Net_Server_Ssl_Data *pd, const char *address) +{ + return efl_net_server_serve(pd->server, address); +} + +EOLIAN static const char * +_efl_net_server_ssl_efl_net_server_address_get(Eo *o EINA_UNUSED, Efl_Net_Server_Ssl_Data *pd) +{ + return efl_net_server_address_get(pd->server); +} + +EOLIAN static unsigned int +_efl_net_server_ssl_efl_net_server_clients_count_get(Eo *o EINA_UNUSED, Efl_Net_Server_Ssl_Data *pd) +{ + return efl_net_server_clients_count_get(pd->server); +} + +EOLIAN static void +_efl_net_server_ssl_efl_net_server_clients_limit_set(Eo *o EINA_UNUSED, Efl_Net_Server_Ssl_Data *pd, unsigned int limit, Eina_Bool reject_excess) +{ + efl_net_server_clients_limit_set(pd->server, limit, reject_excess); +} + +EOLIAN static void +_efl_net_server_ssl_efl_net_server_clients_limit_get(Eo *o EINA_UNUSED, Efl_Net_Server_Ssl_Data *pd, unsigned int *limit, Eina_Bool *reject_excess) +{ + efl_net_server_clients_limit_get(pd->server, limit, reject_excess); +} + +EOLIAN static Eina_Bool +_efl_net_server_ssl_efl_net_server_serving_get(Eo *o EINA_UNUSED, Efl_Net_Server_Ssl_Data *pd) +{ + return efl_net_server_serving_get(pd->server); +} + +EOLIAN int +_efl_net_server_ssl_family_get(Eo *o EINA_UNUSED, Efl_Net_Server_Ssl_Data *pd) +{ + return efl_net_server_fd_family_get(pd->server); +} + +EOLIAN Eina_Bool +_efl_net_server_ssl_close_on_exec_set(Eo *o EINA_UNUSED, Efl_Net_Server_Ssl_Data *pd, Eina_Bool close_on_exec) +{ + return efl_net_server_fd_close_on_exec_set(pd->server, close_on_exec); +} + +EOLIAN Eina_Bool +_efl_net_server_ssl_close_on_exec_get(Eo *o EINA_UNUSED, Efl_Net_Server_Ssl_Data *pd) +{ + return efl_net_server_fd_close_on_exec_get(pd->server); +} + +EOLIAN Eina_Bool +_efl_net_server_ssl_reuse_address_set(Eo *o EINA_UNUSED, Efl_Net_Server_Ssl_Data *pd, Eina_Bool reuse_address) +{ + return efl_net_server_fd_reuse_address_set(pd->server, reuse_address); +} + +EOLIAN Eina_Bool +_efl_net_server_ssl_reuse_address_get(Eo *o EINA_UNUSED, Efl_Net_Server_Ssl_Data *pd) +{ + return efl_net_server_fd_reuse_address_get(pd->server); +} + +EOLIAN Eina_Bool +_efl_net_server_ssl_reuse_port_set(Eo *o EINA_UNUSED, Efl_Net_Server_Ssl_Data *pd, Eina_Bool reuse_port) +{ + return efl_net_server_fd_reuse_port_set(pd->server, reuse_port); +} + +EOLIAN Eina_Bool +_efl_net_server_ssl_reuse_port_get(Eo *o EINA_UNUSED, Efl_Net_Server_Ssl_Data *pd) +{ + return efl_net_server_fd_reuse_port_get(pd->server); +} + +EOLIAN void +_efl_net_server_ssl_ipv6_only_set(Eo *o EINA_UNUSED, Efl_Net_Server_Ssl_Data *pd, Eina_Bool ipv6_only) +{ + efl_net_server_tcp_ipv6_only_set(pd->server, ipv6_only); +} + +EOLIAN Eina_Bool +_efl_net_server_ssl_ipv6_only_get(Eo *o EINA_UNUSED, Efl_Net_Server_Ssl_Data *pd) +{ + return efl_net_server_tcp_ipv6_only_get(pd->server); +} + + +#include "efl_net_server_ssl.eo.c" diff --git a/src/lib/ecore_con/efl_net_server_ssl.eo b/src/lib/ecore_con/efl_net_server_ssl.eo new file mode 100644 index 0000000000..000a86ec53 --- /dev/null +++ b/src/lib/ecore_con/efl_net_server_ssl.eo @@ -0,0 +1,111 @@ +class Efl.Net.Server.Ssl (Efl.Loop_User, Efl.Net.Server) { + [[A SSL server over TCP. + + @since 1.19 + ]] + + methods { + @property ssl_context { + [[Defines the SSL context to use for this server. + + This specifies a shared context for all clients, with + certificate, private keys, CRL, CAs... + + If changed in runtime, it will only affect new clients. + ]] + values { + ssl_context: Efl.Net.Ssl.Context; + } + } + + @property family { + [[The address family (AF_*) family of this socket. + + It will be one of AF_INET (IPv4), AF_INET6 (IPv6), + AF_UNIX... + + It must be set before the @Efl.Loop.Fd.fd.set is called + with a valid file descriptor. + ]] + get { } + values { + family: int; + } + } + + @property close_on_exec { + [[Controls Close-on-Exec() using FD_CLOEXEC. + + Children socket will inherit the server's setting by + default. One can change the behavior using each instance + @Efl.Io.Closer.close_on_exec.set. + ]] + get { } + set { + return: bool (false); [[$true on success]] + } + values { + close_on_exec: bool; + } + } + + @property reuse_address { + [[Controls address reuse() using SO_REUSEADDR]] + get { } + set { + return: bool (false); [[$true on success]] + } + values { + reuse_address: bool; + } + } + + @property reuse_port { + [[Controls port reuse() using SO_REUSEPORT (since linux 3.9)]] + get { } + set { + return: bool (false); [[$true on success]] + } + values { + reuse_port: bool; + } + } + + @property ipv6_only { + [[Whenever IPv6 listen address will accept only same-family clients or will allow IPv4 to connect as well. + + Since Linux 2.4.21, Windows Vista and MacOS X these + control whenever a server that did bind to an IPv6 + address will accept only IPv6 clients or will also + accept IPv4 by automatically converting them in an IPv6 + address, allowing a single socket to handle both + protocols. + + If an IPv6 address was used in @Efl.Net.Server.address, + this property is $false and an IPv4 connects, then an + address such as [::ffff:IPv4]:PORT will be used, such as + [::ffff:192.168.0.2]:1234, where the IPv4 address can be + extracted. + + If an IPv4 address was used in @Efl.Net.Server.address, + this has no effect. + + Systems can configure their default value, usually true + (allows only IPv6 clients). + ]] + values { + ipv6_only: bool; + } + } + } + + implements { + Efl.Object.constructor; + Efl.Object.destructor; + Efl.Net.Server.serve; + Efl.Net.Server.address.get; + Efl.Net.Server.clients_count.get; + Efl.Net.Server.clients_limit; + Efl.Net.Server.serving.get; + } +} diff --git a/src/lib/ecore_con/efl_net_socket_ssl.eo b/src/lib/ecore_con/efl_net_socket_ssl.eo index 0dd60e158a..71b43c4849 100644 --- a/src/lib/ecore_con/efl_net_socket_ssl.eo +++ b/src/lib/ecore_con/efl_net_socket_ssl.eo @@ -1,7 +1,7 @@ var Efl.Net.Socket.Ssl.Error.HANDSHAKE: Eina.Error; [[Failed SSL handshake]] var Efl.Net.Socket.Ssl.Error.CERTIFICATE_VERIFY_FAILED: Eina.Error; [[Failed to verify peer's certificate]] -class Efl.Net.Socket.Ssl (Efl.Object, Efl.Net.Socket) { +class Efl.Net.Socket.Ssl (Efl.Loop_User, Efl.Net.Socket) { [[A wrapper socket doing SSL (Secure Sockets Layer). Use this wrapper around an existing socket to do secure