Merge branch 'redis-ssl' into 'master' (fixes #201)
commit
119ebc85b6
@ -0,0 +1,84 @@
|
|||||||
|
#ifndef __HIREDIS_IVYKIS_H__
|
||||||
|
#define __HIREDIS_IVYKIS_H__
|
||||||
|
#include <iv.h>
|
||||||
|
#include "../hiredis.h"
|
||||||
|
#include "../async.h"
|
||||||
|
|
||||||
|
typedef struct redisIvykisEvents {
|
||||||
|
redisAsyncContext *context;
|
||||||
|
struct iv_fd fd;
|
||||||
|
} redisIvykisEvents;
|
||||||
|
|
||||||
|
static void redisIvykisReadEvent(void *arg) {
|
||||||
|
redisAsyncContext *context = (redisAsyncContext *)arg;
|
||||||
|
redisAsyncHandleRead(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void redisIvykisWriteEvent(void *arg) {
|
||||||
|
redisAsyncContext *context = (redisAsyncContext *)arg;
|
||||||
|
redisAsyncHandleWrite(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void redisIvykisAddRead(void *privdata) {
|
||||||
|
redisIvykisEvents *e = (redisIvykisEvents*)privdata;
|
||||||
|
iv_fd_set_handler_in(&e->fd, redisIvykisReadEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void redisIvykisDelRead(void *privdata) {
|
||||||
|
redisIvykisEvents *e = (redisIvykisEvents*)privdata;
|
||||||
|
iv_fd_set_handler_in(&e->fd, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void redisIvykisAddWrite(void *privdata) {
|
||||||
|
redisIvykisEvents *e = (redisIvykisEvents*)privdata;
|
||||||
|
iv_fd_set_handler_out(&e->fd, redisIvykisWriteEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void redisIvykisDelWrite(void *privdata) {
|
||||||
|
redisIvykisEvents *e = (redisIvykisEvents*)privdata;
|
||||||
|
iv_fd_set_handler_out(&e->fd, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void redisIvykisCleanup(void *privdata) {
|
||||||
|
redisIvykisEvents *e = (redisIvykisEvents*)privdata;
|
||||||
|
|
||||||
|
iv_fd_unregister(&e->fd);
|
||||||
|
hi_free(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int redisIvykisAttach(redisAsyncContext *ac) {
|
||||||
|
redisContext *c = &(ac->c);
|
||||||
|
redisIvykisEvents *e;
|
||||||
|
|
||||||
|
/* Nothing should be attached when something is already attached */
|
||||||
|
if (ac->ev.data != NULL)
|
||||||
|
return REDIS_ERR;
|
||||||
|
|
||||||
|
/* Create container for context and r/w events */
|
||||||
|
e = (redisIvykisEvents*)hi_malloc(sizeof(*e));
|
||||||
|
if (e == NULL)
|
||||||
|
return REDIS_ERR;
|
||||||
|
|
||||||
|
e->context = ac;
|
||||||
|
|
||||||
|
/* Register functions to start/stop listening for events */
|
||||||
|
ac->ev.addRead = redisIvykisAddRead;
|
||||||
|
ac->ev.delRead = redisIvykisDelRead;
|
||||||
|
ac->ev.addWrite = redisIvykisAddWrite;
|
||||||
|
ac->ev.delWrite = redisIvykisDelWrite;
|
||||||
|
ac->ev.cleanup = redisIvykisCleanup;
|
||||||
|
ac->ev.data = e;
|
||||||
|
|
||||||
|
/* Initialize and install read/write events */
|
||||||
|
IV_FD_INIT(&e->fd);
|
||||||
|
e->fd.fd = c->fd;
|
||||||
|
e->fd.handler_in = redisIvykisReadEvent;
|
||||||
|
e->fd.handler_out = redisIvykisWriteEvent;
|
||||||
|
e->fd.handler_err = NULL;
|
||||||
|
e->fd.cookie = e->context;
|
||||||
|
|
||||||
|
iv_fd_register(&e->fd);
|
||||||
|
|
||||||
|
return REDIS_OK;
|
||||||
|
}
|
||||||
|
#endif
|
@ -0,0 +1,115 @@
|
|||||||
|
//
|
||||||
|
// Created by Дмитрий Бахвалов on 13.07.15.
|
||||||
|
// Copyright (c) 2015 Dmitry Bakhvalov. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef __HIREDIS_MACOSX_H__
|
||||||
|
#define __HIREDIS_MACOSX_H__
|
||||||
|
|
||||||
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
|
|
||||||
|
#include "../hiredis.h"
|
||||||
|
#include "../async.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
redisAsyncContext *context;
|
||||||
|
CFSocketRef socketRef;
|
||||||
|
CFRunLoopSourceRef sourceRef;
|
||||||
|
} RedisRunLoop;
|
||||||
|
|
||||||
|
static int freeRedisRunLoop(RedisRunLoop* redisRunLoop) {
|
||||||
|
if( redisRunLoop != NULL ) {
|
||||||
|
if( redisRunLoop->sourceRef != NULL ) {
|
||||||
|
CFRunLoopSourceInvalidate(redisRunLoop->sourceRef);
|
||||||
|
CFRelease(redisRunLoop->sourceRef);
|
||||||
|
}
|
||||||
|
if( redisRunLoop->socketRef != NULL ) {
|
||||||
|
CFSocketInvalidate(redisRunLoop->socketRef);
|
||||||
|
CFRelease(redisRunLoop->socketRef);
|
||||||
|
}
|
||||||
|
hi_free(redisRunLoop);
|
||||||
|
}
|
||||||
|
return REDIS_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void redisMacOSAddRead(void *privdata) {
|
||||||
|
RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
|
||||||
|
CFSocketEnableCallBacks(redisRunLoop->socketRef, kCFSocketReadCallBack);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void redisMacOSDelRead(void *privdata) {
|
||||||
|
RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
|
||||||
|
CFSocketDisableCallBacks(redisRunLoop->socketRef, kCFSocketReadCallBack);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void redisMacOSAddWrite(void *privdata) {
|
||||||
|
RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
|
||||||
|
CFSocketEnableCallBacks(redisRunLoop->socketRef, kCFSocketWriteCallBack);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void redisMacOSDelWrite(void *privdata) {
|
||||||
|
RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
|
||||||
|
CFSocketDisableCallBacks(redisRunLoop->socketRef, kCFSocketWriteCallBack);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void redisMacOSCleanup(void *privdata) {
|
||||||
|
RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
|
||||||
|
freeRedisRunLoop(redisRunLoop);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void redisMacOSAsyncCallback(CFSocketRef __unused s, CFSocketCallBackType callbackType, CFDataRef __unused address, const void __unused *data, void *info) {
|
||||||
|
redisAsyncContext* context = (redisAsyncContext*) info;
|
||||||
|
|
||||||
|
switch (callbackType) {
|
||||||
|
case kCFSocketReadCallBack:
|
||||||
|
redisAsyncHandleRead(context);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kCFSocketWriteCallBack:
|
||||||
|
redisAsyncHandleWrite(context);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int redisMacOSAttach(redisAsyncContext *redisAsyncCtx, CFRunLoopRef runLoop) {
|
||||||
|
redisContext *redisCtx = &(redisAsyncCtx->c);
|
||||||
|
|
||||||
|
/* Nothing should be attached when something is already attached */
|
||||||
|
if( redisAsyncCtx->ev.data != NULL ) return REDIS_ERR;
|
||||||
|
|
||||||
|
RedisRunLoop* redisRunLoop = (RedisRunLoop*) hi_calloc(1, sizeof(RedisRunLoop));
|
||||||
|
if (redisRunLoop == NULL)
|
||||||
|
return REDIS_ERR;
|
||||||
|
|
||||||
|
/* Setup redis stuff */
|
||||||
|
redisRunLoop->context = redisAsyncCtx;
|
||||||
|
|
||||||
|
redisAsyncCtx->ev.addRead = redisMacOSAddRead;
|
||||||
|
redisAsyncCtx->ev.delRead = redisMacOSDelRead;
|
||||||
|
redisAsyncCtx->ev.addWrite = redisMacOSAddWrite;
|
||||||
|
redisAsyncCtx->ev.delWrite = redisMacOSDelWrite;
|
||||||
|
redisAsyncCtx->ev.cleanup = redisMacOSCleanup;
|
||||||
|
redisAsyncCtx->ev.data = redisRunLoop;
|
||||||
|
|
||||||
|
/* Initialize and install read/write events */
|
||||||
|
CFSocketContext socketCtx = { 0, redisAsyncCtx, NULL, NULL, NULL };
|
||||||
|
|
||||||
|
redisRunLoop->socketRef = CFSocketCreateWithNative(NULL, redisCtx->fd,
|
||||||
|
kCFSocketReadCallBack | kCFSocketWriteCallBack,
|
||||||
|
redisMacOSAsyncCallback,
|
||||||
|
&socketCtx);
|
||||||
|
if( !redisRunLoop->socketRef ) return freeRedisRunLoop(redisRunLoop);
|
||||||
|
|
||||||
|
redisRunLoop->sourceRef = CFSocketCreateRunLoopSource(NULL, redisRunLoop->socketRef, 0);
|
||||||
|
if( !redisRunLoop->sourceRef ) return freeRedisRunLoop(redisRunLoop);
|
||||||
|
|
||||||
|
CFRunLoopAddSource(runLoop, redisRunLoop->sourceRef, kCFRunLoopDefaultMode);
|
||||||
|
|
||||||
|
return REDIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,135 @@
|
|||||||
|
/*-
|
||||||
|
* Copyright (C) 2014 Pietro Cerutti <gahr@gahr.ch>
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. 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.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR 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 __HIREDIS_QT_H__
|
||||||
|
#define __HIREDIS_QT_H__
|
||||||
|
#include <QSocketNotifier>
|
||||||
|
#include "../async.h"
|
||||||
|
|
||||||
|
static void RedisQtAddRead(void *);
|
||||||
|
static void RedisQtDelRead(void *);
|
||||||
|
static void RedisQtAddWrite(void *);
|
||||||
|
static void RedisQtDelWrite(void *);
|
||||||
|
static void RedisQtCleanup(void *);
|
||||||
|
|
||||||
|
class RedisQtAdapter : public QObject {
|
||||||
|
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
friend
|
||||||
|
void RedisQtAddRead(void * adapter) {
|
||||||
|
RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);
|
||||||
|
a->addRead();
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
void RedisQtDelRead(void * adapter) {
|
||||||
|
RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);
|
||||||
|
a->delRead();
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
void RedisQtAddWrite(void * adapter) {
|
||||||
|
RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);
|
||||||
|
a->addWrite();
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
void RedisQtDelWrite(void * adapter) {
|
||||||
|
RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);
|
||||||
|
a->delWrite();
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
void RedisQtCleanup(void * adapter) {
|
||||||
|
RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);
|
||||||
|
a->cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
RedisQtAdapter(QObject * parent = 0)
|
||||||
|
: QObject(parent), m_ctx(0), m_read(0), m_write(0) { }
|
||||||
|
|
||||||
|
~RedisQtAdapter() {
|
||||||
|
if (m_ctx != 0) {
|
||||||
|
m_ctx->ev.data = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int setContext(redisAsyncContext * ac) {
|
||||||
|
if (ac->ev.data != NULL) {
|
||||||
|
return REDIS_ERR;
|
||||||
|
}
|
||||||
|
m_ctx = ac;
|
||||||
|
m_ctx->ev.data = this;
|
||||||
|
m_ctx->ev.addRead = RedisQtAddRead;
|
||||||
|
m_ctx->ev.delRead = RedisQtDelRead;
|
||||||
|
m_ctx->ev.addWrite = RedisQtAddWrite;
|
||||||
|
m_ctx->ev.delWrite = RedisQtDelWrite;
|
||||||
|
m_ctx->ev.cleanup = RedisQtCleanup;
|
||||||
|
return REDIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void addRead() {
|
||||||
|
if (m_read) return;
|
||||||
|
m_read = new QSocketNotifier(m_ctx->c.fd, QSocketNotifier::Read, 0);
|
||||||
|
connect(m_read, SIGNAL(activated(int)), this, SLOT(read()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void delRead() {
|
||||||
|
if (!m_read) return;
|
||||||
|
delete m_read;
|
||||||
|
m_read = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addWrite() {
|
||||||
|
if (m_write) return;
|
||||||
|
m_write = new QSocketNotifier(m_ctx->c.fd, QSocketNotifier::Write, 0);
|
||||||
|
connect(m_write, SIGNAL(activated(int)), this, SLOT(write()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void delWrite() {
|
||||||
|
if (!m_write) return;
|
||||||
|
delete m_write;
|
||||||
|
m_write = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cleanup() {
|
||||||
|
delRead();
|
||||||
|
delWrite();
|
||||||
|
}
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void read() { redisAsyncHandleRead(m_ctx); }
|
||||||
|
void write() { redisAsyncHandleWrite(m_ctx); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
redisAsyncContext * m_ctx;
|
||||||
|
QSocketNotifier * m_read;
|
||||||
|
QSocketNotifier * m_write;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* !__HIREDIS_QT_H__ */
|
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Michael Grunder <michael dot grunder at gmail dot com>
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "fmacros.h"
|
||||||
|
#include "alloc.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
hiredisAllocFuncs hiredisAllocFns = {
|
||||||
|
.mallocFn = malloc,
|
||||||
|
.callocFn = calloc,
|
||||||
|
.reallocFn = realloc,
|
||||||
|
.strdupFn = strdup,
|
||||||
|
.freeFn = free,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Override hiredis' allocators with ones supplied by the user */
|
||||||
|
hiredisAllocFuncs hiredisSetAllocators(hiredisAllocFuncs *override) {
|
||||||
|
hiredisAllocFuncs orig = hiredisAllocFns;
|
||||||
|
|
||||||
|
hiredisAllocFns = *override;
|
||||||
|
|
||||||
|
return orig;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reset allocators to use libc defaults */
|
||||||
|
void hiredisResetAllocators(void) {
|
||||||
|
hiredisAllocFns = (hiredisAllocFuncs) {
|
||||||
|
.mallocFn = malloc,
|
||||||
|
.callocFn = calloc,
|
||||||
|
.reallocFn = realloc,
|
||||||
|
.strdupFn = strdup,
|
||||||
|
.freeFn = free,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
void *hi_malloc(size_t size) {
|
||||||
|
return hiredisAllocFns.mallocFn(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *hi_calloc(size_t nmemb, size_t size) {
|
||||||
|
return hiredisAllocFns.callocFn(nmemb, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *hi_realloc(void *ptr, size_t size) {
|
||||||
|
return hiredisAllocFns.reallocFn(ptr, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *hi_strdup(const char *str) {
|
||||||
|
return hiredisAllocFns.strdupFn(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hi_free(void *ptr) {
|
||||||
|
hiredisAllocFns.freeFn(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Michael Grunder <michael dot grunder at gmail dot com>
|
||||||
|
*
|
||||||
|
* 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 HIREDIS_ALLOC_H
|
||||||
|
#define HIREDIS_ALLOC_H
|
||||||
|
|
||||||
|
#include <stddef.h> /* for size_t */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Structure pointing to our actually configured allocators */
|
||||||
|
typedef struct hiredisAllocFuncs {
|
||||||
|
void *(*mallocFn)(size_t);
|
||||||
|
void *(*callocFn)(size_t,size_t);
|
||||||
|
void *(*reallocFn)(void*,size_t);
|
||||||
|
char *(*strdupFn)(const char*);
|
||||||
|
void (*freeFn)(void*);
|
||||||
|
} hiredisAllocFuncs;
|
||||||
|
|
||||||
|
hiredisAllocFuncs hiredisSetAllocators(hiredisAllocFuncs *ha);
|
||||||
|
void hiredisResetAllocators(void);
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
|
||||||
|
/* Hiredis' configured allocator function pointer struct */
|
||||||
|
extern hiredisAllocFuncs hiredisAllocFns;
|
||||||
|
|
||||||
|
static inline void *hi_malloc(size_t size) {
|
||||||
|
return hiredisAllocFns.mallocFn(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void *hi_calloc(size_t nmemb, size_t size) {
|
||||||
|
return hiredisAllocFns.callocFn(nmemb, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void *hi_realloc(void *ptr, size_t size) {
|
||||||
|
return hiredisAllocFns.reallocFn(ptr, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline char *hi_strdup(const char *str) {
|
||||||
|
return hiredisAllocFns.strdupFn(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void hi_free(void *ptr) {
|
||||||
|
hiredisAllocFns.freeFn(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
void *hi_malloc(size_t size);
|
||||||
|
void *hi_calloc(size_t nmemb, size_t size);
|
||||||
|
void *hi_realloc(void *ptr, size_t size);
|
||||||
|
char *hi_strdup(const char *str);
|
||||||
|
void hi_free(void *ptr);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* HIREDIS_ALLOC_H */
|
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||||
|
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||||
|
*
|
||||||
|
* 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 __HIREDIS_ASYNC_PRIVATE_H
|
||||||
|
#define __HIREDIS_ASYNC_PRIVATE_H
|
||||||
|
|
||||||
|
#define _EL_ADD_READ(ctx) \
|
||||||
|
do { \
|
||||||
|
refreshTimeout(ctx); \
|
||||||
|
if ((ctx)->ev.addRead) (ctx)->ev.addRead((ctx)->ev.data); \
|
||||||
|
} while (0)
|
||||||
|
#define _EL_DEL_READ(ctx) do { \
|
||||||
|
if ((ctx)->ev.delRead) (ctx)->ev.delRead((ctx)->ev.data); \
|
||||||
|
} while(0)
|
||||||
|
#define _EL_ADD_WRITE(ctx) \
|
||||||
|
do { \
|
||||||
|
refreshTimeout(ctx); \
|
||||||
|
if ((ctx)->ev.addWrite) (ctx)->ev.addWrite((ctx)->ev.data); \
|
||||||
|
} while (0)
|
||||||
|
#define _EL_DEL_WRITE(ctx) do { \
|
||||||
|
if ((ctx)->ev.delWrite) (ctx)->ev.delWrite((ctx)->ev.data); \
|
||||||
|
} while(0)
|
||||||
|
#define _EL_CLEANUP(ctx) do { \
|
||||||
|
if ((ctx)->ev.cleanup) (ctx)->ev.cleanup((ctx)->ev.data); \
|
||||||
|
ctx->ev.cleanup = NULL; \
|
||||||
|
} while(0);
|
||||||
|
|
||||||
|
static inline void refreshTimeout(redisAsyncContext *ctx) {
|
||||||
|
#define REDIS_TIMER_ISSET(tvp) \
|
||||||
|
(tvp && ((tvp)->tv_sec || (tvp)->tv_usec))
|
||||||
|
|
||||||
|
#define REDIS_EL_TIMER(ac, tvp) \
|
||||||
|
if ((ac)->ev.scheduleTimer && REDIS_TIMER_ISSET(tvp)) { \
|
||||||
|
(ac)->ev.scheduleTimer((ac)->ev.data, *(tvp)); \
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->c.flags & REDIS_CONNECTED) {
|
||||||
|
REDIS_EL_TIMER(ctx, ctx->c.command_timeout);
|
||||||
|
} else {
|
||||||
|
REDIS_EL_TIMER(ctx, ctx->c.connect_timeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void __redisAsyncDisconnect(redisAsyncContext *ac);
|
||||||
|
void redisProcessCallbacks(redisAsyncContext *ac);
|
||||||
|
|
||||||
|
#endif /* __HIREDIS_ASYNC_PRIVATE_H */
|
@ -1,21 +1,12 @@
|
|||||||
#ifndef __HIREDIS_FMACRO_H
|
#ifndef __HIREDIS_FMACRO_H
|
||||||
#define __HIREDIS_FMACRO_H
|
#define __HIREDIS_FMACRO_H
|
||||||
|
|
||||||
#if defined(__linux__)
|
|
||||||
#define _BSD_SOURCE
|
|
||||||
#define _DEFAULT_SOURCE
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(__sun__)
|
|
||||||
#define _POSIX_C_SOURCE 200112L
|
|
||||||
#elif defined(__linux__) || defined(__OpenBSD__) || defined(__NetBSD__)
|
|
||||||
#define _XOPEN_SOURCE 600
|
#define _XOPEN_SOURCE 600
|
||||||
#else
|
#define _POSIX_C_SOURCE 200112L
|
||||||
#define _XOPEN_SOURCE
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if __APPLE__ && __MACH__
|
#if defined(__APPLE__) && defined(__MACH__)
|
||||||
#define _OSX
|
/* Enable TCP_KEEPALIVE */
|
||||||
|
#define _DARWIN_C_SOURCE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -0,0 +1,127 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019, Redis Labs
|
||||||
|
*
|
||||||
|
* 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 __HIREDIS_SSL_H
|
||||||
|
#define __HIREDIS_SSL_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* This is the underlying struct for SSL in ssl.h, which is not included to
|
||||||
|
* keep build dependencies short here.
|
||||||
|
*/
|
||||||
|
struct ssl_st;
|
||||||
|
|
||||||
|
/* A wrapper around OpenSSL SSL_CTX to allow easy SSL use without directly
|
||||||
|
* calling OpenSSL.
|
||||||
|
*/
|
||||||
|
typedef struct redisSSLContext redisSSLContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialization errors that redisCreateSSLContext() may return.
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
REDIS_SSL_CTX_NONE = 0, /* No Error */
|
||||||
|
REDIS_SSL_CTX_CREATE_FAILED, /* Failed to create OpenSSL SSL_CTX */
|
||||||
|
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_CLIENT_CERT_LOAD_FAILED, /* Failed to load client certificate */
|
||||||
|
REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED /* Failed to load private key */
|
||||||
|
} redisSSLContextError;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the error message corresponding with the specified error code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const char *redisSSLContextGetError(redisSSLContextError error);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to initialize the OpenSSL library.
|
||||||
|
*
|
||||||
|
* OpenSSL requires one-time initialization before it can be used. Callers should
|
||||||
|
* call this function only once, and only if OpenSSL is not directly initialized
|
||||||
|
* elsewhere.
|
||||||
|
*/
|
||||||
|
int redisInitOpenSSL(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to initialize an OpenSSL context that can be used
|
||||||
|
* to initiate SSL connections.
|
||||||
|
*
|
||||||
|
* cacert_filename is an optional name of a CA certificate/bundle file to load
|
||||||
|
* and use for validation.
|
||||||
|
*
|
||||||
|
* capath is an optional directory path where trusted CA certificate files are
|
||||||
|
* stored in an OpenSSL-compatible structure.
|
||||||
|
*
|
||||||
|
* cert_filename and private_key_filename are optional names of a client side
|
||||||
|
* certificate and private key files to use for authentication. They need to
|
||||||
|
* be both specified or omitted.
|
||||||
|
*
|
||||||
|
* server_name is an optional and will be used as a server name indication
|
||||||
|
* (SNI) TLS extension.
|
||||||
|
*
|
||||||
|
* If error is non-null, it will be populated in case the context creation fails
|
||||||
|
* (returning a NULL).
|
||||||
|
*/
|
||||||
|
|
||||||
|
redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char *capath,
|
||||||
|
const char *cert_filename, const char *private_key_filename,
|
||||||
|
const char *server_name, redisSSLContextError *error);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free a previously created OpenSSL context.
|
||||||
|
*/
|
||||||
|
void redisFreeSSLContext(redisSSLContext *redis_ssl_ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiate SSL on an existing redisContext.
|
||||||
|
*
|
||||||
|
* This is similar to redisInitiateSSL() but does not require the caller
|
||||||
|
* to directly interact with OpenSSL, and instead uses a redisSSLContext
|
||||||
|
* previously created using redisCreateSSLContext().
|
||||||
|
*/
|
||||||
|
|
||||||
|
int redisInitiateSSLWithContext(redisContext *c, redisSSLContext *redis_ssl_ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiate SSL/TLS negotiation on a provided OpenSSL SSL object.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int redisInitiateSSL(redisContext *c, struct ssl_st *ssl);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __HIREDIS_SSL_H */
|
@ -0,0 +1,44 @@
|
|||||||
|
/* SDSLib 2.0 -- A C dynamic strings library
|
||||||
|
*
|
||||||
|
* Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||||
|
* Copyright (c) 2015, Oran Agra
|
||||||
|
* Copyright (c) 2015, Redis Labs, Inc
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* SDS allocator selection.
|
||||||
|
*
|
||||||
|
* This file is used in order to change the SDS allocator at compile time.
|
||||||
|
* Just define the following defines to what you want to use. Also add
|
||||||
|
* the include of your alternate allocator if needed (not needed in order
|
||||||
|
* to use the default libc allocator). */
|
||||||
|
|
||||||
|
#include "alloc.h"
|
||||||
|
|
||||||
|
#define s_malloc hi_malloc
|
||||||
|
#define s_realloc hi_realloc
|
||||||
|
#define s_free hi_free
|
@ -0,0 +1,248 @@
|
|||||||
|
/*
|
||||||
|
* 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))) {
|
||||||
|
const 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 */
|
@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* 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>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
typedef long 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);
|
||||||
|
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 */
|
@ -0,0 +1,526 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||||
|
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||||
|
* Copyright (c) 2019, Redis Labs
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "hiredis.h"
|
||||||
|
#include "async.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#include <pthread.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <openssl/ssl.h>
|
||||||
|
#include <openssl/err.h>
|
||||||
|
|
||||||
|
#include "win32.h"
|
||||||
|
#include "async_private.h"
|
||||||
|
#include "hiredis_ssl.h"
|
||||||
|
|
||||||
|
void __redisSetError(redisContext *c, int type, const char *str);
|
||||||
|
|
||||||
|
struct redisSSLContext {
|
||||||
|
/* Associated OpenSSL SSL_CTX as created by redisCreateSSLContext() */
|
||||||
|
SSL_CTX *ssl_ctx;
|
||||||
|
|
||||||
|
/* Requested SNI, or NULL */
|
||||||
|
char *server_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* The SSL connection context is attached to SSL/TLS connections as a privdata. */
|
||||||
|
typedef struct redisSSL {
|
||||||
|
/**
|
||||||
|
* OpenSSL SSL object.
|
||||||
|
*/
|
||||||
|
SSL *ssl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/* Forward declaration */
|
||||||
|
redisContextFuncs redisContextSSLFuncs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OpenSSL global initialization and locking handling callbacks.
|
||||||
|
* Note that this is only required for OpenSSL < 1.1.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||||
|
#define HIREDIS_USE_CRYPTO_LOCKS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HIREDIS_USE_CRYPTO_LOCKS
|
||||||
|
#ifdef _WIN32
|
||||||
|
typedef CRITICAL_SECTION sslLockType;
|
||||||
|
static void sslLockInit(sslLockType* l) {
|
||||||
|
InitializeCriticalSection(l);
|
||||||
|
}
|
||||||
|
static void sslLockAcquire(sslLockType* l) {
|
||||||
|
EnterCriticalSection(l);
|
||||||
|
}
|
||||||
|
static void sslLockRelease(sslLockType* l) {
|
||||||
|
LeaveCriticalSection(l);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static sslLockType* 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 int initOpensslLocks(void) {
|
||||||
|
unsigned ii, nlocks;
|
||||||
|
if (CRYPTO_get_locking_callback() != NULL) {
|
||||||
|
/* Someone already set the callback before us. Don't destroy it! */
|
||||||
|
return REDIS_OK;
|
||||||
|
}
|
||||||
|
nlocks = CRYPTO_num_locks();
|
||||||
|
ossl_locks = hi_malloc(sizeof(*ossl_locks) * nlocks);
|
||||||
|
if (ossl_locks == NULL)
|
||||||
|
return REDIS_ERR;
|
||||||
|
|
||||||
|
for (ii = 0; ii < nlocks; ii++) {
|
||||||
|
sslLockInit(ossl_locks + ii);
|
||||||
|
}
|
||||||
|
CRYPTO_set_locking_callback(opensslDoLock);
|
||||||
|
return REDIS_OK;
|
||||||
|
}
|
||||||
|
#endif /* HIREDIS_USE_CRYPTO_LOCKS */
|
||||||
|
|
||||||
|
int redisInitOpenSSL(void)
|
||||||
|
{
|
||||||
|
SSL_library_init();
|
||||||
|
#ifdef HIREDIS_USE_CRYPTO_LOCKS
|
||||||
|
initOpensslLocks();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return REDIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* redisSSLContext helper context destruction.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const char *redisSSLContextGetError(redisSSLContextError error)
|
||||||
|
{
|
||||||
|
switch (error) {
|
||||||
|
case REDIS_SSL_CTX_NONE:
|
||||||
|
return "No Error";
|
||||||
|
case REDIS_SSL_CTX_CREATE_FAILED:
|
||||||
|
return "Failed to create OpenSSL SSL_CTX";
|
||||||
|
case REDIS_SSL_CTX_CERT_KEY_REQUIRED:
|
||||||
|
return "Client cert and key must both be specified or skipped";
|
||||||
|
case REDIS_SSL_CTX_CA_CERT_LOAD_FAILED:
|
||||||
|
return "Failed to load CA Certificate or CA Path";
|
||||||
|
case REDIS_SSL_CTX_CLIENT_CERT_LOAD_FAILED:
|
||||||
|
return "Failed to load client certificate";
|
||||||
|
case REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED:
|
||||||
|
return "Failed to load private key";
|
||||||
|
default:
|
||||||
|
return "Unknown error code";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void redisFreeSSLContext(redisSSLContext *ctx)
|
||||||
|
{
|
||||||
|
if (!ctx)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ctx->server_name) {
|
||||||
|
hi_free(ctx->server_name);
|
||||||
|
ctx->server_name = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->ssl_ctx) {
|
||||||
|
SSL_CTX_free(ctx->ssl_ctx);
|
||||||
|
ctx->ssl_ctx = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
hi_free(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* redisSSLContext helper context initialization.
|
||||||
|
*/
|
||||||
|
|
||||||
|
redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char *capath,
|
||||||
|
const char *cert_filename, const char *private_key_filename,
|
||||||
|
const char *server_name, redisSSLContextError *error)
|
||||||
|
{
|
||||||
|
redisSSLContext *ctx = hi_calloc(1, sizeof(redisSSLContext));
|
||||||
|
if (ctx == NULL)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method());
|
||||||
|
if (!ctx->ssl_ctx) {
|
||||||
|
if (error) *error = REDIS_SSL_CTX_CREATE_FAILED;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
|
||||||
|
SSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_PEER, NULL);
|
||||||
|
|
||||||
|
if ((cert_filename != NULL && private_key_filename == NULL) ||
|
||||||
|
(private_key_filename != NULL && cert_filename == NULL)) {
|
||||||
|
if (error) *error = REDIS_SSL_CTX_CERT_KEY_REQUIRED;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (capath || cacert_filename) {
|
||||||
|
if (!SSL_CTX_load_verify_locations(ctx->ssl_ctx, cacert_filename, capath)) {
|
||||||
|
if (error) *error = REDIS_SSL_CTX_CA_CERT_LOAD_FAILED;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cert_filename) {
|
||||||
|
if (!SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, cert_filename)) {
|
||||||
|
if (error) *error = REDIS_SSL_CTX_CLIENT_CERT_LOAD_FAILED;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
if (!SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, private_key_filename, SSL_FILETYPE_PEM)) {
|
||||||
|
if (error) *error = REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (server_name)
|
||||||
|
ctx->server_name = hi_strdup(server_name);
|
||||||
|
|
||||||
|
return ctx;
|
||||||
|
|
||||||
|
error:
|
||||||
|
redisFreeSSLContext(ctx);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSL Connection initialization.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
static int redisSSLConnect(redisContext *c, SSL *ssl) {
|
||||||
|
if (c->privctx) {
|
||||||
|
__redisSetError(c, REDIS_ERR_OTHER, "redisContext was already associated");
|
||||||
|
return REDIS_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
redisSSL *rssl = hi_calloc(1, sizeof(redisSSL));
|
||||||
|
if (rssl == NULL) {
|
||||||
|
__redisSetError(c, REDIS_ERR_OOM, "Out of memory");
|
||||||
|
return REDIS_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
c->funcs = &redisContextSSLFuncs;
|
||||||
|
rssl->ssl = ssl;
|
||||||
|
|
||||||
|
SSL_set_mode(rssl->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
|
||||||
|
SSL_set_fd(rssl->ssl, c->fd);
|
||||||
|
SSL_set_connect_state(rssl->ssl);
|
||||||
|
|
||||||
|
ERR_clear_error();
|
||||||
|
int rv = SSL_connect(rssl->ssl);
|
||||||
|
if (rv == 1) {
|
||||||
|
c->privctx = rssl;
|
||||||
|
return REDIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = SSL_get_error(rssl->ssl, rv);
|
||||||
|
if (((c->flags & REDIS_BLOCK) == 0) &&
|
||||||
|
(rv == SSL_ERROR_WANT_READ || rv == SSL_ERROR_WANT_WRITE)) {
|
||||||
|
c->privctx = rssl;
|
||||||
|
return REDIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c->err == 0) {
|
||||||
|
char err[512];
|
||||||
|
if (rv == SSL_ERROR_SYSCALL)
|
||||||
|
snprintf(err,sizeof(err)-1,"SSL_connect failed: %s",strerror(errno));
|
||||||
|
else {
|
||||||
|
unsigned long e = ERR_peek_last_error();
|
||||||
|
snprintf(err,sizeof(err)-1,"SSL_connect failed: %s",
|
||||||
|
ERR_reason_error_string(e));
|
||||||
|
}
|
||||||
|
__redisSetError(c, REDIS_ERR_IO, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
hi_free(rssl);
|
||||||
|
return REDIS_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A wrapper around redisSSLConnect() for users who manage their own context and
|
||||||
|
* create their own SSL object.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int redisInitiateSSL(redisContext *c, SSL *ssl) {
|
||||||
|
return redisSSLConnect(c, ssl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A wrapper around redisSSLConnect() for users who use redisSSLContext and don't
|
||||||
|
* manage their own SSL objects.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int redisInitiateSSLWithContext(redisContext *c, redisSSLContext *redis_ssl_ctx)
|
||||||
|
{
|
||||||
|
if (!c || !redis_ssl_ctx)
|
||||||
|
return REDIS_ERR;
|
||||||
|
|
||||||
|
/* We want to verify that redisSSLConnect() won't fail on this, as it will
|
||||||
|
* not own the SSL object in that case and we'll end up leaking.
|
||||||
|
*/
|
||||||
|
if (c->privctx)
|
||||||
|
return REDIS_ERR;
|
||||||
|
|
||||||
|
SSL *ssl = SSL_new(redis_ssl_ctx->ssl_ctx);
|
||||||
|
if (!ssl) {
|
||||||
|
__redisSetError(c, REDIS_ERR_OTHER, "Couldn't create new SSL instance");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (redis_ssl_ctx->server_name) {
|
||||||
|
if (!SSL_set_tlsext_host_name(ssl, redis_ssl_ctx->server_name)) {
|
||||||
|
__redisSetError(c, REDIS_ERR_OTHER, "Failed to set server_name/SNI");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return redisSSLConnect(c, ssl);
|
||||||
|
|
||||||
|
error:
|
||||||
|
if (ssl)
|
||||||
|
SSL_free(ssl);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of redisContextFuncs for SSL connections.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void redisSSLFree(void *privctx){
|
||||||
|
redisSSL *rsc = privctx;
|
||||||
|
|
||||||
|
if (!rsc) return;
|
||||||
|
if (rsc->ssl) {
|
||||||
|
SSL_free(rsc->ssl);
|
||||||
|
rsc->ssl = NULL;
|
||||||
|
}
|
||||||
|
hi_free(rsc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t redisSSLRead(redisContext *c, char *buf, size_t bufcap) {
|
||||||
|
redisSSL *rssl = c->privctx;
|
||||||
|
|
||||||
|
int nread = SSL_read(rssl->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(rssl->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 = "Resource temporarily unavailable";
|
||||||
|
}
|
||||||
|
__redisSetError(c, REDIS_ERR_IO, msg);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We can very well get an EWOULDBLOCK/EAGAIN, however
|
||||||
|
*/
|
||||||
|
if (maybeCheckWant(rssl, err)) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
__redisSetError(c, REDIS_ERR_IO, NULL);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t redisSSLWrite(redisContext *c) {
|
||||||
|
redisSSL *rssl = c->privctx;
|
||||||
|
|
||||||
|
size_t len = rssl->lastLen ? rssl->lastLen : sdslen(c->obuf);
|
||||||
|
int rv = SSL_write(rssl->ssl, c->obuf, len);
|
||||||
|
|
||||||
|
if (rv > 0) {
|
||||||
|
rssl->lastLen = 0;
|
||||||
|
} else if (rv < 0) {
|
||||||
|
rssl->lastLen = len;
|
||||||
|
|
||||||
|
int err = SSL_get_error(rssl->ssl, rv);
|
||||||
|
if ((c->flags & REDIS_BLOCK) == 0 && maybeCheckWant(rssl, err)) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
__redisSetError(c, REDIS_ERR_IO, NULL);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void redisSSLAsyncRead(redisAsyncContext *ac) {
|
||||||
|
int rv;
|
||||||
|
redisSSL *rssl = ac->c.privctx;
|
||||||
|
redisContext *c = &ac->c;
|
||||||
|
|
||||||
|
rssl->wantRead = 0;
|
||||||
|
|
||||||
|
if (rssl->pendingWrite) {
|
||||||
|
int done;
|
||||||
|
|
||||||
|
/* This is probably just a write event */
|
||||||
|
rssl->pendingWrite = 0;
|
||||||
|
rv = redisBufferWrite(c, &done);
|
||||||
|
if (rv == REDIS_ERR) {
|
||||||
|
__redisAsyncDisconnect(ac);
|
||||||
|
return;
|
||||||
|
} else if (!done) {
|
||||||
|
_EL_ADD_WRITE(ac);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = redisBufferRead(c);
|
||||||
|
if (rv == REDIS_ERR) {
|
||||||
|
__redisAsyncDisconnect(ac);
|
||||||
|
} else {
|
||||||
|
_EL_ADD_READ(ac);
|
||||||
|
redisProcessCallbacks(ac);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void redisSSLAsyncWrite(redisAsyncContext *ac) {
|
||||||
|
int rv, done = 0;
|
||||||
|
redisSSL *rssl = ac->c.privctx;
|
||||||
|
redisContext *c = &ac->c;
|
||||||
|
|
||||||
|
rssl->pendingWrite = 0;
|
||||||
|
rv = redisBufferWrite(c, &done);
|
||||||
|
if (rv == REDIS_ERR) {
|
||||||
|
__redisAsyncDisconnect(ac);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!done) {
|
||||||
|
if (rssl->wantRead) {
|
||||||
|
/* Need to read-before-write */
|
||||||
|
rssl->pendingWrite = 1;
|
||||||
|
_EL_DEL_WRITE(ac);
|
||||||
|
} else {
|
||||||
|
/* No extra reads needed, just need to write more */
|
||||||
|
_EL_ADD_WRITE(ac);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Already done! */
|
||||||
|
_EL_DEL_WRITE(ac);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Always reschedule a read */
|
||||||
|
_EL_ADD_READ(ac);
|
||||||
|
}
|
||||||
|
|
||||||
|
redisContextFuncs redisContextSSLFuncs = {
|
||||||
|
.free_privctx = redisSSLFree,
|
||||||
|
.async_read = redisSSLAsyncRead,
|
||||||
|
.async_write = redisSSLAsyncWrite,
|
||||||
|
.read = redisSSLRead,
|
||||||
|
.write = redisSSLWrite
|
||||||
|
};
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue