bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[RFT final 1/2] Provide winsock wrappers for increased compatibility


From: Paolo Bonzini
Subject: [RFT final 1/2] Provide winsock wrappers for increased compatibility
Date: Sat, 13 Sep 2008 11:13:00 +0200
User-agent: Thunderbird 2.0.0.16 (Macintosh/20080707)

This patch fixes all the issues that Bruno found, except the need to
move parts of the sys_socket module to errno/stdio/string.  I didn't do
that for these reasons:

1) regarding errno, the #defines were already there before the patch;

2) regarding string, strerror does not need to be replaced under mingw;

3) regarding stdio, laziness.

The patch was tested together with the poll rewrite patch 2/2 under
Wine; the poll tests provide a decent coverage (the errno translation,
ioctl, read/write, socket/bind/connect/accept/close).

AFAIK, Eric tested it under Cygwin.  A proper test under MinGW should
not be necessary, but it would not be bad either of course.

The patch disables Winsock's select, and breaks the current
implementation of poll in gnulib (because both expect socket descriptors
under Windows).  The proper way to fix it would be to mutuate code from
the poll implementation in part 2, modifying the interface to the one of
select(2); until this is done, we could ask gnulib applications to use
poll(2) which on POSIX systems is more efficient.  Anyway, for this
reason the two patches should go in together.

Here is a possible NEWS entry:

"Under Windows (MinGW), the sys_socket module now makes socket
descriptors compatible with file descriptors.  As a result, you should
use ioctl and close instead of ioctlsocket and closesocket, and test
errno instead of WSAGetLastError ().  This change does not remove the
need to call the gl_sockets_startup function from the sockets gnulib module.

In general, this change will only make your code more portable between
POSIX platforms and Windows.  However, for now select is disabled when
you include the sys_socket module.  For portability to Windows it is
suggested to use the poll system call and gnulib module instead."

Paolo
commit 548bc6b238492bf4f22e5202d27cae529bbde20d
Author: Paolo Bonzini <address@hidden>
Date:   Fri Sep 12 08:43:03 2008 +0200

    add sockets wrappers
    
    2008-09-12  Paolo Bonzini  <address@hidden>
    
        * lib/sys_socket.in.h: For Win32, map WinSock error codes so that
        those overlapping with MSVCRT error codes coincide.  Do not
        implement rpl_setsockopt here, instead define prototypes for
        a full set of wrappers.  Ensure that Cygwin does not use the
        compatibility code, which is only for MinGW.
        * lib/winsock.c: New.
        * m4/sys_socket_h.m4: Compile lib/winsock.c if WinSock is being used.
        * modules/sys_socket: Add lib/winsock.c.
        * tests/test-poll.c: Use ioctl, not ioctlsocket.

diff --git a/lib/sys_socket.in.h b/lib/sys_socket.in.h
index 7c8ad30..ef464a1 100644
--- a/lib/sys_socket.in.h
+++ b/lib/sys_socket.in.h
@@ -58,6 +58,10 @@
 
 #else
 
+# ifdef __CYGWIN__
+#  error "Cygwin does have a sys/socket.h, doesn't it?!?"
+# endif
+
 /* A platform that lacks <sys/socket.h>.
 
    Currently only MinGW is supported.  See the gnulib manual regarding
@@ -94,26 +98,114 @@
 #  define SHUT_RDWR SD_BOTH
 # endif
 
-# if defined _WIN32 || defined __WIN32__
-#  define EINPROGRESS            WSAEINPROGRESS
-#  define ENOTSOCK                WSAENOTSOCK
-#  define EADDRINUSE              WSAEADDRINUSE
-#  define ENETRESET               WSAENETRESET
-#  define ECONNABORTED            WSAECONNABORTED
-#  define ECONNRESET              WSAECONNRESET
-#  define ENOTCONN                WSAENOTCONN
-#  define ESHUTDOWN               WSAESHUTDOWN
-# endif
-
-# if (defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__
-#  define setsockopt(a,b,c,d,e) rpl_setsockopt(a,b,c,d,e)
+# if @HAVE_WINSOCK2_H@
+/* Include headers needed by the emulation code.  */
+#  include <sys/types.h>
+#  include <io.h>
+
+/* Define POSIX-compatible error codes.  */
+#  define EADDRINUSE           WSAEADDRINUSE
+#  define EADDRNOTAVAIL                WSAEADDRNOTAVAIL
+#  define EAFNOSUPPORT         WSAEAFNOSUPPORT
+#  define ECONNABORTED         WSAECONNABORTED
+#  define ECONNREFUSED         WSAECONNREFUSED
+#  define ECONNRESET           WSAECONNRESET
+#  define EINPROGRESS          WSAEINPROGRESS
+#  define EISCONN              WSAEISCONN
+#  define ENETDOWN             WSAENETDOWN
+#  define ENETRESET            WSAENETRESET
+#  define ENETUNREACH          WSAENETUNREACH
+#  define ENOPROTOOPT          WSAENOPROTOOPT
+#  define ENOTCONN             WSAENOTCONN
+#  define ENOTSOCK             WSAENOTSOCK
+#  define EPFNOSUPPORT         WSAEPFNOSUPPORT
+#  define EPROTONOSUPPORT      WSAEPROTONOSUPPORT
+#  define EPROTOTYPE           WSAEPROTOTYPE
+#  define ESHUTDOWN            WSAESHUTDOWN
+#  define ESOCKTNOSUPPORT      WSAESOCKTNOSUPPORT
+#  define ETIMEDOUT            WSAETIMEDOUT
+#  define EWOULDBLOCK          WSAEWOULDBLOCK
+
+typedef int socklen_t;
+
+/* Re-define FD_ISSET to avoid a WSA call while we are not using 
+   network sockets.  */
 static inline int
-rpl_setsockopt(int socket, int level, int optname, const void *optval,
-              socklen_t optlen)
+rpl_fd_isset (int fd, fd_set * set)
 {
-  return (setsockopt)(socket, level, optname, optval, optlen);
+  int i;
+  if (set == NULL)
+    return 0;
+
+  for (i = 0; i < set->fd_count; i++)
+    if (set->fd_array[i] == fd)
+      return 1;
+
+  return 0;
 }
-# endif
+
+#  undef FD_ISSET
+#  define FD_ISSET(fd, set) rpl_fd_isset(fd, set)
+
+/* Wrap everything else to use libc file descriptors for sockets.  */
+
+#  undef close
+#  define close                        rpl_close
+#  undef socket
+#  define socket               rpl_socket
+#  undef connect
+#  define connect              rpl_connect
+#  undef accept
+#  define accept               rpl_accept
+#  undef bind
+#  define bind                 rpl_bind
+#  undef getpeername
+#  define getpeername          rpl_getpeername
+#  undef getsockname
+#  define getsockname          rpl_getsockname
+#  undef getsockopt
+#  define getsockopt           rpl_getsockopt
+#  undef listen
+#  define listen               rpl_listen
+#  undef ioctl
+#  define ioctl                        rpl_ioctl
+#  undef recv
+#  define recv                 rpl_recv
+#  undef send
+#  define send                 rpl_send
+#  undef recvfrom
+#  define recvfrom             rpl_recvfrom
+#  undef sendto
+#  define sendto               rpl_sendto
+#  undef setsockopt
+#  define setsockopt           rpl_setsockopt
+#  undef strerror
+#  define strerror             rpl_strerror
+#  undef perror
+#  define perror               rpl_perror
+
+#  undef select
+#  define select               select_not_supported_under_win32_use_poll
+
+extern int rpl_close(int);
+extern int rpl_socket (int, int, int protocol);
+extern int rpl_connect (int, struct sockaddr *, int);
+extern int rpl_accept (int, struct sockaddr *, int *);
+extern int rpl_bind (int, struct sockaddr *, int);
+extern int rpl_getpeername (int, struct sockaddr *, int *);
+extern int rpl_getsockname (int, struct sockaddr *, int *);
+extern int rpl_getsockopt (int, int, int, void *, int *);
+extern int rpl_listen (int, int);
+extern int rpl_ioctl (int, unsigned long, char *);
+extern int rpl_recv (int, void *, int, int);
+extern int rpl_send (int, const void *, int, int);
+extern int rpl_recvfrom (int, void *, int, int, struct sockaddr *, int *);
+extern int rpl_sendto (int, const void *, int, int, struct sockaddr *, int);
+extern int rpl_setsockopt (int, int, int, const void *, int);
+extern const char *rpl_strerror (int);
+extern void rpl_perror (const char *);
+
+# endif /* HAVE_WINSOCK2_H */
 
 #endif /* HAVE_SYS_SOCKET_H */
 
diff --git a/lib/winsock.c b/lib/winsock.c
new file mode 100644
index 0000000..5fc2f87
--- /dev/null
+++ b/lib/winsock.c
@@ -0,0 +1,401 @@
+/* winsock.c --- wrappers for Windows socket functions
+
+   Copyright (C) 2008 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Written by Paolo Bonzini */
+
+#include <config.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <io.h>
+#include <sys/socket.h>
+
+#undef close
+#undef socket
+#undef connect
+#undef accept
+#undef bind
+#undef getpeername
+#undef getsockname
+#undef getsockopt
+#undef listen
+#undef recv
+#undef send
+#undef recvfrom
+#undef sendto
+#undef setsockopt
+#undef strerror
+
+# define FD_TO_SOCKET(fd)   ((SOCKET) _get_osfhandle ((fd)))
+# define SOCKET_TO_FD(fh)   (_open_osfhandle ((long) (fh), O_RDWR | O_BINARY))
+
+/* internal to Microsoft CRTLIB */
+typedef struct
+{
+  long osfhnd;         /* underlying OS file HANDLE */
+  char osfile;         /* attributes of file (e.g., open in text mode?) */
+  char pipech;         /* one char buffer for handles opened on pipes */
+#ifdef _MT
+  int lockinitflag;
+  CRITICAL_SECTION lock;
+#endif                         /* _MT */
+} ioinfo;
+
+#define IOINFO_L2E          5
+#define IOINFO_ARRAY_ELTS   (1 << IOINFO_L2E)
+#define _pioinfo(i)        ( __pioinfo[(i) >> IOINFO_L2E] + \
+                            ((i) & (IOINFO_ARRAY_ELTS - 1)) )
+#define _osfile(i)         (_pioinfo(i)->osfile)
+#define _osfhnd(i)         (_pioinfo(i)->osfhnd)
+
+#define FOPEN                      0x01
+
+/* TODO: support MSVC++.  */
+#ifdef __declspec
+extern __attribute__ ((dllimport)) ioinfo * __pioinfo[];
+#else
+extern __declspec(dllimport) ioinfo * __pioinfo[];
+#endif
+
+static int
+my_free_osfhnd (int filehandle)
+{
+  if ((_osfile (filehandle) & FOPEN) &&
+      (_osfhnd (filehandle) != (long) INVALID_HANDLE_VALUE))
+    {
+      switch (filehandle)
+       {
+       case 0:
+         SetStdHandle (STD_INPUT_HANDLE, NULL);
+         break;
+       case 1:
+         SetStdHandle (STD_OUTPUT_HANDLE, NULL);
+         break;
+       case 2:
+         SetStdHandle (STD_ERROR_HANDLE, NULL);
+         break;
+       }
+      _osfhnd (filehandle) = (long) INVALID_HANDLE_VALUE;
+      return (0);
+    }
+  else
+    {
+      errno = EBADF;           /* bad handle */
+      _doserrno = 0L;          /* not an OS error */
+      return -1;
+    }
+}
+
+
+/* Wrappers for libc functions.  */
+
+int
+rpl_close (int fd)
+{
+  char buf[sizeof (int)];
+  int bufsize = sizeof (buf);
+  SOCKET sock = FD_TO_SOCKET (fd);
+
+  if (getsockopt (sock, SOL_SOCKET, SO_TYPE, buf, &bufsize) == 0)
+    {
+      int r = closesocket (sock);
+
+      my_free_osfhnd (fd);
+      _osfile (fd) = 0;
+      return r;
+    }
+  else
+    return _close (fd);
+}
+
+
+/* Wrappers for WinSock functions.  */
+
+static inline void
+set_winsock_errno (void)
+{
+  int err = WSAGetLastError ();
+  WSASetLastError (0);
+
+  /* Map some WSAE* errors to the runtime library's error codes.  */
+  if (err > 10000 && err < 10025)
+    errno = err - 10000;
+  else
+    errno = err;
+}
+
+int
+rpl_socket (int domain, int type, int protocol)
+{
+  int fd;
+  SOCKET fh = socket (domain, type, protocol);
+  if (fh == INVALID_SOCKET)
+    {
+      set_winsock_errno ();
+      return -1;
+    }
+  else
+    return SOCKET_TO_FD (fh);
+}
+
+
+int
+rpl_connect (int fd, struct sockaddr *sockaddr, int len)
+{
+  SOCKET sock = FD_TO_SOCKET (fd);
+  int r = connect (sock, sockaddr, len);
+  if (r < 0)
+    {
+      /* EINPROGRESS is not returned by WinSock 2.0; for backwards
+        compatibility, connect(2) uses EWOULDBLOCK.  */
+      if (WSAGetLastError () == WSAEWOULDBLOCK)
+        WSASetLastError (WSAEINPROGRESS);
+
+      set_winsock_errno ();
+    }
+
+  return r;
+}
+
+int
+rpl_accept (int fd, struct sockaddr *addr, int *addrlen)
+{
+  SOCKET fh = accept (FD_TO_SOCKET (fd), addr, addrlen);
+  if (fh == INVALID_SOCKET)
+    {
+      set_winsock_errno ();
+      return -1;
+    }
+  else
+    return SOCKET_TO_FD (fh);
+}
+
+int
+rpl_bind (int fd, struct sockaddr *sockaddr, int len)
+{
+  SOCKET sock = FD_TO_SOCKET (fd);
+  int r = bind (sock, sockaddr, len);
+  if (r < 0)
+    set_winsock_errno ();
+
+  return r;
+}
+
+int
+rpl_getpeername (int fd, struct sockaddr *addr, int *addrlen)
+{
+  SOCKET sock = FD_TO_SOCKET (fd);
+  int r = getpeername (sock, addr, addrlen);
+  if (r < 0)
+    set_winsock_errno ();
+
+  return r;
+}
+
+int
+rpl_getsockname (int fd, struct sockaddr *addr, int *addrlen)
+{
+  SOCKET sock = FD_TO_SOCKET (fd);
+  int r = getsockname (sock, addr, addrlen);
+  if (r < 0)
+    set_winsock_errno ();
+
+  return r;
+}
+
+int
+rpl_getsockopt (int fd, int level, int optname, void *optval, int *optlen)
+{
+  SOCKET sock = FD_TO_SOCKET (fd);
+  int r = getsockopt (sock, level, optname, optval, optlen);
+  if (r < 0)
+    set_winsock_errno ();
+
+  return r;
+}
+
+int
+rpl_listen (int fd, int backlog)
+{
+  SOCKET sock = FD_TO_SOCKET (fd);
+  int r = listen (sock, backlog);
+  if (r < 0)
+    set_winsock_errno ();
+
+  return r;
+}
+
+int
+rpl_ioctl (int fd, unsigned long req, char *buf)
+{
+  SOCKET sock = FD_TO_SOCKET (fd);
+  int r = ioctlsocket (sock, req, (void *) buf);
+  if (r < 0)
+    set_winsock_errno ();
+
+  return r;
+}
+
+int
+rpl_recv (int fd, void *buf, int len, int flags)
+{
+  SOCKET sock = FD_TO_SOCKET (fd);
+  int r = recv (sock, buf, len, flags);
+  if (r < 0)
+    set_winsock_errno ();
+
+  return r;
+}
+
+int
+rpl_send (int fd, const void *buf, int len, int flags)
+{
+  SOCKET sock = FD_TO_SOCKET (fd);
+  int r = send (sock, buf, len, flags);
+  if (r < 0)
+    set_winsock_errno ();
+
+  return r;
+}
+
+int
+rpl_recvfrom (int fd, void *buf, int len, int flags, struct sockaddr *from,
+             int *fromlen)
+{
+  int frombufsize = *fromlen;
+  SOCKET sock = FD_TO_SOCKET (fd);
+  int r = recvfrom (sock, buf, len, flags, from, fromlen);
+
+  if (r < 0)
+    set_winsock_errno ();
+
+  /* Winsock recvfrom() only returns a valid 'from' when the socket is
+     connectionless.  POSIX gives a valid 'from' for all types of sockets.  */
+  else if (*fromlen == frombufsize)
+    rpl_getpeername (fd, from, fromlen);
+
+  return r;
+}
+
+int
+rpl_sendto (int fd, const void *buf, int len, int flags,
+           struct sockaddr *to, int tolen)
+{
+  SOCKET sock = FD_TO_SOCKET (fd);
+  int r = sendto (sock, buf, len, flags, to, tolen);
+  if (r < 0)
+    set_winsock_errno ();
+
+  return r;
+}
+
+int
+rpl_setsockopt (int fd, int level, int optname, const void *optval, int optlen)
+{
+  SOCKET sock = FD_TO_SOCKET (fd);
+  int r = setsockopt (sock, level, optname, optval, optlen);
+  if (r < 0)
+    set_winsock_errno ();
+
+  return r;
+}
+
+
+/* Support for WinSock errors in errno functions.  */
+
+const char *
+rpl_strerror (int err)
+{
+  switch (err)
+    {
+    case EADDRINUSE:
+      return "Address already in use";
+
+    case EADDRNOTAVAIL:
+      return "Cannot assign requested address";
+
+    case EAFNOSUPPORT:
+      return "Address family not supported by protocol family";
+
+    case ECONNABORTED:
+      return "Software caused connection abort";
+
+    case ECONNREFUSED:
+      return "Connection refused";
+
+    case ECONNRESET:
+      return "Connection reset by peer";
+
+    case EINPROGRESS:
+      return "Operation in progress";
+
+    case EISCONN:
+      return "Socket is already connected";
+
+    case ENETDOWN:
+      return "Network is down";
+
+    case ENETRESET:
+      return "Network dropped connection on reset";
+
+    case ENETUNREACH:
+      return "Network is unreachable";
+
+    case ENOPROTOOPT:
+      return "Protocol not available";
+
+    case ENOTCONN:
+      return "Socket is not connected";
+
+    case ENOTSOCK:
+      return "Socket operation on non-socket";
+
+    case EPFNOSUPPORT:
+      return "Protocol family not supported";
+
+    case EPROTONOSUPPORT:
+      return "Protocol not supported";
+
+    case EPROTOTYPE:
+      return "Wrong protocol type for socket";
+
+    case ESHUTDOWN:
+      return "Can't send after socket shutdown";
+
+    case ESOCKTNOSUPPORT:
+      return "Socket type not supported";
+
+    case ETIMEDOUT:
+      return "Operation timed out";
+
+    case EWOULDBLOCK:
+      return "Operation would block";
+
+    default:
+      return strerror (err);
+    }
+}
+
+void
+rpl_perror (const char *string)
+{
+  if (string && *string)
+    fprintf (stderr, "%s: %s\n", string, rpl_strerror (errno));
+  else
+    fprintf (stderr, "%s\n", string);
+}
diff --git a/m4/sys_socket_h.m4 b/m4/sys_socket_h.m4
index 2e4e2f6..6a5b349 100644
--- a/m4/sys_socket_h.m4
+++ b/m4/sys_socket_h.m4
@@ -64,6 +64,9 @@ AC_DEFUN([gl_HEADER_SYS_SOCKET],
         HAVE_WS2TCPIP_H=0
       fi
     fi
+    if test x$ac_cv_header_winsock2_h = xyes; then
+      AC_LIBOBJ(winsock)
+    fi
     AC_SUBST([HAVE_SYS_SOCKET_H])
     AC_SUBST([HAVE_WINSOCK2_H])
     AC_SUBST([HAVE_WS2TCPIP_H])
diff --git a/modules/sys_socket b/modules/sys_socket
index d619a54..27ee5d8 100644
--- a/modules/sys_socket
+++ b/modules/sys_socket
@@ -3,6 +3,7 @@ A POSIX-like <sys/socket.h>.
 
 Files:
 lib/sys_socket.in.h
+lib/winsock.c
 m4/sys_socket_h.m4
 m4/sockpfaf.m4
 
diff --git a/tests/test-poll.c b/tests/test-poll.c
index 1f66a63..d339117 100644
--- a/tests/test-poll.c
+++ b/tests/test-poll.c
@@ -125,7 +125,7 @@ connect_to_socket (int blocking)
     {
 #ifdef __MSVCRT__
       unsigned long iMode = 1;
-      ioctlsocket (s, FIONBIO, (void *) &iMode);
+      ioctl (s, FIONBIO, (char *) &iMode);
  
 #elif defined F_GETFL
       int oldflags = fcntl (s, F_GETFL, NULL);

reply via email to

[Prev in Thread] Current Thread [Next in Thread]