diff --git a/Make.mk b/Make.mk index 8f2eff70..b8ed99e7 100644 --- a/Make.mk +++ b/Make.mk @@ -159,10 +159,6 @@ CFLAGS += -Wall -W -Wno-unused-parameter -Wpointer-arith \ -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs \ -Wredundant-decls endif -ifeq ($(empthread),Windows) # really: W32, regardless of thread package -# FIXME split src/lib/w32/posixio.c -LDLIBS := $(LIBS_server) -endif $(client): LDLIBS := $(LIBS_client) $(server): LDLIBS := $(LIBS_server) diff --git a/src/lib/w32/posixio.c b/src/lib/w32/posixio.c index b2b7f3d7..257e7268 100644 --- a/src/lib/w32/posixio.c +++ b/src/lib/w32/posixio.c @@ -25,7 +25,7 @@ * * --- * - * posixio.c: POSIX I/O emulation layer for WIN32 + * posixio.c: POSIX I/O emulation layer for Windows * * Known contributors to this file: * Ron Koenderink, 2007 @@ -43,257 +43,15 @@ #include #include -#include #include -#include #include -/* - * Need to include winsock2.h before ws2tcpip.h. - * Use sys/socket.h to ensure the #undef NS_ALL - * is not missed after including winsock2.h. - */ -#include "sys/socket.h" -#include -#include - +#include #include "misc.h" #include "sys/uio.h" -#include "unistd.h" -#define W32_FD_TO_SOCKET(fd) ((SOCKET)_get_osfhandle((fd))) -#define W32_SOCKET_TO_FD(fh) (_open_osfhandle((long)(fh), O_RDWR | O_BINARY)) - -SOCKET -posix_fd2socket(int fd) -{ - return W32_FD_TO_SOCKET(fd); -} - -static int -fd_is_socket(int fd, SOCKET *sockp) -{ - SOCKET sock; - WSANETWORKEVENTS ev; - - sock = W32_FD_TO_SOCKET(fd); - if (sockp) - *sockp = sock; - return WSAEnumNetworkEvents(sock, NULL, &ev) == 0; -} - -void -w32_set_winsock_errno(void) -{ - int err = WSAGetLastError(); - WSASetLastError(0); - - /* Map some WSAE* errors to the runtime library's error codes. */ - switch (err) - { - case WSA_INVALID_HANDLE: - errno = EBADF; - break; - case WSA_NOT_ENOUGH_MEMORY: - errno = ENOMEM; - break; - case WSA_INVALID_PARAMETER: - errno = EINVAL; - break; - case WSAEWOULDBLOCK: - errno = EAGAIN; - break; - case WSAENAMETOOLONG: - errno = ENAMETOOLONG; - break; - case WSAENOTEMPTY: - errno = ENOTEMPTY; - break; - default: - errno = (err > 10000 && err < 10025) ? err - 10000 : err; - break; - } -} - -#define SOCKET_FUNCTION(expr) do { \ - SOCKET sock = W32_FD_TO_SOCKET(fd); \ - int res = (expr); \ - if (res == SOCKET_ERROR) { \ - w32_set_winsock_errno(); \ - return -1; \ - } \ - return res; \ - } while (0) - -/* - * POSIX equivalent for accept(). - */ -#undef accept -int -posix_accept(int fd, struct sockaddr *addr, socklen_t *addrlen) -{ - SOCKET sock; - - sock = accept(W32_FD_TO_SOCKET(fd), addr, addrlen); - if (sock == INVALID_SOCKET) { - w32_set_winsock_errno(); - return -1; - } - - return W32_SOCKET_TO_FD(sock); -} - -/* - * POSIX equivalent for bind(). - */ -#undef bind -int -posix_bind(int fd, const struct sockaddr *name, socklen_t namelen) -{ - SOCKET_FUNCTION(bind(sock, name, namelen)); -} - -/* - * POSIX equivalent for listen(). - */ -#undef listen -int -posix_listen(int fd, int backlog) -{ - SOCKET_FUNCTION(listen(sock, backlog)); -} - -/* - * POSIX equivalent for setsockopt(). - */ -#undef setsockopt -int -posix_setsockopt(int fd, int level, int optname, - const void *optval, socklen_t optlen) -{ - /* - * SO_REUSEADDR requests to permit another bind even when the - * port is still in state TIME_WAIT. Windows' SO_REUSEADDR is - * broken: it makes bind() succeed no matter what, even if - * there's another server running on the same port. Luckily, - * bind() seems to be broken as well: it seems to succeed while - * the port is in state TIME_WAIT by default; thus we get the - * behavior we want by not setting SO_REUSEADDR. - */ - if (level == SOL_SOCKET && optname == SO_REUSEADDR) - return 0; - { - SOCKET_FUNCTION(setsockopt(sock, level, optname, optval, optlen)); - } -} - -/* - * POSIX equivalent for shutdown(). - */ -#undef shutdown -int -posix_shutdown(int fd, int how) -{ - SOCKET_FUNCTION(shutdown(sock, how)); -} - -/* - * POSIX equivalent for socket(). - */ -#undef socket -int -posix_socket(int domain, int type, int protocol) -{ - SOCKET sock; - - /* - * We have to use WSASocket() to create non-overlapped IO sockets. - * Overlapped IO sockets cannot be used with read/write. - */ - sock = WSASocket(domain, type, protocol, NULL, 0, 0); - if (sock == INVALID_SOCKET) { - w32_set_winsock_errno(); - return -1; - } - return W32_SOCKET_TO_FD(sock); -} - -#ifdef HAVE_GETADDRINFO -const char * -inet_ntop(int af, const void *src, char *dst, socklen_t len) -{ - struct sockaddr *sa; - struct sockaddr_in sin; - struct sockaddr_in6 sin6; - size_t salen; - - if (af == AF_INET) { - memset(&sin, 0, sizeof(sin)); - sin.sin_family = af; - memcpy(&sin.sin_addr, src, sizeof(sin.sin_addr)); - sa = (struct sockaddr *)&sin; - salen = sizeof(sin); - } else if (af == AF_INET6) { - memset(&sin6, 0, sizeof(sin6)); - sin6.sin6_family = af; - memcpy(&sin6.sin6_addr, src, sizeof(sin6.sin6_addr)); - sa = (struct sockaddr *)&sin6; - salen = sizeof(sin6); - } else { - WSASetLastError(WSAEAFNOSUPPORT); - w32_set_winsock_errno(); - return NULL; - } - - if (getnameinfo(sa, salen, dst, len, NULL, 0, NI_NUMERICHOST)) { - WSASetLastError(WSAEAFNOSUPPORT); - w32_set_winsock_errno(); - return NULL; - } - - return dst; -} -#endif - -/* - * POSIX equivalent for close(). - */ -int -posix_close(int fd) -{ - SOCKET sock; - - if (fd_is_socket(fd, &sock)) { - if (closesocket(sock)) { - w32_set_winsock_errno(); - return -1; - } - /* - * This always fails because the underlying handle is already - * gone, but it closes the fd just fine. - */ - _close(fd); - return 0; - } - return _close(fd); -} - -/* - * POSIX equivalent for read(). - */ -ssize_t -posix_read(int fd, void *buf, size_t sz) -{ - SOCKET sock; - ssize_t res; - - if (fd_is_socket(fd, &sock)) { - res = recv(sock, buf, sz, 0); - if (res < 0) - w32_set_winsock_errno(); - return res; - } - return _read(fd, buf, sz); -} +int (*w32_close_function)(int) = _close; +int (*w32_read_function)(int, void *, unsigned) = _read; +int (*w32_write_function)(int, const void *, unsigned) = _write; /* * POSIX equivalent for readv @@ -318,7 +76,7 @@ readv(int fd, const struct iovec *iov, int iovcnt) return -1; } - bytes_read = posix_read(fd, buffer, total_bytes); + bytes_read = read(fd, buffer, total_bytes); if (bytes_read <= 0) { free(buffer); return -1; @@ -342,24 +100,6 @@ readv(int fd, const struct iovec *iov, int iovcnt) return bytes_read; } -/* - * POSIX equivalent for write(). - */ -ssize_t -posix_write(int fd, const void *buf, size_t sz) -{ - SOCKET sock; - ssize_t res; - - if (fd_is_socket(fd, &sock)) { - res = send(sock, buf, sz, 0); - if (res < 0) - w32_set_winsock_errno(); - return res; - } - return _write(fd, buf, sz); -} - /* * POSIX equivalent for writev * Modelled after the GNU's libc/sysdeps/posix/writev.c @@ -387,7 +127,7 @@ writev(int fd, const struct iovec *iov, int iovcnt) buffer_location += iov[i].iov_len; } - bytes_written = posix_write(fd, buffer, total_bytes); + bytes_written = write(fd, buffer, total_bytes); free(buffer); @@ -395,38 +135,3 @@ writev(int fd, const struct iovec *iov, int iovcnt) return -1; return bytes_written; } - -/* - * POSIX equivalent for fcntl(). - * Horrible hacks, just good enough support Empire's use of fcntl(). - * F_GETFL / F_SETFL support making a socket (non-)blocking by getting - * flags, adding or removing O_NONBLOCK, and setting the result. - */ -int -fcntl(int fd, int cmd, ...) -{ - va_list ap; - int value; - unsigned long nonblocking; - SOCKET sock; - - switch (cmd) - { - case F_GETFL: - return 0; - case F_SETFL: - sock = W32_FD_TO_SOCKET(fd); - va_start(ap, cmd); - value = va_arg(ap, int); - va_end(ap); - nonblocking = (value & O_NONBLOCK) != 0; - - if (ioctlsocket(sock, FIONBIO, &nonblocking) == SOCKET_ERROR) { - w32_set_winsock_errno(); - return -1; - } - return 0; - } - errno = EINVAL; - return -1; -} diff --git a/src/lib/w32/sys/socket.h b/src/lib/w32/sys/socket.h index 646f725f..f9ed35fa 100644 --- a/src/lib/w32/sys/socket.h +++ b/src/lib/w32/sys/socket.h @@ -63,5 +63,6 @@ extern int posix_socket(int domain, int type, int protocol); /* Low-level stuff specific to the emulation */ extern SOCKET posix_fd2socket(int fd); extern void w32_set_winsock_errno(void); +extern int w32_socket_init(void); #endif /* SYS_SOCKET_H */ diff --git a/src/lib/w32/unistd.h b/src/lib/w32/unistd.h index 963c07b0..22944494 100644 --- a/src/lib/w32/unistd.h +++ b/src/lib/w32/unistd.h @@ -95,14 +95,12 @@ extern int posix_mkdir(const char *dirname, mode_t perm); extern int fcntl(int fd, int cmd, ...); /* Stuff that actually belongs here */ -#define close(fd) \ - posix_close((fd)) -#define read posix_read -extern ssize_t posix_read(int, void *, size_t); -#define write(fd, buffer, count) \ - posix_write((fd), (buffer), (count)) -extern ssize_t posix_write(int, const void *, size_t); -extern int posix_close(int fd); +#define close(fd) w32_close_function((fd)) +extern int (*w32_close_function)(int); #define ftruncate(fd, length) _chsize((fd), (length)) +#define read(fd, buf, sz) w32_read_function((fd), (buf), (sz)) +extern int (*w32_read_function)(int, void *, unsigned); +#define write(fd, buf, sz) w32_write_function((fd), (buf), (sz)) +extern int (*w32_write_function)(int, const void *, unsigned); #endif /* UNISTD_H */ diff --git a/src/lib/w32/w32sockets.c b/src/lib/w32/w32sockets.c new file mode 100644 index 00000000..13feba3e --- /dev/null +++ b/src/lib/w32/w32sockets.c @@ -0,0 +1,362 @@ +/* + * Empire - A multi-player, client/server Internet based war game. + * Copyright (C) 1986-2009, Dave Pare, Jeff Bailey, Thomas Ruschak, + * Ken Stevens, Steve McClure + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * --- + * + * See files README, COPYING and CREDITS in the root of the source + * tree for related information and legal notices. It is expected + * that future projects/authors will amend these files as needed. + * + * --- + * + * w32sockets.c: POSIX socket emulation layer for Windows + * + * Known contributors to this file: + * Ron Koenderink, 2007 + * Markus Armbruster, 2007-2009 + */ + +/* + * POSIX sockets are file descriptors. Windows sockets are something + * else, with a separate set of functions to operate on them. To + * present a more POSIX-like interface to our application code, we + * provide a compatibility layer that wraps file descriptors around + * sockets. + */ + +#include + +#include +#include +#include +/* + * Need to include winsock2.h before ws2tcpip.h. + * Use sys/socket.h to ensure the #undef NS_ALL + * is not missed after including winsock2.h. + */ +#include "sys/socket.h" +#include +#include +#include "unistd.h" + +#define W32_FD_TO_SOCKET(fd) ((SOCKET)_get_osfhandle((fd))) +#define W32_SOCKET_TO_FD(fh) (_open_osfhandle((long)(fh), O_RDWR | O_BINARY)) + +SOCKET +posix_fd2socket(int fd) +{ + return W32_FD_TO_SOCKET(fd); +} + +static int +fd_is_socket(int fd, SOCKET *sockp) +{ + SOCKET sock; + WSANETWORKEVENTS ev; + + sock = W32_FD_TO_SOCKET(fd); + if (sockp) + *sockp = sock; + return WSAEnumNetworkEvents(sock, NULL, &ev) == 0; +} + +void +w32_set_winsock_errno(void) +{ + int err = WSAGetLastError(); + WSASetLastError(0); + + /* Map some WSAE* errors to the runtime library's error codes. */ + switch (err) + { + case WSA_INVALID_HANDLE: + errno = EBADF; + break; + case WSA_NOT_ENOUGH_MEMORY: + errno = ENOMEM; + break; + case WSA_INVALID_PARAMETER: + errno = EINVAL; + break; + case WSAEWOULDBLOCK: + errno = EAGAIN; + break; + case WSAENAMETOOLONG: + errno = ENAMETOOLONG; + break; + case WSAENOTEMPTY: + errno = ENOTEMPTY; + break; + default: + errno = (err > 10000 && err < 10025) ? err - 10000 : err; + break; + } +} + +#define SOCKET_FUNCTION(expr) do { \ + SOCKET sock = W32_FD_TO_SOCKET(fd); \ + int res = (expr); \ + if (res == SOCKET_ERROR) { \ + w32_set_winsock_errno(); \ + return -1; \ + } \ + return res; \ + } while (0) + +/* + * POSIX equivalent for accept(). + */ +#undef accept +int +posix_accept(int fd, struct sockaddr *addr, socklen_t *addrlen) +{ + SOCKET sock; + + sock = accept(W32_FD_TO_SOCKET(fd), addr, addrlen); + if (sock == INVALID_SOCKET) { + w32_set_winsock_errno(); + return -1; + } + + return W32_SOCKET_TO_FD(sock); +} + +/* + * POSIX equivalent for bind(). + */ +#undef bind +int +posix_bind(int fd, const struct sockaddr *name, socklen_t namelen) +{ + SOCKET_FUNCTION(bind(sock, name, namelen)); +} + +/* + * POSIX equivalent for listen(). + */ +#undef listen +int +posix_listen(int fd, int backlog) +{ + SOCKET_FUNCTION(listen(sock, backlog)); +} + +/* + * POSIX equivalent for setsockopt(). + */ +#undef setsockopt +int +posix_setsockopt(int fd, int level, int optname, + const void *optval, socklen_t optlen) +{ + /* + * SO_REUSEADDR requests to permit another bind even when the + * port is still in state TIME_WAIT. Windows' SO_REUSEADDR is + * broken: it makes bind() succeed no matter what, even if + * there's another server running on the same port. Luckily, + * bind() seems to be broken as well: it seems to succeed while + * the port is in state TIME_WAIT by default; thus we get the + * behavior we want by not setting SO_REUSEADDR. + */ + if (level == SOL_SOCKET && optname == SO_REUSEADDR) + return 0; + { + SOCKET_FUNCTION(setsockopt(sock, level, optname, optval, optlen)); + } +} + +/* + * POSIX equivalent for shutdown(). + */ +#undef shutdown +int +posix_shutdown(int fd, int how) +{ + SOCKET_FUNCTION(shutdown(sock, how)); +} + +/* + * POSIX equivalent for socket(). + */ +#undef socket +int +posix_socket(int domain, int type, int protocol) +{ + SOCKET sock; + + /* + * We have to use WSASocket() to create non-overlapped IO sockets. + * Overlapped IO sockets cannot be used with read/write. + */ + sock = WSASocket(domain, type, protocol, NULL, 0, 0); + if (sock == INVALID_SOCKET) { + w32_set_winsock_errno(); + return -1; + } + return W32_SOCKET_TO_FD(sock); +} + +#ifdef HAVE_GETADDRINFO +const char * +inet_ntop(int af, const void *src, char *dst, socklen_t len) +{ + struct sockaddr *sa; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + size_t salen; + + if (af == AF_INET) { + memset(&sin, 0, sizeof(sin)); + sin.sin_family = af; + memcpy(&sin.sin_addr, src, sizeof(sin.sin_addr)); + sa = (struct sockaddr *)&sin; + salen = sizeof(sin); + } else if (af == AF_INET6) { + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_family = af; + memcpy(&sin6.sin6_addr, src, sizeof(sin6.sin6_addr)); + sa = (struct sockaddr *)&sin6; + salen = sizeof(sin6); + } else { + WSASetLastError(WSAEAFNOSUPPORT); + w32_set_winsock_errno(); + return NULL; + } + + if (getnameinfo(sa, salen, dst, len, NULL, 0, NI_NUMERICHOST)) { + WSASetLastError(WSAEAFNOSUPPORT); + w32_set_winsock_errno(); + return NULL; + } + + return dst; +} +#endif + +/* + * POSIX equivalent for fcntl(). + * Horrible hacks, just good enough support Empire's use of fcntl(). + * F_GETFL / F_SETFL support making a socket (non-)blocking by getting + * flags, adding or removing O_NONBLOCK, and setting the result. + */ +int +fcntl(int fd, int cmd, ...) +{ + va_list ap; + int value; + unsigned long nonblocking; + SOCKET sock; + + switch (cmd) + { + case F_GETFL: + return 0; + case F_SETFL: + sock = W32_FD_TO_SOCKET(fd); + va_start(ap, cmd); + value = va_arg(ap, int); + va_end(ap); + nonblocking = (value & O_NONBLOCK) != 0; + + if (ioctlsocket(sock, FIONBIO, &nonblocking) == SOCKET_ERROR) { + w32_set_winsock_errno(); + return -1; + } + return 0; + } + errno = EINVAL; + return -1; +} + +/* + * close() replacement that does the right thing for sockets + */ +static int +w32_close_maybe_socket(int fd) +{ + SOCKET sock; + + if (fd_is_socket(fd, &sock)) { + if (closesocket(sock)) { + w32_set_winsock_errno(); + return -1; + } + /* + * This always fails because the underlying handle is already + * gone, but it closes the fd just fine. + */ + _close(fd); + return 0; + } + return _close(fd); +} + +/* + * read() replacement that does the right thing for sockets + */ +static int +w32_read_maybe_socket(int fd, void *buf, unsigned sz) +{ + SOCKET sock; + ssize_t res; + + if (fd_is_socket(fd, &sock)) { + res = recv(sock, buf, sz, 0); + if (res < 0) + w32_set_winsock_errno(); + return res; + } + return _read(fd, buf, sz); +} + +/* + * write() replacement that does the right thing for sockets + */ +static int +w32_write_maybe_socket(int fd, const void *buf, unsigned sz) +{ + SOCKET sock; + ssize_t res; + + if (fd_is_socket(fd, &sock)) { + res = send(sock, buf, sz, 0); + if (res < 0) + w32_set_winsock_errno(); + return res; + } + return _write(fd, buf, sz); +} + +int +w32_socket_init(void) +{ + int rc; + WORD wVersionRequested; + WSADATA wsaData; + + wVersionRequested = MAKEWORD(2, 0); + rc = WSAStartup(wVersionRequested, &wsaData); + if (rc != 0) + return -1; + + w32_close_function = w32_close_maybe_socket; + w32_read_function = w32_read_maybe_socket; + w32_write_function = w32_write_maybe_socket; + return 0; +} diff --git a/src/server/main.c b/src/server/main.c index ca601581..fed1c093 100644 --- a/src/server/main.c +++ b/src/server/main.c @@ -32,7 +32,7 @@ * Steve McClure, 1996, 1998 * Doug Hay, 1998 * Ron Koenderink, 2004-2009 - * Markus Armbruster, 2005-2008 + * Markus Armbruster, 2005-2009 */ #include @@ -48,6 +48,7 @@ #if defined(_WIN32) #include #include "service.h" +#include "sys/socket.h" #endif #include "empio.h" @@ -75,7 +76,6 @@ static void create_pidfile(char *, pid_t); #if defined(_WIN32) static void loc_NTInit(void); -static void loc_NTTerm(void); #endif /* @@ -385,9 +385,6 @@ void finish_server(void) { ef_fin_srv(); -#if defined(_WIN32) - loc_NTTerm(); -#endif journal_shutdown(); remove(pidfname); } @@ -464,20 +461,11 @@ static void loc_NTInit(void) { int rc; - WORD wVersionRequested; - WSADATA wsaData; - wVersionRequested = MAKEWORD(2, 0); - rc = WSAStartup(wVersionRequested, &wsaData); + rc = w32_socket_init(); if (rc != 0) { - logerror("WSAStartup failed. %d", rc); + logerror("WSAStartup Failed, error code %d\n", rc); exit(1); } } - -static void -loc_NTTerm(void) -{ - WSACleanup(); -} #endif