[win32] - completed win32 port
[deb_libnfs.git] / lib / socket.c
index e9c103e55b0cd124ce04baaeef1721fb43d5e5fd..bc101e7a6821b01b78d987bd5e8aad917ecfe126 100644 (file)
    You should have received a copy of the GNU Lesser General Public License
    along with this program; if not, see <http://www.gnu.org/licenses/>.
 */
-
-#if defined(WIN32)
-#include <winsock2.h>
-#include <ws2tcpip.h>
-#include <basetsd.h>
-#define ssize_t SSIZE_T
-#define MSG_DONTWAIT 0
+#ifdef WIN32
+#include "win32_compat.h"
 #else
 #include <unistd.h>
 #include <poll.h>
@@ -28,7 +23,7 @@
 #include <sys/ioctl.h>
 #include <sys/socket.h>
 #include <netdb.h>
-#endif
+#endif/*WIN32*/
 
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #include "libnfs-private.h"
 #include "slist.h"
 
+#ifdef WIN32
+//has to be included after stdlib!!
+#include "win32_errnowrapper.h"
+#endif
+
+
 static int rpc_disconnect_requeue(struct rpc_context *rpc);
 
 static void set_nonblocking(int fd)
 {
+       int v = 0;
 #if defined(WIN32)
+       long nonblocking=1;
+       v = ioctlsocket(fd, FIONBIO,&nonblocking);
 #else
-       unsigned v;
        v = fcntl(fd, F_GETFL, 0);
         fcntl(fd, F_SETFL, v | O_NONBLOCK);
-#endif
+#endif //FIXME
 }
 
 int rpc_get_fd(struct rpc_context *rpc)
@@ -140,6 +143,7 @@ static int rpc_read_from_socket(struct rpc_context *rpc)
                rpc_set_error(rpc, "Ioctl FIONREAD returned error : %d. Closing socket.", errno);
                return -1;
        }
+
        if (available == 0) {
                rpc_set_error(rpc, "Socket has been closed");
                return -1;
@@ -254,11 +258,15 @@ static int rpc_read_from_socket(struct rpc_context *rpc)
 int rpc_service(struct rpc_context *rpc, int revents)
 {
        if (revents & POLLERR) {
+#ifdef WIN32
+               char err = 0;
+#else
                int err = 0;
+#endif
                socklen_t err_size = sizeof(err);
 
                if (getsockopt(rpc->fd, SOL_SOCKET, SO_ERROR,
-                               &err, &err_size) != 0 || err != 0) {
+                               (char *)&err, &err_size) != 0 || err != 0) {
                        if (err == 0) {
                                err = errno;
                        }
@@ -283,7 +291,7 @@ int rpc_service(struct rpc_context *rpc, int revents)
                socklen_t err_size = sizeof(err);
 
                if (getsockopt(rpc->fd, SOL_SOCKET, SO_ERROR,
-                               &err, &err_size) != 0 || err != 0) {
+                               (char *)&err, &err_size) != 0 || err != 0) {
                        if (err == 0) {
                                err = errno;
                        }
@@ -302,7 +310,7 @@ int rpc_service(struct rpc_context *rpc, int revents)
 
        if (revents & POLLIN) {
                if (rpc_read_from_socket(rpc) != 0) {
-                       rpc_disconnect_requeue(rpc);
+                       rpc_disconnect_requeue(rpc);
                        return 0;
                }
        }
@@ -317,7 +325,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;
@@ -359,9 +366,54 @@ 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;
+
+                       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 (connect(rpc->fd, (struct sockaddr *)&s, socksize) != 0 && errno != EINPROGRESS) {
+#if defined(WIN32)
+       if (connect(rpc->fd, (struct sockaddr *)&s, socksize) == 0 && errno != EINPROGRESS   )
+#else
+       if (connect(rpc->fd, (struct sockaddr *)&s, socksize) != 0 && errno != EINPROGRESS) 
+#endif
+       {
                rpc_set_error(rpc, "connect() to server failed");
                return -1;
        }               
@@ -488,7 +540,7 @@ int rpc_set_udp_destination(struct rpc_context *rpc, char *addr, int port, int i
        freeaddrinfo(ai);
 
        rpc->is_broadcast = is_broadcast;
-       setsockopt(rpc->fd, SOL_SOCKET, SO_BROADCAST, &is_broadcast, sizeof(is_broadcast));
+       setsockopt(rpc->fd, SOL_SOCKET, SO_BROADCAST, (char *)&is_broadcast, sizeof(is_broadcast));
 
        return 0;
 }
@@ -497,3 +549,17 @@ struct sockaddr *rpc_get_recv_sockaddr(struct rpc_context *rpc)
 {
        return (struct sockaddr *)&rpc->udp_src;
 }
+
+int rpc_queue_length(struct rpc_context *rpc)
+{
+       int i=0;
+       struct rpc_pdu *pdu;
+
+       for(pdu = rpc->outqueue; pdu; pdu = pdu->next) {
+               i++;
+       }
+       for(pdu = rpc->waitpdu; pdu; pdu = pdu->next) {
+               i++;
+       }
+       return i;
+}