Files
redis-For-Windows/src/Win32_Interop/Win32_FDAPI.cpp
T
Enrico Giordani a6dcd555a9 [Build] Fixed a build break (regression).
Credits to @tortuetorche for pointing it out.
2015-10-01 10:43:47 +02:00

1220 lines
41 KiB
C++

/*
* Copyright (c), Microsoft Open Technologies, Inc.
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "win32_types.h"
#define FDAPI_NOCRTREDEFS
#include "Win32_FDAPI.h"
#include "win32_rfdmap.h"
#include <exception>
#include <mswsock.h>
#include <sys/stat.h>
#include "Win32_fdapi_crt.h"
#include "Win32_variadicFunctor.h"
#include "Win32_ANSI.h"
#include "win32_util.h"
#include "Win32_RedisLog.h"
#include "Win32_Common.h"
#include "Win32_Assert.h"
using namespace std;
#define CATCH_AND_REPORT() catch(const std::exception &){::redisLog(REDIS_WARNING, "FDAPI: std exception");}catch(...){::redisLog(REDIS_WARNING, "FDAPI: other exception");}
static fnWSIOCP_CloseSocketStateRFD* wsiocp_CloseSocketState;
void FDAPI_SetCloseSocketState(fnWSIOCP_CloseSocketStateRFD* func) {
wsiocp_CloseSocketState = func;
}
extern "C" {
// FD lookup Winsock equivalents for Win32_wsiocp.c
redis_WSAGetLastError WSAGetLastError = NULL;
// other API forwards
redis_fwrite fdapi_fwrite = NULL;
redis_fclose fdapi_fclose = NULL;
redis_fileno fdapi_fileno = NULL;
redis_setmode fdapi_setmode = NULL;
redis_select select = NULL;
redis_ntohl ntohl = NULL;
redis_isatty isatty = NULL;
redis_access access = NULL;
redis_lseek64 lseek64 = NULL;
redis_get_osfhandle fdapi_get_osfhandle = NULL;
redis_open_osfhandle fdapi_open_osfhandle = NULL;
_redis_fstat fdapi_fstat64 = NULL;
// Unix compatible FD based routines
redis_pipe pipe = NULL;
redis_socket socket = NULL;
redis_close fdapi_close = NULL;
redis_open open = NULL;
redis_accept accept = NULL;
redis_setsockopt setsockopt = NULL;
redis_fcntl fcntl = NULL;
redis_poll poll = NULL;
redis_getsockopt getsockopt = NULL;
redis_connect connect = NULL;
redis_read read = NULL;
redis_write write = NULL;
redis_fsync fsync = NULL;
redis_listen listen = NULL;
redis_ftruncate ftruncate = NULL;
redis_bind bind = NULL;
redis_htons htons = NULL;
redis_htonl htonl = NULL;
redis_ntohs ntohs = NULL;
redis_getpeername getpeername = NULL;
redis_getsockname getsockname = NULL;
redis_freeaddrinfo freeaddrinfo = NULL;
redis_getaddrinfo getaddrinfo = NULL;
redis_inet_ntop inet_ntop = NULL;
}
auto f_socket = dllfunctor_stdcall<SOCKET, int, int, int>("ws2_32.dll", "socket");
auto f_closesocket = dllfunctor_stdcall<int, SOCKET>("ws2_32.dll", "closesocket");
auto f_accept = dllfunctor_stdcall<SOCKET, SOCKET, struct sockaddr*, int*>("ws2_32.dll", "accept");
auto f_setsockopt = dllfunctor_stdcall<int, SOCKET, int, int, const char*, int>("ws2_32.dll", "setsockopt");
auto f_ioctlsocket = dllfunctor_stdcall<int, SOCKET, long, u_long*>("ws2_32.dll", "ioctlsocket");
auto f_getsockopt = dllfunctor_stdcall<int, SOCKET, int, int, char*, int*>("ws2_32.dll", "getsockopt");
auto f_connect = dllfunctor_stdcall<int, SOCKET, const struct sockaddr*, int>("ws2_32.dll", "connect");
auto f_recv = dllfunctor_stdcall<int, SOCKET, char*, int, int>("ws2_32.dll", "recv");
auto f_send = dllfunctor_stdcall<int, SOCKET, const char*, int, int>("ws2_32.dll", "send");
auto f_listen = dllfunctor_stdcall<int, SOCKET, int>("ws2_32.dll", "listen");
auto f_bind = dllfunctor_stdcall<int, SOCKET, const struct sockaddr*, int>("ws2_32.dll", "bind");
auto f_htons = dllfunctor_stdcall<u_short, u_short>("ws2_32.dll", "htons");
auto f_htonl = dllfunctor_stdcall<u_long, u_long>("ws2_32.dll", "htonl");
auto f_getpeername = dllfunctor_stdcall<int, SOCKET, struct sockaddr*, int*>("ws2_32.dll", "getpeername");
auto f_getsockname = dllfunctor_stdcall<int, SOCKET, struct sockaddr*, int*>("ws2_32.dll", "getsockname");
auto f_ntohs = dllfunctor_stdcall<u_short, u_short>("ws2_32.dll", "ntohs");
auto f_select = dllfunctor_stdcall<int, int, fd_set*, fd_set*, fd_set*, const struct timeval*>("ws2_32.dll", "select");
auto f_ntohl = dllfunctor_stdcall<u_long, u_long>("ws2_32.dll", "ntohl");
auto f_freeaddrinfo = dllfunctor_stdcall<void, addrinfo*>("ws2_32.dll", "freeaddrinfo");
auto f_getaddrinfo = dllfunctor_stdcall<int, PCSTR, PCSTR, const ADDRINFOA*, ADDRINFOA**>("ws2_32.dll", "getaddrinfo");
static auto f_WSAFDIsSet = dllfunctor_stdcall<int, SOCKET, fd_set*>("ws2_32.dll", "__WSAFDIsSet");
auto f_WSAStartup = dllfunctor_stdcall<int, WORD, LPWSADATA>("ws2_32.dll", "WSAStartup");
auto f_WSACleanup = dllfunctor_stdcall<int>("ws2_32.dll", "WSACleanup");
auto f_WSAIoctl = dllfunctor_stdcall<int, SOCKET, DWORD, LPVOID, DWORD, LPVOID, DWORD, LPVOID, LPWSAOVERLAPPED, LPWSAOVERLAPPED_COMPLETION_ROUTINE>("ws2_32.dll", "WSAIoctl");
auto f_WSASetLastError = dllfunctor_stdcall<void, int>("ws2_32.dll", "WSASetLastError");
auto f_WSAGetLastError = dllfunctor_stdcall<int>("ws2_32.dll", "WSAGetLastError");
auto f_WSAGetOverlappedResult = dllfunctor_stdcall<BOOL, SOCKET, LPWSAOVERLAPPED, LPDWORD, BOOL, LPDWORD>("ws2_32.dll", "WSAGetOverlappedResult");
auto f_WSADuplicateSocket = dllfunctor_stdcall<int, SOCKET, DWORD, LPWSAPROTOCOL_INFO>("ws2_32.dll", "WSADuplicateSocketW");
auto f_WSASocket = dllfunctor_stdcall<SOCKET, int, int, int, LPWSAPROTOCOL_INFO, GROUP, DWORD>("ws2_32.dll", "WSASocketW");
auto f_WSASend = dllfunctor_stdcall<int, SOCKET, LPWSABUF, DWORD, LPDWORD, DWORD, LPWSAOVERLAPPED, LPWSAOVERLAPPED_COMPLETION_ROUTINE>("ws2_32.dll", "WSASend");
auto f_WSARecv = dllfunctor_stdcall<int, SOCKET, LPWSABUF, DWORD, LPDWORD, LPDWORD, LPWSAOVERLAPPED, LPWSAOVERLAPPED_COMPLETION_ROUTINE>("ws2_32.dll", "WSARecv");
BOOL FDAPI_SetFDInformation(int rfd, DWORD mask, DWORD flags){
try
{
SOCKET socket = RFDMap::getInstance().lookupSocket(rfd);
if (socket != INVALID_SOCKET) {
return SetHandleInformation((HANDLE) socket, mask, flags);
}
} CATCH_AND_REPORT();
return FALSE;
}
HANDLE FDAPI_CreateIoCompletionPort(int rfd, HANDLE ExistingCompletionPort,
ULONG_PTR CompletionKey, DWORD NumberOfConcurrentThreads) {
try
{
SOCKET socket = RFDMap::getInstance().lookupSocket(rfd);
if (socket != INVALID_SOCKET) {
return CreateIoCompletionPort((HANDLE) socket,
ExistingCompletionPort,
CompletionKey,
NumberOfConcurrentThreads);
}
} CATCH_AND_REPORT();
return INVALID_HANDLE_VALUE;
}
BOOL FDAPI_AcceptEx(int listenRFD, int acceptRFD, PVOID lpOutputBuffer,
DWORD dwReceiveDataLength, DWORD dwLocalAddressLength,
DWORD dwRemoteAddressLength, LPDWORD lpdwBytesReceived,
LPOVERLAPPED lpOverlapped)
{
try
{
SOCKET sListen = RFDMap::getInstance().lookupSocket(listenRFD);
SOCKET sAccept = RFDMap::getInstance().lookupSocket(acceptRFD);
if (sListen != INVALID_SOCKET && sAccept != INVALID_SOCKET) {
LPFN_ACCEPTEX acceptex;
const GUID wsaid_acceptex = WSAID_ACCEPTEX;
DWORD bytes;
if (SOCKET_ERROR == f_WSAIoctl(sListen,
SIO_GET_EXTENSION_FUNCTION_POINTER,
(void *) &wsaid_acceptex,
sizeof(GUID),
&acceptex,
sizeof(LPFN_ACCEPTEX),
&bytes,
NULL,
NULL)) {
return FALSE;
}
return acceptex(sListen,
sAccept,
lpOutputBuffer,
dwReceiveDataLength,
dwLocalAddressLength,
dwRemoteAddressLength,
lpdwBytesReceived,
lpOverlapped);
}
} CATCH_AND_REPORT();
return FALSE;
}
#ifndef SIO_LOOPBACK_FAST_PATH
const DWORD SIO_LOOPBACK_FAST_PATH = 0x98000010; // from Win8 SDK
#endif
#ifndef _WIN32_WINNT_WIN8
#define _WIN32_WINNT_WIN8 0x0602
#endif
void EnableFastLoopback(SOCKET socket) {
// if Win8+, use fast path option on loopback
if (IsWindowsVersionAtLeast(HIBYTE(_WIN32_WINNT_WIN8), LOBYTE(_WIN32_WINNT_WIN8), 0)) {
int enabled = 1;
DWORD result_byte_count = -1;
int result = f_WSAIoctl(socket,
SIO_LOOPBACK_FAST_PATH,
&enabled,
sizeof(enabled),
NULL,
0,
&result_byte_count,
NULL,
NULL);
if (result != 0) {
throw std::system_error(WSAGetLastError(), system_category(), "WSAIoctl failed");
}
}
}
BOOL FDAPI_ConnectEx(int rfd, const struct sockaddr *name, int namelen,
PVOID lpSendBuffer, DWORD dwSendDataLength, LPDWORD lpdwBytesSent,
LPOVERLAPPED lpOverlapped)
{
try
{
SOCKET socket = RFDMap::getInstance().lookupSocket(rfd);
if (socket != INVALID_SOCKET) {
LPFN_CONNECTEX connectex;
const GUID wsaid_connectex = WSAID_CONNECTEX;
DWORD bytes;
if (SOCKET_ERROR == f_WSAIoctl(socket,
SIO_GET_EXTENSION_FUNCTION_POINTER,
(void *) &wsaid_connectex,
sizeof(GUID),
&connectex,
sizeof(LPFN_ACCEPTEX),
&bytes,
NULL,
NULL)) {
return FALSE;
}
EnableFastLoopback(socket);
return connectex(socket,
name,
namelen,
lpSendBuffer,
dwSendDataLength,
lpdwBytesSent,
lpOverlapped);
}
} CATCH_AND_REPORT();
return FALSE;
}
void FDAPI_GetAcceptExSockaddrs(int rfd, PVOID lpOutputBuffer,
DWORD dwReceiveDataLength, DWORD dwLocalAddressLength,
DWORD dwRemoteAddressLength, LPSOCKADDR *LocalSockaddr,
LPINT LocalSockaddrLength, LPSOCKADDR *RemoteSockaddr,
LPINT RemoteSockaddrLength)
{
try
{
SOCKET socket = RFDMap::getInstance().lookupSocket(rfd);
if (socket != INVALID_SOCKET) {
LPFN_GETACCEPTEXSOCKADDRS getacceptsockaddrs;
const GUID wsaid_getacceptsockaddrs = WSAID_GETACCEPTEXSOCKADDRS;
DWORD bytes;
if (SOCKET_ERROR == f_WSAIoctl(socket,
SIO_GET_EXTENSION_FUNCTION_POINTER,
(void *) &wsaid_getacceptsockaddrs,
sizeof(GUID),
&getacceptsockaddrs,
sizeof(LPFN_ACCEPTEX),
&bytes,
NULL,
NULL)) {
return;
}
getacceptsockaddrs(lpOutputBuffer,
dwReceiveDataLength,
dwLocalAddressLength,
dwRemoteAddressLength,
LocalSockaddr,
LocalSockaddrLength,
RemoteSockaddr,
RemoteSockaddrLength);
}
} CATCH_AND_REPORT();
}
int FDAPI_UpdateAcceptContext(int rfd) {
try
{
SOCKET socket = RFDMap::getInstance().lookupSocket(rfd);
if (socket != INVALID_SOCKET) {
return setsockopt(rfd,
SOL_SOCKET,
SO_UPDATE_ACCEPT_CONTEXT,
(char*) &socket,
sizeof(SOCKET));
}
} CATCH_AND_REPORT();
errno = EBADF;
return INVALID_FD;
}
void** FDAPI_GetSocketStatePtr(int rfd) {
SocketInfo* socket_info = RFDMap::getInstance().lookupSocketInfo(rfd);
if (socket_info == NULL) {
return NULL;
} else {
return &(socket_info->state);
}
}
void FDAPI_ClearSocketInfo(int rfd) {
SocketInfo* socketInfo = RFDMap::getInstance().lookupSocketInfo(rfd);
ASSERT(socketInfo != NULL);
ASSERT(socketInfo->socket == INVALID_SOCKET);
if (socketInfo != NULL) {
if (socketInfo->socket == INVALID_SOCKET) {
RFDMap::getInstance().removeRFDToSocketInfo(rfd);
return;
} else {
redisLog(REDIS_WARNING, "FDAPI_ClearSocketInfo called on non closed socket.");
}
} else {
redisLog(REDIS_WARNING, "FDAPI_ClearSocketInfo called on non attached socket.");
}
}
int redis_socket_impl(int domain, int type, int protocol) {
RFD rfd = INVALID_FD;
try {
SOCKET socket = f_socket(domain, type, protocol);
if (socket != INVALID_SOCKET) {
rfd = RFDMap::getInstance().addSocket(socket);
}
return rfd;
} CATCH_AND_REPORT();
return rfd;
}
int redis_pipe_impl(int *pfds) {
int result = -1;
try {
// Not passing _O_NOINHERIT since the underlying handles are
// inheritable by default
result = crt_pipe(pfds, 8192, _O_BINARY);
if (result == 0) {
pfds[0] = RFDMap::getInstance().addCrtFD(pfds[0]);
pfds[1] = RFDMap::getInstance().addCrtFD(pfds[1]);
}
} CATCH_AND_REPORT();
return result;
}
// In unix a fd is a fd. All are closed with close().
int redis_close_impl(RFD rfd) {
try {
SocketInfo* socketInfo = RFDMap::getInstance().lookupSocketInfo(rfd);
if (socketInfo != NULL) {
ASSERT(socketInfo->socket != INVALID_SOCKET);
if (socketInfo->socket != INVALID_SOCKET) {
SOCKET socket = socketInfo->socket;
socketInfo->socket = INVALID_SOCKET;
if (socketInfo->state != NULL) {
if (wsiocp_CloseSocketState != NULL) {
if (wsiocp_CloseSocketState(rfd)) {
RFDMap::getInstance().removeRFDToSocketInfo(rfd);
}
}
} else {
RFDMap::getInstance().removeRFDToSocketInfo(rfd);
}
RFDMap::getInstance().removeSocketToRFD(socket);
return f_closesocket(socket);
}
} else {
int crt_fd = RFDMap::getInstance().lookupCrtFD(rfd);
if (crt_fd != INVALID_FD) {
RFDMap::getInstance().removeCrtFD(crt_fd);
return crt_close(crt_fd);
}
}
} CATCH_AND_REPORT();
errno = EBADF;
return -1;
}
int __cdecl redis_open_impl(const char * filename, int openFlag, int flags = 0) {
RFD rfd = INVALID_FD;
try {
int crt_fd = crt_open(filename, openFlag, flags);
if (crt_fd != -1) {
rfd = RFDMap::getInstance().addCrtFD(crt_fd);
}
return rfd;
} CATCH_AND_REPORT();
return rfd;
}
int redis_accept_impl(int rfd, struct sockaddr *addr, socklen_t *addrlen) {
try {
SOCKET socket = RFDMap::getInstance().lookupSocket(rfd);
if (socket != INVALID_SOCKET) {
SOCKET sAccept = f_accept(socket, addr, addrlen);
if (sAccept != INVALID_SOCKET) {
return RFDMap::getInstance().addSocket(sAccept);
} else {
errno = WSAGetLastError();
if ((errno == ENOENT) || (errno == WSAEWOULDBLOCK)) {
errno = EAGAIN;
return INVALID_FD;
}
}
}
} CATCH_AND_REPORT();
errno = EBADF;
return INVALID_FD;
}
int redis_setsockopt_impl(int rfd, int level, int optname, const void *optval,
socklen_t optlen) {
try {
SOCKET socket = RFDMap::getInstance().lookupSocket(rfd);
if (socket != INVALID_SOCKET) {
return f_setsockopt(socket, level, optname, (const char*) optval, optlen);
}
} CATCH_AND_REPORT();
errno = EBADF;
return -1;
}
int redis_fcntl_impl(int rfd, int cmd, int flags = 0 ) {
try {
SocketInfo* socket_info = RFDMap::getInstance().lookupSocketInfo(rfd);
if (socket_info != NULL && socket_info->socket != INVALID_SOCKET) {
switch(cmd) {
case F_GETFL:
{
// Since in WinSock there is no way to determine if a socket
// is blocking, we keep track of this separately.
return socket_info->flags;
}
case F_SETFL:
{
u_long fionbio_flags = (flags & O_NONBLOCK);
if (SOCKET_ERROR == f_ioctlsocket(socket_info->socket,
FIONBIO,
&fionbio_flags)) {
errno = WSAGetLastError();
return -1;
} else {
socket_info->flags = flags;
return 0;
}
break;
}
default:
{
ASSERT(cmd == F_GETFL || cmd == F_SETFL);
return -1;
}
}
}
} CATCH_AND_REPORT();
errno = EBADF;
return -1;
}
int redis_poll_impl(struct pollfd *fds, nfds_t nfds, int timeout) {
try {
struct pollfd* pollCopy = new struct pollfd[nfds];
if (pollCopy == NULL) {
errno = ENOMEM;
return -1;
}
// NOTE: Treating the fds.fd as a RFD and converting to a SOCKET for WSAPoll.
for (nfds_t n = 0; n < nfds; n ++) {
pollCopy[n].fd = RFDMap::getInstance().lookupSocket((RFD)(fds[n].fd));
pollCopy[n].events = fds[n].events;
pollCopy[n].revents = fds[n].revents;
}
if (IsWindowsVersionAtLeast(HIBYTE(_WIN32_WINNT_WIN6), LOBYTE(_WIN32_WINNT_WIN6), 0)) {
static auto f_WSAPoll = dllfunctor_stdcall<int, WSAPOLLFD*, ULONG, INT>("ws2_32.dll", "WSAPoll");
// WSAPoll will wait forever if timeout = -1 and the endpoint is not reachable
int ret = f_WSAPoll(pollCopy, nfds, timeout);
for (nfds_t n = 0; n < nfds; n++) {
fds[n].events = pollCopy[n].events;
fds[n].revents = pollCopy[n].revents;
}
delete pollCopy;
pollCopy = NULL;
return ret;
} else {
int ret;
fd_set readSet;
fd_set writeSet;
fd_set excepSet;
FD_ZERO(&readSet);
FD_ZERO(&writeSet);
FD_ZERO(&excepSet);
if (nfds >= FD_SETSIZE) {
errno = EINVAL;
return -1;
}
nfds_t i;
for (i = 0; i < nfds; i++) {
if (fds[i].fd == INVALID_SOCKET) {
continue;
}
if (pollCopy[i].fd >= FD_SETSIZE) {
errno = EINVAL;
return -1;
}
if (pollCopy[i].events & POLLIN) FD_SET(pollCopy[i].fd, &readSet);
if (pollCopy[i].events & POLLOUT) FD_SET(pollCopy[i].fd, &writeSet);
if (pollCopy[i].events & POLLERR) FD_SET(pollCopy[i].fd, &excepSet);
}
if (timeout < 0) {
ret = select(0, &readSet, &writeSet, &excepSet, NULL);
} else {
struct timeval tv;
tv.tv_sec = timeout / 1000;
tv.tv_usec = 1000 * (timeout % 1000);
ret = select(0, &readSet, &writeSet, &excepSet, &tv);
}
if (ret < 0) {
return ret;
}
for (i = 0; i < nfds; i++) {
fds[i].revents = 0;
// f_WSAFDIsSet is equivalent to FD_ISSET
if (f_WSAFDIsSet(pollCopy[i].fd, &readSet)) fds[i].revents |= POLLIN;
if (f_WSAFDIsSet(pollCopy[i].fd, &writeSet)) fds[i].revents |= POLLOUT;
if (f_WSAFDIsSet(pollCopy[i].fd, &excepSet)) fds[i].revents |= POLLERR;
}
delete pollCopy;
pollCopy = NULL;
return ret;
}
} CATCH_AND_REPORT();
errno = EBADF;
return -1;
}
int redis_getsockopt_impl(int rfd, int level, int optname, void *optval, socklen_t *optlen) {
try {
SOCKET socket = RFDMap::getInstance().lookupSocket(rfd);
if (socket != INVALID_SOCKET) {
return f_getsockopt(socket, level, optname, (char*) optval, optlen);
}
} CATCH_AND_REPORT();
errno = EBADF;
return -1;
}
int redis_connect_impl(int rfd, const struct sockaddr *addr, size_t addrlen) {
try {
SOCKET socket = RFDMap::getInstance().lookupSocket(rfd);
if (socket != INVALID_SOCKET) {
EnableFastLoopback(socket);
int result = f_connect(socket, addr, (int) addrlen);
errno = WSAGetLastError();
if ((errno == WSAEINVAL) || (errno == WSAEWOULDBLOCK) || (errno == WSA_IO_PENDING)) {
errno = EINPROGRESS;
}
return result;
}
} CATCH_AND_REPORT();
errno = EBADF;
return -1;
}
ssize_t redis_read_impl(int rfd, void *buf, size_t count) {
try {
SOCKET socket = RFDMap::getInstance().lookupSocket(rfd);
if (socket != INVALID_SOCKET) {
int retval = f_recv(socket, (char*) buf, (unsigned int) count, 0);
if (retval == -1) {
errno = GetLastError();
if (errno == WSAEWOULDBLOCK) {
errno = EAGAIN;
}
}
return retval;
} else {
int crt_fd = RFDMap::getInstance().lookupCrtFD(rfd);
if (crt_fd != -1) {
int retval = crt_read(crt_fd, buf, (unsigned int) count);
if (retval == -1) {
errno = GetLastError();
}
return retval;
} else {
errno = EBADF;
return 0;
}
}
} CATCH_AND_REPORT();
errno = EBADF;
return -1;
}
ssize_t redis_write_impl(int rfd, const void *buf, size_t count) {
try {
SOCKET socket = RFDMap::getInstance().lookupSocket(rfd);
if (socket != INVALID_SOCKET) {
int ret = f_send(socket, (char*) buf, (unsigned int) count, 0);
if (ret == SOCKET_ERROR) {
set_errno_from_last_error();
}
return ret;
} else {
int crt_fd = RFDMap::getInstance().lookupCrtFD(rfd);
if (crt_fd != -1) {
if (crt_fd == _fileno(stdout)) {
DWORD bytesWritten = 0;
if (FALSE != ParseAndPrintANSIString(GetStdHandle(STD_OUTPUT_HANDLE),
buf,
(DWORD) count,
&bytesWritten)) {
return (int) bytesWritten;
} else {
errno = GetLastError();
return 0;
}
} else if (crt_fd == _fileno(stderr)) {
DWORD bytesWritten = 0;
if (FALSE != ParseAndPrintANSIString(GetStdHandle(STD_ERROR_HANDLE),
buf,
(DWORD) count,
&bytesWritten)) {
return (int) bytesWritten;
} else {
errno = GetLastError();
return 0;
}
} else {
int retval = crt_write(crt_fd, buf, (unsigned int) count);
if (retval == -1) {
errno = GetLastError();
}
return retval;
}
} else {
errno = EBADF;
return 0;
}
}
} CATCH_AND_REPORT();
errno = EBADF;
return -1;
}
int redis_fsync_impl(int rfd) {
try {
int crt_fd = RFDMap::getInstance().lookupCrtFD(rfd);
if (crt_fd != INVALID_FD) {
HANDLE h = (HANDLE) crtget_osfhandle(crt_fd);
if (h == INVALID_HANDLE_VALUE) {
errno = EBADF;
return -1;
}
if (!FlushFileBuffers(h)) {
DWORD err = GetLastError();
switch (err) {
case ERROR_INVALID_HANDLE:
errno = EINVAL;
break;
default:
errno = EIO;
}
return -1;
}
}
return 0;
} CATCH_AND_REPORT();
errno = EBADF;
return -1;
}
int redis_fstat_impl(int rfd, struct __stat64 *buffer) {
try {
int crt_fd = RFDMap::getInstance().lookupCrtFD(rfd);
if (crt_fd != INVALID_FD) {
return _fstat64(crt_fd, buffer);
}
} CATCH_AND_REPORT();
errno = EBADF;
return -1;
}
int redis_listen_impl(int rfd, int backlog) {
try {
SOCKET socket = RFDMap::getInstance().lookupSocket(rfd);
if (socket != INVALID_SOCKET) {
EnableFastLoopback(socket);
int result = f_listen(socket, backlog);
if (result != 0){
errno = WSAGetLastError();
}
return result;
} else {
errno = EBADF;
return -1;
}
} CATCH_AND_REPORT();
errno = EBADF;
return -1;
}
int redis_ftruncate_impl(int rfd, PORT_LONGLONG length) {
try
{
int crt_fd = RFDMap::getInstance().lookupCrtFD(rfd);
if (crt_fd != INVALID_FD) {
HANDLE h = (HANDLE) crtget_osfhandle(crt_fd);
if (h == INVALID_HANDLE_VALUE) {
errno = EBADF;
return -1;
}
LARGE_INTEGER l, o;
l.QuadPart = length;
if (!SetFilePointerEx(h, l, &o, FILE_BEGIN)) return -1;
if (!SetEndOfFile(h)) return -1;
}
return 0;
} CATCH_AND_REPORT();
errno = EBADF;
return -1;
}
int redis_bind_impl(int rfd, const struct sockaddr *addr, socklen_t addrlen) {
try {
SOCKET socket = RFDMap::getInstance().lookupSocket(rfd);
if (socket != INVALID_SOCKET) {
return f_bind(socket, addr, addrlen);
} else {
errno = EBADF;
return 0;
}
} CATCH_AND_REPORT();
errno = EBADF;
return -1;
}
void redis_WSASetLastError_impl(int iError) {
f_WSASetLastError(iError);
}
int redis_WSAGetLastError_impl(void) {
return f_WSAGetLastError();
}
BOOL FDAPI_WSAGetOverlappedResult(int rfd, LPWSAOVERLAPPED lpOverlapped,
LPDWORD lpcbTransfer, BOOL fWait, LPDWORD lpdwFlags) {
try {
SOCKET socket = RFDMap::getInstance().lookupSocket(rfd);
if (socket != INVALID_SOCKET) {
return f_WSAGetOverlappedResult(socket, lpOverlapped, lpcbTransfer, fWait, lpdwFlags);
} else {
errno = EBADF;
return SOCKET_ERROR;
}
} CATCH_AND_REPORT();
return SOCKET_ERROR;
}
int FDAPI_WSADuplicateSocket(int rfd, DWORD dwProcessId,
LPWSAPROTOCOL_INFO lpProtocolInfo) {
try {
SOCKET socket = RFDMap::getInstance().lookupSocket(rfd);
if (socket != INVALID_SOCKET) {
return f_WSADuplicateSocket(socket, dwProcessId, lpProtocolInfo);
} else {
errno = EBADF;
return SOCKET_ERROR;
}
} CATCH_AND_REPORT();
return SOCKET_ERROR;
}
int FDAPI_WSASocket(int af, int type, int protocol,
LPWSAPROTOCOL_INFO lpProtocolInfo, GROUP g, DWORD dwFlags) {
RFD rfd = INVALID_FD;
try {
SOCKET socket = f_WSASocket(af,
type,
protocol,
lpProtocolInfo,
g,
dwFlags);
if (socket != INVALID_SOCKET) {
rfd = RFDMap::getInstance().addSocket(socket);
}
} CATCH_AND_REPORT();
return rfd;
}
int FDAPI_WSAIoctl(RFD rfd, DWORD dwIoControlCode, LPVOID lpvInBuffer,
DWORD cbInBuffer, LPVOID lpvOutBuffer, DWORD cbOutBuffer,
LPDWORD lpcbBytesReturned, LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine) {
try {
SOCKET socket = RFDMap::getInstance().lookupSocket(rfd);
if (socket != INVALID_SOCKET) {
return f_WSAIoctl(socket,
dwIoControlCode,
lpvInBuffer,
cbInBuffer,
lpvOutBuffer,
cbOutBuffer,
lpcbBytesReturned,
lpOverlapped,
lpCompletionRoutine);
} else {
errno = EBADF;
return SOCKET_ERROR;
}
} CATCH_AND_REPORT();
return SOCKET_ERROR;
}
int FDAPI_WSASend(int rfd, LPWSABUF lpBuffers, DWORD dwBufferCount,
LPDWORD lpNumberOfBytesSent, DWORD dwFlags, LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine) {
try {
SOCKET socket = RFDMap::getInstance().lookupSocket(rfd);
if (socket != INVALID_SOCKET) {
return f_WSASend(socket,
lpBuffers,
dwBufferCount,
lpNumberOfBytesSent,
dwFlags,
lpOverlapped,
lpCompletionRoutine);
} else {
errno = EBADF;
return SOCKET_ERROR;
}
} CATCH_AND_REPORT();
return SOCKET_ERROR;
}
int FDAPI_WSARecv(int rfd, LPWSABUF lpBuffers, DWORD dwBufferCount,
LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine) {
try {
SOCKET socket = RFDMap::getInstance().lookupSocket(rfd);
if (socket != INVALID_SOCKET) {
return f_WSARecv(socket,
lpBuffers,
dwBufferCount,
lpNumberOfBytesRecvd,
lpFlags,
lpOverlapped,
lpCompletionRoutine);
} else {
errno = EBADF;
return SOCKET_ERROR;
}
} CATCH_AND_REPORT();
return SOCKET_ERROR;
}
int FDAPI_ioctlsocket(int rfd, long cmd, u_long *argp) {
try {
SOCKET socket = RFDMap::getInstance().lookupSocket(rfd);
if (socket != INVALID_SOCKET) {
return f_ioctlsocket(socket, cmd, argp);
} else {
errno = EBADF;
return SOCKET_ERROR;
}
} CATCH_AND_REPORT();
return SOCKET_ERROR;
}
u_short redis_htons_impl(u_short hostshort) {
return f_htons(hostshort);
}
u_long redis_htonl_impl(u_long hostlong) {
return f_htonl(hostlong);
}
int redis_getpeername_impl(int rfd, struct sockaddr *addr, socklen_t * addrlen) {
try {
SOCKET socket = RFDMap::getInstance().lookupSocket(rfd);
if (socket != INVALID_SOCKET) {
return f_getpeername(socket, addr, addrlen);
} else {
errno = EBADF;
return SOCKET_ERROR;
}
} CATCH_AND_REPORT();
errno = EBADF;
return -1;
}
int redis_getsockname_impl(int rfd, struct sockaddr* addrsock, int* addrlen) {
try {
SOCKET socket = RFDMap::getInstance().lookupSocket(rfd);
if (socket != INVALID_SOCKET) {
return f_getsockname(socket, addrsock, addrlen);
} else {
errno = EBADF;
return 0;
}
} CATCH_AND_REPORT();
errno = EBADF;
return -1;
}
u_short redis_ntohs_impl(u_short netshort) {
return f_ntohs(netshort);
}
int redis_setmode_impl(int rfd, int mode) {
return crt_setmode(rfd, mode);
}
size_t redis_fwrite_impl(const void * _Str, size_t _Size, size_t _Count, FILE * _File) {
return crt_fwrite(_Str, _Size, _Count, _File);
}
int redis_fclose_impl(FILE * file) {
int crt_fd = crt_fileno(file);
if (crt_fd != INVALID_FD) {
RFDMap::getInstance().removeCrtFD(crt_fd);
}
return crt_fclose(file);
}
int redis_fileno_impl(FILE* file) {
int crt_fd = crt_fileno(file);
if (crt_fd != INVALID_FD) {
// If crt_fd is already mapped, addCrtFD() will return the existing rfd.
return RFDMap::getInstance().addCrtFD(crt_fd);
} else {
return INVALID_FD;
}
}
int redis_select_impl(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout) {
try {
if (readfds != NULL) {
for (u_int r = 0; r < readfds->fd_count; r++) {
readfds->fd_array[r] = RFDMap::getInstance().lookupSocket((RFD) readfds->fd_array[r]);
}
}
if (writefds != NULL) {
for (u_int r = 0; r < writefds->fd_count; r++) {
writefds->fd_array[r] = RFDMap::getInstance().lookupSocket((RFD) writefds->fd_array[r]);
}
}
if (exceptfds != NULL) {
for (u_int r = 0; r < exceptfds->fd_count; r++) {
exceptfds->fd_array[r] = RFDMap::getInstance().lookupSocket((RFD) exceptfds->fd_array[r]);
}
}
return f_select(nfds, readfds, writefds, exceptfds, timeout);
} CATCH_AND_REPORT();
errno = EBADF;
return -1;
}
u_int redis_ntohl_impl(u_int netlong){
return f_ntohl(netlong);
}
int redis_isatty_impl(int rfd) {
try {
int crt_fd = RFDMap::getInstance().lookupCrtFD(rfd);
if (crt_fd != INVALID_FD) {
return crt_isatty(crt_fd);
} else if (rfd >= 0 && rfd <= 2) {
return crt_isatty(rfd);
} else {
errno = EBADF;
return 0;
}
} CATCH_AND_REPORT();
errno = EBADF;
return -1;
}
int redis_access_impl(const char *pathname, int mode) {
return crt_access(pathname, mode);
}
u_int64 redis_lseek64_impl(int rfd, u_int64 offset, int whence) {
try {
int crt_fd = RFDMap::getInstance().lookupCrtFD(rfd);
if (crt_fd != INVALID_FD) {
return crt_lseek64(crt_fd, offset, whence);
}
} CATCH_AND_REPORT();
errno = EBADF;
return -1;
}
intptr_t redis_get_osfhandle_impl(int rfd) {
try {
int crt_fd = RFDMap::getInstance().lookupCrtFD(rfd);
if (crt_fd != INVALID_FD) {
return crtget_osfhandle(crt_fd);
}
} CATCH_AND_REPORT();
errno = EBADF;
return -1;
}
int redis_open_osfhandle_impl(intptr_t osfhandle, int flags) {
RFD rfd = INVALID_FD;
try {
int crt_fd = crt_open_osfhandle(osfhandle, flags);
if (crt_fd != INVALID_FD) {
rfd = RFDMap::getInstance().addCrtFD(crt_fd);
}
} CATCH_AND_REPORT();
return rfd;
}
void redis_freeaddrinfo_impl(struct addrinfo *ai) {
f_freeaddrinfo(ai);
}
int redis_getaddrinfo_impl(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) {
return f_getaddrinfo(node, service,hints, res);
}
const char* redis_inet_ntop_impl(int af, const void *src, char *dst, size_t size) {
if (IsWindowsVersionAtLeast(HIBYTE(_WIN32_WINNT_WIN6), LOBYTE(_WIN32_WINNT_WIN6), 0)) {
static auto f_inet_ntop = dllfunctor_stdcall<const char*, int, const void*, char*, size_t>("ws2_32.dll", "inet_ntop");
return f_inet_ntop(af, src, dst, size);
} else {
static auto f_WSAAddressToStringA = dllfunctor_stdcall<int, LPSOCKADDR, DWORD, LPWSAPROTOCOL_INFO, LPSTR, LPDWORD>("ws2_32.dll", "WSAAddressToStringA");
struct sockaddr_in srcaddr;
memset(&srcaddr, 0, sizeof(struct sockaddr_in));
memcpy(&(srcaddr.sin_addr), src, sizeof(srcaddr.sin_addr));
srcaddr.sin_family = af;
if (f_WSAAddressToStringA((struct sockaddr*) &srcaddr, sizeof(struct sockaddr_in), 0, dst, (LPDWORD) &size) != 0) {
return NULL;
}
return dst;
}
}
BOOL ParseStorageAddress(const char *ip, int port, SOCKADDR_STORAGE* pSotrageAddr) {
struct addrinfo hints, *res;
int status;
char port_buffer[6];
sprintf(port_buffer, "%hu", port);
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
/* Setting AI_PASSIVE will give you a wildcard address if addr is NULL */
hints.ai_flags = AI_NUMERICSERV | AI_PASSIVE;
if ((status = getaddrinfo(ip, port_buffer, &hints, &res)) != 0) {
return FALSE;
}
/* Note, we're taking the first valid address, there may be more than one */
memcpy(pSotrageAddr, res->ai_addr, res->ai_addrlen);
freeaddrinfo(res);
return TRUE;
}
int InitWinsock() {
WSADATA t_wsa;
WORD wVers;
int iError;
wVers = MAKEWORD(2, 2);
iError = f_WSAStartup(wVers, &t_wsa);
if (iError != NO_ERROR || LOBYTE(t_wsa.wVersion) != 2 || HIBYTE(t_wsa.wVersion) != 2) {
exit(1);
} else {
return 0;
}
}
class Win32_FDSockMap {
public:
static Win32_FDSockMap& getInstance() {
static Win32_FDSockMap instance; // Instantiated on first use. Guaranteed to be destroyed.
return instance;
}
private:
Win32_FDSockMap() {
InitWinsock();
pipe = redis_pipe_impl;
socket = redis_socket_impl;
fdapi_close = redis_close_impl;
open = redis_open_impl;
setsockopt = redis_setsockopt_impl;
fcntl = redis_fcntl_impl;
poll = redis_poll_impl;
getsockopt = redis_getsockopt_impl;
connect = redis_connect_impl;
read = redis_read_impl;
write = redis_write_impl;
fsync = redis_fsync_impl;
fdapi_fstat64 = (_redis_fstat)redis_fstat_impl;
listen = redis_listen_impl;
ftruncate = redis_ftruncate_impl;
bind = redis_bind_impl;
htons = redis_htons_impl;
htonl = redis_htonl_impl;
getpeername = redis_getpeername_impl;
getsockname = redis_getsockname_impl;
ntohs = redis_ntohs_impl;
fdapi_fwrite = redis_fwrite_impl;
fdapi_fclose = redis_fclose_impl;
fdapi_fileno = redis_fileno_impl;
fdapi_setmode = redis_setmode_impl;
WSAGetLastError = redis_WSAGetLastError_impl;
select = redis_select_impl;
ntohl = redis_ntohl_impl;
isatty = redis_isatty_impl;
access = redis_access_impl;
lseek64 = redis_lseek64_impl;
fdapi_get_osfhandle = redis_get_osfhandle_impl;
fdapi_open_osfhandle = redis_open_osfhandle_impl;
freeaddrinfo = redis_freeaddrinfo_impl;
getaddrinfo = redis_getaddrinfo_impl;
inet_ntop = redis_inet_ntop_impl;
accept = redis_accept_impl;
}
~Win32_FDSockMap() {
// WinSock cleanup
f_WSACleanup();
}
Win32_FDSockMap(Win32_FDSockMap const&); // Don't implement to guarantee singleton semantics
void operator=(Win32_FDSockMap const&); // Don't implement to guarantee singleton semantics
};
// guarantee global initialization
static class Win32_FDSockMap& init = Win32_FDSockMap::getInstance();