efl_net_*_udp: expose SO_DONTROUTE.
authorGustavo Sverzut Barbieri <barbieri@profusion.mobi>
Sat, 22 Oct 2016 13:46:19 +0000 (11:46 -0200)
committerGustavo Sverzut Barbieri <barbieri@profusion.mobi>
Sat, 22 Oct 2016 13:46:19 +0000 (11:46 -0200)
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
src/examples/ecore/efl_net_server_example.c
src/lib/ecore_con/efl_net_server_udp.c
src/lib/ecore_con/efl_net_server_udp.eo
src/lib/ecore_con/efl_net_socket_udp.c
src/lib/ecore_con/efl_net_socket_udp.eo

index 6e113a9..0ba367d 100644 (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));
 
index 2028a42..3c4c518 100644 (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
index f5d79fb..38971ee 100644 (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"
index 02e0be9..0aa4ba0 100644 (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 {
index d76d4fd..5b584c2 100644 (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"
index 6843693..086d312 100644 (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 {