From 26866ca2a81138c41af39cb652d22975bc82a238 Mon Sep 17 00:00:00 2001 From: Gustavo Sverzut Barbieri Date: Sat, 22 Oct 2016 11:46:19 -0200 Subject: [PATCH] efl_net_*_udp: expose SO_DONTROUTE. It's common to have protocols that are restricted to local network only, thus allow exposing SO_DONTROUTE to avoid mistakes. --- src/examples/ecore/efl_net_dialer_udp_example.c | 4 ++ src/examples/ecore/efl_net_server_example.c | 38 +++++++++++++++- src/lib/ecore_con/efl_net_server_udp.c | 58 +++++++++++++++++++++++++ src/lib/ecore_con/efl_net_server_udp.eo | 17 ++++++++ src/lib/ecore_con/efl_net_socket_udp.c | 56 ++++++++++++++++++++++++ src/lib/ecore_con/efl_net_socket_udp.eo | 17 ++++++++ 6 files changed, 189 insertions(+), 1 deletion(-) diff --git a/src/examples/ecore/efl_net_dialer_udp_example.c b/src/examples/ecore/efl_net_dialer_udp_example.c index 6e113a9..0ba367d 100644 --- a/src/examples/ecore/efl_net_dialer_udp_example.c +++ b/src/examples/ecore/efl_net_dialer_udp_example.c @@ -151,6 +151,7 @@ static const Ecore_Getopt options = { { 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_BOOL('R', "dont-route", "Do not route packets via a gateway."), ECORE_GETOPT_STORE_DOUBLE('t', "connect-timeout", "timeout in seconds for the connection phase"), ECORE_GETOPT_VERSION('V', "version"), ECORE_GETOPT_COPYRIGHT('C', "copyright"), @@ -168,11 +169,13 @@ main(int argc, char **argv) char *address = NULL; Eina_Bool cork = EINA_FALSE; Eina_Bool do_read = EINA_FALSE; + Eina_Bool dont_route = 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_BOOL(dont_route), ECORE_GETOPT_VALUE_DOUBLE(timeout_dial), /* standard block to provide version, copyright, license and help */ @@ -216,6 +219,7 @@ main(int argc, char **argv) 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_socket_udp_dont_route_set(efl_added, dont_route), efl_net_dialer_timeout_dial_set(efl_added, timeout_dial), efl_event_callback_array_add(efl_added, dialer_cbs(), NULL)); diff --git a/src/examples/ecore/efl_net_server_example.c b/src/examples/ecore/efl_net_server_example.c index 2028a42..3c4c518 100644 --- a/src/examples/ecore/efl_net_server_example.c +++ b/src/examples/ecore/efl_net_server_example.c @@ -433,6 +433,23 @@ _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_tcp_ipv6_only_get(event->object)); + } + else if (efl_class_get(event->object) == EFL_NET_SERVER_UDP_CLASS) + { + fprintf(stderr, + "UDP options:\n" + " - IPv6 only: %u\n" + " - don't route: %u\n", + efl_net_server_udp_ipv6_only_get(event->object), + efl_net_server_udp_dont_route_get(event->object)); + } } EFL_CALLBACKS_ARRAY_DEFINE(server_cbs, @@ -476,12 +493,17 @@ static const Ecore_Getopt options = { ECORE_GETOPT_LICENSE('L', "license"), ECORE_GETOPT_HELP('h', "help"), + ECORE_GETOPT_CATEGORY("udp", "UDP options"), + ECORE_GETOPT_STORE_BOOL(0, "udp-dont-route", + "If true, datagrams won't be routed using a gateway, being restricted to the local network."), + 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 } }; @@ -495,6 +517,7 @@ main(int argc, char **argv) unsigned int clients_limit = 0; Eina_Bool clients_reject_excess = EINA_FALSE; Eina_Bool ipv6_only = EINA_TRUE; + Eina_Bool udp_dont_route = EINA_FALSE; Eina_Bool quit_option = EINA_FALSE; Ecore_Getopt_Value values[] = { ECORE_GETOPT_VALUE_BOOL(echo), @@ -509,6 +532,9 @@ main(int argc, char **argv) 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), + /* positional argument */ ECORE_GETOPT_VALUE_STR(protocol), ECORE_GETOPT_VALUE_STR(address), @@ -540,6 +566,13 @@ main(int argc, char **argv) goto end; } + if (!protocol) + { + fputs("ERROR: missing protocol.\n", stderr); + retval = EXIT_FAILURE; + 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 @@ -566,7 +599,10 @@ main(int argc, char **argv) if (cls == EFL_NET_SERVER_TCP_CLASS) efl_net_server_tcp_ipv6_only_set(server, ipv6_only); else if (cls == EFL_NET_SERVER_UDP_CLASS) - efl_net_server_udp_ipv6_only_set(server, ipv6_only); + { + efl_net_server_udp_ipv6_only_set(server, ipv6_only); + efl_net_server_udp_dont_route_set(server, udp_dont_route); + } /* an explicit call to efl_net_server_serve() after the object is * constructed allows for more complex setup, such as interacting diff --git a/src/lib/ecore_con/efl_net_server_udp.c b/src/lib/ecore_con/efl_net_server_udp.c index f5d79fb..38971ee 100644 --- a/src/lib/ecore_con/efl_net_server_udp.c +++ b/src/lib/ecore_con/efl_net_server_udp.c @@ -38,6 +38,7 @@ typedef struct _Efl_Net_Server_Udp_Data Ecore_Thread *resolver; Eina_Hash *clients; /* addr (string) -> client (Efl.Net.Server.Udp.Client) */ Eina_Bool ipv6_only; + Eina_Bool dont_route; } Efl_Net_Server_Udp_Data; EOLIAN Efl_Object * @@ -96,6 +97,8 @@ _efl_net_server_udp_resolved_bind(Eo *o, Efl_Net_Server_Udp_Data *pd, const stru efl_net_server_udp_ipv6_only_set(o, pd->ipv6_only); } + efl_net_server_udp_dont_route_set(o, pd->dont_route); + r = bind(fd, addr->ai_addr, addrlen); if (r < 0) { @@ -355,4 +358,59 @@ _efl_net_server_udp_ipv6_only_get(Eo *o EINA_UNUSED, Efl_Net_Server_Udp_Data *pd return pd->ipv6_only; } +EOLIAN static Eina_Bool +_efl_net_server_udp_dont_route_set(Eo *o, Efl_Net_Server_Udp_Data *pd, Eina_Bool dont_route) +{ + Eina_Bool old = pd->dont_route; + int fd = efl_loop_fd_get(o); +#ifdef _WIN32 + DWORD value = dont_route; +#else + int value = dont_route; +#endif + + pd->dont_route = dont_route; + + if (fd < 0) return EINA_TRUE; + + if (setsockopt(fd, SOL_SOCKET, SO_DONTROUTE, &value, sizeof(value)) != 0) + { + Eina_Error err = efl_net_socket_error_get(); + ERR("setsockopt(%d, SOL_SOCKET, SO_DONTROUTE, %u): %s", fd, dont_route, eina_error_msg_get(err)); + pd->dont_route = old; + return EINA_FALSE; + } + + return EINA_TRUE; +} + +EOLIAN static Eina_Bool +_efl_net_server_udp_dont_route_get(Eo *o, Efl_Net_Server_Udp_Data *pd) +{ + int fd = efl_loop_fd_get(o); +#ifdef _WIN32 + DWORD value; +#else + int value; +#endif + socklen_t valuelen; + + if (fd < 0) return pd->dont_route; + + /* if there is a fd, always query it directly as it may be modified + * elsewhere by nasty users. + */ + valuelen = sizeof(value); + if (getsockopt(fd, SOL_SOCKET, SO_DONTROUTE, &value, &valuelen) != 0) + { + Eina_Error err = efl_net_socket_error_get(); + ERR("getsockopt(%d, SOL_SOCKET, SO_DONTROUTE): %s", fd, eina_error_msg_get(err)); + return EINA_FALSE; + } + + pd->dont_route = !!value; /* sync */ + return pd->dont_route; +} + + #include "efl_net_server_udp.eo.c" diff --git a/src/lib/ecore_con/efl_net_server_udp.eo b/src/lib/ecore_con/efl_net_server_udp.eo index 02e0be9..0aa4ba0 100644 --- a/src/lib/ecore_con/efl_net_server_udp.eo +++ b/src/lib/ecore_con/efl_net_server_udp.eo @@ -31,6 +31,23 @@ class Efl.Net.Server.Udp (Efl.Net.Server.Fd) { ipv6_only: bool; } } + + @property dont_route { + [[Avoid sent UDP packets being routed by a gateway, limiting them to the local network. + + This will use SO_DONTROUTE option to avoid gateways + routing sent packets to outside of local network. It's + useful for some protocols that only want local area to + be affected. + ]] + get { } + set { + return: bool (false); [[$true on success]] + } + values { + dont_route: bool; + } + } } implements { diff --git a/src/lib/ecore_con/efl_net_socket_udp.c b/src/lib/ecore_con/efl_net_socket_udp.c index d76d4fd..5b584c2 100644 --- a/src/lib/ecore_con/efl_net_socket_udp.c +++ b/src/lib/ecore_con/efl_net_socket_udp.c @@ -38,6 +38,7 @@ typedef struct _Efl_Net_Socket_Udp_Data { Eina_Bool cork; + Eina_Bool dont_route; } Efl_Net_Socket_Udp_Data; EOLIAN static void @@ -53,6 +54,7 @@ _efl_net_socket_udp_efl_loop_fd_fd_set(Eo *o, Efl_Net_Socket_Udp_Data *pd EINA_U /* apply postponed values */ efl_net_socket_udp_cork_set(o, pd->cork); + efl_net_socket_udp_dont_route_set(o, pd->dont_route); family = efl_net_socket_fd_family_get(o); if (family == AF_UNSPEC) return; @@ -152,4 +154,58 @@ _efl_net_socket_udp_cork_get(Eo *o, Efl_Net_Socket_Udp_Data *pd) return pd->cork; } +EOLIAN static Eina_Bool +_efl_net_socket_udp_dont_route_set(Eo *o, Efl_Net_Socket_Udp_Data *pd, Eina_Bool dont_route) +{ + Eina_Bool old = pd->dont_route; + int fd = efl_loop_fd_get(o); +#ifdef _WIN32 + DWORD value = dont_route; +#else + int value = dont_route; +#endif + + pd->dont_route = dont_route; + + if (fd < 0) return EINA_TRUE; + + if (setsockopt(fd, SOL_SOCKET, SO_DONTROUTE, &value, sizeof(value)) != 0) + { + Eina_Error err = efl_net_socket_error_get(); + ERR("setsockopt(%d, SOL_SOCKET, SO_DONTROUTE, %u): %s", fd, dont_route, eina_error_msg_get(err)); + pd->dont_route = old; + return EINA_FALSE; + } + + return EINA_TRUE; +} + +EOLIAN static Eina_Bool +_efl_net_socket_udp_dont_route_get(Eo *o, Efl_Net_Socket_Udp_Data *pd) +{ + int fd = efl_loop_fd_get(o); +#ifdef _WIN32 + DWORD value; +#else + int value; +#endif + socklen_t valuelen; + + if (fd < 0) return pd->dont_route; + + /* if there is a fd, always query it directly as it may be modified + * elsewhere by nasty users. + */ + valuelen = sizeof(value); + if (getsockopt(fd, SOL_SOCKET, SO_DONTROUTE, &value, &valuelen) != 0) + { + Eina_Error err = efl_net_socket_error_get(); + ERR("getsockopt(%d, SOL_SOCKET, SO_DONTROUTE): %s", fd, eina_error_msg_get(err)); + return EINA_FALSE; + } + + pd->dont_route = !!value; /* sync */ + return pd->dont_route; +} + #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 index 6843693..086d312 100644 --- a/src/lib/ecore_con/efl_net_socket_udp.eo +++ b/src/lib/ecore_con/efl_net_socket_udp.eo @@ -22,6 +22,23 @@ class Efl.Net.Socket.Udp (Efl.Net.Socket.Fd) { cork: bool; } } + + @property dont_route { + [[Avoid sent UDP packets being routed by a gateway, limiting them to the local network. + + This will use SO_DONTROUTE option to avoid gateways + routing sent packets to outside of local network. It's + useful for some protocols that only want local area to + be affected. + ]] + get { } + set { + return: bool (false); [[$true on success]] + } + values { + dont_route: bool; + } + } } implements { -- 2.7.4