efl_net_{server,dialer}_ssl: TCP + SSL easy to use.

in the previous commit we're manually upgrading an existing TCP socket
to SSL. It is desired since some protocols need to negotiate, like
STARTTLS and the likes

Now we offer 2 classes that does autostart SSL once the socket is
ready.
This commit is contained in:
Gustavo Sverzut Barbieri 2016-11-01 01:31:56 -02:00
parent 5e8dd491a5
commit a5ebf67a83
9 changed files with 776 additions and 2 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -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 <config.h>
#endif
#include "Ecore.h"
#include "Ecore_Con.h"
#include "ecore_con_private.h"
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_SSL_H
# include <netinet/ssl.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
#ifdef HAVE_EVIL
# include <Evil.h>
#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"

View File

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

View File

@ -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 <config.h>
#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"

View File

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

View File

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