diff options
| -rw-r--r-- | .travis.yml | 33 | ||||
| -rw-r--r-- | CHANGELOG.md | 15 | ||||
| -rw-r--r-- | CMakeLists.txt | 5 | ||||
| -rw-r--r-- | README.md | 3 | ||||
| -rw-r--r-- | async.c | 10 | ||||
| -rw-r--r-- | hiredis.c | 4 | ||||
| -rw-r--r-- | hiredis.h | 4 | ||||
| -rw-r--r-- | net.c | 19 | ||||
| -rw-r--r-- | read.c | 7 | ||||
| -rw-r--r-- | read.h | 2 | ||||
| -rw-r--r-- | sds.c | 2 | ||||
| -rw-r--r-- | sds.h | 30 | ||||
| -rw-r--r-- | sockcompat.c | 26 | ||||
| -rw-r--r-- | sockcompat.h | 2 | ||||
| -rw-r--r-- | sslio.c | 4 | ||||
| -rw-r--r-- | test.c | 5 | ||||
| -rw-r--r-- | win32.h | 10 | 
17 files changed, 139 insertions, 42 deletions
| diff --git a/.travis.yml b/.travis.yml index 8580ade..dd8e0e7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -62,3 +62,36 @@ script:    - cmake .. ${EXTRA_CMAKE_OPTS}    - make VERBOSE=1    - ctest -V + +matrix: +  include: +    # Windows MinGW cross compile on Linux +    - os: linux +      dist: xenial +      compiler: mingw +      addons: +        apt: +          packages: +            - ninja-build +            - gcc-mingw-w64-x86-64 +            - g++-mingw-w64-x86-64 +      script: +        - mkdir build && cd build +        - CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_BUILD_WITH_INSTALL_RPATH=on +        - ninja -v + +    # Windows MSVC 2017 +    - os: windows +      compiler: msvc +      env: +        - MATRIX_EVAL="CC=cl.exe && CXX=cl.exe" +      before_install: +        - eval "${MATRIX_EVAL}" +      install: +        - choco install ninja +      script: +        - mkdir build && cd build +        - cmd.exe /C '"C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" amd64 && +          cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release && +          ninja -v' +        - ctest -V diff --git a/CHANGELOG.md b/CHANGELOG.md index a7fe3ac..d1d37e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,16 @@    compare to other values, casting might be necessary or can be removed, if    casting was applied before. +### 0.x.x (unreleased) +**BREAKING CHANGES**: + +* Change `redisReply.len` to `size_t`, as it denotes the the size of a string + +User code should compare this to `size_t` values as well. +If it was used to compare to other values, casting might be necessary or can be removed, if casting was applied before. + +* `redisReplyObjectFunctions.createArray` now takes `size_t` for its length parameter. +  ### 0.14.0 (2018-09-25)  * Make string2ll static to fix conflict with Redis (Tom Lee [c3188b]) @@ -50,8 +60,9 @@  * Import latest upstream sds. This breaks applications that are linked against the old hiredis v0.13  * Fix warnings, when compiled with -Wshadow  * Make hiredis compile in Cygwin on Windows, now CI-tested - -**BREAKING CHANGES**: +* Bulk and multi-bulk lengths less than -1 or greater than `LLONG_MAX` are now +  protocol errors. This is consistent with the RESP specification. On 32-bit +  platforms, the upper bound is lowered to `SIZE_MAX`.  * Remove backwards compatibility macro's diff --git a/CMakeLists.txt b/CMakeLists.txt index c8c8071..30140bf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,7 +47,10 @@ INSTALL(TARGETS hiredis  INSTALL(FILES hiredis.h read.h sds.h async.h      DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis) - +     +INSTALL(DIRECTORY adapters +    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis) +      INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis.pc      DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) @@ -286,6 +286,7 @@ return `REDIS_ERR`. The function to set the disconnect callback has the followin  ```c  int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);  ``` +`ac->data` may be used to pass user data to this callback, the same can be done for redisConnectCallback.  ### Sending commands and their callbacks  In an asynchronous context, commands are automatically pipelined due to the nature of an event loop. @@ -406,6 +407,6 @@ as soon as possible in order to prevent allocation of useless memory.  ## AUTHORS  Hiredis was written by Salvatore Sanfilippo (antirez at gmail) and -Pieter Noordhuis (pcnoordhuis at gmail) and is released under the BSD license.   +Pieter Noordhuis (pcnoordhuis at gmail) and is released under the BSD license.  Hiredis is currently maintained by Matt Stancliff (matt at genges dot com) and  Jan-Erik Rediger (janerik at fnordig dot com) @@ -32,11 +32,8 @@  #include "fmacros.h"  #include <stdlib.h>  #include <string.h> -#ifndef _WIN32 +#ifndef _MSC_VER  #include <strings.h> -#else
 -#define strcasecmp stricmp
 -#define strncasecmp  strnicmp  #endif  #include <assert.h>  #include <ctype.h> @@ -46,6 +43,7 @@  #include "dict.c"  #include "sds.h"  #include "sslio.h" +#include "win32.h"  #define _EL_ADD_READ(ctx)                                         \      do {                                                          \ @@ -438,7 +436,7 @@ static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply,                  assert(reply->element[2]->type == REDIS_REPLY_INTEGER);                  /* Unset subscribed flag only when no pipelined pending subscribe. */ -                if (reply->element[2]->integer == 0  +                if (reply->element[2]->integer == 0                      && dictSize(ac->sub.channels) == 0                      && dictSize(ac->sub.patterns) == 0)                      c->flags &= ~REDIS_SUBSCRIBED; @@ -557,7 +555,7 @@ static int __redisAsyncHandleConnect(redisAsyncContext *ac) {  /**   * Handle SSL when socket becomes available for reading. This also handles   * read-while-write and write-while-read. - *  + *   * These functions will not work properly unless `HIREDIS_SSL` is defined   * (however, they will compile)   */ @@ -46,7 +46,7 @@  static redisReply *createReplyObject(int type);  static void *createStringObject(const redisReadTask *task, char *str, size_t len); -static void *createArrayObject(const redisReadTask *task, int elements); +static void *createArrayObject(const redisReadTask *task, size_t elements);  static void *createIntegerObject(const redisReadTask *task, long long value);  static void *createDoubleObject(const redisReadTask *task, double value, char *str, size_t len);  static void *createNilObject(const redisReadTask *task); @@ -139,7 +139,7 @@ static void *createStringObject(const redisReadTask *task, char *str, size_t len      return r;  } -static void *createArrayObject(const redisReadTask *task, int elements) { +static void *createArrayObject(const redisReadTask *task, size_t elements) {      redisReply *r, *parent;      r = createReplyObject(task->type); @@ -35,10 +35,10 @@  #define __HIREDIS_H  #include "read.h"  #include <stdarg.h> /* for va_list */ -#ifndef _WIN32 +#ifndef _MSC_VER  #include <sys/time.h> /* for struct timeval */  #else -#include <winsock2.h> +struct timeval; /* forward declaration */  #endif  #include <stdint.h> /* uintXX_t, etc */  #include "sds.h" /* for sds */ @@ -63,6 +63,10 @@ int redisNetRead(redisContext *c, char *buf, size_t bufcap) {          if ((errno == EWOULDBLOCK && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) {              /* Try again later */              return 0; +        } else if(errno == ETIMEDOUT && (c->flags & REDIS_BLOCK)) { +            /* especially in windows */ +            __redisSetError(c, REDIS_ERR_TIMEOUT, "recv timeout"); +            return -1;          } else {              __redisSetError(c, REDIS_ERR_IO, NULL);              return -1; @@ -310,11 +314,18 @@ int redisCheckSocketError(redisContext *c) {  }  int redisContextSetTimeout(redisContext *c, const struct timeval tv) { -    if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv)) == -1) { +    const void *to_ptr = &tv; +    size_t to_sz = sizeof(tv); +#ifdef _WIN32 +    DWORD timeout_msec = tv.tv_sec * 1000 + tv.tv_usec / 1000; +    to_ptr = &timeout_msec; +    to_sz = sizeof(timeout_msec); +#endif +    if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,to_ptr,to_sz) == -1) {          __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)");          return REDIS_ERR;      } -    if (setsockopt(c->fd,SOL_SOCKET,SO_SNDTIMEO,&tv,sizeof(tv)) == -1) { +    if (setsockopt(c->fd,SOL_SOCKET,SO_SNDTIMEO,to_ptr,to_sz) == -1) {          __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_SNDTIMEO)");          return REDIS_ERR;      } @@ -435,9 +446,7 @@ addrretry:          }          /* For repeat connection */ -        if (c->saddr) { -            free(c->saddr); -        } +        free(c->saddr);          c->saddr = malloc(p->ai_addrlen);          memcpy(c->saddr, p->ai_addr, p->ai_addrlen);          c->addrlen = p->ai_addrlen; @@ -430,7 +430,7 @@ static int processAggregateItem(redisReader *r) {          root = (r->ridx == 0); -        if (elements < -1 || elements > INT_MAX) { +        if (elements < -1 || (LLONG_MAX > SIZE_MAX && elements > SIZE_MAX)) {              __redisReaderSetError(r,REDIS_ERR_PROTOCOL,                      "Multi-bulk length out of range");              return REDIS_ERR; @@ -657,8 +657,11 @@ int redisReaderGetReply(redisReader *r, void **reply) {      /* Emit a reply when there is one. */      if (r->ridx == -1) { -        if (reply != NULL) +        if (reply != NULL) {              *reply = r->reply; +        } else if (r->reply != NULL && r->fn && r->fn->freeObject) { +            r->fn->freeObject(r->reply); +        }          r->reply = NULL;      }      return REDIS_OK; @@ -80,7 +80,7 @@ typedef struct redisReadTask {  typedef struct redisReplyObjectFunctions {      void *(*createString)(const redisReadTask*, char*, size_t); -    void *(*createArray)(const redisReadTask*, int); +    void *(*createArray)(const redisReadTask*, size_t);      void *(*createInteger)(const redisReadTask*, long long);      void *(*createDouble)(const redisReadTask*, double, char*, size_t);      void *(*createNil)(const redisReadTask*); @@ -1035,7 +1035,7 @@ sds *sdssplitargs(const char *line, int *argc) {                      s_free(vector);                      return NULL;                  } -                 +                  vector = new_vector;                  vector[*argc] = current;                  (*argc)++; @@ -34,7 +34,7 @@  #define __SDS_H  #define SDS_MAX_PREALLOC (1024*1024) -#ifdef _WIN32 +#ifdef _MSC_VER  #define __attribute__(x)  #endif @@ -135,20 +135,20 @@ static inline void sdssetlen(sds s, size_t newlen) {          case SDS_TYPE_5:              {                  unsigned char *fp = ((unsigned char*)s)-1; -                *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS); +                *fp = (unsigned char)(SDS_TYPE_5 | (newlen << SDS_TYPE_BITS));              }              break;          case SDS_TYPE_8: -            SDS_HDR(8,s)->len = newlen; +            SDS_HDR(8,s)->len = (uint8_t)newlen;              break;          case SDS_TYPE_16: -            SDS_HDR(16,s)->len = newlen; +            SDS_HDR(16,s)->len = (uint16_t)newlen;              break;          case SDS_TYPE_32: -            SDS_HDR(32,s)->len = newlen; +            SDS_HDR(32,s)->len = (uint32_t)newlen;              break;          case SDS_TYPE_64: -            SDS_HDR(64,s)->len = newlen; +            SDS_HDR(64,s)->len = (uint64_t)newlen;              break;      }  } @@ -159,21 +159,21 @@ static inline void sdsinclen(sds s, size_t inc) {          case SDS_TYPE_5:              {                  unsigned char *fp = ((unsigned char*)s)-1; -                unsigned char newlen = SDS_TYPE_5_LEN(flags)+inc; +                unsigned char newlen = SDS_TYPE_5_LEN(flags)+(unsigned char)inc;                  *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);              }              break;          case SDS_TYPE_8: -            SDS_HDR(8,s)->len += inc; +            SDS_HDR(8,s)->len += (uint8_t)inc;              break;          case SDS_TYPE_16: -            SDS_HDR(16,s)->len += inc; +            SDS_HDR(16,s)->len += (uint16_t)inc;              break;          case SDS_TYPE_32: -            SDS_HDR(32,s)->len += inc; +            SDS_HDR(32,s)->len += (uint32_t)inc;              break;          case SDS_TYPE_64: -            SDS_HDR(64,s)->len += inc; +            SDS_HDR(64,s)->len += (uint64_t)inc;              break;      }  } @@ -203,16 +203,16 @@ static inline void sdssetalloc(sds s, size_t newlen) {              /* Nothing to do, this type has no total allocation info. */              break;          case SDS_TYPE_8: -            SDS_HDR(8,s)->alloc = newlen; +            SDS_HDR(8,s)->alloc = (uint8_t)newlen;              break;          case SDS_TYPE_16: -            SDS_HDR(16,s)->alloc = newlen; +            SDS_HDR(16,s)->alloc = (uint16_t)newlen;              break;          case SDS_TYPE_32: -            SDS_HDR(32,s)->alloc = newlen; +            SDS_HDR(32,s)->alloc = (uint32_t)newlen;              break;          case SDS_TYPE_64: -            SDS_HDR(64,s)->alloc = newlen; +            SDS_HDR(64,s)->alloc = (uint64_t)newlen;              break;      }  } diff --git a/sockcompat.c b/sockcompat.c index b52cbc6..4cc2f41 100644 --- a/sockcompat.c +++ b/sockcompat.c @@ -189,13 +189,35 @@ 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 ret = getsockopt(sockfd, level, optname, (char*)optval, optlen); +    int ret = 0; +    if ((level == SOL_SOCKET) && ((optname == SO_RCVTIMEO) || (optname == SO_SNDTIMEO))) { +        if (*optlen >= sizeof (struct timeval)) { +            struct timeval *tv = optval; +            DWORD timeout = 0; +            socklen_t dwlen = 0; +            ret = getsockopt(sockfd, level, optname, (char *)&timeout, &dwlen); +            tv->tv_sec = timeout / 1000; +            tv->tv_usec = (timeout * 1000) % 1000000; +        } else { +            ret = WSAEFAULT; +        } +        *optlen = sizeof (struct timeval); +    } else { +        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); +    int ret = 0; +    if ((level == SOL_SOCKET) && ((optname == SO_RCVTIMEO) || (optname == SO_SNDTIMEO))) { +        struct timeval *tv = optval; +        DWORD timeout = tv->tv_sec * 1000 + tv->tv_usec / 1000; +        ret = setsockopt(sockfd, level, optname, (const char*)&timeout, sizeof(DWORD)); +    } else { +        ret = setsockopt(sockfd, level, optname, (const char*)optval, optlen); +    }      _updateErrno(ret != SOCKET_ERROR);      return ret != SOCKET_ERROR ? ret : -1;  } diff --git a/sockcompat.h b/sockcompat.h index e0b2e5e..56006c1 100644 --- a/sockcompat.h +++ b/sockcompat.h @@ -50,7 +50,9 @@  #include <ws2tcpip.h>  #include <stddef.h> +#ifdef _MSC_VER  typedef signed long ssize_t; +#endif  /* 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); @@ -8,6 +8,7 @@  void __redisSetError(redisContext *c, int type, const char *str); +#ifdef HIREDIS_SSL_TRACE  /**   * Callback used for debugging   */ @@ -37,6 +38,7 @@ static void sslLogCallback(const SSL *ssl, int where, int ret) {          printf("Using SSL version %s. Cipher=%s\n", SSL_get_version(ssl), SSL_get_cipher_name(ssl));      }  } +#endif  typedef pthread_mutex_t sslLockType;  static void sslLockInit(sslLockType *l) { @@ -100,7 +102,9 @@ int redisSslCreate(redisContext *c, const char *capath, const char *certpath,      redisSsl *s = c->ssl;      s->ctx = SSL_CTX_new(SSLv23_client_method()); +#ifdef HIREDIS_SSL_TRACE      SSL_CTX_set_info_callback(s->ctx, sslLogCallback); +#endif      SSL_CTX_set_mode(s->ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);      SSL_CTX_set_options(s->ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);      SSL_CTX_set_verify(s->ctx, SSL_VERIFY_PEER, NULL); @@ -360,7 +360,8 @@ static void test_reply_reader(void) {      freeReplyObject(reply);      redisReaderFree(reader); -    test("Set error when array > INT_MAX: "); +#if LLONG_MAX > SIZE_MAX +    test("Set error when array > SIZE_MAX: ");      reader = redisReaderCreate();      redisReaderFeed(reader, "*9223372036854775807\r\n+asdf\r\n",29);      ret = redisReaderGetReply(reader,&reply); @@ -369,7 +370,6 @@ static void test_reply_reader(void) {      freeReplyObject(reply);      redisReaderFree(reader); -#if LLONG_MAX > SIZE_MAX      test("Set error when bulk > SIZE_MAX: ");      reader = redisReaderCreate();      redisReaderFeed(reader, "$9223372036854775807\r\nasdf\r\n",28); @@ -450,6 +450,7 @@ static void test_blocking_connection_errors(void) {              c->err == REDIS_ERR_OTHER &&              (strcmp(c->errstr, "Name or service not known") == 0 ||               strcmp(c->errstr, "Can't resolve: " HIREDIS_BAD_DOMAIN) == 0 || +             strcmp(c->errstr, "Name does not resolve") == 0 ||               strcmp(c->errstr,                      "nodename nor servname provided, or not known") == 0 ||               strcmp(c->errstr, "No address associated with hostname") == 0 || @@ -2,10 +2,20 @@  #define _WIN32_HELPER_INCLUDE  #ifdef _MSC_VER +#include <winsock2.h> /* for struct timeval */ +  #ifndef inline  #define inline __inline  #endif +#ifndef strcasecmp +#define strcasecmp stricmp +#endif + +#ifndef strncasecmp +#define strncasecmp strnicmp +#endif +  #ifndef va_copy  #define va_copy(d,s) ((d) = (s))  #endif | 
