when creating a socket, spin over a number of low-numbered ports
authorRonnie Sahlberg <ronniesahlberg@gmail.com>
Mon, 12 Sep 2011 12:14:26 +0000 (22:14 +1000)
committerRonnie Sahlberg <ronniesahlberg@gmail.com>
Mon, 12 Sep 2011 12:14:26 +0000 (22:14 +1000)
and try to bind to a system port in case the user is root or the
binary has the CAP_NET_BIND_SERVICE capability

this removes the need to use 'insecure' on the server

lib/socket.c

index d93acd80b717c9f4be0fab8168862bab3bdf6dbc..f833df130dacb9ae80597b616decb69816f5020a 100644 (file)
@@ -330,7 +330,6 @@ int rpc_service(struct rpc_context *rpc, int revents)
        return 0;
 }
 
-
 int rpc_connect_async(struct rpc_context *rpc, const char *server, int port, rpc_cb cb, void *private_data)
 {
        struct sockaddr_storage s;
@@ -372,7 +371,49 @@ int rpc_connect_async(struct rpc_context *rpc, const char *server, int port, rpc
        rpc->connect_cb  = cb;
        rpc->connect_data = private_data;
 
+
+#if !defined(WIN32)
+       /* Some systems allow you to set capabilities on an executable
+        * to allow the file to be executed with privilege to bind to
+        * privileged system ports, even if the user is not root.
+        *
+        * Opportunistically try to bind the socket to a low numbered
+        * system port in the hope that the user is either root or the
+        * executable has the CAP_NET_BIND_SERVICE.
+        *
+        * As soon as we fail the bind() with EACCES we know we will never
+        * be able to bind to a system port so we terminate the loop.
+        *
+        * On linux, use
+        *    sudo setcap 'cap_net_bind_service=+ep' /path/executable
+        * to make the executable able to bind to a system port.
+        */
+       if (1) {
+               int port;
+               int one = 1;
+
+               setsockopt(rpc->fd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one));
+
+               for (port = 200; port < 500; port++) {
+                       struct sockaddr_in sin;
+
+                       printf("try port %d\n", port);
+                       memset(&sin, 0, sizeof(sin));
+                       sin.sin_port        = htons(port);
+                       sin.sin_family      = AF_INET;
+                       sin.sin_addr.s_addr = 0;
+
+                       if (bind(rpc->fd, (struct sockaddr *)&sin, sizeof(struct sockaddr_in)) != 0 && errno != EACCES) {
+                               /* we didnt get EACCES, so try again */
+                               continue;
+                       }
+                       break;
+               }
+       }
+#endif
+
        set_nonblocking(rpc->fd);
+
 #if defined(WIN32)
        if (connect(rpc->fd, (struct sockaddr *)&s, socksize) == 0 && GetLastError() != WSAEINPROGRESS   )
 #else