bug-gnulib
[Top][All Lists]
Advanced

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

Re: [RFT] Wrap winsock functions for improved compatibility


From: Paolo Bonzini
Subject: Re: [RFT] Wrap winsock functions for improved compatibility
Date: Fri, 12 Sep 2008 10:43:35 +0200
User-agent: Thunderbird 2.0.0.16 (Macintosh/20080707)

Paolo Bonzini wrote:
> Hi all,
> 
> this proposed patch wraps winsock functions that take or return socket
> descriptors, so that C run-time library descriptors are used instead.
> This should be fully transparent to the user, except that you can close
> these sockets with close, read them with read/write, and so on.
> 
> The only remaining difference is the different implementation of
> FD_SETs.  However, this does not matter because (mostly out of laziness)
> I haven't implemented select.  So this means that select does not work
> at all.  It could be done, but it would be replaced with my poll
> rewrite, which would be committed at the same time as this.  You
> shouldn't use select anyway. :-)
> 
> It needs to be tested on Windows (mingw and cygwin), however.  Here is a
> trivial echo server (using port 12345) using the sys_socket and
> netinet_in gnulib modules.  It can be tested using netcat or telnet:
> 
> #include <string.h>
> #include <sys/socket.h>
> #include <netinet/in.h>
> 
> #ifdef __MSVCRT__
> #include <io.h>
> #else
> #include <unistd.h>
> #endif
> 
> #ifndef SO_REUSEPORT
> #define SO_REUSEPORT    SO_REUSEADDR
> #endif
> 
> int
> main (int argc, char **argv)
> {
>   int code, s, fd, x;
>   struct sockaddr_in ia;
>   socklen_t addrlen;
>   char buf[1024];
> 
>   s = socket (AF_INET, SOCK_STREAM, 0);
> 
>   memset (&ia, 0, sizeof (ia));
>   ia.sin_family = AF_INET;
>   inet_aton ("127.0.0.1", &ia.sin_addr);
>   ia.sin_port = htons (12345);
>   bind (s, (struct sockaddr *) &ia, sizeof (ia));
> 
>   x = 1;
>   setsockopt (s, SOL_SOCKET, SO_REUSEPORT, &x, sizeof (x));
> 
>   listen (s, 1);
>   fd = accept (s, (struct sockaddr *) &ia, &addrlen);
>   close (s);
> 
>   while ((x = read (fd, buf, 1024)) > 0)
>     write (fd, buf, x);
> 
>   x = close (fd);
>   return x < 0 ? 1 : 0;
> }
> 
> Thanks in advance!
> 
> Paolo
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.

diff --git a/lib/sys_socket.in.h b/lib/sys_socket.in.h
index d237e9c..d71f9cd 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,25 +98,112 @@
 #  define SHUT_RDWR SD_BOTH
 # endif
 
-# if defined _WIN32 || defined __WIN32__
-#  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 - 10000)
+#  define EADDRNOTAVAIL                (WSAEADDRNOTAVAIL - 10000)
+#  define EAFNOSUPPORT         (WSAEAFNOSUPPORT - 10000)
+#  define ECONNABORTED         (WSAECONNABORTED - 10000)
+#  define ECONNREFUSED         (WSAECONNREFUSED - 10000)
+#  define ECONNRESET           (WSAECONNRESET - 10000)
+#  define EISCONN              (WSAEISCONN - 10000)
+#  define ENETDOWN             (WSAENETDOWN - 10000)
+#  define ENETRESET            (WSAENETRESET - 10000)
+#  define ENETUNREACH          (WSAENETUNREACH - 10000)
+#  define ENOPROTOOPT          (WSAENOPROTOOPT - 10000)
+#  define ENOTCONN             (WSAENOTCONN - 10000)
+#  define ENOTSOCK             (WSAENOTSOCK - 10000)
+#  define EPFNOSUPPORT         (WSAEPFNOSUPPORT - 10000)
+#  define EPROTONOSUPPORT      (WSAEPROTONOSUPPORT - 10000)
+#  define EPROTOTYPE           (WSAEPROTOTYPE - 10000)
+#  define ESHUTDOWN            (WSAESHUTDOWN - 10000)
+#  define ESOCKTNOSUPPORT      (WSAESOCKTNOSUPPORT - 10000)
+#  define ETIMEDOUT            (WSAETIMEDOUT - 10000)
+
+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)
+win_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) win_fd_isset(fd, set)
+
+/* Wrap everything else to use libc file descriptors for sockets.  */
+
+#  undef close
+#  define close                        win_close
+#  undef socket
+#  define socket               win_socket
+#  undef connect
+#  define connect              win_connect
+#  undef accept
+#  define accept               win_accept
+#  undef bind
+#  define bind                 win_bind
+#  undef getpeername
+#  define getpeername          win_getpeername
+#  undef getsockname
+#  define getsockname          win_getsockname
+#  undef getsockopt
+#  define getsockopt           win_getsockopt
+#  undef listen
+#  define listen               win_listen
+#  undef ioctl
+#  define ioctl                        win_ioctl
+#  undef recv
+#  define recv                 win_recv
+#  undef send
+#  define send                 win_send
+#  undef recvfrom
+#  define recvfrom             win_recvfrom
+#  undef sendto
+#  define sendto               win_sendto
+#  undef setsockopt
+#  define setsockopt           win_setsockopt
+#  undef strerror
+#  define strerror             win_strerror
+#  undef perror
+#  define perror               win_perror
+
+#  undef select
+#  define select               select_not_supported_under_win32_use_poll
+
+extern int win_close(int);
+extern int win_socket (int, int, int protocol);
+extern int win_connect (int, struct sockaddr *, int);
+extern int win_accept (int, struct sockaddr *, int *);
+extern int win_bind (int, struct sockaddr *, int);
+extern int win_getpeername (int, struct sockaddr *, int *);
+extern int win_getsockname (int, struct sockaddr *, int *);
+extern int win_getsockopt (int, int, int, void *, int *);
+extern int win_listen (int, int);
+extern int win_ioctl (int, unsigned long, char *);
+extern int win_recv (int, void *, int, int);
+extern int win_send (int, const void *, int, int);
+extern int win_recvfrom (int, void *, int, int, struct sockaddr *, int *);
+extern int win_sendto (int, const void *, int, int, struct sockaddr *, int);
+extern int win_setsockopt (int, int, int, const void *, int);
+extern const char *win_strerror (int);
+extern void win_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..35acce3
--- /dev/null
+++ b/lib/winsock.c
@@ -0,0 +1,385 @@
+/* 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 <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 ((HANDLE) (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
+win_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);
+  if (err > 10000 && err < 10100)
+    errno = err - 10000;
+  else
+    errno = EINVAL;
+}
+
+int
+win_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
+win_connect (int fd, struct sockaddr *sockaddr, int len)
+{
+  SOCKET sock = FD_TO_SOCKET (fd);
+  int r = connect (sock, sockaddr, len);
+  if (r < 0)
+    set_winsock_errno ();
+
+  return r;
+}
+
+int
+win_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
+win_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
+win_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
+win_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
+win_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
+win_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
+win_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
+win_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
+win_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
+win_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)
+    win_getpeername (fd, from, fromlen);
+
+  return r;
+}
+
+int
+win_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
+win_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 *
+win_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 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";
+
+    default:
+      return strerror (err);
+    }
+}
+
+void
+win_perror (const char *string)
+{
+  if (string && *string)
+    fprintf (stderr, "%s: %s\n", string, win_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
 

reply via email to

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