summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md78
-rw-r--r--examples/example-libevent-ssl.c16
-rw-r--r--examples/example-ssl.c14
-rw-r--r--hiredis_ssl.h76
-rw-r--r--ssl.c266
-rw-r--r--test.c21
6 files changed, 320 insertions, 151 deletions
diff --git a/README.md b/README.md
index 4e7b228..d5ed553 100644
--- a/README.md
+++ b/README.md
@@ -438,42 +438,68 @@ First, you'll need to make sure you include the SSL header file:
#include "hiredis_ssl.h"
```
-SSL can only be enabled on a `redisContext` connection after the connection has
-been established and before any command has been processed. For example:
+You will also need to link against `libhiredis_ssl`, **in addition** to
+`libhiredis` and add `-lssl -lcrypto` to satisfy its dependencies.
+
+Hiredis implements SSL/TLS on top of its normal `redisContext` or
+`redisAsyncContext`, so you will need to establish a connection first and then
+initiate an SSL/TLS handshake.
+
+#### Hiredis OpenSSL Wrappers
+
+Before Hiredis can negotiate an SSL/TLS connection, it is necessary to
+initialize OpenSSL and create a context. You can do that in two ways:
+
+1. Work directly with the OpenSSL API to initialize the library's global context
+ and create `SSL_CTX *` and `SSL *` contexts. With an `SSL *` object you can
+ call `redisInitiateSSL()`.
+2. Work with a set of Hiredis-provided wrappers around OpenSSL, create a
+ `redisSSLContext` object to hold configuration and use
+ `redisInitiateSSLWithContext()` to initiate the SSL/TLS handshake.
```c
+/* An Hiredis SSL context. It holds SSL configuration and can be reused across
+ * many contexts.
+ */
+redisSSLContext *ssl;
+
+/* An error variable to indicate what went wrong, if the context fails to
+ * initialize.
+ */
+redisSSLContextError ssl_error;
+
+/* Initialize global OpenSSL state.
+ *
+ * You should call this only once when your app initializes, and only if
+ * you don't explicitly or implicitly initialize OpenSSL it elsewhere.
+ */
+redisInitOpenSSL();
+
+/* Create SSL context */
+ssl = redisCreateSSLContext(
+ "cacertbundle.crt", /* File name of trusted CA/ca bundle file, optional */
+ "/path/to/certs", /* Path of trusted certificates, optional */
+ "client_cert.pem", /* File name of client certificate file, optional */
+ "client_key.pem", /* File name of client private key, optional */
+ "redis.mydomain.com", /* Server name to request (SNI), optional */
+ &ssl_error
+ ) != REDIS_OK) {
+ printf("SSL error: %s\n", redisSSLContextGetError(ssl_error);
+ /* Abort... */
+ }
+
+/* Create Redis context and establish connection */
c = redisConnect("localhost", 6443);
if (c == NULL || c->err) {
/* Handle error and abort... */
}
-if (redisSecureConnection(c,
- "cacertbundle.crt", /* File name of trusted CA/ca bundle file */
- "client_cert.pem", /* File name of client certificate file */
- "client_key.pem", /* File name of client private key */
- "redis.mydomain.com" /* Server name to request (SNI) */
- ) != REDIS_OK) {
- printf("SSL error: %s\n", c->errstr);
- /* Abort... */
+/* Negotiate SSL/TLS */
+if (redisInitiateSSLWithContext(c, ssl) != REDIS_OK) {
+ /* Handle error, in c->err / c->errstr */
}
```
-You will also need to link against `libhiredis_ssl`, **in addition** to
-`libhiredis` and add `-lssl -lcrypto` to satisfy its dependencies.
-
-### OpenSSL Global State Initialization
-
-OpenSSL needs to have certain global state initialized before it can be used.
-Using `redisSecureConnection()` will handle this automatically on the first
-call.
-
-**If the calling application itself also initializes and uses OpenSSL directly,
-`redisSecureConnection()` must not be used.**
-
-Instead, use `redisInitiateSSL()` which also provides greater control over the
-configuration of the SSL connection, as the caller is responsible to create a
-connection context using `SSL_new()` and configure it as required.
-
## AUTHORS
Hiredis was written by Salvatore Sanfilippo (antirez at gmail) and
diff --git a/examples/example-libevent-ssl.c b/examples/example-libevent-ssl.c
index 1021113..aac5770 100644
--- a/examples/example-libevent-ssl.c
+++ b/examples/example-libevent-ssl.c
@@ -52,13 +52,25 @@ int main (int argc, char **argv) {
const char *certKey = argv[5];
const char *caCert = argc > 5 ? argv[6] : NULL;
+ redisSSLContext *ssl;
+ redisSSLContextError ssl_error;
+
+ redisInitOpenSSL();
+
+ ssl = redisCreateSSLContext(caCert, NULL,
+ cert, certKey, NULL, &ssl_error);
+ if (!ssl) {
+ printf("Error: %s\n", redisSSLContextGetError(ssl_error));
+ return 1;
+ }
+
redisAsyncContext *c = redisAsyncConnect(hostname, port);
if (c->err) {
/* Let *c leak for now... */
printf("Error: %s\n", c->errstr);
return 1;
}
- if (redisSecureConnection(&c->c, caCert, cert, certKey, "sni") != REDIS_OK) {
+ if (redisInitiateSSLWithContext(&c->c, ssl) != REDIS_OK) {
printf("SSL Error!\n");
exit(1);
}
@@ -69,5 +81,7 @@ int main (int argc, char **argv) {
redisAsyncCommand(c, NULL, NULL, "SET key %b", value, nvalue);
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
event_base_dispatch(base);
+
+ redisFreeSSLContext(ssl);
return 0;
}
diff --git a/examples/example-ssl.c b/examples/example-ssl.c
index 81f4648..c676ed8 100644
--- a/examples/example-ssl.c
+++ b/examples/example-ssl.c
@@ -7,6 +7,8 @@
int main(int argc, char **argv) {
unsigned int j;
+ redisSSLContext *ssl;
+ redisSSLContextError ssl_error;
redisContext *c;
redisReply *reply;
if (argc < 4) {
@@ -19,6 +21,14 @@ int main(int argc, char **argv) {
const char *key = argv[4];
const char *ca = argc > 4 ? argv[5] : NULL;
+ redisInitOpenSSL();
+ ssl = redisCreateSSLContext(ca, NULL, cert, key, NULL, &ssl_error);
+ if (!ssl) {
+ printf("SSL Context error: %s\n",
+ redisSSLContextGetError(ssl_error));
+ exit(1);
+ }
+
struct timeval tv = { 1, 500000 }; // 1.5 seconds
redisOptions options = {0};
REDIS_OPTIONS_SET_TCP(&options, hostname, port);
@@ -35,7 +45,7 @@ int main(int argc, char **argv) {
exit(1);
}
- if (redisSecureConnection(c, ca, cert, key, "sni") != REDIS_OK) {
+ if (redisInitiateSSLWithContext(c, ssl) != REDIS_OK) {
printf("Couldn't initialize SSL!\n");
printf("Error: %s\n", c->errstr);
redisFree(c);
@@ -93,5 +103,7 @@ int main(int argc, char **argv) {
/* Disconnects and frees the context */
redisFree(c);
+ redisFreeSSLContext(ssl);
+
return 0;
}
diff --git a/hiredis_ssl.h b/hiredis_ssl.h
index 21e8580..604efe0 100644
--- a/hiredis_ssl.h
+++ b/hiredis_ssl.h
@@ -41,15 +41,81 @@ extern "C" {
*/
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);
+
/**
- * Secure the connection using SSL. This should be done before any command is
- * executed on the connection.
+ * 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.
*/
-int redisSecureConnection(redisContext *c, const char *capath, const char *certpath,
- const char *keypath, const char *servername);
+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 context.
+ * Initiate SSL/TLS negotiation on a provided OpenSSL SSL object.
*/
int redisInitiateSSL(redisContext *c, struct ssl_st *ssl);
diff --git a/ssl.c b/ssl.c
index 8cb133a..f25dce2 100644
--- a/ssl.c
+++ b/ssl.c
@@ -47,17 +47,20 @@
#include "win32.h"
#include "async_private.h"
+#include "hiredis_ssl.h"
void __redisSetError(redisContext *c, int type, const char *str);
-/* The SSL context is attached to SSL/TLS connections as a privdata. */
-typedef struct redisSSLContext {
- /**
- * OpenSSL SSL_CTX; It is optional and will not be set when using
- * user-supplied SSL.
- */
+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.
*/
@@ -77,43 +80,11 @@ typedef struct redisSSLContext {
* should resume whenever a read takes place, if possible
*/
int pendingWrite;
-} redisSSLContext;
+} redisSSL;
/* Forward declaration */
redisContextFuncs redisContextSSLFuncs;
-#ifdef HIREDIS_SSL_TRACE
-/**
- * Callback used for debugging
- */
-static void sslLogCallback(const SSL *ssl, int where, int ret) {
- const char *retstr;
- int should_log = 0;
- /* 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));
- }
-}
-#endif
-
/**
* OpenSSL global initialization and locking handling callbacks.
* Note that this is only required for OpenSSL < 1.1.0.
@@ -182,26 +153,130 @@ static int initOpensslLocks(void) {
}
#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));
+
+ 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_CTX *ssl_ctx, SSL *ssl) {
- redisSSLContext *rssl;
+static int redisSSLConnect(redisContext *c, SSL *ssl) {
if (c->privdata) {
__redisSetError(c, REDIS_ERR_OTHER, "redisContext was already associated");
return REDIS_ERR;
}
- rssl = hi_calloc(1, sizeof(redisSSLContext));
+ 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_ctx = ssl_ctx;
rssl->ssl = ssl;
SSL_set_mode(rssl->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
@@ -238,84 +313,53 @@ static int redisSSLConnect(redisContext *c, SSL_CTX *ssl_ctx, SSL *ssl) {
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, NULL, ssl);
+ return redisSSLConnect(c, ssl);
}
-int redisSecureConnection(redisContext *c, const char *capath,
- const char *certpath, const char *keypath, const char *servername) {
-
- SSL_CTX *ssl_ctx = NULL;
- SSL *ssl = NULL;
-
- /* Initialize global OpenSSL stuff */
- static int isInit = 0;
- if (!isInit) {
- isInit = 1;
- SSL_library_init();
-#ifdef HIREDIS_USE_CRYPTO_LOCKS
- if (initOpensslLocks() == REDIS_ERR) {
- __redisSetError(c, REDIS_ERR_OOM, "Out of memory");
- goto error;
- }
-#endif
- }
-
- ssl_ctx = SSL_CTX_new(SSLv23_client_method());
- if (!ssl_ctx) {
- __redisSetError(c, REDIS_ERR_OTHER, "Failed to create SSL_CTX");
- goto error;
- }
+/**
+ * A wrapper around redisSSLConnect() for users who use redisSSLContext and don't
+ * manage their own SSL objects.
+ */
-#ifdef HIREDIS_SSL_TRACE
- SSL_CTX_set_info_callback(ssl_ctx, sslLogCallback);
-#endif
- SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
- SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
- if ((certpath != NULL && keypath == NULL) || (keypath != NULL && certpath == NULL)) {
- __redisSetError(c, REDIS_ERR_OTHER, "certpath and keypath must be specified together");
- goto error;
- }
+int redisInitiateSSLWithContext(redisContext *c, redisSSLContext *redis_ssl_ctx)
+{
+ if (!c || !redis_ssl_ctx)
+ return REDIS_ERR;
- if (capath) {
- if (!SSL_CTX_load_verify_locations(ssl_ctx, capath, NULL)) {
- __redisSetError(c, REDIS_ERR_OTHER, "Invalid CA certificate");
- goto error;
- }
- }
- if (certpath) {
- if (!SSL_CTX_use_certificate_chain_file(ssl_ctx, certpath)) {
- __redisSetError(c, REDIS_ERR_OTHER, "Invalid client certificate");
- goto error;
- }
- if (!SSL_CTX_use_PrivateKey_file(ssl_ctx, keypath, SSL_FILETYPE_PEM)) {
- __redisSetError(c, REDIS_ERR_OTHER, "Invalid client key");
- goto error;
- }
- }
+ /* 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->privdata)
+ return REDIS_ERR;
- ssl = SSL_new(ssl_ctx);
+ 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 (servername) {
- if (!SSL_set_tlsext_host_name(ssl, servername)) {
- __redisSetError(c, REDIS_ERR_OTHER, "Couldn't set server name indication");
+
+ 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;
}
}
- if (redisSSLConnect(c, ssl_ctx, ssl) == REDIS_OK)
- return REDIS_OK;
+ return redisSSLConnect(c, ssl);
error:
- if (ssl) SSL_free(ssl);
- if (ssl_ctx) SSL_CTX_free(ssl_ctx);
+ if (ssl)
+ SSL_free(ssl);
return REDIS_ERR;
}
-static int maybeCheckWant(redisSSLContext *rssl, int rv) {
+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
@@ -335,23 +379,19 @@ static int maybeCheckWant(redisSSLContext *rssl, int rv) {
* Implementation of redisContextFuncs for SSL connections.
*/
-static void redisSSLFreeContext(void *privdata){
- redisSSLContext *rsc = privdata;
+static void redisSSLFree(void *privdata){
+ redisSSL *rsc = privdata;
if (!rsc) return;
if (rsc->ssl) {
SSL_free(rsc->ssl);
rsc->ssl = NULL;
}
- if (rsc->ssl_ctx) {
- SSL_CTX_free(rsc->ssl_ctx);
- rsc->ssl_ctx = NULL;
- }
hi_free(rsc);
}
static int redisSSLRead(redisContext *c, char *buf, size_t bufcap) {
- redisSSLContext *rssl = c->privdata;
+ redisSSL *rssl = c->privdata;
int nread = SSL_read(rssl->ssl, buf, bufcap);
if (nread > 0) {
@@ -393,7 +433,7 @@ static int redisSSLRead(redisContext *c, char *buf, size_t bufcap) {
}
static int redisSSLWrite(redisContext *c) {
- redisSSLContext *rssl = c->privdata;
+ redisSSL *rssl = c->privdata;
size_t len = rssl->lastLen ? rssl->lastLen : sdslen(c->obuf);
int rv = SSL_write(rssl->ssl, c->obuf, len);
@@ -416,7 +456,7 @@ static int redisSSLWrite(redisContext *c) {
static void redisSSLAsyncRead(redisAsyncContext *ac) {
int rv;
- redisSSLContext *rssl = ac->c.privdata;
+ redisSSL *rssl = ac->c.privdata;
redisContext *c = &ac->c;
rssl->wantRead = 0;
@@ -446,7 +486,7 @@ static void redisSSLAsyncRead(redisAsyncContext *ac) {
static void redisSSLAsyncWrite(redisAsyncContext *ac) {
int rv, done = 0;
- redisSSLContext *rssl = ac->c.privdata;
+ redisSSL *rssl = ac->c.privdata;
redisContext *c = &ac->c;
rssl->pendingWrite = 0;
@@ -475,7 +515,7 @@ static void redisSSLAsyncWrite(redisAsyncContext *ac) {
}
redisContextFuncs redisContextSSLFuncs = {
- .free_privdata = redisSSLFreeContext,
+ .free_privdata = redisSSLFree,
.async_read = redisSSLAsyncRead,
.async_write = redisSSLAsyncWrite,
.read = redisSSLRead,
diff --git a/test.c b/test.c
index 48d36d0..fba8eba 100644
--- a/test.c
+++ b/test.c
@@ -48,6 +48,10 @@ struct config {
} ssl;
};
+#ifdef HIREDIS_TEST_SSL
+redisSSLContext *_ssl_ctx = NULL;
+#endif
+
/* The following lines make up our testing "framework" :) */
static int tests = 0, fails = 0, skips = 0;
#define test(_s) { printf("#%02d ", ++tests); printf(_s); }
@@ -113,9 +117,9 @@ static int disconnect(redisContext *c, int keep_fd) {
return -1;
}
-static void do_ssl_handshake(redisContext *c, struct config config) {
+static void do_ssl_handshake(redisContext *c) {
#ifdef HIREDIS_TEST_SSL
- redisSecureConnection(c, config.ssl.ca_cert, config.ssl.cert, config.ssl.key, NULL);
+ redisInitiateSSLWithContext(c, _ssl_ctx);
if (c->err) {
printf("SSL error: %s\n", c->errstr);
redisFree(c);
@@ -123,7 +127,6 @@ static void do_ssl_handshake(redisContext *c, struct config config) {
}
#else
(void) c;
- (void) config;
#endif
}
@@ -158,7 +161,7 @@ static redisContext *do_connect(struct config config) {
}
if (config.type == CONN_SSL) {
- do_ssl_handshake(c, config);
+ do_ssl_handshake(c);
}
return select_database(c);
@@ -168,7 +171,7 @@ static void do_reconnect(redisContext *c, struct config config) {
redisReconnect(c);
if (config.type == CONN_SSL) {
- do_ssl_handshake(c, config);
+ do_ssl_handshake(c);
}
}
@@ -1147,6 +1150,11 @@ int main(int argc, char **argv) {
#ifdef HIREDIS_TEST_SSL
if (cfg.ssl.port && cfg.ssl.host) {
+
+ redisInitOpenSSL();
+ _ssl_ctx = redisCreateSSLContext(cfg.ssl.ca_cert, NULL, cfg.ssl.cert, cfg.ssl.key, NULL, NULL);
+ assert(_ssl_ctx != NULL);
+
printf("\nTesting against SSL connection (%s:%d):\n", cfg.ssl.host, cfg.ssl.port);
cfg.type = CONN_SSL;
@@ -1156,6 +1164,9 @@ int main(int argc, char **argv) {
test_invalid_timeout_errors(cfg);
test_append_formatted_commands(cfg);
if (throughput) test_throughput(cfg);
+
+ redisFreeSSLContext(_ssl_ctx);
+ _ssl_ctx = NULL;
}
#endif