examples/examples/c/ecore/efl_net_server_example.c

766 lines
28 KiB
C

#include <Efl_Net.h>
#include <Ecore_Getopt.h>
#include <fcntl.h>
static Eina_Bool echo = EINA_FALSE;
static double timeout = 10.0;
/* NOTE: client i/o events are only used as debug, you can omit these */
static void
_client_can_read_changed(void *data EINA_UNUSED, const Efl_Event *event)
{
fprintf(stderr, "INFO: client %s can_read=%d\n",
efl_net_socket_address_remote_get(event->object),
efl_io_reader_can_read_get(event->object));
}
static void
_client_can_write_changed(void *data EINA_UNUSED, const Efl_Event *event)
{
fprintf(stderr, "INFO: client %s can_write=%d\n",
efl_net_socket_address_remote_get(event->object),
efl_io_writer_can_write_get(event->object));
}
static void
_client_eos(void *data EINA_UNUSED, const Efl_Event *event)
{
fprintf(stderr, "INFO: client %s eos.\n",
efl_net_socket_address_remote_get(event->object));
}
static void
_client_closed(void *data EINA_UNUSED, const Efl_Event *event)
{
fprintf(stderr, "INFO: client %s closed.\n",
efl_net_socket_address_remote_get(event->object));
}
EFL_CALLBACKS_ARRAY_DEFINE(client_cbs,
{ EFL_IO_READER_EVENT_CAN_READ_CHANGED, _client_can_read_changed },
{ EFL_IO_READER_EVENT_EOS, _client_eos },
{ EFL_IO_WRITER_EVENT_CAN_WRITE_CHANGED, _client_can_write_changed },
{ EFL_IO_CLOSER_EVENT_CLOSED, _client_closed });
/* copier events are of interest, you should hook to at least "done"
* and "error"
*/
/* echo copier is about the same socket, you can close it right away */
static void
_echo_copier_done(void *data EINA_UNUSED, const Efl_Event *event)
{
Eo *copier = event->object;
fprintf(stderr, "INFO: echo copier done, close and del %p\n", copier);
efl_del(copier); /* set to close_on_destructor, will auto close copier and client */
}
static void
_echo_copier_error(void *data EINA_UNUSED, const Efl_Event *event)
{
Eo *copier = event->object;
const Eina_Error *perr = event->info;
if (*perr == ETIMEDOUT)
{
Eo *client = efl_io_copier_source_get(copier);
fprintf(stderr, "INFO: client '%s' timed out, delete it.\n",
efl_net_socket_address_remote_get(client));
efl_del(copier);
return;
}
efl_loop_quit(efl_loop_get(event->object), eina_value_int_init(EXIT_FAILURE));
fprintf(stderr, "ERROR: echo copier %p failed %d '%s', close and del.\n",
copier, *perr, eina_error_msg_get(*perr));
efl_del(copier);
}
EFL_CALLBACKS_ARRAY_DEFINE(echo_copier_cbs,
{ EFL_IO_COPIER_EVENT_DONE, _echo_copier_done },
{ EFL_IO_COPIER_EVENT_ERROR, _echo_copier_error});
/* When sender and receiver peers are about different entities, you
* can only close when both are done, otherwise the socket will be
* prematurely closed.
*
* Here we use a struct with both copiers and NULL them once they are
* done, when both are done we close the socket and free the struct.
*/
typedef struct {
Eo *send_copier;
Eo *recv_copier;
Eo *client;
} Send_Recv_Data;
static Send_Recv_Data *
_send_recv_new(Eo *client)
{
Send_Recv_Data *d = calloc(1, sizeof(Send_Recv_Data));
if (!d) return NULL;
/* take a reference since copiers will only hold their reference
* while they are alive. As we're deleting them before calling
* efl_io_closer_close(), then we need it for bit longer.
*/
d->client = efl_ref(client);
return d;
}
static void
_send_recv_free(Send_Recv_Data *d)
{
efl_unref(d->client);
free(d);
}
static void
_send_recv_done(Send_Recv_Data *d, Eo *copier)
{
if (d->send_copier == copier) d->send_copier = NULL;
else d->recv_copier = NULL;
efl_del(copier);
if (d->send_copier || d->recv_copier) return;
efl_io_closer_close(d->client); /* manually close once both copiers are done */
_send_recv_free(d);
}
static void
_send_copier_done(void *data, const Efl_Event *event)
{
Eo *copier = event->object;
Eo *buffer = efl_io_copier_source_get(copier);
Eo *client = efl_io_copier_destination_get(copier);
Send_Recv_Data *d = data;
Eina_Slice slice = efl_io_buffer_slice_get(buffer);
/* show what we sent, just for debug */
fprintf(stderr,
"INFO: sent to %s %zd bytes:"
"\n--BEGIN SENT DATA--\n"
EINA_SLICE_STR_FMT
"\n--END SENT DATA--\n",
efl_net_socket_address_remote_get(client),
slice.len, EINA_SLICE_STR_PRINT(slice));
if (d->recv_copier)
{
/* only start the reader inactivity timeout once the sender is done */
efl_io_copier_timeout_inactivity_set(d->recv_copier, efl_io_copier_timeout_inactivity_get(copier));
}
fprintf(stderr, "INFO: send copier done, check if should close %p\n", copier);
_send_recv_done(d, copier);
}
static void
_send_copier_error(void *data, const Efl_Event *event)
{
Eo *copier = event->object;
Eo *buffer = efl_io_copier_source_get(copier);
Eo *client = efl_io_copier_destination_get(copier);
const Eina_Error *perr = event->info;
Send_Recv_Data *d = data;
uint64_t offset;
Eina_Slice slice, remaining;
if (*perr == ETIMEDOUT)
{
fprintf(stderr, "INFO: client '%s' timed out send, delete it.\n",
efl_net_socket_address_remote_get(client));
efl_io_closer_close(copier); /* forces client to be closed, thus closes the recv copier as an effect */
return;
}
efl_loop_quit(efl_loop_get(event->object), eina_value_int_init(EXIT_FAILURE));
offset = efl_io_buffer_position_read_get(buffer);
slice = efl_io_buffer_slice_get(buffer);
remaining = slice;
remaining.bytes += offset;
remaining.len -= offset;
slice.len = offset;
fprintf(stderr,
"ERROR: sent to %s only %zd bytes:"
"\n--BEGIN SENT DATA--\n"
EINA_SLICE_STR_FMT
"\n--END SENT DATA--\n"
"Remaining %zd bytes:"
"\n--BEGIN REMAINING DATA--\n"
EINA_SLICE_STR_FMT
"\n--END REMAINING DATA--\n",
efl_net_socket_address_remote_get(client),
slice.len, EINA_SLICE_STR_PRINT(slice),
remaining.len, EINA_SLICE_STR_PRINT(remaining));
fprintf(stderr, "ERROR: send copier %p failed %d '%s', check if should close..\n",
copier, *perr, eina_error_msg_get(*perr));
_send_recv_done(d, copier);
}
EFL_CALLBACKS_ARRAY_DEFINE(send_copier_cbs,
{ EFL_IO_COPIER_EVENT_DONE, _send_copier_done },
{ EFL_IO_COPIER_EVENT_ERROR, _send_copier_error});
static void
_recv_copier_done(void *data, const Efl_Event *event)
{
Eo *copier = event->object;
Eo *client = efl_io_copier_source_get(copier);
Eo *buffer = efl_io_copier_destination_get(copier);
Send_Recv_Data *d = data;
Eina_Slice slice = efl_io_buffer_slice_get(buffer);
/* show case, you could use a copier to Efl_Io_Stdout, a
* file... and get progressive processing.
*
* Here we're using a memory buffer and printing everything at
* once.
*
* You could also steal the binbuf with
* efl_io_buffer_binbuf_steal()
*/
fprintf(stderr,
"INFO: recv from %s %zd bytes:"
"\n--BEGIN RECV DATA--\n"
EINA_SLICE_STR_FMT "\n"
"\n--END RECV DATA--\n",
efl_net_socket_address_remote_get(client),
slice.len, EINA_SLICE_STR_PRINT(slice));
fprintf(stderr, "INFO: receive copier done, check if should close %p\n", copier);
_send_recv_done(d, copier);
}
static void
_recv_copier_error(void *data, const Efl_Event *event)
{
Eo *copier = event->object;
Eo *buffer = efl_io_copier_destination_get(copier);
Eo *client = efl_io_copier_source_get(copier);
const Eina_Error *perr = event->info;
Send_Recv_Data *d = data;
Eina_Slice slice;
if (*perr == ETIMEDOUT)
{
fprintf(stderr, "INFO: client '%s' timed out recv, delete it.\n",
efl_net_socket_address_remote_get(client));
efl_io_closer_close(copier); /* forces client to be closed, thus closes the send copier as an effect */
return;
}
efl_loop_quit(efl_loop_get(event->object), eina_value_int_init(EXIT_FAILURE));
slice = efl_io_buffer_slice_get(buffer);
fprintf(stderr,
"ERROR: recv to %s only %zd bytes:"
"\n--BEGIN RECV DATA--\n"
EINA_SLICE_STR_FMT "\n"
"\n--END RECV DATA--\n",
efl_net_socket_address_remote_get(client),
slice.len, EINA_SLICE_STR_PRINT(slice));
fprintf(stderr, "ERROR: receive copier %p failed %d '%s', check if should close..\n",
copier, *perr, eina_error_msg_get(*perr));
_send_recv_done(d, copier);
}
EFL_CALLBACKS_ARRAY_DEFINE(recv_copier_cbs,
{ EFL_IO_COPIER_EVENT_DONE, _recv_copier_done },
{ EFL_IO_COPIER_EVENT_ERROR, _recv_copier_error});
/* server events are mandatory, afterall you need to define what's
* going to happen after a client socket is connected. This is the
* "client,add" event.
*
* if clients_limit and clients_reject_excess are set, then
* "client,rejected" is dispatched for rejected sockets, they contain
* the string with socket identification.
*/
static void
_server_client_add(void *data EINA_UNUSED, const Efl_Event *event)
{
Efl_Net_Socket *client = event->info;
fprintf(stderr, "INFO: accepted client %s\n",
efl_net_socket_address_remote_get(client));
/* to use a client, you must efl_ref() it. Here we're not doing it
* explicitly because copiers do take a reference.
*/
/*
* monitor the client socket for debug purposes (optional)
*/
efl_event_callback_array_add(client, client_cbs(), NULL);
/*
* Since sockets are reader/writer/closer objects, we can use the
* Efl_Io_Copier utility.
*/
if (echo)
{
/*
* An echo copier is pretty simple, use the socket as both
* source and destination.
*/
Eo *echo_copier = efl_add(EFL_IO_COPIER_CLASS, efl_parent_get(client),
efl_io_copier_source_set(efl_added, client),
efl_io_copier_destination_set(efl_added, client),
efl_io_copier_timeout_inactivity_set(efl_added, timeout),
efl_event_callback_array_add(efl_added, echo_copier_cbs(), client),
efl_io_closer_close_on_invalidate_set(efl_added, EINA_TRUE) /* we want to auto-close as we have a single copier */
);
fprintf(stderr, "INFO: using an echo copier=%p for client %s\n",
echo_copier, efl_net_socket_address_remote_get(client));
return;
}
else
{
/*
* Here we create a fixed buffer with a string to send:
* - "Hello World!"
* and another one to store the received buffer so we can print as
* a single blob at the end.
*
* One can change these to Efl_Io_File or event pipe to something
* else like Efl_Io_Stdin, Efl_Io_Stdout and it would just work.
*/
static const Eina_Slice hello_world_slice = EINA_SLICE_STR_LITERAL("Hello World!");
Send_Recv_Data *d;
Eo *send_buffer, *recv_buffer;
d = _send_recv_new(client);
if (!d)
{
fprintf(stderr, "ERROR: could not allocate memory\n");
return;
}
send_buffer = efl_add_ref(EFL_IO_BUFFER_CLASS, NULL,
efl_io_buffer_adopt_readonly(efl_added, hello_world_slice));
/* Unlimited buffer to store the received data. */
recv_buffer = efl_add_ref(EFL_IO_BUFFER_CLASS, NULL);
/* an input copier that takes data from send_buffer and pushes to client */
d->send_copier = efl_add(EFL_IO_COPIER_CLASS, efl_parent_get(client),
efl_io_copier_source_set(efl_added, send_buffer),
efl_io_copier_destination_set(efl_added, client),
efl_io_copier_timeout_inactivity_set(efl_added, timeout),
efl_event_callback_array_add(efl_added, send_copier_cbs(), d),
efl_io_closer_close_on_invalidate_set(efl_added, EINA_FALSE) /* we must wait both copiers to finish before we close! */
);
fprintf(stderr, "INFO: using sender buffer %p with copier %p for client %s\n",
send_buffer, d->send_copier, efl_net_socket_address_remote_get(client));
efl_unref(send_buffer); /* d->send_copier adds a reference */
if (!d->send_copier)
fprintf(stderr, "ERROR: failed to create sender copier\n");
/* an output copier that takes data from socket and pushes to recv_buffer. */
d->recv_copier = efl_add(EFL_IO_COPIER_CLASS, efl_parent_get(client),
efl_io_copier_source_set(efl_added, client),
efl_io_copier_destination_set(efl_added, recv_buffer),
efl_io_copier_timeout_inactivity_set(efl_added, 0.0), /* we'll only set an inactivity timeout once the sender is done */
efl_event_callback_array_add(efl_added, recv_copier_cbs(), d),
efl_io_closer_close_on_invalidate_set(efl_added, EINA_FALSE) /* we must wait both copiers to finish before we close! */
);
fprintf(stderr, "INFO: using receiver buffer %p with copier %p for client %s\n",
recv_buffer, d->recv_copier, efl_net_socket_address_remote_get(client));
efl_unref(recv_buffer); /* d->recv_copier adds a reference */
if (!d->recv_copier)
fprintf(stderr, "ERROR: failed to create receiver copier\n");
if (!d->recv_copier && !d->send_copier)
_send_recv_free(d);
}
}
static void
_server_client_rejected(void *data EINA_UNUSED, const Efl_Event *event)
{
const char *client_address = event->info;
fprintf(stderr, "INFO: rejected client %s\n", client_address);
}
static void
_server_error(void *data EINA_UNUSED, const Efl_Event *event)
{
const Eina_Error *perr = event->info;
fprintf(stderr, "ERROR: %d '%s'\n", *perr, eina_error_msg_get(*perr));
efl_loop_quit(efl_loop_get(event->object), eina_value_int_init(EXIT_FAILURE));
}
static void
_server_serving(void *data EINA_UNUSED, const Efl_Event *event)
{
fprintf(stderr, "INFO: serving at %s\n",
efl_net_server_address_get(event->object));
if (efl_class_get(event->object) == EFL_NET_SERVER_TCP_CLASS)
{
fprintf(stderr,
"TCP options:\n"
" - IPv6 only: %u\n",
efl_net_server_ip_ipv6_only_get(event->object));
}
else if (efl_class_get(event->object) == EFL_NET_SERVER_UDP_CLASS)
{
Eina_Iterator *it;
const char *str;
fprintf(stderr,
"UDP options:\n"
" - IPv6 only: %u\n"
" - don't route: %u\n"
" - multicast TTL: %u\n"
" - multicast loopback: %u\n"
" - multicast groups:\n",
efl_net_server_ip_ipv6_only_get(event->object),
efl_net_server_udp_dont_route_get(event->object),
efl_net_server_udp_multicast_time_to_live_get(event->object),
efl_net_server_udp_multicast_loopback_get(event->object));
it = efl_net_server_udp_multicast_groups_get(event->object);
EINA_ITERATOR_FOREACH(it, str)
fprintf(stderr, " * %s\n", str);
eina_iterator_free(it);
}
}
EFL_CALLBACKS_ARRAY_DEFINE(server_cbs,
{ EFL_NET_SERVER_EVENT_CLIENT_ADD, _server_client_add },
{ EFL_NET_SERVER_EVENT_CLIENT_REJECTED, _server_client_rejected },
{ EFL_NET_SERVER_EVENT_ERROR, _server_error },
{ EFL_NET_SERVER_EVENT_SERVING, _server_serving });
static const char * protocols[] = {
"tcp",
"udp",
"ssl",
#ifdef EFL_NET_SERVER_WINDOWS_CLASS
"windows",
#endif
#ifdef EFL_NET_SERVER_UNIX_CLASS
"unix",
#endif
NULL
};
static const char *ciphers_strs[] = {
"auto",
"tlsv1",
"tlsv1.1",
"tlsv1.2",
NULL
};
static const Ecore_Getopt options = {
"efl_net_server_example", /* program name */
NULL, /* usage line */
"1", /* version */
"(C) 2016 Enlightenment Project", /* copyright */
"BSD 2-Clause", /* license */
/* long description, may be multiline and contain \n */
"Example of Efl_Net_Server objects usage.\n"
"\n"
"This example spawns a server of the given protocol at the given address.",
EINA_FALSE,
{
ECORE_GETOPT_STORE_TRUE('e', "echo",
"Behave as 'echo' server, send back to client all the data receive"),
ECORE_GETOPT_STORE_TRUE(0, "socket-activated",
"Try to use $LISTEN_FDS from systemd, if not do a regular serve()"),
ECORE_GETOPT_STORE_UINT('l', "clients-limit",
"If set will limit number of clients to accept"),
ECORE_GETOPT_STORE_TRUE('r', "clients-reject-excess",
"Immediately reject excess clients (over limit)"),
ECORE_GETOPT_STORE_FALSE(0, "ipv4-on-ipv6",
"IPv4 clients will be automatically converted into IPv6 and handled transparently."),
ECORE_GETOPT_STORE_DOUBLE('t', "inactivity-timeout",
"The timeout in seconds to disconnect a client. The timeout is restarted for each client when there is some activity. It's particularly useful for UDP where there is no disconnection event."),
ECORE_GETOPT_VERSION('V', "version"),
ECORE_GETOPT_COPYRIGHT('C', "copyright"),
ECORE_GETOPT_LICENSE('L', "license"),
ECORE_GETOPT_HELP('h', "help"),
ECORE_GETOPT_CATEGORY("udp", "UDP options"),
ECORE_GETOPT_STORE_TRUE(0, "udp-dont-route",
"If true, datagrams won't be routed using a gateway, being restricted to the local network."),
ECORE_GETOPT_STORE_UINT(0, "udp-multicast-ttl",
"Multicast time to live in number of hops from 0-255. Defaults to 1 (only local network)."),
ECORE_GETOPT_STORE_FALSE(0, "udp-multicast-noloopback",
"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 revocation 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,
"The server address to listen, such as "
"IPv4:PORT, [IPv6]:PORT, Unix socket path...",
"address"),
ECORE_GETOPT_SENTINEL
}
};
static Eo *server = NULL;
EAPI_MAIN void
efl_pause(void *data EINA_UNUSED,
const Efl_Event *ev EINA_UNUSED)
{
}
EAPI_MAIN void
efl_resume(void *data EINA_UNUSED,
const Efl_Event *ev EINA_UNUSED)
{
}
EAPI_MAIN void
efl_terminate(void *data EINA_UNUSED,
const Efl_Event *ev EINA_UNUSED)
{
/* FIXME: For the moment the main loop doesn't get
properly destroyed on shutdown which disallow
relying on parent destroying their children */
if (server)
{
efl_del(server);
server = NULL;
}
fprintf(stderr, "INFO: main loop finished.\n");
}
EAPI_MAIN void
efl_main(void *data EINA_UNUSED,
const Efl_Event *ev)
{
const Efl_Class *cls;
char *protocol = NULL;
char *address = NULL;
Eina_List *udp_mcast_groups = NULL;
char *str;
unsigned int clients_limit = 0;
unsigned udp_mcast_ttl = 1;
Eina_Bool clients_reject_excess = EINA_FALSE;
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 socket_activated = EINA_FALSE;
Eina_Bool quit_option = EINA_FALSE;
Ecore_Getopt_Value values[] = {
ECORE_GETOPT_VALUE_BOOL(echo),
ECORE_GETOPT_VALUE_BOOL(socket_activated),
ECORE_GETOPT_VALUE_UINT(clients_limit),
ECORE_GETOPT_VALUE_BOOL(clients_reject_excess),
ECORE_GETOPT_VALUE_BOOL(ipv6_only),
ECORE_GETOPT_VALUE_DOUBLE(timeout),
/* 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 */
ECORE_GETOPT_VALUE_BOOL(quit_option), /* -L/--license quits */
ECORE_GETOPT_VALUE_BOOL(quit_option), /* -h/--help quits */
ECORE_GETOPT_VALUE_BOOL(quit_option), /* category: udp */
ECORE_GETOPT_VALUE_BOOL(udp_dont_route),
ECORE_GETOPT_VALUE_UINT(udp_mcast_ttl),
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),
ECORE_GETOPT_VALUE_NONE /* sentinel */
};
int args;
Eina_Error err;
args = ecore_getopt_parse(&options, values, 0, NULL);
if (args < 0)
{
fputs("ERROR: Could not parse command line options.\n", stderr);
goto end;
}
if (quit_option) goto end;
args = ecore_getopt_parse_positional(&options, values, 0, NULL, args);
if (args < 0)
{
fputs("ERROR: Could not parse positional arguments.\n", stderr);
goto end;
}
if (!protocol)
{
fputs("ERROR: missing protocol.\n", stderr);
goto end;
}
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;
#ifdef EFL_NET_SERVER_WINDOWS_CLASS
else if (strcmp(protocol, "windows") == 0) cls = EFL_NET_SERVER_WINDOWS_CLASS;
#endif
#ifdef EFL_NET_SERVER_UNIX_CLASS
else if (strcmp(protocol, "unix") == 0) cls = EFL_NET_SERVER_UNIX_CLASS;
#endif
else
{
fprintf(stderr, "ERROR: unsupported protocol: %s\n", protocol);
goto end;
}
server = efl_add(cls, ev->object, /* it's mandatory to use a main loop provider as the server parent */
efl_net_server_clients_limit_set(efl_added,
clients_limit,
clients_reject_excess), /* optional */
efl_event_callback_array_add(efl_added, server_cbs(), NULL)); /* mandatory to have "client,add" in order to be useful */
if (!server)
{
fprintf(stderr, "ERROR: could not create class %p (%s)\n",
cls, efl_class_name_get(cls));
goto end;
}
if (cls == EFL_NET_SERVER_TCP_CLASS)
{
efl_net_server_ip_ipv6_only_set(server, ipv6_only);
efl_net_server_fd_reuse_address_set(server, EINA_TRUE); /* optional, but nice for testing */
efl_net_server_fd_reuse_port_set(server, EINA_TRUE); /* optional, but nice for testing... not secure unless you know what you're doing */
if (socket_activated) efl_net_server_fd_socket_activate(server, address);
}
else if (cls == EFL_NET_SERVER_UDP_CLASS)
{
const Eina_List *lst;
efl_net_server_ip_ipv6_only_set(server, ipv6_only);
efl_net_server_udp_dont_route_set(server, udp_dont_route);
efl_net_server_udp_multicast_time_to_live_set(server, udp_mcast_ttl);
efl_net_server_udp_multicast_loopback_set(server, udp_mcast_loopback);
EINA_LIST_FOREACH(udp_mcast_groups, lst, str)
efl_net_server_udp_multicast_join(server, str);
efl_net_server_fd_reuse_address_set(server, EINA_TRUE); /* optional, but nice for testing */
efl_net_server_fd_reuse_port_set(server, EINA_TRUE); /* optional, but nice for testing... not secure unless you know what you're doing */
if (socket_activated) efl_net_server_fd_socket_activate(server, address);
}
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, "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_ref(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_revocation_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);
efl_net_server_fd_reuse_address_set(server, EINA_TRUE); /* optional, but nice for testing */
efl_net_server_fd_reuse_port_set(server, EINA_TRUE); /* optional, but nice for testing... not secure unless you know what you're doing */
if (socket_activated) efl_net_server_fd_socket_activate(server, address);
}
#ifdef EFL_NET_SERVER_UNIX_CLASS
else if (cls == EFL_NET_SERVER_UNIX_CLASS)
{
efl_net_server_unix_unlink_before_bind_set(server, EINA_TRUE); /* makes testing easier */
if (socket_activated) efl_net_server_fd_socket_activate(server, address);
}
#endif
/* an explicit call to efl_net_server_serve() after the object is
* constructed allows for more complex setup, such as interacting
* with the object to add more properties that couldn't be done
* during efl_add().
*/
if (!efl_net_server_serving_get(server))
{
if (socket_activated)
fprintf(stderr, "WARNING: --socket-activated, but not able to use $LISTEN_FDS descriptors. Try to start the server...\n");
err = efl_net_server_serve(server, address);
if (err)
{
fprintf(stderr, "ERROR: could not serve(%s): %s\n",
address, eina_error_msg_get(err));
goto end_server;
}
}
return ;
end_server:
efl_del(server);
server = NULL;
end:
EINA_LIST_FREE(udp_mcast_groups, str)
free(str);
efl_loop_quit(efl_loop_get(ev->object), eina_value_int_init(EXIT_FAILURE));
}
EFL_MAIN_EX();