summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml33
-rw-r--r--CHANGELOG.md15
-rw-r--r--CMakeLists.txt5
-rw-r--r--README.md3
-rw-r--r--async.c10
-rw-r--r--hiredis.c4
-rw-r--r--hiredis.h4
-rw-r--r--net.c19
-rw-r--r--read.c7
-rw-r--r--read.h2
-rw-r--r--sds.c2
-rw-r--r--sds.h30
-rw-r--r--sockcompat.c26
-rw-r--r--sockcompat.h2
-rw-r--r--sslio.c4
-rw-r--r--test.c5
-rw-r--r--win32.h10
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)
diff --git a/README.md b/README.md
index 01223ea..c0b432f 100644
--- a/README.md
+++ b/README.md
@@ -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)
diff --git a/async.c b/async.c
index 50eb2d9..e46573f 100644
--- a/async.c
+++ b/async.c
@@ -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)
*/
diff --git a/hiredis.c b/hiredis.c
index 6bf0115..9627832 100644
--- a/hiredis.c
+++ b/hiredis.c
@@ -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);
diff --git a/hiredis.h b/hiredis.h
index e5c5e19..d76a9e3 100644
--- a/hiredis.h
+++ b/hiredis.h
@@ -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 */
diff --git a/net.c b/net.c
index ac304b4..e5f40b0 100644
--- a/net.c
+++ b/net.c
@@ -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;
diff --git a/read.c b/read.c
index cc0f3cc..2553c01 100644
--- a/read.c
+++ b/read.c
@@ -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;
diff --git a/read.h b/read.h
index f211894..af02aaf 100644
--- a/read.h
+++ b/read.h
@@ -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*);
diff --git a/sds.c b/sds.c
index 44777b1..6cf7584 100644
--- a/sds.c
+++ b/sds.c
@@ -1035,7 +1035,7 @@ sds *sdssplitargs(const char *line, int *argc) {
s_free(vector);
return NULL;
}
-
+
vector = new_vector;
vector[*argc] = current;
(*argc)++;
diff --git a/sds.h b/sds.h
index eafe2cf..3f9a964 100644
--- a/sds.h
+++ b/sds.h
@@ -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);
diff --git a/sslio.c b/sslio.c
index 25cb5c2..47064db 100644
--- a/sslio.c
+++ b/sslio.c
@@ -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);
diff --git a/test.c b/test.c
index 4af99b2..a35b984 100644
--- a/test.c
+++ b/test.c
@@ -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 ||
diff --git a/win32.h b/win32.h
index 7cb5706..04289c6 100644
--- a/win32.h
+++ b/win32.h
@@ -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