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 <[EMAIL PROTECTED]> Date: Fri Sep 12 08:43:03 2008 +0200 add sockets wrappers 2008-09-12 Paolo Bonzini <[EMAIL PROTECTED]> * 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);