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.
This commit is contained in:
Gustavo Sverzut Barbieri 2016-10-22 11:46:19 -02:00
parent 5e54c3aa57
commit 26866ca2a8
6 changed files with 189 additions and 1 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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