diff --git a/configure.ac b/configure.ac index 079485232f..c74306381e 100644 --- a/configure.ac +++ b/configure.ac @@ -477,6 +477,7 @@ EFL_CHECK_PATH_MAX EFL_CHECK_DEFINE(TCP_CORK, netinet/tcp.h) EFL_CHECK_DEFINE(TCP_NOPUSH, netinet/tcp.h) +EFL_CHECK_DEFINE(UDP_CORK, netinet/udp.h) #### Checks for types @@ -1784,6 +1785,7 @@ langinfo.h \ features.h \ netinet/in.h \ netinet/tcp.h \ +netinet/udp.h \ sys/prctl.h \ sys/resource.h \ sys/timerfd.h \ diff --git a/src/Makefile_Ecore_Con.am b/src/Makefile_Ecore_Con.am index ae7166d9e2..e5a5cd081a 100644 --- a/src/Makefile_Ecore_Con.am +++ b/src/Makefile_Ecore_Con.am @@ -9,8 +9,10 @@ ecore_con_eolian_files = \ lib/ecore_con/efl_net_socket.eo \ lib/ecore_con/efl_net_socket_fd.eo \ lib/ecore_con/efl_net_socket_tcp.eo \ + lib/ecore_con/efl_net_socket_udp.eo \ lib/ecore_con/efl_net_dialer.eo \ lib/ecore_con/efl_net_dialer_tcp.eo \ + lib/ecore_con/efl_net_dialer_udp.eo \ lib/ecore_con/efl_net_dialer_http.eo \ lib/ecore_con/efl_net_dialer_websocket.eo \ lib/ecore_con/efl_net_server.eo \ @@ -73,8 +75,10 @@ lib/ecore_con/ecore_con_info.c \ lib/ecore_con/efl_net_socket.c \ lib/ecore_con/efl_net_socket_fd.c \ lib/ecore_con/efl_net_socket_tcp.c \ +lib/ecore_con/efl_net_socket_udp.c \ lib/ecore_con/efl_net_dialer.c \ lib/ecore_con/efl_net_dialer_tcp.c \ +lib/ecore_con/efl_net_dialer_udp.c \ lib/ecore_con/efl_net_dialer_http.c \ lib/ecore_con/efl_net_dialer_websocket.c \ lib/ecore_con/efl_net_server.c \ diff --git a/src/examples/ecore/.gitignore b/src/examples/ecore/.gitignore index 826eebe38d..3aaec192ac 100644 --- a/src/examples/ecore/.gitignore +++ b/src/examples/ecore/.gitignore @@ -54,3 +54,4 @@ /efl_net_dialer_http_example /efl_net_dialer_websocket_example /efl_net_dialer_websocket_autobahntestee +/efl_net_dialer_udp_example diff --git a/src/examples/ecore/Makefile.am b/src/examples/ecore/Makefile.am index 35c930b63b..da6b086a3d 100644 --- a/src/examples/ecore/Makefile.am +++ b/src/examples/ecore/Makefile.am @@ -84,7 +84,8 @@ efl_io_queue_example \ efl_net_server_example \ efl_net_dialer_http_example \ efl_net_dialer_websocket_example \ -efl_net_dialer_websocket_autobahntestee +efl_net_dialer_websocket_autobahntestee \ +efl_net_dialer_udp_example ECORE_COMMON_LDADD = \ $(top_builddir)/src/lib/ecore/libecore.la \ @@ -310,6 +311,9 @@ efl_net_dialer_websocket_example_LDADD = $(ECORE_CON_COMMON_LDADD) efl_net_dialer_websocket_autobahntestee_SOURCES = efl_net_dialer_websocket_autobahntestee.c efl_net_dialer_websocket_autobahntestee_LDADD = $(ECORE_CON_COMMON_LDADD) +efl_net_dialer_udp_example_SOURCES = efl_net_dialer_udp_example.c +efl_net_dialer_udp_example_LDADD = $(ECORE_CON_COMMON_LDADD) + SRCS = \ ecore_animator_example.c \ ecore_buffer_example.c \ @@ -363,7 +367,8 @@ efl_io_queue_example.c \ efl_net_server_example.c \ efl_net_dialer_http_example.c \ efl_net_dialer_websocket_example.c \ -efl_net_dialer_websocket_autobahntestee.c +efl_net_dialer_websocket_autobahntestee.c \ +efl_net_dialer_udp_example.c DATA_FILES = red.png Makefile.examples diff --git a/src/examples/ecore/efl_io_copier_example.c b/src/examples/ecore/efl_io_copier_example.c index 2a2af78f6d..d6b2907b33 100644 --- a/src/examples/ecore/efl_io_copier_example.c +++ b/src/examples/ecore/efl_io_copier_example.c @@ -344,6 +344,7 @@ static const Ecore_Getopt options = { "tcp://IP:PORT to connect using TCP and an IPv4 (A.B.C.D:PORT) or IPv6 ([A:B:C:D::E]:PORT).\n" "http://address to do a PUT request\n" "ws://address or wss:// to do WebSocket request\n" + "udp://IP:PORT to connect using UDP and an IPv4 (A.B.C.D:PORT) or IPv6 ([A:B:C:D::E]:PORT).\n" "", "output-file"), ECORE_GETOPT_SENTINEL @@ -658,6 +659,33 @@ main(int argc, char **argv) goto end_output; } } + else if (strncmp(output_fname, "udp://", strlen("udp://")) == 0) + { + /* + * Since Efl.Net.Socket implements the required interfaces, + * they can be used here as well. + */ + const char *address = output_fname + strlen("udp://"); + Eina_Error err; + output = efl_add(EFL_NET_DIALER_UDP_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 UDP Dialer.\n"); + retval = EXIT_FAILURE; + goto end_input; + } + + err = efl_net_dialer_dial(output, address); + if (err) + { + fprintf(stderr, "ERROR: could not UDP 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_dialer_udp_example.c b/src/examples/ecore/efl_net_dialer_udp_example.c new file mode 100644 index 0000000000..4193cc1691 --- /dev/null +++ b/src/examples/ecore/efl_net_dialer_udp_example.c @@ -0,0 +1,197 @@ +#define EFL_BETA_API_SUPPORT 1 +#define EFL_EO_API_SUPPORT 1 +#include +#include +#include +#include +#include + +static int retval = EXIT_SUCCESS; +static Eina_Bool do_read = EINA_FALSE; + +static void +_connected(void *data EINA_UNUSED, const Efl_Event *event) +{ + char buf[1024]; + Eina_Rw_Slice rw_slice = {.mem = buf, .len = sizeof(buf)}; + Eina_Slice slice = EINA_SLICE_STR_LITERAL("Hello World"); + Eina_Error err; + + fprintf(stderr, "INFO: connected %s\n", + efl_net_dialer_address_dial_get(event->object)); + + err = efl_io_writer_write(event->object, &slice, NULL); + if (err) + { + fprintf(stderr, "ERROR: could not write: %s\n", eina_error_msg_get(err)); + retval = EXIT_FAILURE; + ecore_main_loop_quit(); + return; + } + + fprintf(stderr, "INFO: wrote " EINA_SLICE_STR_FMT "\n", EINA_SLICE_STR_PRINT(slice)); + + slice = (Eina_Slice)EINA_SLICE_STR_LITERAL("Second Write"); + err = efl_io_writer_write(event->object, &slice, NULL); + if (err) + { + fprintf(stderr, "ERROR: could not write: %s\n", eina_error_msg_get(err)); + retval = EXIT_FAILURE; + ecore_main_loop_quit(); + return; + } + + fprintf(stderr, "INFO: wrote " EINA_SLICE_STR_FMT "\n", EINA_SLICE_STR_PRINT(slice)); + + /* if CORK was used, then say we're done to generate the single final datagram */ + efl_net_socket_udp_cork_set(event->object, EINA_FALSE); + + if (!do_read) goto end; + + err = efl_io_reader_read(event->object, &rw_slice); + if (err) + { + fprintf(stderr, "ERROR: could not read: %s\n", eina_error_msg_get(err)); + retval = EXIT_FAILURE; + ecore_main_loop_quit(); + return; + } + + fprintf(stderr, "INFO: read " EINA_SLICE_STR_FMT "\n", EINA_SLICE_STR_PRINT(rw_slice)); + + end: + retval = EXIT_SUCCESS; + ecore_main_loop_quit(); +} + +static void +_resolved(void *data EINA_UNUSED, const Efl_Event *event) +{ + fprintf(stderr, "INFO: resolved %s => %s\n", + efl_net_dialer_address_dial_get(event->object), + efl_net_socket_address_remote_get(event->object)); +} + +static void +_error(void *data EINA_UNUSED, const Efl_Event *event) +{ + const Eina_Error *perr = event->info; + fprintf(stderr, "INFO: error: %d '%s'\n", *perr, eina_error_msg_get(*perr)); + retval = EXIT_FAILURE; +} + +EFL_CALLBACKS_ARRAY_DEFINE(dialer_cbs, + { EFL_NET_DIALER_EVENT_CONNECTED, _connected }, + { EFL_NET_DIALER_EVENT_RESOLVED, _resolved }, + { EFL_NET_DIALER_EVENT_ERROR, _error }); + +static const Ecore_Getopt options = { + "efl_net_dialer_udp_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_Dialer_Udp usage, sending a message and receiving a reply\n", + EINA_FALSE, + { + ECORE_GETOPT_STORE_BOOL('r', "read-after-write", "Do a read after writes are done."), + ECORE_GETOPT_STORE_BOOL('c', "cork", "use UDP_CORK around messages to generate a single datagram."), + ECORE_GETOPT_STORE_DOUBLE('t', "connect-timeout", "timeout in seconds for the connection phase"), + ECORE_GETOPT_VERSION('V', "version"), + ECORE_GETOPT_COPYRIGHT('C', "copyright"), + ECORE_GETOPT_LICENSE('L', "license"), + ECORE_GETOPT_HELP('h', "help"), + ECORE_GETOPT_STORE_METAVAR_STR(0, NULL, + "The address (URL) to dial", "address"), + ECORE_GETOPT_SENTINEL + } +}; + +int +main(int argc, char **argv) +{ + char *address = NULL; + Eina_Bool cork = EINA_FALSE; + Eina_Bool quit_option = EINA_FALSE; + double timeout_dial = 30.0; + Ecore_Getopt_Value values[] = { + ECORE_GETOPT_VALUE_BOOL(do_read), + ECORE_GETOPT_VALUE_BOOL(cork), + ECORE_GETOPT_VALUE_DOUBLE(timeout_dial), + + /* 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 */ + + /* positional argument */ + ECORE_GETOPT_VALUE_STR(address), + + ECORE_GETOPT_VALUE_NONE /* sentinel */ + }; + int args; + Eo *dialer, *loop; + Eina_Error err; + + ecore_init(); + ecore_con_init(); + + args = ecore_getopt_parse(&options, values, argc, argv); + if (args < 0) + { + fputs("ERROR: Could not parse command line options.\n", stderr); + retval = EXIT_FAILURE; + goto end; + } + + if (quit_option) goto end; + + loop = ecore_main_loop_get(); + + args = ecore_getopt_parse_positional(&options, values, argc, argv, args); + if (args < 0) + { + fputs("ERROR: Could not parse positional arguments.\n", stderr); + retval = EXIT_FAILURE; + goto end; + } + + dialer = efl_add(EFL_NET_DIALER_UDP_CLASS, loop, + efl_name_set(efl_added, "dialer"), + efl_net_socket_udp_cork_set(efl_added, cork), + efl_net_dialer_timeout_dial_set(efl_added, timeout_dial), + efl_event_callback_array_add(efl_added, dialer_cbs(), NULL)); + + err = efl_net_dialer_dial(dialer, address); + if (err != 0) + { + fprintf(stderr, "ERROR: could not dial '%s': %s", + address, eina_error_msg_get(err)); + goto no_mainloop; + } + + fprintf(stderr, + "INFO: dialed %s\n" + "INFO: - read-after-write=%hhu\n" + "INFO: - cork=%hhu\n" + "INFO: - timeout_dial=%fs\n", + efl_net_dialer_address_dial_get(dialer), + do_read, + efl_net_socket_udp_cork_get(dialer), + efl_net_dialer_timeout_dial_get(dialer)); + + ecore_main_loop_begin(); + + fprintf(stderr, "INFO: main loop finished.\n"); + + no_mainloop: + efl_del(dialer); + + end: + ecore_con_shutdown(); + ecore_shutdown(); + + return retval; +} diff --git a/src/lib/ecore_con/Ecore_Con_Eo.h b/src/lib/ecore_con/Ecore_Con_Eo.h index ec7a228fdf..6d6fb03d7e 100644 --- a/src/lib/ecore_con/Ecore_Con_Eo.h +++ b/src/lib/ecore_con/Ecore_Con_Eo.h @@ -15,6 +15,9 @@ #include "efl_net_dialer_tcp.eo.h" #include "efl_net_server_tcp.eo.h" +#include "efl_net_socket_udp.eo.h" +#include "efl_net_dialer_udp.eo.h" + #include "efl_net_http_types.eot.h" /* TODO: should be generated from 'var Efl.Net.Dialer.Error.*' */ diff --git a/src/lib/ecore_con/efl_net_dialer_udp.c b/src/lib/ecore_con/efl_net_dialer_udp.c new file mode 100644 index 0000000000..cc84eebab8 --- /dev/null +++ b/src/lib/ecore_con/efl_net_dialer_udp.c @@ -0,0 +1,220 @@ +#define EFL_NET_DIALER_UDP_PROTECTED 1 +#define EFL_NET_DIALER_PROTECTED 1 +#define EFL_NET_SOCKET_FD_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_UDP_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_UDP_CLASS + +typedef struct _Efl_Net_Dialer_Udp_Data +{ + struct { + Ecore_Thread *thread; + Efl_Future *timeout; + } connect; + Eina_Stringshare *address_dial; + Eina_Bool connected; + Eina_Bool closed; + double timeout_dial; +} Efl_Net_Dialer_Udp_Data; + +EOLIAN static Eo* +_efl_net_dialer_udp_efl_object_constructor(Eo *o, Efl_Net_Dialer_Udp_Data *pd EINA_UNUSED) +{ + o = efl_constructor(efl_super(o, MY_CLASS)); + if (!o) return NULL; + + efl_net_dialer_timeout_dial_set(o, 30.0); + return o; +} + +EOLIAN static void +_efl_net_dialer_udp_efl_object_destructor(Eo *o, Efl_Net_Dialer_Udp_Data *pd) +{ + if (efl_io_closer_close_on_destructor_get(o) && + (!efl_io_closer_closed_get(o))) + efl_io_closer_close(o); + + if (pd->connect.thread) + { + ecore_thread_cancel(pd->connect.thread); + pd->connect.thread = NULL; + } + + efl_destructor(efl_super(o, MY_CLASS)); + + eina_stringshare_replace(&pd->address_dial, NULL); +} + +static void +_efl_net_dialer_udp_connect_timeout(void *data, const Efl_Event *ev EINA_UNUSED) +{ + Eo *o = data; + Efl_Net_Dialer_Udp_Data *pd = efl_data_scope_get(o, MY_CLASS); + Eina_Error err = ETIMEDOUT; + + if (pd->connect.thread) + { + ecore_thread_cancel(pd->connect.thread); + pd->connect.thread = NULL; + } + + 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); +} + +static void +_efl_net_dialer_udp_connected(void *data, const struct sockaddr *addr, socklen_t addrlen EINA_UNUSED, int sockfd, Eina_Error err) +{ + Eo *o = data; + Efl_Net_Dialer_Udp_Data *pd = efl_data_scope_get(o, MY_CLASS); + char buf[INET6_ADDRSTRLEN + sizeof("[]:65536")]; + + pd->connect.thread = NULL; + + efl_ref(o); /* we're emitting callbacks then continuing the workflow */ + + if (err) + { + efl_io_reader_eos_set(o, EINA_TRUE); + efl_event_callback_call(o, EFL_NET_DIALER_EVENT_ERROR, &err); + } + else + { + efl_net_socket_fd_family_set(o, addr->sa_family); + efl_loop_fd_set(o, sockfd); + if (efl_net_ip_port_fmt(buf, sizeof(buf), addr)) + { + efl_net_socket_address_remote_set(o, buf); + efl_event_callback_call(o, EFL_NET_DIALER_EVENT_RESOLVED, NULL); + } + efl_net_dialer_connected_set(o, EINA_TRUE); + } + + efl_unref(o); +} + +EOLIAN static Eina_Error +_efl_net_dialer_udp_efl_net_dialer_dial(Eo *o, Efl_Net_Dialer_Udp_Data *pd EINA_UNUSED, const char *address) +{ + 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); + EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_loop_fd_get(o) >= 0, EALREADY); + + if (pd->connect.thread) + { + ecore_thread_cancel(pd->connect.thread); + pd->connect.thread = NULL; + } + + if (pd->connect.thread) + ecore_thread_cancel(pd->connect.thread); + + pd->connect.thread = efl_net_ip_connect_async_new(address, + "", + NULL, + NULL, + SOCK_DGRAM, + IPPROTO_UDP, + efl_io_closer_close_on_exec_get(o), + _efl_net_dialer_udp_connected, o); + EINA_SAFETY_ON_NULL_RETURN_VAL(pd->connect.thread, EINVAL); + + efl_net_dialer_address_dial_set(o, address); + + if (pd->connect.timeout) + efl_future_cancel(pd->connect.timeout); + if (pd->timeout_dial > 0.0) + { + efl_future_use(&pd->connect.timeout, efl_loop_timeout(efl_loop_get(o), pd->timeout_dial, o)); + efl_future_then(pd->connect.timeout, _efl_net_dialer_udp_connect_timeout, NULL, NULL, o); + efl_future_link(o, pd->connect.timeout); + } + + return 0; +} + +EOLIAN static void +_efl_net_dialer_udp_efl_net_dialer_address_dial_set(Eo *o EINA_UNUSED, Efl_Net_Dialer_Udp_Data *pd, const char *address) +{ + eina_stringshare_replace(&pd->address_dial, address); +} + +EOLIAN static const char * +_efl_net_dialer_udp_efl_net_dialer_address_dial_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Udp_Data *pd) +{ + return pd->address_dial; +} + +EOLIAN static void +_efl_net_dialer_udp_efl_net_dialer_timeout_dial_set(Eo *o EINA_UNUSED, Efl_Net_Dialer_Udp_Data *pd, double seconds) +{ + pd->timeout_dial = seconds; + if (pd->connect.timeout) + efl_future_cancel(pd->connect.timeout); + + if ((pd->timeout_dial > 0.0) && (pd->connect.thread)) + { + efl_future_use(&pd->connect.timeout, efl_loop_timeout(efl_loop_get(o), pd->timeout_dial, o)); + efl_future_then(pd->connect.timeout, _efl_net_dialer_udp_connect_timeout, NULL, NULL, o); + } +} + +EOLIAN static double +_efl_net_dialer_udp_efl_net_dialer_timeout_dial_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Udp_Data *pd) +{ + return pd->timeout_dial; +} + +EOLIAN static void +_efl_net_dialer_udp_efl_net_dialer_connected_set(Eo *o, Efl_Net_Dialer_Udp_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_udp_efl_net_dialer_connected_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Udp_Data *pd) +{ + return pd->connected; +} + +EOLIAN static Eina_Error +_efl_net_dialer_udp_efl_io_closer_close(Eo *o, Efl_Net_Dialer_Udp_Data *pd) +{ + pd->closed = EINA_TRUE; + efl_net_dialer_connected_set(o, EINA_FALSE); + return efl_io_closer_close(efl_super(o, MY_CLASS)); +} + +#include "efl_net_dialer_udp.eo.c" diff --git a/src/lib/ecore_con/efl_net_dialer_udp.eo b/src/lib/ecore_con/efl_net_dialer_udp.eo new file mode 100644 index 0000000000..41ed220ca5 --- /dev/null +++ b/src/lib/ecore_con/efl_net_dialer_udp.eo @@ -0,0 +1,18 @@ +class Efl.Net.Dialer.Udp (Efl.Net.Socket.Udp, Efl.Net.Dialer) { + [[Connects to a remote UDP server. + + UDP proxies are not supported, not even using SOCKSv5. + + @since 1.19 + ]] + + implements { + Efl.Object.constructor; + Efl.Object.destructor; + Efl.Net.Dialer.dial; + Efl.Net.Dialer.address_dial; + Efl.Net.Dialer.connected; + Efl.Net.Dialer.timeout_dial; + Efl.Io.Closer.close; + } +} diff --git a/src/lib/ecore_con/efl_net_socket_udp.c b/src/lib/ecore_con/efl_net_socket_udp.c new file mode 100644 index 0000000000..d76d4fda7f --- /dev/null +++ b/src/lib/ecore_con/efl_net_socket_udp.c @@ -0,0 +1,155 @@ +#define EFL_NET_SOCKET_UDP_PROTECTED 1 +#define EFL_NET_SOCKET_FD_PROTECTED 1 +#define EFL_LOOP_FD_PROTECTED 1 +#define EFL_IO_READER_FD_PROTECTED 1 +#define EFL_IO_WRITER_FD_PROTECTED 1 +#define EFL_IO_CLOSER_FD_PROTECTED 1 +#define EFL_IO_READER_PROTECTED 1 +#define EFL_IO_WRITER_PROTECTED 1 +#define EFL_IO_CLOSER_PROTECTED 1 +#define EFL_NET_SOCKET_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_UDP_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_SOCKET_UDP_CLASS + +typedef struct _Efl_Net_Socket_Udp_Data +{ + Eina_Bool cork; +} Efl_Net_Socket_Udp_Data; + +EOLIAN static void +_efl_net_socket_udp_efl_loop_fd_fd_set(Eo *o, Efl_Net_Socket_Udp_Data *pd EINA_UNUSED, int fd) +{ + efl_loop_fd_set(efl_super(o, MY_CLASS), fd); + + if (fd >= 0) + { + struct sockaddr_storage addr; + socklen_t addrlen; + int family; + + /* apply postponed values */ + efl_net_socket_udp_cork_set(o, pd->cork); + + family = efl_net_socket_fd_family_get(o); + if (family == AF_UNSPEC) return; + + addrlen = sizeof(addr); + if (getsockname(fd, (struct sockaddr *)&addr, &addrlen) < 0) + ERR("getsockname(%d): %s", fd, strerror(errno)); + else + { + char str[INET6_ADDRSTRLEN + sizeof("[]:65536")]; + if (efl_net_ip_port_fmt(str, sizeof(str), (struct sockaddr *)&addr)) + efl_net_socket_address_local_set(o, str); + } + + addrlen = sizeof(addr); + if (getpeername(fd, (struct sockaddr *)&addr, &addrlen) < 0) + ERR("getpeername(%d): %s", fd, strerror(errno)); + else + { + char str[INET6_ADDRSTRLEN + sizeof("[]:65536")]; + if (efl_net_ip_port_fmt(str, sizeof(str), (struct sockaddr *)&addr)) + efl_net_socket_address_remote_set(o, str); + } + } +} + +static inline int +_cork_option_get(void) +{ +#if defined(HAVE_UDP_CORK) + return UDP_CORK; +#else + return -1; +#endif +} + +EOLIAN static Eina_Bool +_efl_net_socket_udp_cork_set(Eo *o, Efl_Net_Socket_Udp_Data *pd, Eina_Bool cork) +{ + int value, fd, option; + Eina_Bool old = pd->cork; + + option = _cork_option_get(); + if (EINA_UNLIKELY(option < 0)) + { + if (cork) + ERR("Could not find a UDP_CORK equivalent on your system"); + return EINA_FALSE; + } + + pd->cork = cork; + + fd = efl_loop_fd_get(o); + if (fd < 0) return EINA_TRUE; /* postpone until fd_set() */ + + value = cork; + if (setsockopt(fd, IPPROTO_UDP, option, &value, sizeof(value)) < 0) + { + ERR("setsockopt(%d, IPPROTO_UDP, 0x%x, %d): %s", + fd, option, value, strerror(errno)); + pd->cork = old; + return EINA_FALSE; + } + + return EINA_TRUE; +} + +EOLIAN static Eina_Bool +_efl_net_socket_udp_cork_get(Eo *o, Efl_Net_Socket_Udp_Data *pd) +{ + int value = 0, fd; + socklen_t valuelen; + int option; + + option = _cork_option_get(); + if (EINA_UNLIKELY(option < 0)) + { + ERR("Could not find a UDP_CORK equivalent on your system"); + return EINA_FALSE; + } + + fd = efl_loop_fd_get(o); + if (fd < 0) return pd->cork; + + /* if there is a fd, always query it directly as it may be modified + * elsewhere by nasty users. + */ + valuelen = sizeof(value); + if (getsockopt(fd, IPPROTO_UDP, option, &value, &valuelen) < 0) + { + ERR("getsockopt(%d, IPPROTO_UDP, 0x%x): %s", + fd, option, strerror(errno)); + return EINA_FALSE; + } + + pd->cork = !!value; /* sync */ + return pd->cork; +} + +#include "efl_net_socket_udp.eo.c" diff --git a/src/lib/ecore_con/efl_net_socket_udp.eo b/src/lib/ecore_con/efl_net_socket_udp.eo new file mode 100644 index 0000000000..782184b908 --- /dev/null +++ b/src/lib/ecore_con/efl_net_socket_udp.eo @@ -0,0 +1,26 @@ +class Efl.Net.Socket.Udp (Efl.Net.Socket.Fd) { + [[A base UDP socket. + + This is the common class and takes an existing FD, usually + created by an dialer or server. + + @since 1.19 + ]] + + methods { + @property cork { + [[Controls UDP's cork using UDP_CORK]] + get { } + set { + return: bool (false); [[$true on success]] + } + values { + cork: bool; + } + } + } + + implements { + Efl.Loop.Fd.fd.set; + } +}