Unverified Commit f9bccfb7 authored by Mark Nunberg's avatar Mark Nunberg Committed by GitHub
Browse files

Merge branch 'master' into createArray-size_t

parents 300fc013 5d013039
...@@ -37,6 +37,10 @@ ...@@ -37,6 +37,10 @@
#include "hiredis.h" #include "hiredis.h"
void redisNetClose(redisContext *c);
int redisNetRead(redisContext *c, char *buf, size_t bufcap);
int redisNetWrite(redisContext *c);
int redisCheckSocketError(redisContext *c); int redisCheckSocketError(redisContext *c);
int redisContextSetTimeout(redisContext *c, const struct timeval tv); int redisContextSetTimeout(redisContext *c, const struct timeval tv);
int redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout); int redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout);
...@@ -45,5 +49,6 @@ int redisContextConnectBindTcp(redisContext *c, const char *addr, int port, ...@@ -45,5 +49,6 @@ int redisContextConnectBindTcp(redisContext *c, const char *addr, int port,
const char *source_addr); const char *source_addr);
int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout); int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout);
int redisKeepAlive(redisContext *c, int interval); int redisKeepAlive(redisContext *c, int interval);
int redisCheckConnectDone(redisContext *c, int *completed);
#endif #endif
...@@ -154,7 +154,7 @@ static char *seekNewline(char *s, size_t len) { ...@@ -154,7 +154,7 @@ static char *seekNewline(char *s, size_t len) {
* Because of its strictness, it is safe to use this function to check if * Because of its strictness, it is safe to use this function to check if
* you can convert a string into a long long, and obtain back the string * you can convert a string into a long long, and obtain back the string
* from the number without any loss in the string representation. */ * from the number without any loss in the string representation. */
int string2ll(const char *s, size_t slen, long long *value) { static int string2ll(const char *s, size_t slen, long long *value) {
const char *p = s; const char *p = s;
size_t plen = 0; size_t plen = 0;
int negative = 0; int negative = 0;
...@@ -590,8 +590,11 @@ int redisReaderGetReply(redisReader *r, void **reply) { ...@@ -590,8 +590,11 @@ int redisReaderGetReply(redisReader *r, void **reply) {
/* Emit a reply when there is one. */ /* Emit a reply when there is one. */
if (r->ridx == -1) { if (r->ridx == -1) {
if (reply != NULL) if (reply != NULL) {
*reply = r->reply; *reply = r->reply;
} else if (r->reply != NULL && r->fn && r->fn->freeObject) {
r->fn->freeObject(r->reply);
}
r->reply = NULL; r->reply = NULL;
} }
return REDIS_OK; return REDIS_OK;
......
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
#define REDIS_ERR_EOF 3 /* End of file */ #define REDIS_ERR_EOF 3 /* End of file */
#define REDIS_ERR_PROTOCOL 4 /* Protocol error */ #define REDIS_ERR_PROTOCOL 4 /* Protocol error */
#define REDIS_ERR_OOM 5 /* Out of memory */ #define REDIS_ERR_OOM 5 /* Out of memory */
#define REDIS_ERR_TIMEOUT 6 /* Timed out */
#define REDIS_ERR_OTHER 2 /* Everything else... */ #define REDIS_ERR_OTHER 2 /* Everything else... */
#define REDIS_REPLY_STRING 1 #define REDIS_REPLY_STRING 1
......
...@@ -219,7 +219,10 @@ sds sdsMakeRoomFor(sds s, size_t addlen) { ...@@ -219,7 +219,10 @@ sds sdsMakeRoomFor(sds s, size_t addlen) {
hdrlen = sdsHdrSize(type); hdrlen = sdsHdrSize(type);
if (oldtype==type) { if (oldtype==type) {
newsh = s_realloc(sh, hdrlen+newlen+1); newsh = s_realloc(sh, hdrlen+newlen+1);
if (newsh == NULL) return NULL; if (newsh == NULL) {
s_free(sh);
return NULL;
}
s = (char*)newsh+hdrlen; s = (char*)newsh+hdrlen;
} else { } else {
/* Since the header size changes, need to move the string forward, /* Since the header size changes, need to move the string forward,
...@@ -592,6 +595,7 @@ sds sdscatfmt(sds s, char const *fmt, ...) { ...@@ -592,6 +595,7 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
/* Make sure there is always space for at least 1 char. */ /* Make sure there is always space for at least 1 char. */
if (sdsavail(s)==0) { if (sdsavail(s)==0) {
s = sdsMakeRoomFor(s,1); s = sdsMakeRoomFor(s,1);
if (s == NULL) goto fmt_error;
} }
switch(*f) { switch(*f) {
...@@ -605,6 +609,7 @@ sds sdscatfmt(sds s, char const *fmt, ...) { ...@@ -605,6 +609,7 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
l = (next == 's') ? strlen(str) : sdslen(str); l = (next == 's') ? strlen(str) : sdslen(str);
if (sdsavail(s) < l) { if (sdsavail(s) < l) {
s = sdsMakeRoomFor(s,l); s = sdsMakeRoomFor(s,l);
if (s == NULL) goto fmt_error;
} }
memcpy(s+i,str,l); memcpy(s+i,str,l);
sdsinclen(s,l); sdsinclen(s,l);
...@@ -621,6 +626,7 @@ sds sdscatfmt(sds s, char const *fmt, ...) { ...@@ -621,6 +626,7 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
l = sdsll2str(buf,num); l = sdsll2str(buf,num);
if (sdsavail(s) < l) { if (sdsavail(s) < l) {
s = sdsMakeRoomFor(s,l); s = sdsMakeRoomFor(s,l);
if (s == NULL) goto fmt_error;
} }
memcpy(s+i,buf,l); memcpy(s+i,buf,l);
sdsinclen(s,l); sdsinclen(s,l);
...@@ -638,6 +644,7 @@ sds sdscatfmt(sds s, char const *fmt, ...) { ...@@ -638,6 +644,7 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
l = sdsull2str(buf,unum); l = sdsull2str(buf,unum);
if (sdsavail(s) < l) { if (sdsavail(s) < l) {
s = sdsMakeRoomFor(s,l); s = sdsMakeRoomFor(s,l);
if (s == NULL) goto fmt_error;
} }
memcpy(s+i,buf,l); memcpy(s+i,buf,l);
sdsinclen(s,l); sdsinclen(s,l);
...@@ -662,6 +669,10 @@ sds sdscatfmt(sds s, char const *fmt, ...) { ...@@ -662,6 +669,10 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
/* Add null-term */ /* Add null-term */
s[i] = '\0'; s[i] = '\0';
return s; return s;
fmt_error:
va_end(ap);
return NULL;
} }
/* Remove the part of the string from left and from right composed just of /* Remove the part of the string from left and from right composed just of
...@@ -1018,10 +1029,18 @@ sds *sdssplitargs(const char *line, int *argc) { ...@@ -1018,10 +1029,18 @@ sds *sdssplitargs(const char *line, int *argc) {
if (*p) p++; if (*p) p++;
} }
/* add the token to the vector */ /* add the token to the vector */
vector = s_realloc(vector,((*argc)+1)*sizeof(char*)); {
char **new_vector = s_realloc(vector,((*argc)+1)*sizeof(char*));
if (new_vector == NULL) {
s_free(vector);
return NULL;
}
vector = new_vector;
vector[*argc] = current; vector[*argc] = current;
(*argc)++; (*argc)++;
current = NULL; current = NULL;
}
} else { } else {
/* Even on empty input string return something not NULL. */ /* Even on empty input string return something not NULL. */
if (vector == NULL) vector = s_malloc(sizeof(void*)); if (vector == NULL) vector = s_malloc(sizeof(void*));
......
...@@ -34,6 +34,9 @@ ...@@ -34,6 +34,9 @@
#define __SDS_H #define __SDS_H
#define SDS_MAX_PREALLOC (1024*1024) #define SDS_MAX_PREALLOC (1024*1024)
#ifdef _WIN32
#define __attribute__(x)
#endif
#include <sys/types.h> #include <sys/types.h>
#include <stdarg.h> #include <stdarg.h>
......
/*
* 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 = 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 = 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;
}
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 */
/*
* 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>
typedef signed long ssize_t;
/* 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 */
#include "hiredis.h"
#include "sslio.h"
#include <assert.h>
#ifdef HIREDIS_SSL
#include <pthread.h>
#include <errno.h>
void __redisSetError(redisContext *c, int type, const char *str);
/**
* Callback used for debugging
*/
static void sslLogCallback(const SSL *ssl, int where, int ret) {
const char *retstr = "";
int should_log = 1;
/* Ignore low-level SSL stuff */
if (where & SSL_CB_ALERT) {
should_log = 1;
}
if (where == SSL_CB_HANDSHAKE_START || where == SSL_CB_HANDSHAKE_DONE) {
should_log = 1;
}
if ((where & SSL_CB_EXIT) && ret == 0) {
should_log = 1;
}
if (!should_log) {
return;
}
retstr = SSL_alert_type_string(ret);
printf("ST(0x%x). %s. R(0x%x)%s\n", where, SSL_state_string_long(ssl), ret, retstr);
if (where == SSL_CB_HANDSHAKE_DONE) {
printf("Using SSL version %s. Cipher=%s\n", SSL_get_version(ssl), SSL_get_cipher_name(ssl));
}
}
typedef pthread_mutex_t sslLockType;
static void sslLockInit(sslLockType *l) {
pthread_mutex_init(l, NULL);
}
static void sslLockAcquire(sslLockType *l) {
pthread_mutex_lock(l);
}
static void sslLockRelease(sslLockType *l) {
pthread_mutex_unlock(l);
}
static pthread_mutex_t *ossl_locks;
static void opensslDoLock(int mode, int lkid, const char *f, int line) {
sslLockType *l = ossl_locks + lkid;
if (mode & CRYPTO_LOCK) {
sslLockAcquire(l);
} else {
sslLockRelease(l);
}
(void)f;
(void)line;
}
static void initOpensslLocks(void) {
unsigned ii, nlocks;
if (CRYPTO_get_locking_callback() != NULL) {
/* Someone already set the callback before us. Don't destroy it! */
return;
}
nlocks = CRYPTO_num_locks();
ossl_locks = malloc(sizeof(*ossl_locks) * nlocks);
for (ii = 0; ii < nlocks; ii++) {
sslLockInit(ossl_locks + ii);
}
CRYPTO_set_locking_callback(opensslDoLock);
}
void redisFreeSsl(redisSsl *ssl){
if (ssl->ctx) {
SSL_CTX_free(ssl->ctx);
}
if (ssl->ssl) {
SSL_free(ssl->ssl);
}
free(ssl);
}
int redisSslCreate(redisContext *c, const char *capath, const char *certpath,
const char *keypath, const char *servername) {
assert(!c->ssl);
c->ssl = calloc(1, sizeof(*c->ssl));
static int isInit = 0;
if (!isInit) {
isInit = 1;
SSL_library_init();
initOpensslLocks();
}
redisSsl *s = c->ssl;
s->ctx = SSL_CTX_new(SSLv23_client_method());
SSL_CTX_set_info_callback(s->ctx, sslLogCallback);
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);
if ((certpath != NULL && keypath == NULL) || (keypath != NULL && certpath == NULL)) {
__redisSetError(c, REDIS_ERR, "certpath and keypath must be specified together");
return REDIS_ERR;
}
if (capath) {
if (!SSL_CTX_load_verify_locations(s->ctx, capath, NULL)) {
__redisSetError(c, REDIS_ERR, "Invalid CA certificate");
return REDIS_ERR;
}
}
if (certpath) {
if (!SSL_CTX_use_certificate_chain_file(s->ctx, certpath)) {
__redisSetError(c, REDIS_ERR, "Invalid client certificate");
return REDIS_ERR;
}
if (!SSL_CTX_use_PrivateKey_file(s->ctx, keypath, SSL_FILETYPE_PEM)) {
__redisSetError(c, REDIS_ERR, "Invalid client key");
return REDIS_ERR;
}
}
s->ssl = SSL_new(s->ctx);
if (!s->ssl) {
__redisSetError(c, REDIS_ERR, "Couldn't create new SSL instance");
return REDIS_ERR;
}
if (servername) {
if (!SSL_set_tlsext_host_name(s->ssl, servername)) {
__redisSetError(c, REDIS_ERR, "Couldn't set server name indication");
return REDIS_ERR;
}
}
SSL_set_fd(s->ssl, c->fd);
SSL_set_connect_state(s->ssl);
c->flags |= REDIS_SSL;
int rv = SSL_connect(c->ssl->ssl);
if (rv == 1) {
return REDIS_OK;
}
rv = SSL_get_error(s->ssl, rv);
if (((c->flags & REDIS_BLOCK) == 0) &&
(rv == SSL_ERROR_WANT_READ || rv == SSL_ERROR_WANT_WRITE)) {
return REDIS_OK;
}
if (c->err == 0) {
__redisSetError(c, REDIS_ERR_IO, "SSL_connect() failed");
}
return REDIS_ERR;
}
static int maybeCheckWant(redisSsl *rssl, int rv) {
/**
* If the error is WANT_READ or WANT_WRITE, the appropriate flags are set
* and true is returned. False is returned otherwise
*/
if (rv == SSL_ERROR_WANT_READ) {
rssl->wantRead = 1;
return 1;
} else if (rv == SSL_ERROR_WANT_WRITE) {
rssl->pendingWrite = 1;
return 1;
} else {
return 0;
}
}
int redisSslRead(redisContext *c, char *buf, size_t bufcap) {
int nread = SSL_read(c->ssl->ssl, buf, bufcap);
if (nread > 0) {
return nread;
} else if (nread == 0) {
__redisSetError(c, REDIS_ERR_EOF, "Server closed the connection");
return -1;
} else {
int err = SSL_get_error(c->ssl->ssl, nread);
if (c->flags & REDIS_BLOCK) {
/**
* In blocking mode, we should never end up in a situation where
* we get an error without it being an actual error, except
* in the case of EINTR, which can be spuriously received from
* debuggers or whatever.
*/
if (errno == EINTR) {
return 0;
} else {
const char *msg = NULL;
if (errno == EAGAIN) {
msg = "Timed out";
}
__redisSetError(c, REDIS_ERR_IO, msg);
return -1;
}
}
/**
* We can very well get an EWOULDBLOCK/EAGAIN, however
*/
if (maybeCheckWant(c->ssl, err)) {
return 0;
} else {
__redisSetError(c, REDIS_ERR_IO, NULL);
return -1;
}
}
}
int redisSslWrite(redisContext *c) {
size_t len = c->ssl->lastLen ? c->ssl->lastLen : sdslen(c->obuf);
int rv = SSL_write(c->ssl->ssl, c->obuf, len);
if (rv > 0) {
c->ssl->lastLen = 0;
} else if (rv < 0) {
c->ssl->lastLen = len;
int err = SSL_get_error(c->ssl->ssl, rv);
if ((c->flags & REDIS_BLOCK) == 0 && maybeCheckWant(c->ssl, err)) {
return 0;
} else {
__redisSetError(c, REDIS_ERR_IO, NULL);
return -1;
}
}
return rv;
}
#endif
#ifndef REDIS_SSLIO_H
#define REDIS_SSLIO_H
#ifndef HIREDIS_SSL
typedef struct redisSsl {
size_t lastLen;
int wantRead;
int pendingWrite;
} redisSsl;
static inline void redisFreeSsl(redisSsl *ssl) {
(void)ssl;
}
static inline int redisSslCreate(struct redisContext *c, const char *ca,
const char *cert, const char *key, const char *servername) {
(void)c;(void)ca;(void)cert;(void)key;(void)servername;
return REDIS_ERR;
}
static inline int redisSslRead(struct redisContext *c, char *s, size_t n) {
(void)c;(void)s;(void)n;
return -1;
}
static inline int redisSslWrite(struct redisContext *c) {
(void)c;
return -1;
}
#else
#include <openssl/ssl.h>
/**
* This file contains routines for HIREDIS' SSL
*/
typedef struct redisSsl {
SSL *ssl;
SSL_CTX *ctx;
/**
* SSL_write() requires to be called again with the same arguments it was
* previously called with in the event of an SSL_read/SSL_write situation
*/
size_t lastLen;
/** Whether the SSL layer requires read (possibly before a write) */
int wantRead;
/**
* Whether a write was requested prior to a read. If set, the write()
* should resume whenever a read takes place, if possible
*/
int pendingWrite;
} redisSsl;
struct redisContext;
void redisFreeSsl(redisSsl *);
int redisSslCreate(struct redisContext *c, const char *caPath,
const char *certPath, const char *keyPath, const char *servername);
int redisSslRead(struct redisContext *c, char *buf, size_t bufcap);
int redisSslWrite(struct redisContext *c);
#endif /* HIREDIS_SSL */
#endif /* HIREDIS_SSLIO_H */
...@@ -3,7 +3,9 @@ ...@@ -3,7 +3,9 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <strings.h> #include <strings.h>
#include <sys/socket.h>
#include <sys/time.h> #include <sys/time.h>
#include <netdb.h>
#include <assert.h> #include <assert.h>
#include <unistd.h> #include <unistd.h>
#include <signal.h> #include <signal.h>
...@@ -91,7 +93,7 @@ static int disconnect(redisContext *c, int keep_fd) { ...@@ -91,7 +93,7 @@ static int disconnect(redisContext *c, int keep_fd) {
return -1; return -1;
} }
static redisContext *connect(struct config config) { static redisContext *do_connect(struct config config) {
redisContext *c = NULL; redisContext *c = NULL;
if (config.type == CONN_TCP) { if (config.type == CONN_TCP) {
...@@ -248,7 +250,7 @@ static void test_append_formatted_commands(struct config config) { ...@@ -248,7 +250,7 @@ static void test_append_formatted_commands(struct config config) {
char *cmd; char *cmd;
int len; int len;
c = connect(config); c = do_connect(config);
test("Append format command: "); test("Append format command: ");
...@@ -432,20 +434,35 @@ static void test_free_null(void) { ...@@ -432,20 +434,35 @@ static void test_free_null(void) {
test_cond(reply == NULL); test_cond(reply == NULL);
} }
#define HIREDIS_BAD_DOMAIN "idontexist-noreally.com"
static void test_blocking_connection_errors(void) { static void test_blocking_connection_errors(void) {
redisContext *c; redisContext *c;
struct addrinfo hints = {.ai_family = AF_INET};
struct addrinfo *ai_tmp = NULL;
int rv = getaddrinfo(HIREDIS_BAD_DOMAIN, "6379", &hints, &ai_tmp);
if (rv != 0) {
// Address does *not* exist
test("Returns error when host cannot be resolved: "); test("Returns error when host cannot be resolved: ");
c = redisConnect((char*)"idontexist.test", 6379); // First see if this domain name *actually* resolves to NXDOMAIN
test_cond(c->err == REDIS_ERR_OTHER && c = redisConnect(HIREDIS_BAD_DOMAIN, 6379);
(strcmp(c->errstr,"Name or service not known") == 0 || test_cond(
strcmp(c->errstr,"Can't resolve: idontexist.test") == 0 || c->err == REDIS_ERR_OTHER &&
strcmp(c->errstr,"nodename nor servname provided, or not known") == 0 || (strcmp(c->errstr, "Name or service not known") == 0 ||
strcmp(c->errstr,"No address associated with hostname") == 0 || strcmp(c->errstr, "Can't resolve: " HIREDIS_BAD_DOMAIN) == 0 ||
strcmp(c->errstr,"Temporary failure in name resolution") == 0 || strcmp(c->errstr, "Name does not resolve") == 0 ||
strcmp(c->errstr,"hostname nor servname provided, or not known") == 0 || strcmp(c->errstr,
strcmp(c->errstr,"no address associated with name") == 0)); "nodename nor servname provided, or not known") == 0 ||
strcmp(c->errstr, "No address associated with hostname") == 0 ||
strcmp(c->errstr, "Temporary failure in name resolution") == 0 ||
strcmp(c->errstr,
"hostname nor servname provided, or not known") == 0 ||
strcmp(c->errstr, "no address associated with name") == 0));
redisFree(c); redisFree(c);
} else {
printf("Skipping NXDOMAIN test. Found evil ISP!\n");
freeaddrinfo(ai_tmp);
}
test("Returns error when the port is not open: "); test("Returns error when the port is not open: ");
c = redisConnect((char*)"localhost", 1); c = redisConnect((char*)"localhost", 1);
...@@ -463,7 +480,7 @@ static void test_blocking_connection(struct config config) { ...@@ -463,7 +480,7 @@ static void test_blocking_connection(struct config config) {
redisContext *c; redisContext *c;
redisReply *reply; redisReply *reply;
c = connect(config); c = do_connect(config);
test("Is able to deliver commands: "); test("Is able to deliver commands: ");
reply = redisCommand(c,"PING"); reply = redisCommand(c,"PING");
...@@ -544,7 +561,7 @@ static void test_blocking_connection_timeouts(struct config config) { ...@@ -544,7 +561,7 @@ static void test_blocking_connection_timeouts(struct config config) {
const char *cmd = "DEBUG SLEEP 3\r\n"; const char *cmd = "DEBUG SLEEP 3\r\n";
struct timeval tv; struct timeval tv;
c = connect(config); c = do_connect(config);
test("Successfully completes a command when the timeout is not exceeded: "); test("Successfully completes a command when the timeout is not exceeded: ");
reply = redisCommand(c,"SET foo fast"); reply = redisCommand(c,"SET foo fast");
freeReplyObject(reply); freeReplyObject(reply);
...@@ -556,7 +573,7 @@ static void test_blocking_connection_timeouts(struct config config) { ...@@ -556,7 +573,7 @@ static void test_blocking_connection_timeouts(struct config config) {
freeReplyObject(reply); freeReplyObject(reply);
disconnect(c, 0); disconnect(c, 0);
c = connect(config); c = do_connect(config);
test("Does not return a reply when the command times out: "); test("Does not return a reply when the command times out: ");
s = write(c->fd, cmd, strlen(cmd)); s = write(c->fd, cmd, strlen(cmd));
tv.tv_sec = 0; tv.tv_sec = 0;
...@@ -590,7 +607,7 @@ static void test_blocking_io_errors(struct config config) { ...@@ -590,7 +607,7 @@ static void test_blocking_io_errors(struct config config) {
int major, minor; int major, minor;
/* Connect to target given by config. */ /* Connect to target given by config. */
c = connect(config); c = do_connect(config);
{ {
/* Find out Redis version to determine the path for the next test */ /* Find out Redis version to determine the path for the next test */
const char *field = "redis_version:"; const char *field = "redis_version:";
...@@ -625,7 +642,7 @@ static void test_blocking_io_errors(struct config config) { ...@@ -625,7 +642,7 @@ static void test_blocking_io_errors(struct config config) {
strcmp(c->errstr,"Server closed the connection") == 0); strcmp(c->errstr,"Server closed the connection") == 0);
redisFree(c); redisFree(c);
c = connect(config); c = do_connect(config);
test("Returns I/O error on socket timeout: "); test("Returns I/O error on socket timeout: ");
struct timeval tv = { 0, 1000 }; struct timeval tv = { 0, 1000 };
assert(redisSetTimeout(c,tv) == REDIS_OK); assert(redisSetTimeout(c,tv) == REDIS_OK);
...@@ -659,7 +676,7 @@ static void test_invalid_timeout_errors(struct config config) { ...@@ -659,7 +676,7 @@ static void test_invalid_timeout_errors(struct config config) {
} }
static void test_throughput(struct config config) { static void test_throughput(struct config config) {
redisContext *c = connect(config); redisContext *c = do_connect(config);
redisReply **replies; redisReply **replies;
int i, num; int i, num;
long long t1, t2; long long t1, t2;
......
#!/bin/sh -ue
REDIS_SERVER=${REDIS_SERVER:-redis-server}
REDIS_PORT=${REDIS_PORT:-56379}
tmpdir=$(mktemp -d)
PID_FILE=${tmpdir}/hiredis-test-redis.pid
SOCK_FILE=${tmpdir}/hiredis-test-redis.sock
cleanup() {
set +e
kill $(cat ${PID_FILE})
rm -rf ${tmpdir}
}
trap cleanup INT TERM EXIT
${REDIS_SERVER} - <<EOF
daemonize yes
pidfile ${PID_FILE}
port ${REDIS_PORT}
bind 127.0.0.1
unixsocket ${SOCK_FILE}
EOF
${TEST_PREFIX:-} ./hiredis-test -h 127.0.0.1 -p ${REDIS_PORT} -s ${SOCK_FILE}
...@@ -37,6 +37,10 @@ __inline int c99_snprintf(char* str, size_t size, const char* format, ...) ...@@ -37,6 +37,10 @@ __inline int c99_snprintf(char* str, size_t size, const char* format, ...)
return count; return count;
} }
#endif #endif
#endif /* _MSC_VER */
#endif #ifdef _WIN32
#endif #define strerror_r(errno,buf,len) strerror_s(buf,len,errno)
\ No newline at end of file #endif /* _WIN32 */
#endif /* _WIN32_HELPER_INCLUDE */
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