Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
ruanhaishen
hiredis
Commits
0c145449
Commit
0c145449
authored
Nov 27, 2017
by
Mark Nunberg
Browse files
Initial SSL (sync) implementation
parent
4d00404b
Changes
5
Show whitespace changes
Inline
Side-by-side
Makefile
View file @
0c145449
...
...
@@ -3,8 +3,8 @@
# Copyright (C) 2010-2011 Pieter Noordhuis <pcnoordhuis at gmail dot com>
# This file is released under the BSD license, see the COPYING file
OBJ
=
net.o hiredis.o sds.o async.o read.o
EXAMPLES
=
hiredis-example hiredis-example-libevent hiredis-example-libev hiredis-example-glib
OBJ
=
net.o hiredis.o sds.o async.o read.o
sslio.o
EXAMPLES
=
hiredis-example hiredis-example-libevent hiredis-example-libev hiredis-example-glib
hiredis-example-ssl
TESTS
=
hiredis-test
LIBNAME
=
libhiredis
PKGCONFNAME
=
hiredis.pc
...
...
@@ -53,6 +53,10 @@ DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(L
STLIBNAME
=
$(LIBNAME)
.
$(STLIBSUFFIX)
STLIB_MAKE_CMD
=
$(AR)
rcs
$(STLIBNAME)
OPENSSL_PREFIX
=
/usr/local/opt/openssl
CFLAGS
+=
-I
$(OPENSSL_PREFIX)
/include
LDFLAGS
+=
-L
$(OPENSSL_PREFIX)
/lib
-lssl
-lcrypto
# Platform-specific overrides
uname_S
:=
$(
shell
sh
-c
'uname -s 2>/dev/null || echo not'
)
ifeq
($(uname_S),SunOS)
...
...
@@ -70,10 +74,11 @@ all: $(DYLIBNAME) $(STLIBNAME) hiredis-test $(PKGCONFNAME)
# Deps (use make dep to generate this)
async.o
:
async.c fmacros.h async.h hiredis.h read.h sds.h net.h dict.c dict.h
dict.o
:
dict.c fmacros.h dict.h
hiredis.o
:
hiredis.c fmacros.h hiredis.h read.h sds.h net.h
hiredis.o
:
hiredis.c fmacros.h hiredis.h read.h sds.h net.h
sslio.h
net.o
:
net.c fmacros.h net.h hiredis.h read.h sds.h
read.o
:
read.c fmacros.h read.h sds.h
sds.o
:
sds.c sds.h
sslio.o
:
sslio.c sslio.h hiredis.h
test.o
:
test.c fmacros.h hiredis.h read.h sds.h
$(DYLIBNAME)
:
$(OBJ)
...
...
@@ -101,6 +106,9 @@ hiredis-example-ivykis: examples/example-ivykis.c adapters/ivykis.h $(STLIBNAME)
hiredis-example-macosx
:
examples/example-macosx.c adapters/macosx.h $(STLIBNAME)
$(CC)
-o
examples/
$@
$(REAL_CFLAGS)
$(REAL_LDFLAGS)
-I
.
$<
-framework
CoreFoundation
$(STLIBNAME)
hiredis-example-ssl
:
examples/example-ssl.c $(STLIBNAME)
$(CC)
-o
examples/
$@
$(REAL_CFLAGS)
$(REAL_LDFLAGS)
-I
.
$<
$(STLIBNAME)
ifndef
AE_DIR
hiredis-example-ae
:
@
echo
"Please specify AE_DIR (e.g. <redis repository>/src)"
...
...
@@ -158,7 +166,7 @@ clean:
rm
-rf
$(DYLIBNAME)
$(STLIBNAME)
$(TESTS)
$(PKGCONFNAME)
examples/hiredis-example
*
*
.o
*
.gcda
*
.gcno
*
.gcov
dep
:
$(CC)
-MM
*
.c
$(CC)
$(CPPFLAGS)
$(CFLAGS)
-MM
*
.c
INSTALL
?=
cp
-pPR
...
...
hiredis.c
View file @
0c145449
...
...
@@ -42,6 +42,7 @@
#include "hiredis.h"
#include "net.h"
#include "sds.h"
#include "sslio.h"
static
redisReply
*
createReplyObject
(
int
type
);
static
void
*
createStringObject
(
const
redisReadTask
*
task
,
char
*
str
,
size_t
len
);
...
...
@@ -614,7 +615,9 @@ void redisFree(redisContext *c) {
free
(
c
->
unix_sock
.
path
);
free
(
c
->
timeout
);
free
(
c
->
saddr
);
free
(
c
);
if
(
c
->
ssl
)
{
redisFreeSsl
(
c
->
ssl
);
}
}
int
redisFreeKeepFd
(
redisContext
*
c
)
{
...
...
@@ -760,6 +763,11 @@ redisContext *redisConnectFd(int fd) {
return
c
;
}
int
redisSecureConnection
(
redisContext
*
c
,
const
char
*
caPath
,
const
char
*
certPath
,
const
char
*
keyPath
)
{
return
redisSslCreate
(
c
,
caPath
,
certPath
,
keyPath
);
}
/* Set read/write timeout on a blocking socket. */
int
redisSetTimeout
(
redisContext
*
c
,
const
struct
timeval
tv
)
{
if
(
c
->
flags
&
REDIS_BLOCK
)
...
...
@@ -774,6 +782,24 @@ int redisEnableKeepAlive(redisContext *c) {
return
REDIS_OK
;
}
static
int
rawRead
(
redisContext
*
c
,
char
*
buf
,
size_t
bufcap
)
{
int
nread
=
read
(
c
->
fd
,
buf
,
bufcap
);
if
(
nread
==
-
1
)
{
if
((
errno
==
EAGAIN
&&
!
(
c
->
flags
&
REDIS_BLOCK
))
||
(
errno
==
EINTR
))
{
/* Try again later */
return
0
;
}
else
{
__redisSetError
(
c
,
REDIS_ERR_IO
,
NULL
);
return
-
1
;
}
}
else
if
(
nread
==
0
)
{
__redisSetError
(
c
,
REDIS_ERR_EOF
,
"Server closed the connection"
);
return
-
1
;
}
else
{
return
nread
;
}
}
/* Use this function to handle a read event on the descriptor. It will try
* and read some bytes from the socket and feed them to the reply parser.
*
...
...
@@ -787,24 +813,31 @@ int redisBufferRead(redisContext *c) {
if
(
c
->
err
)
return
REDIS_ERR
;
nread
=
read
(
c
->
fd
,
buf
,
sizeof
(
buf
));
if
(
nread
==
-
1
)
{
if
((
errno
==
EAGAIN
&&
!
(
c
->
flags
&
REDIS_BLOCK
))
||
(
errno
==
EINTR
))
{
/* Try again later */
}
else
{
__redisSetError
(
c
,
REDIS_ERR_IO
,
NULL
);
nread
=
c
->
flags
&
REDIS_SSL
?
redisSslRead
(
c
,
buf
,
sizeof
(
buf
))
:
rawRead
(
c
,
buf
,
sizeof
(
buf
));
if
(
nread
>
0
)
{
if
(
redisReaderFeed
(
c
->
reader
,
buf
,
nread
)
!=
REDIS_OK
)
{
__redisSetError
(
c
,
c
->
reader
->
err
,
c
->
reader
->
errstr
);
return
REDIS_ERR
;
}
else
{
}
}
else
if
(
nread
==
0
)
{
__redisSetError
(
c
,
REDIS_ERR_EOF
,
"Server closed the connection"
);
}
else
if
(
nread
<
0
)
{
return
REDIS_ERR
;
}
return
REDIS_OK
;
}
static
int
rawWrite
(
redisContext
*
c
)
{
int
nwritten
=
write
(
c
->
fd
,
c
->
obuf
,
sdslen
(
c
->
obuf
));
if
(
nwritten
<
0
)
{
if
((
errno
==
EAGAIN
&&
!
(
c
->
flags
&
REDIS_BLOCK
))
||
(
errno
==
EINTR
))
{
/* Try again later */
}
else
{
if
(
redisReaderFeed
(
c
->
reader
,
buf
,
nread
)
!=
REDIS_OK
)
{
__redisSetError
(
c
,
c
->
reader
->
err
,
c
->
reader
->
errstr
);
return
REDIS_ERR
;
__redisSetError
(
c
,
REDIS_ERR_IO
,
NULL
);
return
-
1
;
}
}
return
REDIS_OK
;
return
nwritten
;
}
/* Write the output buffer to the socket.
...
...
@@ -817,21 +850,15 @@ int redisBufferRead(redisContext *c) {
* c->errstr to hold the appropriate error string.
*/
int
redisBufferWrite
(
redisContext
*
c
,
int
*
done
)
{
int
nwritten
;
/* Return early when the context has seen an error. */
if
(
c
->
err
)
return
REDIS_ERR
;
if
(
sdslen
(
c
->
obuf
)
>
0
)
{
nwritten
=
write
(
c
->
fd
,
c
->
obuf
,
sdslen
(
c
->
obuf
));
if
(
nwritten
==
-
1
)
{
if
((
errno
==
EAGAIN
&&
!
(
c
->
flags
&
REDIS_BLOCK
))
||
(
errno
==
EINTR
))
{
/* Try again later */
}
else
{
__redisSetError
(
c
,
REDIS_ERR_IO
,
NULL
);
int
nwritten
=
(
c
->
flags
&
REDIS_SSL
)
?
redisSslWrite
(
c
)
:
rawWrite
(
c
);
if
(
nwritten
<
0
)
{
return
REDIS_ERR
;
}
}
else
if
(
nwritten
>
0
)
{
if
(
nwritten
==
(
signed
)
sdslen
(
c
->
obuf
))
{
sdsfree
(
c
->
obuf
);
...
...
hiredis.h
View file @
0c145449
...
...
@@ -74,6 +74,9 @@
/* Flag that is set when we should set SO_REUSEADDR before calling bind() */
#define REDIS_REUSEADDR 0x80
/* Flag that is set when this connection is done through SSL */
#define REDIS_SSL 0x100
#define REDIS_KEEPALIVE_INTERVAL 15
/* seconds */
/* number of times we retry to connect in the case of EADDRNOTAVAIL and
...
...
@@ -112,6 +115,8 @@ enum redisConnectionType {
REDIS_CONN_UNIX
};
struct
redisSsl
;
/* Context for a connection to Redis */
typedef
struct
redisContext
{
int
err
;
/* Error flags, 0 when there is no error */
...
...
@@ -137,6 +142,9 @@ typedef struct redisContext {
/* For non-blocking connect */
struct
sockadr
*
saddr
;
size_t
addrlen
;
/* For SSL communication */
struct
redisSsl
*
ssl
;
}
redisContext
;
redisContext
*
redisConnect
(
const
char
*
ip
,
int
port
);
...
...
@@ -151,6 +159,13 @@ redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval
redisContext
*
redisConnectUnixNonBlock
(
const
char
*
path
);
redisContext
*
redisConnectFd
(
int
fd
);
/**
* Secure the connection using SSL. This should be done before any command is
* executed on the connection.
*/
int
redisSecureConnection
(
redisContext
*
c
,
const
char
*
capath
,
const
char
*
certpath
,
const
char
*
keypath
);
/**
* Reconnect the given context using the saved information.
*
...
...
sslio.c
0 → 100644
View file @
0c145449
#include "hiredis.h"
#include "sslio.h"
#include <assert.h>
#ifndef HIREDIS_NOSSL
#include <pthread.h>
void
__redisSetError
(
redisContext
*
c
,
int
type
,
const
char
*
str
);
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
)
{
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
;
}
printf
(
"Loaded certificate!
\n
"
);
}
s
->
ssl
=
SSL_new
(
s
->
ctx
);
if
(
!
s
->
ssl
)
{
__redisSetError
(
c
,
REDIS_ERR
,
"Couldn't create new SSL instance"
);
return
REDIS_ERR
;
}
SSL_set_fd
(
s
->
ssl
,
c
->
fd
);
SSL_set_connect_state
(
s
->
ssl
);
c
->
flags
|=
REDIS_SSL
;
printf
(
"Before SSL_connect()
\n
"
);
int
rv
=
SSL_connect
(
c
->
ssl
->
ssl
);
if
(
rv
==
1
)
{
printf
(
"SSL_connect() success!
\n
"
);
return
REDIS_OK
;
}
printf
(
"ConnectRV: %d
\n
"
,
rv
);
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"
);
printf
(
"rv: %d
\n
"
,
rv
);
}
printf
(
"ERROR!!!
\n
"
);
return
REDIS_ERR
;
}
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
(
err
==
SSL_ERROR_WANT_READ
||
err
==
SSL_ERROR_WANT_WRITE
)
{
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
(
err
==
SSL_ERROR_WANT_READ
||
err
==
SSL_ERROR_WANT_WRITE
)
{
return
0
;
}
else
{
__redisSetError
(
c
,
REDIS_ERR_IO
,
NULL
);
return
-
1
;
}
}
return
rv
;
}
#endif
sslio.h
0 → 100644
View file @
0c145449
#ifndef REDIS_SSLIO_H
#define REDIS_SSLIO_H
#ifdef HIREDIS_NOSSL
typedef
struct
redisSsl
{
int
dummy
;
}
redisSsl
;
static
void
redisFreeSsl
(
redisSsl
*
)
{
}
static
int
redisSslCreate
(
struct
redisContext
*
c
)
{
return
REDIS_ERR
;
}
static
int
redisSslRead
(
struct
redisContect
*
c
,
char
*
s
,
size_t
,
n
)
{
return
-
1
;
}
static
int
redisSslWrite
(
struct
redisContext
*
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
;
}
redisSsl
;
struct
redisContext
;
void
redisFreeSsl
(
redisSsl
*
);
int
redisSslCreate
(
struct
redisContext
*
c
,
const
char
*
caPath
,
const
char
*
certPath
,
const
char
*
keyPath
);
int
redisSslRead
(
struct
redisContext
*
c
,
char
*
buf
,
size_t
bufcap
);
int
redisSslWrite
(
struct
redisContext
*
c
);
#endif
/* !HIREDIS_NOSSL */
#endif
/* HIREDIS_SSLIO_H */
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment