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 <[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. 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