diff options
-rw-r--r-- | CHANGELOG.md | 31 | ||||
-rw-r--r-- | README.md | 12 | ||||
-rw-r--r-- | appveyor.yml | 36 | ||||
-rw-r--r-- | fmacros.h | 4 | ||||
-rw-r--r-- | net.c | 39 | ||||
-rw-r--r-- | read.c | 2 | ||||
-rw-r--r-- | read.h | 11 | ||||
-rw-r--r-- | sds.c | 4 | ||||
-rw-r--r-- | test.c | 22 |
9 files changed, 126 insertions, 35 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index a5015c5..5c62fe9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,34 @@ +### 1.0.0 (unreleased) + +**Fixes**: + +* Catch a buffer overflow when formatting the error message +* 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**: + +* 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. + +* Remove backwards compatibility macro's + +This removes the following old function aliases, use the new name now: + +| Old | New | +| --------------------------- | ---------------------- | +| redisReplyReaderCreate | redisReaderCreate | +| redisReplyReaderCreate | redisReaderCreate | +| redisReplyReaderFree | redisReaderFree | +| redisReplyReaderFeed | redisReaderFeed | +| redisReplyReaderGetReply | redisReaderGetReply | +| redisReplyReaderSetPrivdata | redisReaderSetPrivdata | +| redisReplyReaderGetObject | redisReaderGetObject | +| redisReplyReaderGetError | redisReaderGetError | + ### 0.13.3 (2015-09-16) * Revert "Clear `REDIS_CONNECTED` flag when connection is closed". @@ -1,5 +1,7 @@ [![Build Status](https://travis-ci.org/redis/hiredis.png)](https://travis-ci.org/redis/hiredis) +**This Readme reflects the latest changed in the master branch. See [v0.13.3](https://github.com/redis/hiredis/tree/v0.13.3) for the Readme and documentation for the latest release.** + # HIREDIS Hiredis is a minimalistic C client library for the [Redis](http://redis.io/) database. @@ -20,7 +22,15 @@ Redis version >= 1.2.0. The library comes with multiple APIs. There is the *synchronous API*, the *asynchronous API* and the *reply parsing API*. -## UPGRADING +## Upgrading to `1.0.0` + +Version 1.0.0 marks a stable release of hiredis. +It includes some minor breaking changes, mostly to make the exposed API more uniform and self-explanatory. +It also bundles the updated `sds` library, to sync up with upstream and Redis. +For most applications a recompile against the new hiredis should be enough. +For code changes see the [Changelog](CHANGELOG.md). + +## Upgrading from `<0.9.0` Version 0.9.0 is a major overhaul of hiredis in every aspect. However, upgrading existing code using hiredis should not be a big pain. The key thing to keep in mind when diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..06bbef1 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,36 @@ +# Appveyor configuration file for CI build of hiredis on Windows (under Cygwin) +environment: + matrix: + - CYG_ROOT: C:\cygwin64 + CYG_SETUP: setup-x86_64.exe + CYG_MIRROR: http://cygwin.mirror.constant.com + CYG_CACHE: C:\cygwin64\var\cache\setup + CYG_BASH: C:\cygwin64\bin\bash + CC: gcc + - CYG_ROOT: C:\cygwin + CYG_SETUP: setup-x86.exe + CYG_MIRROR: http://cygwin.mirror.constant.com + CYG_CACHE: C:\cygwin\var\cache\setup + CYG_BASH: C:\cygwin\bin\bash + CC: gcc + TARGET: 32bit + TARGET_VARS: 32bit-vars + +# Cache Cygwin files to speed up build +cache: + - '%CYG_CACHE%' +clone_depth: 1 + +# Attempt to ensure we don't try to convert line endings to Win32 CRLF as this will cause build to fail +init: + - git config --global core.autocrlf input + +# Install needed build dependencies +install: + - ps: 'Start-FileDownload "http://cygwin.com/$env:CYG_SETUP" -FileName "$env:CYG_SETUP"' + - '%CYG_SETUP% --quiet-mode --no-shortcuts --only-site --root "%CYG_ROOT%" --site "%CYG_MIRROR%" --local-package-dir "%CYG_CACHE%" --packages automake,bison,gcc-core,libtool,make,gettext-devel,gettext,intltool,pkg-config,clang,llvm > NUL 2>&1' + - '%CYG_BASH% -lc "cygcheck -dc cygwin"' + +build_script: + - 'echo building...' + - '%CYG_BASH% -lc "cd $APPVEYOR_BUILD_FOLDER; exec 0</dev/null; make LDFLAGS=$LDFLAGS CC=$CC $TARGET CFLAGS=$CFLAGS && make LDFLAGS=$LDFLAGS CC=$CC $TARGET_VARS hiredis-example"' @@ -6,6 +6,10 @@ #define _DEFAULT_SOURCE #endif +#if defined(__CYGWIN__) +#include <sys/cdefs.h> +#endif + #if defined(__sun__) #define _POSIX_C_SOURCE 200112L #elif defined(__linux__) || defined(__OpenBSD__) || defined(__NetBSD__) @@ -178,19 +178,15 @@ static int redisSetTcpNoDelay(redisContext *c) { #define __MAX_MSEC (((LONG_MAX) - 999) / 1000) -static int redisContextWaitReady(redisContext *c, const struct timeval *timeout) { - struct pollfd wfd[1]; - long msec; - - msec = -1; - wfd[0].fd = c->fd; - wfd[0].events = POLLOUT; +static int redisContextTimeoutMsec(redisContext *c, long *result) +{ + const struct timeval *timeout = c->timeout; + long msec = -1; /* Only use timeout when not NULL. */ if (timeout != NULL) { if (timeout->tv_usec > 1000000 || timeout->tv_sec > __MAX_MSEC) { - __redisSetErrorFromErrno(c, REDIS_ERR_IO, NULL); - redisContextCloseFd(c); + *result = msec; return REDIS_ERR; } @@ -201,6 +197,16 @@ static int redisContextWaitReady(redisContext *c, const struct timeval *timeout) } } + *result = msec; + return REDIS_OK; +} + +static int redisContextWaitReady(redisContext *c, long msec) { + struct pollfd wfd[1]; + + wfd[0].fd = c->fd; + wfd[0].events = POLLOUT; + if (errno == EINPROGRESS) { int res; @@ -265,7 +271,9 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port, int blocking = (c->flags & REDIS_BLOCK); int reuseaddr = (c->flags & REDIS_REUSEADDR); int reuses = 0; + long timeout_msec = -1; + servinfo = NULL; c->connection_type = REDIS_CONN_TCP; c->tcp.port = port; @@ -296,6 +304,11 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port, c->timeout = NULL; } + if (redisContextTimeoutMsec(c, &timeout_msec) != REDIS_OK) { + __redisSetError(c, REDIS_ERR_IO, "Invalid timeout specified"); + goto error; + } + if (source_addr == NULL) { free(c->tcp.source_addr); c->tcp.source_addr = NULL; @@ -374,7 +387,7 @@ addrretry: goto addrretry; } } else { - if (redisContextWaitReady(c,c->timeout) != REDIS_OK) + if (redisContextWaitReady(c,timeout_msec) != REDIS_OK) goto error; } } @@ -415,6 +428,7 @@ int redisContextConnectBindTcp(redisContext *c, const char *addr, int port, int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout) { int blocking = (c->flags & REDIS_BLOCK); struct sockaddr_un sa; + long timeout_msec = -1; if (redisCreateSocket(c,AF_LOCAL) < 0) return REDIS_ERR; @@ -438,13 +452,16 @@ int redisContextConnectUnix(redisContext *c, const char *path, const struct time c->timeout = NULL; } + if (redisContextTimeoutMsec(c,&timeout_msec) != REDIS_OK) + return REDIS_ERR; + sa.sun_family = AF_LOCAL; strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1); if (connect(c->fd, (struct sockaddr*)&sa, sizeof(sa)) == -1) { if (errno == EINPROGRESS && !blocking) { /* This is ok. */ } else { - if (redisContextWaitReady(c,c->timeout) != REDIS_OK) + if (redisContextWaitReady(c,timeout_msec) != REDIS_OK) return REDIS_ERR; } } @@ -127,7 +127,7 @@ static char *seekNewline(char *s, size_t len) { * might not have a trailing NULL character. */ while (pos < _len) { while(pos < _len && s[pos] != '\r') pos++; - if (s[pos] != '\r') { + if (pos==_len) { /* Not found. */ return NULL; } else { @@ -100,14 +100,9 @@ void redisReaderFree(redisReader *r); int redisReaderFeed(redisReader *r, const char *buf, size_t len); int redisReaderGetReply(redisReader *r, void **reply); -/* Backwards compatibility, can be removed on big version bump. */ -#define redisReplyReaderCreate redisReaderCreate -#define redisReplyReaderFree redisReaderFree -#define redisReplyReaderFeed redisReaderFeed -#define redisReplyReaderGetReply redisReaderGetReply -#define redisReplyReaderSetPrivdata(_r, _p) (int)(((redisReader*)(_r))->privdata = (_p)) -#define redisReplyReaderGetObject(_r) (((redisReader*)(_r))->reply) -#define redisReplyReaderGetError(_r) (((redisReader*)(_r))->errstr) +#define redisReaderSetPrivdata(_r, _p) (int)(((redisReader*)(_r))->privdata = (_p)) +#define redisReaderGetObject(_r) (((redisReader*)(_r))->reply) +#define redisReaderGetError(_r) (((redisReader*)(_r))->errstr) #ifdef __cplusplus } @@ -577,14 +577,12 @@ sds sdscatprintf(sds s, const char *fmt, ...) { * %% - Verbatim "%" character. */ sds sdscatfmt(sds s, char const *fmt, ...) { - size_t initlen = sdslen(s); const char *f = fmt; int i; va_list ap; va_start(ap,fmt); - f = fmt; /* Next format specifier byte to process. */ - i = initlen; /* Position of the next byte to write to dest str. */ + i = sdslen(s); /* Position of the next byte to write to dest str. */ while(*f) { char next, *str; size_t l; @@ -30,7 +30,7 @@ struct config { struct { const char *path; - } unix; + } unix_sock; }; /* The following lines make up our testing "framework" :) */ @@ -97,10 +97,10 @@ static redisContext *connect(struct config config) { if (config.type == CONN_TCP) { c = redisConnect(config.tcp.host, config.tcp.port); } else if (config.type == CONN_UNIX) { - c = redisConnectUnix(config.unix.path); + c = redisConnectUnix(config.unix_sock.path); } else if (config.type == CONN_FD) { /* Create a dummy connection just to get an fd to inherit */ - redisContext *dummy_ctx = redisConnectUnix(config.unix.path); + redisContext *dummy_ctx = redisConnectUnix(config.unix_sock.path); if (dummy_ctx) { int fd = disconnect(dummy_ctx, 1); printf("Connecting to inherited fd %d\n", fd); @@ -361,7 +361,7 @@ static void test_blocking_connection_errors(void) { strcmp(c->errstr,"Connection refused") == 0); redisFree(c); - test("Returns error when the unix socket path doesn't accept connections: "); + test("Returns error when the unix_sock socket path doesn't accept connections: "); c = redisConnectUnix((char*)"/tmp/idontexist.sock"); test_cond(c->err == REDIS_ERR_IO); /* Don't care about the message... */ redisFree(c); @@ -482,7 +482,7 @@ static void test_blocking_connection_timeouts(struct config config) { test("Reconnect properly uses owned parameters: "); config.tcp.host = "foo"; - config.unix.path = "foo"; + config.unix_sock.path = "foo"; redisReconnect(c); reply = redisCommand(c, "PING"); test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && strcmp(reply->str, "PONG") == 0); @@ -552,7 +552,7 @@ static void test_invalid_timeout_errors(struct config config) { c = redisConnectWithTimeout(config.tcp.host, config.tcp.port, config.tcp.timeout); - test_cond(c->err == REDIS_ERR_IO); + test_cond(c->err == REDIS_ERR_IO && strcmp(c->errstr, "Invalid timeout specified") == 0); redisFree(c); test("Set error when an invalid timeout sec value is given to redisConnectWithTimeout: "); @@ -562,7 +562,7 @@ static void test_invalid_timeout_errors(struct config config) { c = redisConnectWithTimeout(config.tcp.host, config.tcp.port, config.tcp.timeout); - test_cond(c->err == REDIS_ERR_IO); + test_cond(c->err == REDIS_ERR_IO && strcmp(c->errstr, "Invalid timeout specified") == 0); redisFree(c); } @@ -736,7 +736,7 @@ int main(int argc, char **argv) { .host = "127.0.0.1", .port = 6379 }, - .unix = { + .unix_sock = { .path = "/tmp/redis.sock" } }; @@ -757,7 +757,7 @@ int main(int argc, char **argv) { cfg.tcp.port = atoi(argv[0]); } else if (argc >= 2 && !strcmp(argv[0],"-s")) { argv++; argc--; - cfg.unix.path = argv[0]; + cfg.unix_sock.path = argv[0]; } else if (argc >= 1 && !strcmp(argv[0],"--skip-throughput")) { throughput = 0; } else if (argc >= 1 && !strcmp(argv[0],"--skip-inherit-fd")) { @@ -783,7 +783,7 @@ int main(int argc, char **argv) { test_append_formatted_commands(cfg); if (throughput) test_throughput(cfg); - printf("\nTesting against Unix socket connection (%s):\n", cfg.unix.path); + printf("\nTesting against Unix socket connection (%s):\n", cfg.unix_sock.path); cfg.type = CONN_UNIX; test_blocking_connection(cfg); test_blocking_connection_timeouts(cfg); @@ -791,7 +791,7 @@ int main(int argc, char **argv) { if (throughput) test_throughput(cfg); if (test_inherit_fd) { - printf("\nTesting against inherited fd (%s):\n", cfg.unix.path); + printf("\nTesting against inherited fd (%s):\n", cfg.unix_sock.path); cfg.type = CONN_FD; test_blocking_connection(cfg); } |