Skip to content

Commit

Permalink
Initial TLS support (still needs non-blocking IO...)
Browse files Browse the repository at this point in the history
  • Loading branch information
attipaci committed Jan 7, 2025
1 parent 1092327 commit 9e66394
Show file tree
Hide file tree
Showing 13 changed files with 344 additions and 6 deletions.
7 changes: 6 additions & 1 deletion .cproject
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,12 @@
<tool id="cdt.managedbuild.tool.gnu.archiver.base.1422446010" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.base"/>
<tool id="cdt.managedbuild.tool.gnu.cpp.compiler.base.1815812035" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.base"/>
<tool id="cdt.managedbuild.tool.gnu.c.compiler.base.415530852" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.base">
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="gnu.c.compiler.option.preprocessor.def.symbols.1140071425" superClass="gnu.c.compiler.option.preprocessor.def.symbols" valueType="definedSymbols">
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="gnu.c.compiler.option.preprocessor.def.symbols.1140071425" name="Defined symbols (-D)" superClass="gnu.c.compiler.option.preprocessor.def.symbols" useByScannerDiscovery="false" valueType="definedSymbols">
<listOptionValue builtIn="false" value="WITH_OPENMP=1"/>
<listOptionValue builtIn="false" value="WITH_TLS=1"/>
</option>
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="gnu.c.compiler.option.include.paths.313464504" name="Include paths (-I)" superClass="gnu.c.compiler.option.include.paths" useByScannerDiscovery="false" valueType="includePath">
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/xchange/include}&quot;"/>
</option>
<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.81300143" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
</tool>
Expand All @@ -35,6 +39,7 @@
</tool>
<tool id="cdt.managedbuild.tool.gnu.cpp.linker.base.1554120195" name="GCC C++ Linker" superClass="cdt.managedbuild.tool.gnu.cpp.linker.base"/>
<tool id="cdt.managedbuild.tool.gnu.assembler.base.281878813" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.base">
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="true" id="gnu.both.asm.option.include.paths.1149325868" name="Include paths (-I)" superClass="gnu.both.asm.option.include.paths" valueType="includePath"/>
<inputType id="cdt.managedbuild.tool.gnu.assembler.input.1802027735" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
</tool>
</toolChain>
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
path: xchange

- name: Install build dependencies
run: sudo apt-get install libpopt-dev libreadline-dev libbsd-dev
run: sudo apt-get install libpopt-dev libreadline-dev libbsd-dev libssl-dev

- name: Build static library
run: make static
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,17 @@ jobs:
languages: ${{ matrix.language }}
build-mode: manual

- name: Install build dependencies
run: sudo apt-get install libpopt-dev libreadline-dev libbsd-dev libssl-dev

- name: Manual build
shell: bash
env:
XCHANGE: xchange
run: |
make -C xchange shared
make shared
make tools
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/install.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
CC: gcc
steps:
- name: install build deps
run: sudo apt-get install doxygen libpopt-dev libreadline-dev libbsd-dev
run: sudo apt-get install doxygen libpopt-dev libreadline-dev libbsd-dev libssl-dev

- name: Check out RedisX
uses: actions/checkout@v4
Expand Down
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ distclean:

SOURCES = $(SRC)/redisx.c $(SRC)/resp.c $(SRC)/redisx-net.c $(SRC)/redisx-hooks.c \
$(SRC)/redisx-client.c $(SRC)/redisx-sentinel.c $(SRC)/redisx-cluster.c \
$(SRC)/redisx-tab.c $(SRC)/redisx-sub.c $(SRC)/redisx-script.c
$(SRC)/redisx-tab.c $(SRC)/redisx-sub.c $(SRC)/redisx-script.c \
$(SRC)/redisx-tls.c

# Generate a list of object (obj/*.o) files from the input sources
OBJECTS := $(subst $(SRC),$(OBJ),$(SOURCES))
Expand Down
8 changes: 8 additions & 0 deletions config.mk
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ CC ?= gcc
# Whether to use OpenMP
WITH_OPENMP ?= 1

# Whether to build with TLS support (via OpenSSL)
WITH_TLS ?= 1

# Add include/ directory
CPPFLAGS += -I$(INC)

Expand Down Expand Up @@ -72,6 +75,11 @@ ifeq ($(WITH_OPENMP),1)
LDFLAGS += -fopenmp
endif

ifeq ($(WITH_TLS),1)
CPPFLAGS += -DWITH_TLS=1
LDFLAGS += -lssl
endif

# Search for libraries under LIB
ifneq ($(findstring $(LIB),$(LD_LIBRARY_PATH)),$LIB)
LDFLAGS += -L$(LIB)
Expand Down
23 changes: 22 additions & 1 deletion include/redisx-priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@

#include <pthread.h>
#include <stdint.h>
#if WITH_TLS
# include <openssl/ssl.h>
#endif

#define __XCHANGE_INTERNAL_API__
#include <redisx.h>
#include "redisx.h"

#define IP_ADDRESS_LENGTH 40 ///< IPv6: 39 chars + termination.

Expand Down Expand Up @@ -49,6 +52,10 @@ typedef struct {
int available; ///< Number of bytes available in the buffer.
int next; ///< Index of next unconsumed byte in buffer.
int socket; ///< Changing the socket should require both locks!
#if WITH_TLS
SSL_CTX *ctx;
SSL *ssl;
#endif
int pendingRequests; ///< Number of request sent and not yet answered...
RESP *attributes; ///< Attributes from the last packet received.
} ClientPrivate;
Expand All @@ -60,6 +67,16 @@ typedef struct {
int timeoutMillis; ///< [ms] Connection timeout for sentinel nodes.
} RedisSentinel;

#if WITH_TLS
typedef struct {
boolean enabled; ///< Whether TLS is enabled.
char *certificate; ///< Certificate (mutual TLS only)
char *key; ///< Private key (mutual TLS only)
char *ca_certificate; ///< CA sertificate
char *dh_params; ///< (optional) parameter file for DH based cyphers
} TLSConfig;
#endif

/**
* A set of configuration settings for a Redis server connection.
*
Expand All @@ -74,6 +91,10 @@ typedef struct {
boolean hello; ///< whether to use HELLO (introduced in Redis 6.0.0 only)
RedisSocketConfigurator socketConf; ///< Additional user configuration of client sockets

#if WITH_TLS
TLSConfig tls; ///< TLS configuration settings
#endif

Hook *firstCleanupCall; ///< Linked list of cleanup calls
Hook *firstConnectCall; ///< Linked list of connection calls

Expand Down
3 changes: 3 additions & 0 deletions include/redisx.h
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,9 @@ Redis *redisxInitSentinel(const char *serviceName, const RedisServer *serverList
int redisxValidateSentinel(const char *serviceName, const RedisServer *serverList, int nServers);
int redisxCheckValid(const Redis *redis);
void redisxDestroy(Redis *redis);
int redisxSetTLS(Redis *redis, const char *ca_file);
int redisxSetMutualTLS(Redis *redis, const char *cert_file, const char *key_file);
int redisxSetDHParams(Redis *redis, const char *dh_params_file);
int redisxConnect(Redis *redis, boolean usePipeline);
void redisxDisconnect(Redis *redis);
int redisxReconnect(Redis *redis, boolean usePipeline);
Expand Down
1 change: 1 addition & 0 deletions requirements.apt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# sudo apt install $(grep -v ^# requirements.apt)
libssl-dev
libpopt-dev
libreadline-dev
libbsd-dev
Expand Down
1 change: 1 addition & 0 deletions requirements.dnf
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# sudo dnf install $(grep -v ^# requirements.dnf)
openssl-devel
popt-devel
readline-devel
libbsd-devel
Expand Down
13 changes: 12 additions & 1 deletion src/redisx-client.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
#else
# include <sys/socket.h>
#endif
#if WITH_TLS
# include <openssl/ssl.h>
#endif

#include "redisx-priv.h"

Expand Down Expand Up @@ -104,6 +107,10 @@ static int rReadChunkAsync(ClientPrivate *cp) {
errno = 0;

cp->next = 0;
#if WITH_TLS
if(cp->ssl) cp->available = SSL_read(cp->ssl, cp->in, REDISX_RCVBUF_SIZE);
else
#endif
cp->available = recv(sock, cp->in, REDISX_RCVBUF_SIZE, 0);
trprintf(" ... read %d bytes from client %d socket.\n", cp->available, cp->idx);
if(cp->available <= 0) {
Expand Down Expand Up @@ -272,6 +279,10 @@ static int rSendBytesAsync(ClientPrivate *cp, const char *buf, int length, boole
while(length > 0) {
int n;

#if WITH_TLS
if(cp->ssl) n = SSL_write(cp->ssl, from, length);
else
#endif
#if __linux__
// Linux supports flagging outgoing messages to inform it whether or not more
// imminent data is on its way
Expand All @@ -283,7 +294,7 @@ static int rSendBytesAsync(ClientPrivate *cp, const char *buf, int length, boole
n = send(sock, from, length, 0);
#endif

if(n < 0) {
if(n <= 0) {
int status = rTransmitErrorAsync(cp, "send");
if(cp->isEnabled) x_trace(fn, NULL, status);
cp->isEnabled = FALSE;
Expand Down
21 changes: 21 additions & 0 deletions src/redisx-net.c
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,10 @@ static void rDisconnectClientAsync(RedisClient *cl) {
*
*/
static void rResetClientAsync(RedisClient *cl) {
#if(WITH_TLS)
extern void rDestroyClientTLS(ClientPrivate *cp);
#endif

ClientPrivate *cp = (ClientPrivate *) cl->priv;

pthread_mutex_lock(&cp->pendingLock);
Expand All @@ -349,6 +353,12 @@ static void rResetClientAsync(RedisClient *cl) {
cp->isEnabled = FALSE;
cp->available = 0;
cp->next = 0;

#if(WITH_TLS)
rDestroyClientTLS(cp);
#endif


cp->socket = -1; // Reset the channel's socket descriptor to 'unassigned'
}

Expand Down Expand Up @@ -611,6 +621,10 @@ static int rHelloAsync(RedisClient *cl, char *clientID) {
int rConnectClient(Redis *redis, enum redisx_channel channel) {
static const char *fn = "rConnectClient";

#if WITH_TLS
extern int rConnectTLSClient(ClientPrivate *cp, const TLSConfig *tls);
#endif

struct sockaddr_in serverAddress;
struct utsname u;
RedisPrivate *p;
Expand Down Expand Up @@ -646,6 +660,13 @@ int rConnectClient(Redis *redis, enum redisx_channel channel) {
prop_error(fn, config->socketConf(sock, channel));
}

#if WITH_TLS
if(config->tls.enabled && rConnectTLSClient(cp, &config->tls) != X_SUCCESS) {
close(sock);
return x_error(X_NO_INIT, errno, fn, "failed to connect (with TLS) to %s:%hu: %s", redis->id, port, strerror(errno));
}
else
#endif
if(connect(sock, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) < 0) {
close(sock);
return x_error(X_NO_INIT, errno, fn, "failed to connect to %s:%hu: %s", redis->id, port, strerror(errno));
Expand Down
Loading

0 comments on commit 9e66394

Please sign in to comment.