[Top][All Lists]
[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
- [RFT] Wrap winsock functions for improved compatibility, Paolo Bonzini, 2008/09/12
- Re: [RFT] Wrap winsock functions for improved compatibility,
Paolo Bonzini <=
- Re: [RFT] Wrap winsock functions for improved compatibility, Bruno Haible, 2008/09/12
- Re: [RFT] Wrap winsock functions for improved compatibility, Paolo Bonzini, 2008/09/12
- Re: [RFT] Wrap winsock functions for improved compatibility, Eric Blake, 2008/09/12
- Re: [RFT] Wrap winsock functions for improved compatibility, Paolo Bonzini, 2008/09/12
- Re: [RFT] Wrap winsock functions for improved compatibility, Eric Blake, 2008/09/12
- Re: [RFT] Wrap winsock functions for improved compatibility, Paolo Bonzini, 2008/09/12
- Re: [RFT] Wrap winsock functions for improved compatibility, Bruno Haible, 2008/09/13