Commit 819d291b authored by Oran Agra's avatar Oran Agra
Browse files

Merge remote-tracking branch 'origin/unstable' into 7.2

parents a51eb05b 936cfa46
...@@ -56,7 +56,7 @@ int main (int argc, char **argv) { ...@@ -56,7 +56,7 @@ int main (int argc, char **argv) {
const char *caCert = argc > 5 ? argv[6] : NULL; const char *caCert = argc > 5 ? argv[6] : NULL;
redisSSLContext *ssl; redisSSLContext *ssl;
redisSSLContextError ssl_error; redisSSLContextError ssl_error = REDIS_SSL_CTX_NONE;
redisInitOpenSSL(); redisInitOpenSSL();
......
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <hiredis.h>
#include <async.h>
#include <adapters/libhv.h>
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
redisReply *reply = r;
if (reply == NULL) return;
printf("argv[%s]: %s\n", (char*)privdata, reply->str);
/* Disconnect after receiving the reply to GET */
redisAsyncDisconnect(c);
}
void debugCallback(redisAsyncContext *c, void *r, void *privdata) {
(void)privdata;
redisReply *reply = r;
if (reply == NULL) {
printf("`DEBUG SLEEP` error: %s\n", c->errstr ? c->errstr : "unknown error");
return;
}
redisAsyncDisconnect(c);
}
void connectCallback(const redisAsyncContext *c, int status) {
if (status != REDIS_OK) {
printf("Error: %s\n", c->errstr);
return;
}
printf("Connected...\n");
}
void disconnectCallback(const redisAsyncContext *c, int status) {
if (status != REDIS_OK) {
printf("Error: %s\n", c->errstr);
return;
}
printf("Disconnected...\n");
}
int main (int argc, char **argv) {
#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
#endif
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
if (c->err) {
/* Let *c leak for now... */
printf("Error: %s\n", c->errstr);
return 1;
}
hloop_t* loop = hloop_new(HLOOP_FLAG_QUIT_WHEN_NO_ACTIVE_EVENTS);
redisLibhvAttach(c, loop);
redisAsyncSetTimeout(c, (struct timeval){.tv_sec = 0, .tv_usec = 500000});
redisAsyncSetConnectCallback(c,connectCallback);
redisAsyncSetDisconnectCallback(c,disconnectCallback);
redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1]));
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
redisAsyncCommand(c, debugCallback, NULL, "DEBUG SLEEP %d", 1);
hloop_run(loop);
hloop_free(&loop);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <hiredis.h>
#include <async.h>
#include <adapters/libsdevent.h>
void debugCallback(redisAsyncContext *c, void *r, void *privdata) {
(void)privdata;
redisReply *reply = r;
if (reply == NULL) {
/* The DEBUG SLEEP command will almost always fail, because we have set a 1 second timeout */
printf("`DEBUG SLEEP` error: %s\n", c->errstr ? c->errstr : "unknown error");
return;
}
/* Disconnect after receiving the reply of DEBUG SLEEP (which will not)*/
redisAsyncDisconnect(c);
}
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
redisReply *reply = r;
if (reply == NULL) {
printf("`GET key` error: %s\n", c->errstr ? c->errstr : "unknown error");
return;
}
printf("`GET key` result: argv[%s]: %s\n", (char*)privdata, reply->str);
/* start another request that demonstrate timeout */
redisAsyncCommand(c, debugCallback, NULL, "DEBUG SLEEP %f", 1.5);
}
void connectCallback(const redisAsyncContext *c, int status) {
if (status != REDIS_OK) {
printf("connect error: %s\n", c->errstr);
return;
}
printf("Connected...\n");
}
void disconnectCallback(const redisAsyncContext *c, int status) {
if (status != REDIS_OK) {
printf("disconnect because of error: %s\n", c->errstr);
return;
}
printf("Disconnected...\n");
}
int main (int argc, char **argv) {
signal(SIGPIPE, SIG_IGN);
struct sd_event *event;
sd_event_default(&event);
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
if (c->err) {
printf("Error: %s\n", c->errstr);
redisAsyncFree(c);
return 1;
}
redisLibsdeventAttach(c,event);
redisAsyncSetConnectCallback(c,connectCallback);
redisAsyncSetDisconnectCallback(c,disconnectCallback);
redisAsyncSetTimeout(c, (struct timeval){ .tv_sec = 1, .tv_usec = 0});
/*
In this demo, we first `set key`, then `get key` to demonstrate the basic usage of libsdevent adapter.
Then in `getCallback`, we start a `debug sleep` command to create 1.5 second long request.
Because we have set a 1 second timeout to the connection, the command will always fail with a
timeout error, which is shown in the `debugCallback`.
*/
redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1]));
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
/* sd-event does not quit when there are no handlers registered. Manually exit after 1.5 seconds */
sd_event_source *s;
sd_event_add_time_relative(event, &s, CLOCK_MONOTONIC, 1500000, 1, NULL, NULL);
sd_event_loop(event);
sd_event_source_disable_unref(s);
sd_event_unref(event);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <async.h>
#include <adapters/poll.h>
/* Put in the global scope, so that loop can be explicitly stopped */
static int exit_loop = 0;
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
redisReply *reply = r;
if (reply == NULL) return;
printf("argv[%s]: %s\n", (char*)privdata, reply->str);
/* Disconnect after receiving the reply to GET */
redisAsyncDisconnect(c);
}
void connectCallback(const redisAsyncContext *c, int status) {
if (status != REDIS_OK) {
printf("Error: %s\n", c->errstr);
exit_loop = 1;
return;
}
printf("Connected...\n");
}
void disconnectCallback(const redisAsyncContext *c, int status) {
exit_loop = 1;
if (status != REDIS_OK) {
printf("Error: %s\n", c->errstr);
return;
}
printf("Disconnected...\n");
}
int main (int argc, char **argv) {
signal(SIGPIPE, SIG_IGN);
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
if (c->err) {
/* Let *c leak for now... */
printf("Error: %s\n", c->errstr);
return 1;
}
redisPollAttach(c);
redisAsyncSetConnectCallback(c,connectCallback);
redisAsyncSetDisconnectCallback(c,disconnectCallback);
redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1]));
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
while (!exit_loop)
{
redisPollTick(c, 0.1);
}
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <hiredis.h>
#include <async.h>
#include <adapters/redismoduleapi.h>
void debugCallback(redisAsyncContext *c, void *r, void *privdata) {
(void)privdata; //unused
redisReply *reply = r;
if (reply == NULL) {
/* The DEBUG SLEEP command will almost always fail, because we have set a 1 second timeout */
printf("`DEBUG SLEEP` error: %s\n", c->errstr ? c->errstr : "unknown error");
return;
}
/* Disconnect after receiving the reply of DEBUG SLEEP (which will not)*/
redisAsyncDisconnect(c);
}
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
redisReply *reply = r;
if (reply == NULL) {
if (c->errstr) {
printf("errstr: %s\n", c->errstr);
}
return;
}
printf("argv[%s]: %s\n", (char*)privdata, reply->str);
/* start another request that demonstrate timeout */
redisAsyncCommand(c, debugCallback, NULL, "DEBUG SLEEP %f", 1.5);
}
void connectCallback(const redisAsyncContext *c, int status) {
if (status != REDIS_OK) {
printf("Error: %s\n", c->errstr);
return;
}
printf("Connected...\n");
}
void disconnectCallback(const redisAsyncContext *c, int status) {
if (status != REDIS_OK) {
printf("Error: %s\n", c->errstr);
return;
}
printf("Disconnected...\n");
}
/*
* This example requires Redis 7.0 or above.
*
* 1- Compile this file as a shared library. Directory of "redismodule.h" must
* be in the include path.
* gcc -fPIC -shared -I../../redis/src/ -I.. example-redismoduleapi.c -o example-redismoduleapi.so
*
* 2- Load module:
* redis-server --loadmodule ./example-redismoduleapi.so value
*/
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
int ret = RedisModule_Init(ctx, "example-redismoduleapi", 1, REDISMODULE_APIVER_1);
if (ret != REDISMODULE_OK) {
printf("error module init \n");
return REDISMODULE_ERR;
}
if (redisModuleCompatibilityCheck() != REDIS_OK) {
printf("Redis 7.0 or above is required! \n");
return REDISMODULE_ERR;
}
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
if (c->err) {
/* Let *c leak for now... */
printf("Error: %s\n", c->errstr);
return 1;
}
size_t len;
const char *val = RedisModule_StringPtrLen(argv[argc-1], &len);
RedisModuleCtx *module_ctx = RedisModule_GetDetachedThreadSafeContext(ctx);
redisModuleAttach(c, module_ctx);
redisAsyncSetConnectCallback(c,connectCallback);
redisAsyncSetDisconnectCallback(c,disconnectCallback);
redisAsyncSetTimeout(c, (struct timeval){ .tv_sec = 1, .tv_usec = 0});
/*
In this demo, we first `set key`, then `get key` to demonstrate the basic usage of the adapter.
Then in `getCallback`, we start a `debug sleep` command to create 1.5 second long request.
Because we have set a 1 second timeout to the connection, the command will always fail with a
timeout error, which is shown in the `debugCallback`.
*/
redisAsyncCommand(c, NULL, NULL, "SET key %b", val, len);
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
return 0;
}
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
int main(int argc, char **argv) { int main(int argc, char **argv) {
unsigned int j; unsigned int j;
redisSSLContext *ssl; redisSSLContext *ssl;
redisSSLContextError ssl_error; redisSSLContextError ssl_error = REDIS_SSL_CTX_NONE;
redisContext *c; redisContext *c;
redisReply *reply; redisReply *reply;
if (argc < 4) { if (argc < 4) {
...@@ -27,9 +27,8 @@ int main(int argc, char **argv) { ...@@ -27,9 +27,8 @@ int main(int argc, char **argv) {
redisInitOpenSSL(); redisInitOpenSSL();
ssl = redisCreateSSLContext(ca, NULL, cert, key, NULL, &ssl_error); ssl = redisCreateSSLContext(ca, NULL, cert, key, NULL, &ssl_error);
if (!ssl) { if (!ssl || ssl_error != REDIS_SSL_CTX_NONE) {
printf("SSL Context error: %s\n", printf("SSL Context error: %s\n", redisSSLContextGetError(ssl_error));
redisSSLContextGetError(ssl_error));
exit(1); exit(1);
} }
......
...@@ -7,6 +7,54 @@ ...@@ -7,6 +7,54 @@
#include <winsock2.h> /* For struct timeval */ #include <winsock2.h> /* For struct timeval */
#endif #endif
static void example_argv_command(redisContext *c, size_t n) {
char **argv, tmp[42];
size_t *argvlen;
redisReply *reply;
/* We're allocating two additional elements for command and key */
argv = malloc(sizeof(*argv) * (2 + n));
argvlen = malloc(sizeof(*argvlen) * (2 + n));
/* First the command */
argv[0] = (char*)"RPUSH";
argvlen[0] = sizeof("RPUSH") - 1;
/* Now our key */
argv[1] = (char*)"argvlist";
argvlen[1] = sizeof("argvlist") - 1;
/* Now add the entries we wish to add to the list */
for (size_t i = 2; i < (n + 2); i++) {
argvlen[i] = snprintf(tmp, sizeof(tmp), "argv-element-%zu", i - 2);
argv[i] = strdup(tmp);
}
/* Execute the command using redisCommandArgv. We're sending the arguments with
* two explicit arrays. One for each argument's string, and the other for its
* length. */
reply = redisCommandArgv(c, n + 2, (const char **)argv, (const size_t*)argvlen);
if (reply == NULL || c->err) {
fprintf(stderr, "Error: Couldn't execute redisCommandArgv\n");
exit(1);
}
if (reply->type == REDIS_REPLY_INTEGER) {
printf("%s reply: %lld\n", argv[0], reply->integer);
}
freeReplyObject(reply);
/* Clean up */
for (size_t i = 2; i < (n + 2); i++) {
free(argv[i]);
}
free(argv);
free(argvlen);
}
int main(int argc, char **argv) { int main(int argc, char **argv) {
unsigned int j, isunix = 0; unsigned int j, isunix = 0;
redisContext *c; redisContext *c;
...@@ -87,6 +135,9 @@ int main(int argc, char **argv) { ...@@ -87,6 +135,9 @@ int main(int argc, char **argv) {
} }
freeReplyObject(reply); freeReplyObject(reply);
/* See function for an example of redisCommandArgv */
example_argv_command(c, 10);
/* Disconnects and frees the context */ /* Disconnects and frees the context */
redisFree(c); redisFree(c);
......
...@@ -48,10 +48,9 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { ...@@ -48,10 +48,9 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
memcpy(new_str, data, size); memcpy(new_str, data, size);
new_str[size] = '\0'; new_str[size] = '\0';
redisFormatCommand(&cmd, new_str); if (redisFormatCommand(&cmd, new_str) != -1)
if (cmd != NULL)
hi_free(cmd); hi_free(cmd);
free(new_str); free(new_str);
return 0; return 0;
} }
...@@ -2,11 +2,11 @@ ...@@ -2,11 +2,11 @@
set_and_check(hiredis_INCLUDEDIR "@PACKAGE_INCLUDE_INSTALL_DIR@") set_and_check(hiredis_INCLUDEDIR "@PACKAGE_INCLUDE_INSTALL_DIR@")
IF (NOT TARGET hiredis::hiredis) IF (NOT TARGET hiredis::@hiredis_export_name@)
INCLUDE(${CMAKE_CURRENT_LIST_DIR}/hiredis-targets.cmake) INCLUDE(${CMAKE_CURRENT_LIST_DIR}/hiredis-targets.cmake)
ENDIF() ENDIF()
SET(hiredis_LIBRARIES hiredis::hiredis) SET(hiredis_LIBRARIES hiredis::@hiredis_export_name@)
SET(hiredis_INCLUDE_DIRS ${hiredis_INCLUDEDIR}) SET(hiredis_INCLUDE_DIRS ${hiredis_INCLUDEDIR})
check_required_components(hiredis) check_required_components(hiredis)
......
...@@ -48,6 +48,7 @@ extern int redisContextUpdateConnectTimeout(redisContext *c, const struct timeva ...@@ -48,6 +48,7 @@ extern int redisContextUpdateConnectTimeout(redisContext *c, const struct timeva
extern int redisContextUpdateCommandTimeout(redisContext *c, const struct timeval *timeout); extern int redisContextUpdateCommandTimeout(redisContext *c, const struct timeval *timeout);
static redisContextFuncs redisContextDefaultFuncs = { static redisContextFuncs redisContextDefaultFuncs = {
.close = redisNetClose,
.free_privctx = NULL, .free_privctx = NULL,
.async_read = redisAsyncRead, .async_read = redisAsyncRead,
.async_write = redisAsyncWrite, .async_write = redisAsyncWrite,
...@@ -221,6 +222,9 @@ static void *createIntegerObject(const redisReadTask *task, long long value) { ...@@ -221,6 +222,9 @@ static void *createIntegerObject(const redisReadTask *task, long long value) {
static void *createDoubleObject(const redisReadTask *task, double value, char *str, size_t len) { static void *createDoubleObject(const redisReadTask *task, double value, char *str, size_t len) {
redisReply *r, *parent; redisReply *r, *parent;
if (len == SIZE_MAX) // Prevents hi_malloc(0) if len equals to SIZE_MAX
return NULL;
r = createReplyObject(REDIS_REPLY_DOUBLE); r = createReplyObject(REDIS_REPLY_DOUBLE);
if (r == NULL) if (r == NULL)
return NULL; return NULL;
...@@ -399,6 +403,11 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) { ...@@ -399,6 +403,11 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
/* Copy va_list before consuming with va_arg */ /* Copy va_list before consuming with va_arg */
va_copy(_cpy,ap); va_copy(_cpy,ap);
/* Make sure we have more characters otherwise strchr() accepts
* '\0' as an integer specifier. This is checked after above
* va_copy() to avoid UB in fmt_invalid's call to va_end(). */
if (*_p == '\0') goto fmt_invalid;
/* Integer conversion (without modifiers) */ /* Integer conversion (without modifiers) */
if (strchr(intfmts,*_p) != NULL) { if (strchr(intfmts,*_p) != NULL) {
va_arg(ap,int); va_arg(ap,int);
...@@ -477,6 +486,8 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) { ...@@ -477,6 +486,8 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
touched = 1; touched = 1;
c++; c++;
if (*c == '\0')
break;
} }
c++; c++;
} }
...@@ -719,7 +730,10 @@ static redisContext *redisContextInit(void) { ...@@ -719,7 +730,10 @@ static redisContext *redisContextInit(void) {
void redisFree(redisContext *c) { void redisFree(redisContext *c) {
if (c == NULL) if (c == NULL)
return; return;
redisNetClose(c);
if (c->funcs && c->funcs->close) {
c->funcs->close(c);
}
hi_sdsfree(c->obuf); hi_sdsfree(c->obuf);
redisReaderFree(c->reader); redisReaderFree(c->reader);
...@@ -733,7 +747,7 @@ void redisFree(redisContext *c) { ...@@ -733,7 +747,7 @@ void redisFree(redisContext *c) {
if (c->privdata && c->free_privdata) if (c->privdata && c->free_privdata)
c->free_privdata(c->privdata); c->free_privdata(c->privdata);
if (c->funcs->free_privctx) if (c->funcs && c->funcs->free_privctx)
c->funcs->free_privctx(c->privctx); c->funcs->free_privctx(c->privctx);
memset(c, 0xff, sizeof(*c)); memset(c, 0xff, sizeof(*c));
...@@ -756,7 +770,9 @@ int redisReconnect(redisContext *c) { ...@@ -756,7 +770,9 @@ int redisReconnect(redisContext *c) {
c->privctx = NULL; c->privctx = NULL;
} }
redisNetClose(c); if (c->funcs && c->funcs->close) {
c->funcs->close(c);
}
hi_sdsfree(c->obuf); hi_sdsfree(c->obuf);
redisReaderFree(c->reader); redisReaderFree(c->reader);
...@@ -806,6 +822,12 @@ redisContext *redisConnectWithOptions(const redisOptions *options) { ...@@ -806,6 +822,12 @@ redisContext *redisConnectWithOptions(const redisOptions *options) {
if (options->options & REDIS_OPT_NOAUTOFREEREPLIES) { if (options->options & REDIS_OPT_NOAUTOFREEREPLIES) {
c->flags |= REDIS_NO_AUTO_FREE_REPLIES; c->flags |= REDIS_NO_AUTO_FREE_REPLIES;
} }
if (options->options & REDIS_OPT_PREFER_IPV4) {
c->flags |= REDIS_PREFER_IPV4;
}
if (options->options & REDIS_OPT_PREFER_IPV6) {
c->flags |= REDIS_PREFER_IPV6;
}
/* Set any user supplied RESP3 PUSH handler or use freeReplyObject /* Set any user supplied RESP3 PUSH handler or use freeReplyObject
* as a default unless specifically flagged that we don't want one. */ * as a default unless specifically flagged that we don't want one. */
...@@ -838,7 +860,9 @@ redisContext *redisConnectWithOptions(const redisOptions *options) { ...@@ -838,7 +860,9 @@ redisContext *redisConnectWithOptions(const redisOptions *options) {
return NULL; return NULL;
} }
if (options->command_timeout != NULL && (c->flags & REDIS_BLOCK) && c->fd != REDIS_INVALID_FD) { if (c->err == 0 && c->fd != REDIS_INVALID_FD &&
options->command_timeout != NULL && (c->flags & REDIS_BLOCK))
{
redisContextSetTimeout(c, *options->command_timeout); redisContextSetTimeout(c, *options->command_timeout);
} }
...@@ -920,11 +944,18 @@ int redisSetTimeout(redisContext *c, const struct timeval tv) { ...@@ -920,11 +944,18 @@ int redisSetTimeout(redisContext *c, const struct timeval tv) {
return REDIS_ERR; return REDIS_ERR;
} }
int redisEnableKeepAliveWithInterval(redisContext *c, int interval) {
return redisKeepAlive(c, interval);
}
/* Enable connection KeepAlive. */ /* Enable connection KeepAlive. */
int redisEnableKeepAlive(redisContext *c) { int redisEnableKeepAlive(redisContext *c) {
if (redisKeepAlive(c, REDIS_KEEPALIVE_INTERVAL) != REDIS_OK) return redisKeepAlive(c, REDIS_KEEPALIVE_INTERVAL);
return REDIS_ERR; }
return REDIS_OK;
/* Set the socket option TCP_USER_TIMEOUT. */
int redisSetTcpUserTimeout(redisContext *c, unsigned int timeout) {
return redisContextSetTcpUserTimeout(c, timeout);
} }
/* Set a user provided RESP3 PUSH handler and return any old one set. */ /* Set a user provided RESP3 PUSH handler and return any old one set. */
...@@ -964,8 +995,8 @@ int redisBufferRead(redisContext *c) { ...@@ -964,8 +995,8 @@ int redisBufferRead(redisContext *c) {
* successfully written to the socket. When the buffer is empty after the * successfully written to the socket. When the buffer is empty after the
* write operation, "done" is set to 1 (if given). * write operation, "done" is set to 1 (if given).
* *
* Returns REDIS_ERR if an error occurred trying to write and sets * Returns REDIS_ERR if an unrecoverable error occurred in the underlying
* c->errstr to hold the appropriate error string. * c->funcs->write function.
*/ */
int redisBufferWrite(redisContext *c, int *done) { int redisBufferWrite(redisContext *c, int *done) {
......
...@@ -46,9 +46,9 @@ typedef long long ssize_t; ...@@ -46,9 +46,9 @@ typedef long long ssize_t;
#include "alloc.h" /* for allocation wrappers */ #include "alloc.h" /* for allocation wrappers */
#define HIREDIS_MAJOR 1 #define HIREDIS_MAJOR 1
#define HIREDIS_MINOR 0 #define HIREDIS_MINOR 1
#define HIREDIS_PATCH 3 #define HIREDIS_PATCH 1
#define HIREDIS_SONAME 1.0.3-dev #define HIREDIS_SONAME 1.1.1-dev
/* Connection type can be blocking or non-blocking and is set in the /* Connection type can be blocking or non-blocking and is set in the
* least significant bit of the flags field in redisContext. */ * least significant bit of the flags field in redisContext. */
...@@ -92,6 +92,11 @@ typedef long long ssize_t; ...@@ -92,6 +92,11 @@ typedef long long ssize_t;
/* Flag that indicates the user does not want replies to be automatically freed */ /* Flag that indicates the user does not want replies to be automatically freed */
#define REDIS_NO_AUTO_FREE_REPLIES 0x400 #define REDIS_NO_AUTO_FREE_REPLIES 0x400
/* Flags to prefer IPv6 or IPv4 when doing DNS lookup. (If both are set,
* AF_UNSPEC is used.) */
#define REDIS_PREFER_IPV4 0x800
#define REDIS_PREFER_IPV6 0x1000
#define REDIS_KEEPALIVE_INTERVAL 15 /* seconds */ #define REDIS_KEEPALIVE_INTERVAL 15 /* seconds */
/* number of times we retry to connect in the case of EADDRNOTAVAIL and /* number of times we retry to connect in the case of EADDRNOTAVAIL and
...@@ -149,20 +154,17 @@ struct redisSsl; ...@@ -149,20 +154,17 @@ struct redisSsl;
#define REDIS_OPT_NONBLOCK 0x01 #define REDIS_OPT_NONBLOCK 0x01
#define REDIS_OPT_REUSEADDR 0x02 #define REDIS_OPT_REUSEADDR 0x02
#define REDIS_OPT_NOAUTOFREE 0x04 /* Don't automatically free the async
/** * object on a connection failure, or
* Don't automatically free the async object on a connection failure, * other implicit conditions. Only free
* or other implicit conditions. Only free on an explicit call to disconnect() or free() * on an explicit call to disconnect()
*/ * or free() */
#define REDIS_OPT_NOAUTOFREE 0x04 #define REDIS_OPT_NO_PUSH_AUTOFREE 0x08 /* Don't automatically intercept and
* free RESP3 PUSH replies. */
/* Don't automatically intercept and free RESP3 PUSH replies. */ #define REDIS_OPT_NOAUTOFREEREPLIES 0x10 /* Don't automatically free replies. */
#define REDIS_OPT_NO_PUSH_AUTOFREE 0x08 #define REDIS_OPT_PREFER_IPV4 0x20 /* Prefer IPv4 in DNS lookups. */
#define REDIS_OPT_PREFER_IPV6 0x40 /* Prefer IPv6 in DNS lookups. */
/** #define REDIS_OPT_PREFER_IP_UNSPEC (REDIS_OPT_PREFER_IPV4 | REDIS_OPT_PREFER_IPV6)
* Don't automatically free replies
*/
#define REDIS_OPT_NOAUTOFREEREPLIES 0x10
/* In Unix systems a file descriptor is a regular signed int, with -1 /* In Unix systems a file descriptor is a regular signed int, with -1
* representing an invalid descriptor. In Windows it is a SOCKET * representing an invalid descriptor. In Windows it is a SOCKET
...@@ -220,27 +222,37 @@ typedef struct { ...@@ -220,27 +222,37 @@ typedef struct {
/** /**
* Helper macros to initialize options to their specified fields. * Helper macros to initialize options to their specified fields.
*/ */
#define REDIS_OPTIONS_SET_TCP(opts, ip_, port_) \ #define REDIS_OPTIONS_SET_TCP(opts, ip_, port_) do { \
(opts)->type = REDIS_CONN_TCP; \ (opts)->type = REDIS_CONN_TCP; \
(opts)->endpoint.tcp.ip = ip_; \ (opts)->endpoint.tcp.ip = ip_; \
(opts)->endpoint.tcp.port = port_; (opts)->endpoint.tcp.port = port_; \
} while(0)
#define REDIS_OPTIONS_SET_UNIX(opts, path) \
(opts)->type = REDIS_CONN_UNIX; \ #define REDIS_OPTIONS_SET_UNIX(opts, path) do { \
(opts)->endpoint.unix_socket = path; (opts)->type = REDIS_CONN_UNIX; \
(opts)->endpoint.unix_socket = path; \
#define REDIS_OPTIONS_SET_PRIVDATA(opts, data, dtor) \ } while(0)
(opts)->privdata = data; \
(opts)->free_privdata = dtor; \ #define REDIS_OPTIONS_SET_PRIVDATA(opts, data, dtor) do { \
(opts)->privdata = data; \
(opts)->free_privdata = dtor; \
} while(0)
typedef struct redisContextFuncs { typedef struct redisContextFuncs {
void (*close)(struct redisContext *);
void (*free_privctx)(void *); void (*free_privctx)(void *);
void (*async_read)(struct redisAsyncContext *); void (*async_read)(struct redisAsyncContext *);
void (*async_write)(struct redisAsyncContext *); void (*async_write)(struct redisAsyncContext *);
/* Read/Write data to the underlying communication stream, returning the
* number of bytes read/written. In the event of an unrecoverable error
* these functions shall return a value < 0. In the event of a
* recoverable error, they should return 0. */
ssize_t (*read)(struct redisContext *, char *, size_t); ssize_t (*read)(struct redisContext *, char *, size_t);
ssize_t (*write)(struct redisContext *); ssize_t (*write)(struct redisContext *);
} redisContextFuncs; } redisContextFuncs;
/* Context for a connection to Redis */ /* Context for a connection to Redis */
typedef struct redisContext { typedef struct redisContext {
const redisContextFuncs *funcs; /* Function table */ const redisContextFuncs *funcs; /* Function table */
...@@ -310,6 +322,8 @@ int redisReconnect(redisContext *c); ...@@ -310,6 +322,8 @@ int redisReconnect(redisContext *c);
redisPushFn *redisSetPushCallback(redisContext *c, redisPushFn *fn); redisPushFn *redisSetPushCallback(redisContext *c, redisPushFn *fn);
int redisSetTimeout(redisContext *c, const struct timeval tv); int redisSetTimeout(redisContext *c, const struct timeval tv);
int redisEnableKeepAlive(redisContext *c); int redisEnableKeepAlive(redisContext *c);
int redisEnableKeepAliveWithInterval(redisContext *c, int interval);
int redisSetTcpUserTimeout(redisContext *c, unsigned int timeout);
void redisFree(redisContext *c); void redisFree(redisContext *c);
redisFD redisFreeKeepFd(redisContext *c); redisFD redisFreeKeepFd(redisContext *c);
int redisBufferRead(redisContext *c); int redisBufferRead(redisContext *c);
......
...@@ -9,4 +9,4 @@ Name: hiredis ...@@ -9,4 +9,4 @@ Name: hiredis
Description: Minimalistic C client library for Redis. Description: Minimalistic C client library for Redis.
Version: @PROJECT_VERSION@ Version: @PROJECT_VERSION@
Libs: -L${libdir} -lhiredis Libs: -L${libdir} -lhiredis
Cflags: -I${pkgincludedir} -D_FILE_OFFSET_BITS=64 Cflags: -I${pkgincludedir} -I${includedir} -D_FILE_OFFSET_BITS=64
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
set_and_check(hiredis_ssl_INCLUDEDIR "@PACKAGE_INCLUDE_INSTALL_DIR@") set_and_check(hiredis_ssl_INCLUDEDIR "@PACKAGE_INCLUDE_INSTALL_DIR@")
include(CMakeFindDependencyMacro)
find_dependency(OpenSSL)
IF (NOT TARGET hiredis::hiredis_ssl) IF (NOT TARGET hiredis::hiredis_ssl)
INCLUDE(${CMAKE_CURRENT_LIST_DIR}/hiredis_ssl-targets.cmake) INCLUDE(${CMAKE_CURRENT_LIST_DIR}/hiredis_ssl-targets.cmake)
ENDIF() ENDIF()
......
...@@ -56,11 +56,33 @@ typedef enum { ...@@ -56,11 +56,33 @@ typedef enum {
REDIS_SSL_CTX_CERT_KEY_REQUIRED, /* Client cert and key must both be specified or skipped */ REDIS_SSL_CTX_CERT_KEY_REQUIRED, /* Client cert and key must both be specified or skipped */
REDIS_SSL_CTX_CA_CERT_LOAD_FAILED, /* Failed to load CA Certificate or CA Path */ REDIS_SSL_CTX_CA_CERT_LOAD_FAILED, /* Failed to load CA Certificate or CA Path */
REDIS_SSL_CTX_CLIENT_CERT_LOAD_FAILED, /* Failed to load client certificate */ REDIS_SSL_CTX_CLIENT_CERT_LOAD_FAILED, /* Failed to load client certificate */
REDIS_SSL_CTX_CLIENT_DEFAULT_CERT_FAILED, /* Failed to set client default certificate directory */
REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED, /* Failed to load private key */ REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED, /* Failed to load private key */
REDIS_SSL_CTX_OS_CERTSTORE_OPEN_FAILED, /* Failed to open system certificate store */ REDIS_SSL_CTX_OS_CERTSTORE_OPEN_FAILED, /* Failed to open system certificate store */
REDIS_SSL_CTX_OS_CERT_ADD_FAILED /* Failed to add CA certificates obtained from system to the SSL context */ REDIS_SSL_CTX_OS_CERT_ADD_FAILED /* Failed to add CA certificates obtained from system to the SSL context */
} redisSSLContextError; } redisSSLContextError;
/* Constants that mirror OpenSSL's verify modes. By default,
* REDIS_SSL_VERIFY_PEER is used with redisCreateSSLContext().
* Some Redis clients disable peer verification if there are no
* certificates specified.
*/
#define REDIS_SSL_VERIFY_NONE 0x00
#define REDIS_SSL_VERIFY_PEER 0x01
#define REDIS_SSL_VERIFY_FAIL_IF_NO_PEER_CERT 0x02
#define REDIS_SSL_VERIFY_CLIENT_ONCE 0x04
#define REDIS_SSL_VERIFY_POST_HANDSHAKE 0x08
/* Options to create an OpenSSL context. */
typedef struct {
const char *cacert_filename;
const char *capath;
const char *cert_filename;
const char *private_key_filename;
const char *server_name;
int verify_mode;
} redisSSLOptions;
/** /**
* Return the error message corresponding with the specified error code. * Return the error message corresponding with the specified error code.
*/ */
...@@ -101,6 +123,18 @@ redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char * ...@@ -101,6 +123,18 @@ redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char *
const char *cert_filename, const char *private_key_filename, const char *cert_filename, const char *private_key_filename,
const char *server_name, redisSSLContextError *error); const char *server_name, redisSSLContextError *error);
/**
* Helper function to initialize an OpenSSL context that can be used
* to initiate SSL connections. This is a more extensible version of redisCreateSSLContext().
*
* options contains a structure of SSL options to use.
*
* If error is non-null, it will be populated in case the context creation fails
* (returning a NULL).
*/
redisSSLContext *redisCreateSSLContextWithOptions(redisSSLOptions *options,
redisSSLContextError *error);
/** /**
* Free a previously created OpenSSL context. * Free a previously created OpenSSL context.
*/ */
......
prefix=@CMAKE_INSTALL_PREFIX@ prefix=@CMAKE_INSTALL_PREFIX@
install_libdir=@CMAKE_INSTALL_LIBDIR@
exec_prefix=${prefix} exec_prefix=${prefix}
libdir=${exec_prefix}/lib libdir=${exec_prefix}/${install_libdir}
includedir=${prefix}/include includedir=${prefix}/include
pkgincludedir=${includedir}/hiredis pkgincludedir=${includedir}/hiredis
......
...@@ -50,6 +50,8 @@ ...@@ -50,6 +50,8 @@
/* Defined in hiredis.c */ /* Defined in hiredis.c */
void __redisSetError(redisContext *c, int type, const char *str); void __redisSetError(redisContext *c, int type, const char *str);
int redisContextUpdateCommandTimeout(redisContext *c, const struct timeval *timeout);
void redisNetClose(redisContext *c) { void redisNetClose(redisContext *c) {
if (c && c->fd != REDIS_INVALID_FD) { if (c && c->fd != REDIS_INVALID_FD) {
close(c->fd); close(c->fd);
...@@ -68,7 +70,7 @@ ssize_t redisNetRead(redisContext *c, char *buf, size_t bufcap) { ...@@ -68,7 +70,7 @@ ssize_t redisNetRead(redisContext *c, char *buf, size_t bufcap) {
__redisSetError(c, REDIS_ERR_TIMEOUT, "recv timeout"); __redisSetError(c, REDIS_ERR_TIMEOUT, "recv timeout");
return -1; return -1;
} else { } else {
__redisSetError(c, REDIS_ERR_IO, NULL); __redisSetError(c, REDIS_ERR_IO, strerror(errno));
return -1; return -1;
} }
} else if (nread == 0) { } else if (nread == 0) {
...@@ -80,15 +82,19 @@ ssize_t redisNetRead(redisContext *c, char *buf, size_t bufcap) { ...@@ -80,15 +82,19 @@ ssize_t redisNetRead(redisContext *c, char *buf, size_t bufcap) {
} }
ssize_t redisNetWrite(redisContext *c) { ssize_t redisNetWrite(redisContext *c) {
ssize_t nwritten = send(c->fd, c->obuf, hi_sdslen(c->obuf), 0); ssize_t nwritten;
nwritten = send(c->fd, c->obuf, hi_sdslen(c->obuf), 0);
if (nwritten < 0) { if (nwritten < 0) {
if ((errno == EWOULDBLOCK && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) { if ((errno == EWOULDBLOCK && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) {
/* Try again later */ /* Try again */
return 0;
} else { } else {
__redisSetError(c, REDIS_ERR_IO, NULL); __redisSetError(c, REDIS_ERR_IO, strerror(errno));
return -1; return -1;
} }
} }
return nwritten; return nwritten;
} }
...@@ -166,6 +172,7 @@ int redisKeepAlive(redisContext *c, int interval) { ...@@ -166,6 +172,7 @@ int redisKeepAlive(redisContext *c, int interval) {
int val = 1; int val = 1;
redisFD fd = c->fd; redisFD fd = c->fd;
#ifndef _WIN32
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1){ if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1){
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno)); __redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
return REDIS_ERR; return REDIS_ERR;
...@@ -199,7 +206,15 @@ int redisKeepAlive(redisContext *c, int interval) { ...@@ -199,7 +206,15 @@ int redisKeepAlive(redisContext *c, int interval) {
} }
#endif #endif
#endif #endif
#else
int res;
res = win32_redisKeepAlive(fd, interval * 1000);
if (res != 0) {
__redisSetError(c, REDIS_ERR_OTHER, strerror(res));
return REDIS_ERR;
}
#endif
return REDIS_OK; return REDIS_OK;
} }
...@@ -213,6 +228,22 @@ int redisSetTcpNoDelay(redisContext *c) { ...@@ -213,6 +228,22 @@ int redisSetTcpNoDelay(redisContext *c) {
return REDIS_OK; return REDIS_OK;
} }
int redisContextSetTcpUserTimeout(redisContext *c, unsigned int timeout) {
int res;
#ifdef TCP_USER_TIMEOUT
res = setsockopt(c->fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &timeout, sizeof(timeout));
#else
res = -1;
(void)timeout;
#endif
if (res == -1) {
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(TCP_USER_TIMEOUT)");
redisNetClose(c);
return REDIS_ERR;
}
return REDIS_OK;
}
#define __MAX_MSEC (((LONG_MAX) - 999) / 1000) #define __MAX_MSEC (((LONG_MAX) - 999) / 1000)
static int redisContextTimeoutMsec(redisContext *c, long *result) static int redisContextTimeoutMsec(redisContext *c, long *result)
...@@ -223,6 +254,7 @@ static int redisContextTimeoutMsec(redisContext *c, long *result) ...@@ -223,6 +254,7 @@ static int redisContextTimeoutMsec(redisContext *c, long *result)
/* Only use timeout when not NULL. */ /* Only use timeout when not NULL. */
if (timeout != NULL) { if (timeout != NULL) {
if (timeout->tv_usec > 1000000 || timeout->tv_sec > __MAX_MSEC) { if (timeout->tv_usec > 1000000 || timeout->tv_sec > __MAX_MSEC) {
__redisSetError(c, REDIS_ERR_IO, "Invalid timeout specified");
*result = msec; *result = msec;
return REDIS_ERR; return REDIS_ERR;
} }
...@@ -277,12 +309,28 @@ int redisCheckConnectDone(redisContext *c, int *completed) { ...@@ -277,12 +309,28 @@ int redisCheckConnectDone(redisContext *c, int *completed) {
*completed = 1; *completed = 1;
return REDIS_OK; return REDIS_OK;
} }
switch (errno) { int error = errno;
if (error == EINPROGRESS) {
/* must check error to see if connect failed. Get the socket error */
int fail, so_error;
socklen_t optlen = sizeof(so_error);
fail = getsockopt(c->fd, SOL_SOCKET, SO_ERROR, &so_error, &optlen);
if (fail == 0) {
if (so_error == 0) {
/* Socket is connected! */
*completed = 1;
return REDIS_OK;
}
/* connection error; */
errno = so_error;
error = so_error;
}
}
switch (error) {
case EISCONN: case EISCONN:
*completed = 1; *completed = 1;
return REDIS_OK; return REDIS_OK;
case EALREADY: case EALREADY:
case EINPROGRESS:
case EWOULDBLOCK: case EWOULDBLOCK:
*completed = 0; *completed = 0;
return REDIS_OK; return REDIS_OK;
...@@ -317,6 +365,10 @@ int redisContextSetTimeout(redisContext *c, const struct timeval tv) { ...@@ -317,6 +365,10 @@ int redisContextSetTimeout(redisContext *c, const struct timeval tv) {
const void *to_ptr = &tv; const void *to_ptr = &tv;
size_t to_sz = sizeof(tv); size_t to_sz = sizeof(tv);
if (redisContextUpdateCommandTimeout(c, &tv) != REDIS_OK) {
__redisSetError(c, REDIS_ERR_OOM, "Out of memory");
return REDIS_ERR;
}
if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,to_ptr,to_sz) == -1) { if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,to_ptr,to_sz) == -1) {
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)"); __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)");
return REDIS_ERR; return REDIS_ERR;
...@@ -400,7 +452,6 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port, ...@@ -400,7 +452,6 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
} }
if (redisContextTimeoutMsec(c, &timeout_msec) != REDIS_OK) { if (redisContextTimeoutMsec(c, &timeout_msec) != REDIS_OK) {
__redisSetError(c, REDIS_ERR_IO, "Invalid timeout specified");
goto error; goto error;
} }
...@@ -417,17 +468,25 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port, ...@@ -417,17 +468,25 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
hints.ai_family = AF_INET; hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM; hints.ai_socktype = SOCK_STREAM;
/* Try with IPv6 if no IPv4 address was found. We do it in this order since /* DNS lookup. To use dual stack, set both flags to prefer both IPv4 and
* in a Redis client you can't afford to test if you have IPv6 connectivity * IPv6. By default, for historical reasons, we try IPv4 first and then we
* as this would add latency to every connect. Otherwise a more sensible * try IPv6 only if no IPv4 address was found. */
* route could be: Use IPv6 if both addresses are available and there is IPv6 if (c->flags & REDIS_PREFER_IPV6 && c->flags & REDIS_PREFER_IPV4)
* connectivity. */ hints.ai_family = AF_UNSPEC;
if ((rv = getaddrinfo(c->tcp.host,_port,&hints,&servinfo)) != 0) { else if (c->flags & REDIS_PREFER_IPV6)
hints.ai_family = AF_INET6; hints.ai_family = AF_INET6;
if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) { else
__redisSetError(c,REDIS_ERR_OTHER,gai_strerror(rv)); hints.ai_family = AF_INET;
return REDIS_ERR;
} rv = getaddrinfo(c->tcp.host, _port, &hints, &servinfo);
if (rv != 0 && hints.ai_family != AF_UNSPEC) {
/* Try again with the other IP version. */
hints.ai_family = (hints.ai_family == AF_INET) ? AF_INET6 : AF_INET;
rv = getaddrinfo(c->tcp.host, _port, &hints, &servinfo);
}
if (rv != 0) {
__redisSetError(c, REDIS_ERR_OTHER, gai_strerror(rv));
return REDIS_ERR;
} }
for (p = servinfo; p != NULL; p = p->ai_next) { for (p = servinfo; p != NULL; p = p->ai_next) {
addrretry: addrretry:
......
...@@ -52,5 +52,6 @@ int redisKeepAlive(redisContext *c, int interval); ...@@ -52,5 +52,6 @@ int redisKeepAlive(redisContext *c, int interval);
int redisCheckConnectDone(redisContext *c, int *completed); int redisCheckConnectDone(redisContext *c, int *completed);
int redisSetTcpNoDelay(redisContext *c); int redisSetTcpNoDelay(redisContext *c);
int redisContextSetTcpUserTimeout(redisContext *c, unsigned int timeout);
#endif #endif
...@@ -303,11 +303,14 @@ static int processLineItem(redisReader *r) { ...@@ -303,11 +303,14 @@ static int processLineItem(redisReader *r) {
d = INFINITY; /* Positive infinite. */ d = INFINITY; /* Positive infinite. */
} else if (len == 4 && strcasecmp(buf,"-inf") == 0) { } else if (len == 4 && strcasecmp(buf,"-inf") == 0) {
d = -INFINITY; /* Negative infinite. */ d = -INFINITY; /* Negative infinite. */
} else if ((len == 3 && strcasecmp(buf,"nan") == 0) ||
(len == 4 && strcasecmp(buf, "-nan") == 0)) {
d = NAN; /* nan. */
} else { } else {
d = strtod((char*)buf,&eptr); d = strtod((char*)buf,&eptr);
/* RESP3 only allows "inf", "-inf", and finite values, while /* RESP3 only allows "inf", "-inf", and finite values, while
* strtod() allows other variations on infinity, NaN, * strtod() allows other variations on infinity,
* etc. We explicity handle our two allowed infinite cases * etc. We explicity handle our two allowed infinite cases and NaN
* above, so strtod() should only result in finite values. */ * above, so strtod() should only result in finite values. */
if (buf[0] == '\0' || eptr != &buf[len] || !isfinite(d)) { if (buf[0] == '\0' || eptr != &buf[len] || !isfinite(d)) {
__redisReaderSetError(r,REDIS_ERR_PROTOCOL, __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
...@@ -374,7 +377,7 @@ static int processLineItem(redisReader *r) { ...@@ -374,7 +377,7 @@ static int processLineItem(redisReader *r) {
if (r->fn && r->fn->createString) if (r->fn && r->fn->createString)
obj = r->fn->createString(cur,p,len); obj = r->fn->createString(cur,p,len);
else else
obj = (void*)(size_t)(cur->type); obj = (void*)(uintptr_t)(cur->type);
} }
if (obj == NULL) { if (obj == NULL) {
...@@ -439,7 +442,7 @@ static int processBulkItem(redisReader *r) { ...@@ -439,7 +442,7 @@ static int processBulkItem(redisReader *r) {
if (r->fn && r->fn->createString) if (r->fn && r->fn->createString)
obj = r->fn->createString(cur,s+2,len); obj = r->fn->createString(cur,s+2,len);
else else
obj = (void*)(long)cur->type; obj = (void*)(uintptr_t)cur->type;
success = 1; success = 1;
} }
} }
...@@ -536,7 +539,7 @@ static int processAggregateItem(redisReader *r) { ...@@ -536,7 +539,7 @@ static int processAggregateItem(redisReader *r) {
if (r->fn && r->fn->createArray) if (r->fn && r->fn->createArray)
obj = r->fn->createArray(cur,elements); obj = r->fn->createArray(cur,elements);
else else
obj = (void*)(long)cur->type; obj = (void*)(uintptr_t)cur->type;
if (obj == NULL) { if (obj == NULL) {
__redisReaderSetErrorOOM(r); __redisReaderSetErrorOOM(r);
......
...@@ -90,6 +90,7 @@ hisds hi_sdsnewlen(const void *init, size_t initlen) { ...@@ -90,6 +90,7 @@ hisds hi_sdsnewlen(const void *init, size_t initlen) {
int hdrlen = hi_sdsHdrSize(type); int hdrlen = hi_sdsHdrSize(type);
unsigned char *fp; /* flags pointer. */ unsigned char *fp; /* flags pointer. */
if (hdrlen+initlen+1 <= initlen) return NULL; /* Catch size_t overflow */
sh = hi_s_malloc(hdrlen+initlen+1); sh = hi_s_malloc(hdrlen+initlen+1);
if (sh == NULL) return NULL; if (sh == NULL) return NULL;
if (!init) if (!init)
...@@ -174,7 +175,7 @@ void hi_sdsfree(hisds s) { ...@@ -174,7 +175,7 @@ void hi_sdsfree(hisds s) {
* the output will be "6" as the string was modified but the logical length * the output will be "6" as the string was modified but the logical length
* remains 6 bytes. */ * remains 6 bytes. */
void hi_sdsupdatelen(hisds s) { void hi_sdsupdatelen(hisds s) {
int reallen = strlen(s); size_t reallen = strlen(s);
hi_sdssetlen(s, reallen); hi_sdssetlen(s, reallen);
} }
...@@ -196,7 +197,7 @@ void hi_sdsclear(hisds s) { ...@@ -196,7 +197,7 @@ void hi_sdsclear(hisds s) {
hisds hi_sdsMakeRoomFor(hisds s, size_t addlen) { hisds hi_sdsMakeRoomFor(hisds s, size_t addlen) {
void *sh, *newsh; void *sh, *newsh;
size_t avail = hi_sdsavail(s); size_t avail = hi_sdsavail(s);
size_t len, newlen; size_t len, newlen, reqlen;
char type, oldtype = s[-1] & HI_SDS_TYPE_MASK; char type, oldtype = s[-1] & HI_SDS_TYPE_MASK;
int hdrlen; int hdrlen;
...@@ -205,7 +206,8 @@ hisds hi_sdsMakeRoomFor(hisds s, size_t addlen) { ...@@ -205,7 +206,8 @@ hisds hi_sdsMakeRoomFor(hisds s, size_t addlen) {
len = hi_sdslen(s); len = hi_sdslen(s);
sh = (char*)s-hi_sdsHdrSize(oldtype); sh = (char*)s-hi_sdsHdrSize(oldtype);
newlen = (len+addlen); reqlen = newlen = (len+addlen);
if (newlen <= len) return NULL; /* Catch size_t overflow */
if (newlen < HI_SDS_MAX_PREALLOC) if (newlen < HI_SDS_MAX_PREALLOC)
newlen *= 2; newlen *= 2;
else else
...@@ -219,6 +221,7 @@ hisds hi_sdsMakeRoomFor(hisds s, size_t addlen) { ...@@ -219,6 +221,7 @@ hisds hi_sdsMakeRoomFor(hisds s, size_t addlen) {
if (type == HI_SDS_TYPE_5) type = HI_SDS_TYPE_8; if (type == HI_SDS_TYPE_5) type = HI_SDS_TYPE_8;
hdrlen = hi_sdsHdrSize(type); hdrlen = hi_sdsHdrSize(type);
if (hdrlen+newlen+1 <= reqlen) return NULL; /* Catch size_t overflow */
if (oldtype==type) { if (oldtype==type) {
newsh = hi_s_realloc(sh, hdrlen+newlen+1); newsh = hi_s_realloc(sh, hdrlen+newlen+1);
if (newsh == NULL) return NULL; if (newsh == NULL) return NULL;
...@@ -580,7 +583,7 @@ hisds hi_sdscatprintf(hisds s, const char *fmt, ...) { ...@@ -580,7 +583,7 @@ hisds hi_sdscatprintf(hisds s, const char *fmt, ...) {
*/ */
hisds hi_sdscatfmt(hisds s, char const *fmt, ...) { hisds hi_sdscatfmt(hisds s, char const *fmt, ...) {
const char *f = fmt; const char *f = fmt;
int i; long i;
va_list ap; va_list ap;
va_start(ap,fmt); va_start(ap,fmt);
...@@ -753,16 +756,16 @@ int hi_sdsrange(hisds s, ssize_t start, ssize_t end) { ...@@ -753,16 +756,16 @@ int hi_sdsrange(hisds s, ssize_t start, ssize_t end) {
return 0; return 0;
} }
/* Apply tolower() to every character of the hisds string 's'. */ /* Apply tolower() to every character of the sds string 's'. */
void hi_sdstolower(hisds s) { void hi_sdstolower(hisds s) {
int len = hi_sdslen(s), j; size_t len = hi_sdslen(s), j;
for (j = 0; j < len; j++) s[j] = tolower(s[j]); for (j = 0; j < len; j++) s[j] = tolower(s[j]);
} }
/* Apply toupper() to every character of the hisds string 's'. */ /* Apply toupper() to every character of the sds string 's'. */
void hi_sdstoupper(hisds s) { void hi_sdstoupper(hisds s) {
int len = hi_sdslen(s), j; size_t len = hi_sdslen(s), j;
for (j = 0; j < len; j++) s[j] = toupper(s[j]); for (j = 0; j < len; j++) s[j] = toupper(s[j]);
} }
......
...@@ -35,9 +35,11 @@ ...@@ -35,9 +35,11 @@
#define HI_SDS_MAX_PREALLOC (1024*1024) #define HI_SDS_MAX_PREALLOC (1024*1024)
#ifdef _MSC_VER #ifdef _MSC_VER
#define __attribute__(x)
typedef long long ssize_t; typedef long long ssize_t;
#define SSIZE_MAX (LLONG_MAX >> 1) #define SSIZE_MAX (LLONG_MAX >> 1)
#ifndef __clang__
#define __attribute__(x)
#endif
#endif #endif
#include <sys/types.h> #include <sys/types.h>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment