diff options
| -rw-r--r-- | CMakeLists.txt | 20 | ||||
| -rw-r--r-- | Makefile | 7 | ||||
| -rw-r--r-- | hiredis.c | 1 | ||||
| -rw-r--r-- | hiredis.h | 13 | ||||
| -rw-r--r-- | net.c | 27 | ||||
| -rw-r--r-- | sockcompat.c | 226 | ||||
| -rw-r--r-- | sockcompat.h | 87 | ||||
| -rw-r--r-- | win32.h | 8 | 
8 files changed, 367 insertions, 22 deletions
| diff --git a/CMakeLists.txt b/CMakeLists.txt index cce2c61..0616fa6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -CMAKE_MINIMUM_REQUIRED(VERSION 3.0.0) +CMAKE_MINIMUM_REQUIRED(VERSION 3.4.0)  INCLUDE(GNUInstallDirs)  PROJECT(hiredis) @@ -31,11 +31,15 @@ ADD_LIBRARY(hiredis SHARED      net.c      read.c      sds.c +    sockcompat.c      sslio.c)  SET_TARGET_PROPERTIES(hiredis      PROPERTIES      VERSION "${HIREDIS_SONAME}") +IF(WIN32 OR MINGW) +    TARGET_LINK_LIBRARIES(hiredis PRIVATE ws2_32) +ENDIF()  CONFIGURE_FILE(hiredis.pc.in hiredis.pc @ONLY) @@ -60,13 +64,13 @@ IF(HIREDIS_SSL)      TARGET_LINK_LIBRARIES(hiredis ${OPENSSL_LIBRARIES})  ENDIF() -ENABLE_TESTING() -ADD_EXECUTABLE(hiredis-test test.c) - - -TARGET_LINK_LIBRARIES(hiredis-test hiredis) -ADD_TEST(NAME hiredis-test -    COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test.sh) +IF(NOT (WIN32 OR MINGW)) +    ENABLE_TESTING() +    ADD_EXECUTABLE(hiredis-test test.c) +    TARGET_LINK_LIBRARIES(hiredis-test hiredis) +    ADD_TEST(NAME hiredis-test +        COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test.sh) +ENDIF()  # Add examples  IF(ENABLE_EXAMPLES) @@ -3,7 +3,7 @@  # Copyright (C) 2010-2011 Pieter Noordhuis <pcnoordhuis at gmail dot com>  # This file is released under the BSD license, see the COPYING file -OBJ=net.o hiredis.o sds.o async.o read.o sslio.o +OBJ=net.o hiredis.o sds.o async.o read.o sockcompat.o sslio.o  EXAMPLES=hiredis-example hiredis-example-libevent hiredis-example-libev hiredis-example-glib \  		 hiredis-example-ssl hiredis-example-libevent-ssl  TESTS=hiredis-test @@ -87,10 +87,11 @@ all: $(DYLIBNAME) $(STLIBNAME) hiredis-test $(PKGCONFNAME)  # Deps (use make dep to generate this)  async.o: async.c fmacros.h async.h hiredis.h read.h sds.h net.h dict.c dict.h  dict.o: dict.c fmacros.h dict.h -hiredis.o: hiredis.c fmacros.h hiredis.h read.h sds.h net.h sslio.h -net.o: net.c fmacros.h net.h hiredis.h read.h sds.h +hiredis.o: hiredis.c fmacros.h hiredis.h read.h sds.h net.h sslio.h win32.h +net.o: net.c fmacros.h net.h hiredis.h read.h sds.h sockcompat.h win32.h  read.o: read.c fmacros.h read.h sds.h  sds.o: sds.c sds.h +sockcompat.o: sockcompat.c sockcompat.h  sslio.o: sslio.c sslio.h hiredis.h  test.o: test.c fmacros.h hiredis.h read.h sds.h @@ -42,6 +42,7 @@  #include "net.h"  #include "sds.h"  #include "sslio.h" +#include "win32.h"  static redisReply *createReplyObject(int type);  static void *createStringObject(const redisReadTask *task, char *str, size_t len); @@ -133,8 +133,21 @@ struct redisSsl;   */  #define REDIS_OPT_NOAUTOFREE 0x04 +/* In Unix systems a file descriptor is a regular signed int, with -1 + * representing an invalid descriptor. In Windows it is a SOCKET + * (32- or 64-bit unsigned integer depending on the architecture), where + * all bits set (~0) is INVALID_SOCKET.  */ +#ifndef _WIN32  typedef int redisFD;  #define REDIS_INVALID_FD -1 +#else +#ifdef _WIN64 +typedef unsigned long long redisFD; /* SOCKET = 64-bit UINT_PTR */ +#else +typedef unsigned long redisFD;      /* SOCKET = 32-bit UINT_PTR */ +#endif +#define REDIS_INVALID_FD ((redisFD)(~0)) /* INVALID_SOCKET */ +#endif  typedef struct {      /* @@ -34,25 +34,18 @@  #include "fmacros.h"  #include <sys/types.h> -#include <sys/socket.h> -#include <sys/select.h> -#include <sys/un.h> -#include <netinet/in.h> -#include <netinet/tcp.h> -#include <arpa/inet.h> -#include <unistd.h>  #include <fcntl.h>  #include <string.h> -#include <netdb.h>  #include <errno.h>  #include <stdarg.h>  #include <stdio.h> -#include <poll.h>  #include <limits.h>  #include <stdlib.h>  #include "net.h"  #include "sds.h" +#include "sockcompat.h" +#include "win32.h"  /* Defined in hiredis.c */  void __redisSetError(redisContext *c, int type, const char *str); @@ -132,6 +125,7 @@ static int redisCreateSocket(redisContext *c, int type) {  }  static int redisSetBlocking(redisContext *c, int blocking) { +#ifndef _WIN32      int flags;      /* Set the socket nonblocking. @@ -153,6 +147,14 @@ static int redisSetBlocking(redisContext *c, int blocking) {          redisNetClose(c);          return REDIS_ERR;      } +#else +    u_long mode = blocking ? 0 : 1; +    if (ioctl(c->fd, FIONBIO, &mode) == -1) { +        __redisSetErrorFromErrno(c, REDIS_ERR_IO, "ioctl(FIONBIO)"); +        redisNetClose(c); +        return REDIS_ERR; +    } +#endif /* _WIN32 */      return REDIS_OK;  } @@ -503,6 +505,7 @@ int redisContextConnectBindTcp(redisContext *c, const char *addr, int port,  }  int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout) { +#ifndef _WIN32      int blocking = (c->flags & REDIS_BLOCK);      struct sockaddr_un *sa;      long timeout_msec = -1; @@ -550,4 +553,10 @@ int redisContextConnectUnix(redisContext *c, const char *path, const struct time      c->flags |= REDIS_CONNECTED;      return REDIS_OK; +#else +    /* We currently do not support Unix sockets for Windows. */ +    /* TODO(m): https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/ */ +    errno = EPROTONOSUPPORT; +    return REDIS_ERR; +#endif /* _WIN32 */  } diff --git a/sockcompat.c b/sockcompat.c new file mode 100644 index 0000000..b52cbc6 --- /dev/null +++ b/sockcompat.c @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2019, Marcus Geelnard <m at bitsnbites dot eu> + * + * 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. + *   * Neither the name of Redis nor the names of its contributors may be used + *     to endorse or promote products derived from this software without + *     specific prior written permission. + * + * 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 OWNER 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. + */ + +#define REDIS_SOCKCOMPAT_IMPLEMENTATION +#include "sockcompat.h" + +#ifdef _WIN32 +static int _wsaErrorToErrno(int err) { +    switch (err) { +        case WSAEWOULDBLOCK: +            return EWOULDBLOCK; +        case WSAEINPROGRESS: +            return EINPROGRESS; +        case WSAEALREADY: +            return EALREADY; +        case WSAENOTSOCK: +            return ENOTSOCK; +        case WSAEDESTADDRREQ: +            return EDESTADDRREQ; +        case WSAEMSGSIZE: +            return EMSGSIZE; +        case WSAEPROTOTYPE: +            return EPROTOTYPE; +        case WSAENOPROTOOPT: +            return ENOPROTOOPT; +        case WSAEPROTONOSUPPORT: +            return EPROTONOSUPPORT; +        case WSAEOPNOTSUPP: +            return EOPNOTSUPP; +        case WSAEAFNOSUPPORT: +            return EAFNOSUPPORT; +        case WSAEADDRINUSE: +            return EADDRINUSE; +        case WSAEADDRNOTAVAIL: +            return EADDRNOTAVAIL; +        case WSAENETDOWN: +            return ENETDOWN; +        case WSAENETUNREACH: +            return ENETUNREACH; +        case WSAENETRESET: +            return ENETRESET; +        case WSAECONNABORTED: +            return ECONNABORTED; +        case WSAECONNRESET: +            return ECONNRESET; +        case WSAENOBUFS: +            return ENOBUFS; +        case WSAEISCONN: +            return EISCONN; +        case WSAENOTCONN: +            return ENOTCONN; +        case WSAETIMEDOUT: +            return ETIMEDOUT; +        case WSAECONNREFUSED: +            return ECONNREFUSED; +        case WSAELOOP: +            return ELOOP; +        case WSAENAMETOOLONG: +            return ENAMETOOLONG; +        case WSAEHOSTUNREACH: +            return EHOSTUNREACH; +        case WSAENOTEMPTY: +            return ENOTEMPTY; +        default: +            /* We just return a generic I/O error if we could not find a relevant error. */ +            return EIO; +    } +} + +static void _updateErrno(int success) { +    errno = success ? 0 : _wsaErrorToErrno(WSAGetLastError()); +} + +static int _initWinsock() { +    static int s_initialized = 0; +    if (!s_initialized) { +        static WSADATA wsadata; +        int err = WSAStartup(MAKEWORD(2,2), &wsadata); +        if (err != 0) { +            errno = _wsaErrorToErrno(err); +            return 0; +        } +        s_initialized = 1; +    } +    return 1; +} + +int win32_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) { +    /* Note: This function is likely to be called before other functions, so run init here. */ +    if (!_initWinsock()) { +        return EAI_FAIL; +    } + +    switch (getaddrinfo(node, service, hints, res)) { +        case 0:                     return 0; +        case WSATRY_AGAIN:          return EAI_AGAIN; +        case WSAEINVAL:             return EAI_BADFLAGS; +        case WSAEAFNOSUPPORT:       return EAI_FAMILY; +        case WSA_NOT_ENOUGH_MEMORY: return EAI_MEMORY; +        case WSAHOST_NOT_FOUND:     return EAI_NONAME; +        case WSATYPE_NOT_FOUND:     return EAI_SERVICE; +        case WSAESOCKTNOSUPPORT:    return EAI_SOCKTYPE; +        default:                    return EAI_FAIL;     /* Including WSANO_RECOVERY */ +    } +} + +const char *win32_gai_strerror(int errcode) { +    switch (errcode) { +        case 0:            errcode = 0;                     break; +        case EAI_AGAIN:    errcode = WSATRY_AGAIN;          break; +        case EAI_BADFLAGS: errcode = WSAEINVAL;             break; +        case EAI_FAMILY:   errcode = WSAEAFNOSUPPORT;       break; +        case EAI_MEMORY:   errcode = WSA_NOT_ENOUGH_MEMORY; break; +        case EAI_NONAME:   errcode = WSAHOST_NOT_FOUND;     break; +        case EAI_SERVICE:  errcode = WSATYPE_NOT_FOUND;     break; +        case EAI_SOCKTYPE: errcode = WSAESOCKTNOSUPPORT;    break; +        default:           errcode = WSANO_RECOVERY;        break; /* Including EAI_FAIL */ +    } +    return gai_strerror(errcode); +} + +void win32_freeaddrinfo(struct addrinfo *res) { +    freeaddrinfo(res); +} + +SOCKET win32_socket(int domain, int type, int protocol) { +    SOCKET s; + +    /* Note: This function is likely to be called before other functions, so run init here. */ +    if (!_initWinsock()) { +        return INVALID_SOCKET; +    } + +    _updateErrno((s = socket(domain, type, protocol)) != INVALID_SOCKET); +    return s; +} + +int win32_ioctl(SOCKET fd, unsigned long request, unsigned long *argp) { +    int ret = ioctlsocket(fd, (long)request, argp); +    _updateErrno(ret != SOCKET_ERROR); +    return ret != SOCKET_ERROR ? ret : -1; +} + +int win32_bind(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen) { +    int ret = bind(sockfd, addr, addrlen); +    _updateErrno(ret != SOCKET_ERROR); +    return ret != SOCKET_ERROR ? ret : -1; +} + +int win32_connect(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen) { +    int ret = connect(sockfd, addr, addrlen); +    _updateErrno(ret != SOCKET_ERROR); + +    /* For Winsock connect(), the WSAEWOULDBLOCK error means the same thing as +     * EINPROGRESS for POSIX connect(), so we do that translation to keep POSIX +     * logic consistent. */ +    if (errno == EWOULDBLOCK) { +        errno = EINPROGRESS; +    } + +    return ret != SOCKET_ERROR ? ret : -1; +} + +int win32_getsockopt(SOCKET sockfd, int level, int optname, void *optval, socklen_t *optlen) { +    int ret = getsockopt(sockfd, level, optname, (char*)optval, optlen); +    _updateErrno(ret != SOCKET_ERROR); +    return ret != SOCKET_ERROR ? ret : -1; +} + +int win32_setsockopt(SOCKET sockfd, int level, int optname, const void *optval, socklen_t optlen) { +    int ret = setsockopt(sockfd, level, optname, (const char*)optval, optlen); +    _updateErrno(ret != SOCKET_ERROR); +    return ret != SOCKET_ERROR ? ret : -1; +} + +int win32_close(SOCKET fd) { +    int ret = closesocket(fd); +    _updateErrno(ret != SOCKET_ERROR); +    return ret != SOCKET_ERROR ? ret : -1; +} + +ssize_t win32_recv(SOCKET sockfd, void *buf, size_t len, int flags) { +    int ret = recv(sockfd, (char*)buf, (int)len, flags); +    _updateErrno(ret != SOCKET_ERROR); +    return ret != SOCKET_ERROR ? ret : -1; +} + +ssize_t win32_send(SOCKET sockfd, const void *buf, size_t len, int flags) { +    int ret = send(sockfd, (const char*)buf, (int)len, flags); +    _updateErrno(ret != SOCKET_ERROR); +    return ret != SOCKET_ERROR ? ret : -1; +} + +int win32_poll(struct pollfd *fds, nfds_t nfds, int timeout) { +    int ret = WSAPoll(fds, nfds, timeout); +    _updateErrno(ret != SOCKET_ERROR); +    return ret != SOCKET_ERROR ? ret : -1; +} +#endif /* _WIN32 */ diff --git a/sockcompat.h b/sockcompat.h new file mode 100644 index 0000000..4965b7b --- /dev/null +++ b/sockcompat.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2019, Marcus Geelnard <m at bitsnbites dot eu> + * + * 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. + *   * Neither the name of Redis nor the names of its contributors may be used + *     to endorse or promote products derived from this software without + *     specific prior written permission. + * + * 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 OWNER 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. + */ + +#ifndef __SOCKCOMPAT_H +#define __SOCKCOMPAT_H + +#ifndef _WIN32 +/* For POSIX systems we use the standard BSD socket API. */ +#include <unistd.h> +#include <sys/socket.h> +#include <sys/select.h> +#include <sys/un.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <poll.h> +#else +/* For Windows we use winsock. */ +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0600 /* To get WSAPoll etc. */ +#include <winsock2.h> +#include <ws2tcpip.h> +#include <stddef.h> + +/* Emulate the parts of the BSD socket API that we need (override the winsock signatures). */ +int win32_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res); +const char *win32_gai_strerror(int errcode); +void win32_freeaddrinfo(struct addrinfo *res); +SOCKET win32_socket(int domain, int type, int protocol); +int win32_ioctl(SOCKET fd, unsigned long request, unsigned long *argp); +int win32_bind(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen); +int win32_connect(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen); +int win32_getsockopt(SOCKET sockfd, int level, int optname, void *optval, socklen_t *optlen); +int win32_setsockopt(SOCKET sockfd, int level, int optname, const void *optval, socklen_t optlen); +int win32_close(SOCKET fd); +ssize_t win32_recv(SOCKET sockfd, void *buf, size_t len, int flags); +ssize_t win32_send(SOCKET sockfd, const void *buf, size_t len, int flags); +typedef ULONG nfds_t; +int win32_poll(struct pollfd *fds, nfds_t nfds, int timeout); + +#ifndef REDIS_SOCKCOMPAT_IMPLEMENTATION +#define getaddrinfo(node, service, hints, res) win32_getaddrinfo(node, service, hints, res) +#undef gai_strerror +#define gai_strerror(errcode) win32_gai_strerror(errcode) +#define freeaddrinfo(res) win32_freeaddrinfo(res) +#define socket(domain, type, protocol) win32_socket(domain, type, protocol) +#define ioctl(fd, request, argp) win32_ioctl(fd, request, argp) +#define bind(sockfd, addr, addrlen) win32_bind(sockfd, addr, addrlen) +#define connect(sockfd, addr, addrlen) win32_connect(sockfd, addr, addrlen) +#define getsockopt(sockfd, level, optname, optval, optlen) win32_getsockopt(sockfd, level, optname, optval, optlen) +#define setsockopt(sockfd, level, optname, optval, optlen) win32_setsockopt(sockfd, level, optname, optval, optlen) +#define close(fd) win32_close(fd) +#define recv(sockfd, buf, len, flags) win32_recv(sockfd, buf, len, flags) +#define send(sockfd, buf, len, flags) win32_send(sockfd, buf, len, flags) +#define poll(fds, nfds, timeout) win32_poll(fds, nfds, timeout) +#endif /* REDIS_SOCKCOMPAT_IMPLEMENTATION */ +#endif /* _WIN32 */ + +#endif /* __SOCKCOMPAT_H */ @@ -37,6 +37,10 @@ __inline int c99_snprintf(char* str, size_t size, const char* format, ...)      return count;  }  #endif +#endif /* _MSC_VER */ -#endif -#endif
\ No newline at end of file +#ifdef _WIN32 +#define strerror_r(errno,buf,len) strerror_s(buf,len,errno) +#endif /* _WIN32 */ + +#endif /* _WIN32_HELPER_INCLUDE */ | 
