Build TLS as a loadable module
* Support BUILD_TLS=module to be loaded as a module via config file or command line. e.g. redis-server --loadmodule redis-tls.so * Updates to redismodule.h to allow it to be used side by side with server.h by defining REDISMODULE_CORE_MODULE * Changes to server.h, redismodule.h and module.c to avoid repeated type declarations (gcc 4.8 doesn't like these) * Add a mechanism for non-ABI neutral modules (ones who include server.h) to refuse loading if they detect not being built together with redis (release.c) * Fix wrong signature of RedisModuleDefragFunc, this could break compilation of a module, but not the ABI * Move initialization of listeners in server.c to be after loading the modules * Config TLS after initialization of listeners * Init cluster after initialization of listeners * Add TLS module to CI * Fix a test suite race conditions: Now that the listeners are initialized later, it's not sufficient to wait for the PID message in the log, we need to wait for the "Server Initialized" message. * Fix issues with moduleconfigs test as a result from start_server waiting for "Server Initialized" * Fix issues with modules/infra test as a result of an additional module present Notes about Sentinel: Sentinel can't really rely on the tls module, since it uses hiredis to initiate connections and depends on OpenSSL (won't be able to use any other connection modules for that), so it was decided that when TLS is built as a module, sentinel does not support TLS at all. This means that it keeps using redis_tls_ctx and redis_tls_client_ctx directly. Example code of config in redis-tls.so(may be use in the future): RedisModuleString *tls_cfg = NULL; void tlsInfo(RedisModuleInfoCtx *ctx, int for_crash_report) { UNUSED(for_crash_report); RedisModule_InfoAddSection(ctx, ""); RedisModule_InfoAddFieldLongLong(ctx, "var", 42); } int tlsCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { if (argc != 2) return RedisModule_WrongArity(ctx); return RedisModule_ReplyWithString(ctx, argv[1]); } RedisModuleString *getStringConfigCommand(const char *name, void *privdata) { REDISMODULE_NOT_USED(name); REDISMODULE_NOT_USED(privdata); return tls_cfg; } int setStringConfigCommand(const char *name, RedisModuleString *new, void *privdata, RedisModuleString **err) { REDISMODULE_NOT_USED(name); REDISMODULE_NOT_USED(err); REDISMODULE_NOT_USED(privdata); if (tls_cfg) RedisModule_FreeString(NULL, tls_cfg); RedisModule_RetainString(NULL, new); tls_cfg = new; return REDISMODULE_OK; } int RedisModule_OnLoad(void *ctx, RedisModuleString **argv, int argc) { .... if (RedisModule_CreateCommand(ctx,"tls",tlsCommand,"",0,0,0) == REDISMODULE_ERR) return REDISMODULE_ERR; if (RedisModule_RegisterStringConfig(ctx, "cfg", "", REDISMODULE_CONFIG_DEFAULT, getStringConfigCommand, setStringConfigCommand, NULL, NULL) == REDISMODULE_ERR) return REDISMODULE_ERR; if (RedisModule_LoadConfigs(ctx) == REDISMODULE_ERR) { if (tls_cfg) { RedisModule_FreeString(ctx, tls_cfg); tls_cfg = NULL; } return REDISMODULE_ERR; } ... } Co-authored-by: zhenwei pi <pizhenwei@bytedance.com> Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
This commit is contained in:
parent
89e1148688
commit
4faddf18ca
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
@ -24,7 +24,8 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: make
|
||||
run: make SANITIZER=address REDIS_CFLAGS='-Werror'
|
||||
# build with TLS module just for compilation coverage
|
||||
run: make SANITIZER=address REDIS_CFLAGS='-Werror' BUILD_TLS=module
|
||||
- name: testprep
|
||||
run: sudo apt-get install tcl8.6 tclx -y
|
||||
- name: test
|
||||
|
12
.github/workflows/daily.yml
vendored
12
.github/workflows/daily.yml
vendored
@ -539,7 +539,7 @@ jobs:
|
||||
run: |
|
||||
yum -y install centos-release-scl epel-release
|
||||
yum -y install devtoolset-7 openssl-devel openssl
|
||||
scl enable devtoolset-7 "make BUILD_TLS=yes REDIS_CFLAGS='-Werror'"
|
||||
scl enable devtoolset-7 "make BUILD_TLS=module REDIS_CFLAGS='-Werror'"
|
||||
- name: testprep
|
||||
run: |
|
||||
yum -y install tcl tcltls tclx
|
||||
@ -547,19 +547,19 @@ jobs:
|
||||
- name: test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'redis')
|
||||
run: |
|
||||
./runtest --accurate --verbose --dump-logs --tls --dump-logs ${{github.event.inputs.test_args}}
|
||||
./runtest --accurate --verbose --dump-logs --tls-module --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: module api test
|
||||
if: true && !contains(github.event.inputs.skiptests, 'modules')
|
||||
run: |
|
||||
./runtest-moduleapi --verbose --dump-logs --tls --dump-logs ${{github.event.inputs.test_args}}
|
||||
./runtest-moduleapi --verbose --dump-logs --tls-module --dump-logs ${{github.event.inputs.test_args}}
|
||||
- name: sentinel tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'sentinel')
|
||||
run: |
|
||||
./runtest-sentinel --tls ${{github.event.inputs.cluster_test_args}}
|
||||
./runtest-sentinel ${{github.event.inputs.cluster_test_args}}
|
||||
- name: cluster tests
|
||||
if: true && !contains(github.event.inputs.skiptests, 'cluster')
|
||||
run: |
|
||||
./runtest-cluster --tls ${{github.event.inputs.cluster_test_args}}
|
||||
./runtest-cluster --tls-module ${{github.event.inputs.cluster_test_args}}
|
||||
|
||||
test-centos7-tls-no-tls:
|
||||
runs-on: ubuntu-latest
|
||||
@ -582,7 +582,7 @@ jobs:
|
||||
run: |
|
||||
yum -y install centos-release-scl epel-release
|
||||
yum -y install devtoolset-7 openssl-devel openssl
|
||||
scl enable devtoolset-7 "make BUILD_TLS=yes REDIS_CFLAGS='-Werror'"
|
||||
scl enable devtoolset-7 "make BUILD_TLS=module REDIS_CFLAGS='-Werror'"
|
||||
- name: testprep
|
||||
run: |
|
||||
yum -y install tcl tcltls tclx
|
||||
|
17
TLS.md
17
TLS.md
@ -9,8 +9,14 @@ Getting Started
|
||||
To build with TLS support you'll need OpenSSL development libraries (e.g.
|
||||
libssl-dev on Debian/Ubuntu).
|
||||
|
||||
To build TLS support as Redis built-in:
|
||||
Run `make BUILD_TLS=yes`.
|
||||
|
||||
Or to build TLS as Redis module:
|
||||
Run `make BUILD_TLS=module`.
|
||||
|
||||
Note that sentinel mode does not support TLS module.
|
||||
|
||||
### Tests
|
||||
|
||||
To run Redis test suite with TLS, you'll need TLS support for TCL (i.e.
|
||||
@ -22,16 +28,27 @@ To run Redis test suite with TLS, you'll need TLS support for TCL (i.e.
|
||||
2. Run `./runtest --tls` or `./runtest-cluster --tls` to run Redis and Redis
|
||||
Cluster tests in TLS mode.
|
||||
|
||||
3. Run `./runtest --tls-module` or `./runtest-cluster --tls-module` to
|
||||
run Redis and Redis cluster tests in TLS mode with Redis module.
|
||||
|
||||
### Running manually
|
||||
|
||||
To manually run a Redis server with TLS mode (assuming `gen-test-certs.sh` was
|
||||
invoked so sample certificates/keys are available):
|
||||
|
||||
For TLS built-in mode:
|
||||
./src/redis-server --tls-port 6379 --port 0 \
|
||||
--tls-cert-file ./tests/tls/redis.crt \
|
||||
--tls-key-file ./tests/tls/redis.key \
|
||||
--tls-ca-cert-file ./tests/tls/ca.crt
|
||||
|
||||
For TLS module mode:
|
||||
./src/redis-server --tls-port 6379 --port 0 \
|
||||
--tls-cert-file ./tests/tls/redis.crt \
|
||||
--tls-key-file ./tests/tls/redis.key \
|
||||
--tls-ca-cert-file ./tests/tls/ca.crt \
|
||||
--loadmodule src/redis-tls.so
|
||||
|
||||
To connect to this Redis server with `redis-cli`:
|
||||
|
||||
./src/redis-cli --tls \
|
||||
|
2
deps/Makefile
vendored
2
deps/Makefile
vendored
@ -44,7 +44,7 @@ distclean:
|
||||
|
||||
.PHONY: distclean
|
||||
|
||||
ifeq ($(BUILD_TLS),yes)
|
||||
ifneq (,$(filter $(BUILD_TLS),yes module))
|
||||
HIREDIS_MAKE_FLAGS = USE_SSL=1
|
||||
endif
|
||||
|
||||
|
39
src/Makefile
39
src/Makefile
@ -267,24 +267,41 @@ ifeq ($(MALLOC),jemalloc)
|
||||
FINAL_LIBS := ../deps/jemalloc/lib/libjemalloc.a $(FINAL_LIBS)
|
||||
endif
|
||||
|
||||
ifeq ($(BUILD_TLS),yes)
|
||||
FINAL_CFLAGS+=-DUSE_OPENSSL $(OPENSSL_CFLAGS)
|
||||
FINAL_LDFLAGS+=$(OPENSSL_LDFLAGS)
|
||||
LIBSSL_PKGCONFIG := $(shell $(PKG_CONFIG) --exists libssl && echo $$?)
|
||||
# LIBSSL & LIBCRYPTO
|
||||
LIBSSL_LIBS=
|
||||
LIBSSL_PKGCONFIG := $(shell $(PKG_CONFIG) --exists libssl && echo $$?)
|
||||
ifeq ($(LIBSSL_PKGCONFIG),0)
|
||||
LIBSSL_LIBS=$(shell $(PKG_CONFIG) --libs libssl)
|
||||
else
|
||||
LIBSSL_LIBS=-lssl
|
||||
endif
|
||||
LIBCRYPTO_PKGCONFIG := $(shell $(PKG_CONFIG) --exists libcrypto && echo $$?)
|
||||
LIBCRYPTO_LIBS=
|
||||
LIBCRYPTO_PKGCONFIG := $(shell $(PKG_CONFIG) --exists libcrypto && echo $$?)
|
||||
ifeq ($(LIBCRYPTO_PKGCONFIG),0)
|
||||
LIBCRYPTO_LIBS=$(shell $(PKG_CONFIG) --libs libcrypto)
|
||||
else
|
||||
LIBCRYPTO_LIBS=-lcrypto
|
||||
endif
|
||||
|
||||
BUILD_NO:=0
|
||||
BUILD_YES:=1
|
||||
BUILD_MODULE:=2
|
||||
ifeq ($(BUILD_TLS),yes)
|
||||
FINAL_CFLAGS+=-DUSE_OPENSSL=$(BUILD_YES) $(OPENSSL_CFLAGS) -DBUILD_TLS_MODULE=$(BUILD_NO)
|
||||
FINAL_LDFLAGS+=$(OPENSSL_LDFLAGS)
|
||||
FINAL_LIBS += ../deps/hiredis/libhiredis_ssl.a $(LIBSSL_LIBS) $(LIBCRYPTO_LIBS)
|
||||
endif
|
||||
|
||||
TLS_MODULE=
|
||||
TLS_MODULE_NAME:=redis-tls$(PROG_SUFFIX).so
|
||||
TLS_MODULE_CFLAGS:=$(FINAL_CFLAGS)
|
||||
ifeq ($(BUILD_TLS),module)
|
||||
FINAL_CFLAGS+=-DUSE_OPENSSL=$(BUILD_MODULE) $(OPENSSL_CFLAGS)
|
||||
TLS_CLIENT_LIBS = ../deps/hiredis/libhiredis_ssl.a $(LIBSSL_LIBS) $(LIBCRYPTO_LIBS)
|
||||
TLS_MODULE=$(TLS_MODULE_NAME)
|
||||
TLS_MODULE_CFLAGS+=-DUSE_OPENSSL=$(BUILD_MODULE) $(OPENSSL_CFLAGS) -DBUILD_TLS_MODULE=$(BUILD_MODULE)
|
||||
endif
|
||||
|
||||
ifndef V
|
||||
define MAKE_INSTALL
|
||||
@printf ' %b %b\n' $(LINKCOLOR)INSTALL$(ENDCOLOR) $(BINCOLOR)$(1)$(ENDCOLOR) 1>&2
|
||||
@ -325,7 +342,7 @@ REDIS_CHECK_RDB_NAME=redis-check-rdb$(PROG_SUFFIX)
|
||||
REDIS_CHECK_AOF_NAME=redis-check-aof$(PROG_SUFFIX)
|
||||
ALL_SOURCES=$(sort $(patsubst %.o,%.c,$(REDIS_SERVER_OBJ) $(REDIS_CLI_OBJ) $(REDIS_BENCHMARK_OBJ)))
|
||||
|
||||
all: $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_RDB_NAME) $(REDIS_CHECK_AOF_NAME)
|
||||
all: $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_RDB_NAME) $(REDIS_CHECK_AOF_NAME) $(TLS_MODULE)
|
||||
@echo ""
|
||||
@echo "Hint: It's a good idea to run 'make test' ;)"
|
||||
@echo ""
|
||||
@ -385,13 +402,17 @@ $(REDIS_CHECK_RDB_NAME): $(REDIS_SERVER_NAME)
|
||||
$(REDIS_CHECK_AOF_NAME): $(REDIS_SERVER_NAME)
|
||||
$(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(REDIS_CHECK_AOF_NAME)
|
||||
|
||||
# redis-tls.so
|
||||
$(TLS_MODULE_NAME): $(REDIS_SERVER_NAME)
|
||||
$(QUIET_CC)$(CC) -o $@ tls.c -shared -fPIC $(TLS_MODULE_CFLAGS) $(TLS_CLIENT_LIBS)
|
||||
|
||||
# redis-cli
|
||||
$(REDIS_CLI_NAME): $(REDIS_CLI_OBJ)
|
||||
$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/linenoise/linenoise.o $(FINAL_LIBS)
|
||||
$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/linenoise/linenoise.o $(FINAL_LIBS) $(TLS_CLIENT_LIBS)
|
||||
|
||||
# redis-benchmark
|
||||
$(REDIS_BENCHMARK_NAME): $(REDIS_BENCHMARK_OBJ)
|
||||
$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/hdr_histogram/libhdrhistogram.a $(FINAL_LIBS)
|
||||
$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/hdr_histogram/libhdrhistogram.a $(FINAL_LIBS) $(TLS_CLIENT_LIBS)
|
||||
|
||||
DEP = $(REDIS_SERVER_OBJ:%.o=%.d) $(REDIS_CLI_OBJ:%.o=%.d) $(REDIS_BENCHMARK_OBJ:%.o=%.d)
|
||||
-include $(DEP)
|
||||
@ -410,7 +431,7 @@ commands.c: commands/*.json ../utils/generate-command-code.py
|
||||
endif
|
||||
|
||||
clean:
|
||||
rm -rf $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_RDB_NAME) $(REDIS_CHECK_AOF_NAME) *.o *.gcda *.gcno *.gcov redis.info lcov-html Makefile.dep
|
||||
rm -rf $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_RDB_NAME) $(REDIS_CHECK_AOF_NAME) *.o *.gcda *.gcno *.gcov redis.info lcov-html Makefile.dep *.so
|
||||
rm -f $(DEP)
|
||||
|
||||
.PHONY: clean
|
||||
|
@ -107,8 +107,6 @@ typedef struct ConnectionType {
|
||||
|
||||
/* TLS specified methods */
|
||||
sds (*get_peer_cert)(struct connection *conn);
|
||||
void* (*get_ctx)(void);
|
||||
void* (*get_client_ctx)(void);
|
||||
} ConnectionType;
|
||||
|
||||
struct connection {
|
||||
@ -355,15 +353,6 @@ int connKeepAlive(connection *conn, int interval);
|
||||
int connSendTimeout(connection *conn, long long ms);
|
||||
int connRecvTimeout(connection *conn, long long ms);
|
||||
|
||||
/* Helpers for tls special considerations */
|
||||
static inline void *connTypeGetCtx(ConnectionType *ct) {
|
||||
return ct->get_ctx();
|
||||
}
|
||||
|
||||
static inline void *connTypeGetClientCtx(ConnectionType *ct) {
|
||||
return ct->get_client_ctx();
|
||||
}
|
||||
|
||||
/* Get cert for the secure connection */
|
||||
static inline sds connGetPeerCert(connection *conn) {
|
||||
if (conn->type->get_peer_cert) {
|
||||
@ -399,7 +388,7 @@ static inline connection *connCreate(ConnectionType *ct) {
|
||||
return ct->conn_create();
|
||||
}
|
||||
|
||||
/* Create a accepted connection of specified type.
|
||||
/* Create an accepted connection of specified type.
|
||||
* priv is connection type specified argument */
|
||||
static inline connection *connCreateAccepted(ConnectionType *ct, int fd, void *priv) {
|
||||
return ct->conn_create_accepted(fd, priv);
|
||||
|
@ -11,4 +11,6 @@ test -f release.h || touch release.h
|
||||
echo "#define REDIS_GIT_SHA1 \"$GIT_SHA1\"" > release.h
|
||||
echo "#define REDIS_GIT_DIRTY \"$GIT_DIRTY\"" >> release.h
|
||||
echo "#define REDIS_BUILD_ID \"$BUILD_ID\"" >> release.h
|
||||
echo "#include \"version.h\"" >> release.h
|
||||
echo "#define REDIS_BUILD_ID_RAW REDIS_VERSION REDIS_BUILD_ID REDIS_GIT_DIRTY REDIS_GIT_SHA1" >> release.h
|
||||
touch release.c # Force recompile of release.c
|
||||
|
@ -69,14 +69,14 @@
|
||||
* pointers that have an API the module can call with them)
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
typedef struct RedisModuleInfoCtx {
|
||||
struct RedisModuleInfoCtx {
|
||||
struct RedisModule *module;
|
||||
dict *requested_sections;
|
||||
sds info; /* info string we collected so far */
|
||||
int sections; /* number of sections we collected so far */
|
||||
int in_section; /* indication if we're in an active section or not */
|
||||
int in_dict_field; /* indication that we're currently appending to a dict */
|
||||
} RedisModuleInfoCtx;
|
||||
};
|
||||
|
||||
/* This represents a shared API. Shared APIs will be used to populate
|
||||
* the server.sharedapi dictionary, mapping names of APIs exported by
|
||||
@ -12211,13 +12211,13 @@ const char *RM_GetCurrentCommandName(RedisModuleCtx *ctx) {
|
||||
/* The defrag context, used to manage state during calls to the data type
|
||||
* defrag callback.
|
||||
*/
|
||||
typedef struct RedisModuleDefragCtx {
|
||||
struct RedisModuleDefragCtx {
|
||||
long defragged;
|
||||
long long int endtime;
|
||||
unsigned long *cursor;
|
||||
struct redisObject *key; /* Optional name of key processed, NULL when unknown. */
|
||||
int dbid; /* The dbid of the key being processed, -1 when unknown. */
|
||||
} RedisModuleDefragCtx;
|
||||
};
|
||||
|
||||
/* Register a defrag callback for global data, i.e. anything that the module
|
||||
* may allocate that is not tied to a specific data type.
|
||||
|
@ -750,13 +750,41 @@ typedef enum {
|
||||
REDISMODULE_ACL_LOG_CHANNEL /* Channel authorization failure */
|
||||
} RedisModuleACLLogEntryReason;
|
||||
|
||||
/* Incomplete structures needed by both the core and modules. */
|
||||
typedef struct RedisModuleString RedisModuleString;
|
||||
typedef struct RedisModuleIO RedisModuleIO;
|
||||
typedef struct RedisModuleDigest RedisModuleDigest;
|
||||
typedef struct RedisModuleInfoCtx RedisModuleInfoCtx;
|
||||
typedef struct RedisModuleDefragCtx RedisModuleDefragCtx;
|
||||
|
||||
/* Function pointers needed by both the core and modules, these needs to be
|
||||
* exposed since you can't cast a function pointer to (void *). */
|
||||
typedef void (*RedisModuleInfoFunc)(RedisModuleInfoCtx *ctx, int for_crash_report);
|
||||
typedef void (*RedisModuleDefragFunc)(RedisModuleDefragCtx *ctx);
|
||||
typedef void (*RedisModuleUserChangedFunc) (uint64_t client_id, void *privdata);
|
||||
|
||||
/* ------------------------- End of common defines ------------------------ */
|
||||
|
||||
#ifndef REDISMODULE_CORE
|
||||
#if defined REDISMODULE_CORE
|
||||
/* Things only defined for the modules core (server), not exported to modules
|
||||
* that include this file. */
|
||||
|
||||
#define RedisModuleString robj
|
||||
|
||||
#endif /* defined REDISMODULE_CORE */
|
||||
|
||||
#if !defined REDISMODULE_CORE && !defined REDISMODULE_CORE_MODULE
|
||||
/* Things defined for modules, but not for core-modules. */
|
||||
|
||||
typedef long long mstime_t;
|
||||
typedef long long ustime_t;
|
||||
|
||||
#endif /* !defined REDISMODULE_CORE && !defined REDISMODULE_CORE_MODULE */
|
||||
|
||||
/* ----------- The rest of the defines are only for modules ----------------- */
|
||||
#if !defined REDISMODULE_CORE || defined REDISMODULE_CORE_MODULE
|
||||
/* Things defined for modules and core-modules. */
|
||||
|
||||
/* Macro definitions specific to individual compilers */
|
||||
#ifndef REDISMODULE_ATTR_UNUSED
|
||||
# ifdef __GNUC__
|
||||
@ -786,21 +814,16 @@ typedef long long ustime_t;
|
||||
typedef struct RedisModuleCtx RedisModuleCtx;
|
||||
typedef struct RedisModuleCommand RedisModuleCommand;
|
||||
typedef struct RedisModuleKey RedisModuleKey;
|
||||
typedef struct RedisModuleString RedisModuleString;
|
||||
typedef struct RedisModuleCallReply RedisModuleCallReply;
|
||||
typedef struct RedisModuleIO RedisModuleIO;
|
||||
typedef struct RedisModuleType RedisModuleType;
|
||||
typedef struct RedisModuleDigest RedisModuleDigest;
|
||||
typedef struct RedisModuleBlockedClient RedisModuleBlockedClient;
|
||||
typedef struct RedisModuleClusterInfo RedisModuleClusterInfo;
|
||||
typedef struct RedisModuleDict RedisModuleDict;
|
||||
typedef struct RedisModuleDictIter RedisModuleDictIter;
|
||||
typedef struct RedisModuleCommandFilterCtx RedisModuleCommandFilterCtx;
|
||||
typedef struct RedisModuleCommandFilter RedisModuleCommandFilter;
|
||||
typedef struct RedisModuleInfoCtx RedisModuleInfoCtx;
|
||||
typedef struct RedisModuleServerInfoData RedisModuleServerInfoData;
|
||||
typedef struct RedisModuleScanCursor RedisModuleScanCursor;
|
||||
typedef struct RedisModuleDefragCtx RedisModuleDefragCtx;
|
||||
typedef struct RedisModuleUser RedisModuleUser;
|
||||
typedef struct RedisModuleKeyOptCtx RedisModuleKeyOptCtx;
|
||||
|
||||
@ -827,11 +850,8 @@ typedef void (*RedisModuleClusterMessageReceiver)(RedisModuleCtx *ctx, const cha
|
||||
typedef void (*RedisModuleTimerProc)(RedisModuleCtx *ctx, void *data);
|
||||
typedef void (*RedisModuleCommandFilterFunc) (RedisModuleCommandFilterCtx *filter);
|
||||
typedef void (*RedisModuleForkDoneHandler) (int exitcode, int bysignal, void *user_data);
|
||||
typedef void (*RedisModuleInfoFunc)(RedisModuleInfoCtx *ctx, int for_crash_report);
|
||||
typedef void (*RedisModuleScanCB)(RedisModuleCtx *ctx, RedisModuleString *keyname, RedisModuleKey *key, void *privdata);
|
||||
typedef void (*RedisModuleScanKeyCB)(RedisModuleKey *key, RedisModuleString *field, RedisModuleString *value, void *privdata);
|
||||
typedef void (*RedisModuleUserChangedFunc) (uint64_t client_id, void *privdata);
|
||||
typedef int (*RedisModuleDefragFunc)(RedisModuleDefragCtx *ctx);
|
||||
typedef RedisModuleString * (*RedisModuleConfigGetStringFunc)(const char *name, void *privdata);
|
||||
typedef long long (*RedisModuleConfigGetNumericFunc)(const char *name, void *privdata);
|
||||
typedef int (*RedisModuleConfigGetBoolFunc)(const char *name, void *privdata);
|
||||
@ -1559,11 +1579,5 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
|
||||
|
||||
#define RMAPI_FUNC_SUPPORTED(func) (func != NULL)
|
||||
|
||||
#else
|
||||
|
||||
/* Things only defined for the modules core, not exported to modules
|
||||
* including this file. */
|
||||
#define RedisModuleString robj
|
||||
|
||||
#endif /* REDISMODULE_CORE */
|
||||
#endif /* REDISMODULE_H */
|
||||
|
@ -35,7 +35,6 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "release.h"
|
||||
#include "version.h"
|
||||
#include "crc64.h"
|
||||
|
||||
char *redisGitSHA1(void) {
|
||||
@ -46,8 +45,12 @@ char *redisGitDirty(void) {
|
||||
return REDIS_GIT_DIRTY;
|
||||
}
|
||||
|
||||
const char *redisBuildIdRaw(void) {
|
||||
return REDIS_BUILD_ID_RAW;
|
||||
}
|
||||
|
||||
uint64_t redisBuildId(void) {
|
||||
char *buildid = REDIS_VERSION REDIS_BUILD_ID REDIS_GIT_DIRTY REDIS_GIT_SHA1;
|
||||
char *buildid = REDIS_BUILD_ID_RAW;
|
||||
|
||||
return crc64(0,(unsigned char*)buildid,strlen(buildid));
|
||||
}
|
||||
|
@ -30,7 +30,7 @@
|
||||
|
||||
#include "server.h"
|
||||
#include "hiredis.h"
|
||||
#ifdef USE_OPENSSL
|
||||
#if USE_OPENSSL == 1 /* BUILD_YES */
|
||||
#include "openssl/ssl.h"
|
||||
#include "hiredis_ssl.h"
|
||||
#endif
|
||||
@ -44,6 +44,11 @@
|
||||
|
||||
extern char **environ;
|
||||
|
||||
#if USE_OPENSSL == 1 /* BUILD_YES */
|
||||
extern SSL_CTX *redis_tls_ctx;
|
||||
extern SSL_CTX *redis_tls_client_ctx;
|
||||
#endif
|
||||
|
||||
#define REDIS_SENTINEL_PORT 26379
|
||||
|
||||
/* ======================== Sentinel global state =========================== */
|
||||
@ -2373,12 +2378,7 @@ void sentinelSetClientName(sentinelRedisInstance *ri, redisAsyncContext *c, char
|
||||
}
|
||||
|
||||
static int instanceLinkNegotiateTLS(redisAsyncContext *context) {
|
||||
#ifndef USE_OPENSSL
|
||||
(void) context;
|
||||
#else
|
||||
SSL_CTX *redis_tls_ctx = connTypeGetCtx(connectionTypeTls());
|
||||
SSL_CTX *redis_tls_client_ctx = connTypeGetClientCtx(connectionTypeTls());
|
||||
|
||||
#if USE_OPENSSL == 1 /* BUILD_YES */
|
||||
if (!redis_tls_ctx) return C_ERR;
|
||||
SSL *ssl = SSL_new(redis_tls_client_ctx ? redis_tls_client_ctx : redis_tls_ctx);
|
||||
if (!ssl) return C_ERR;
|
||||
@ -2387,6 +2387,8 @@ static int instanceLinkNegotiateTLS(redisAsyncContext *context) {
|
||||
SSL_free(ssl);
|
||||
return C_ERR;
|
||||
}
|
||||
#else
|
||||
UNUSED(context);
|
||||
#endif
|
||||
return C_OK;
|
||||
}
|
||||
|
152
src/server.c
152
src/server.c
@ -2446,12 +2446,6 @@ void initServer(void) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ((server.tls_port || server.tls_replication || server.tls_cluster)
|
||||
&& connTypeConfigure(connectionTypeTls(), &server.tls_ctx_config, 1) == C_ERR) {
|
||||
serverLog(LL_WARNING, "Failed to configure TLS. Check logs for more info.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (j = 0; j < CLIENT_MEM_USAGE_BUCKETS; j++) {
|
||||
server.client_mem_usage_buckets[j].mem_usage_sum = 0;
|
||||
server.client_mem_usage_buckets[j].clients = listCreate();
|
||||
@ -2470,40 +2464,6 @@ void initServer(void) {
|
||||
}
|
||||
server.db = zmalloc(sizeof(redisDb)*server.dbnum);
|
||||
|
||||
/* Setup listeners from server config for TCP/TLS/Unix */
|
||||
int conn_index;
|
||||
connListener *listener;
|
||||
if (server.port != 0) {
|
||||
conn_index = connectionIndexByType(CONN_TYPE_SOCKET);
|
||||
if (conn_index < 0)
|
||||
serverPanic("Failed finding connection listener of %s", CONN_TYPE_SOCKET);
|
||||
listener = &server.listeners[conn_index];
|
||||
listener->bindaddr = server.bindaddr;
|
||||
listener->bindaddr_count = server.bindaddr_count;
|
||||
listener->port = server.port;
|
||||
listener->ct = connectionByType(CONN_TYPE_SOCKET);
|
||||
}
|
||||
if (server.tls_port != 0) {
|
||||
conn_index = connectionIndexByType(CONN_TYPE_TLS);
|
||||
if (conn_index < 0)
|
||||
serverPanic("Failed finding connection listener of %s", CONN_TYPE_TLS);
|
||||
listener = &server.listeners[conn_index];
|
||||
listener->bindaddr = server.bindaddr;
|
||||
listener->bindaddr_count = server.bindaddr_count;
|
||||
listener->port = server.tls_port;
|
||||
listener->ct = connectionByType(CONN_TYPE_TLS);
|
||||
}
|
||||
if (server.unixsocket != NULL) {
|
||||
conn_index = connectionIndexByType(CONN_TYPE_UNIX);
|
||||
if (conn_index < 0)
|
||||
serverPanic("Failed finding connection listener of %s", CONN_TYPE_UNIX);
|
||||
listener = &server.listeners[conn_index];
|
||||
listener->bindaddr = &server.unixsocket;
|
||||
listener->bindaddr_count = 1;
|
||||
listener->ct = connectionByType(CONN_TYPE_UNIX);
|
||||
listener->priv = &server.unixsocketperm; /* Unix socket specified */
|
||||
}
|
||||
|
||||
/* Create the Redis databases, and initialize other internal state. */
|
||||
for (j = 0; j < server.dbnum; j++) {
|
||||
server.db[j].dict = dictCreate(&dbDictType);
|
||||
@ -2584,29 +2544,6 @@ void initServer(void) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* create all the configured listener, and add handler to start to accept */
|
||||
int listen_fds = 0;
|
||||
for (j = 0; j < CONN_TYPE_MAX; j++) {
|
||||
listener = &server.listeners[j];
|
||||
if (listener->ct == NULL)
|
||||
continue;
|
||||
|
||||
if (connListen(listener) == C_ERR) {
|
||||
serverLog(LL_WARNING, "Failed listening on port %u (%s), aborting.", listener->port, listener->ct->get_type(NULL));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (createSocketAcceptHandler(listener, connAcceptHandler(listener->ct)) != C_OK)
|
||||
serverPanic("Unrecoverable error creating %s listener accept handler.", listener->ct->get_type(NULL));
|
||||
|
||||
listen_fds += listener->count;
|
||||
}
|
||||
|
||||
if (listen_fds == 0) {
|
||||
serverLog(LL_WARNING, "Configured to not listen anywhere, exiting.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Register a readable event for the pipe used to awake the event loop
|
||||
* from module threads. */
|
||||
if (aeCreateFileEvent(server.el, server.module_pipe[0], AE_READABLE,
|
||||
@ -2630,7 +2567,6 @@ void initServer(void) {
|
||||
server.maxmemory_policy = MAXMEMORY_NO_EVICTION;
|
||||
}
|
||||
|
||||
if (server.cluster_enabled) clusterInit();
|
||||
scriptingInit(1);
|
||||
functionsInit();
|
||||
slowlogInit();
|
||||
@ -2642,6 +2578,78 @@ void initServer(void) {
|
||||
applyWatchdogPeriod();
|
||||
}
|
||||
|
||||
void initListeners() {
|
||||
/* Setup listeners from server config for TCP/TLS/Unix */
|
||||
int conn_index;
|
||||
connListener *listener;
|
||||
if (server.port != 0) {
|
||||
conn_index = connectionIndexByType(CONN_TYPE_SOCKET);
|
||||
if (conn_index < 0)
|
||||
serverPanic("Failed finding connection listener of %s", CONN_TYPE_SOCKET);
|
||||
listener = &server.listeners[conn_index];
|
||||
listener->bindaddr = server.bindaddr;
|
||||
listener->bindaddr_count = server.bindaddr_count;
|
||||
listener->port = server.port;
|
||||
listener->ct = connectionByType(CONN_TYPE_SOCKET);
|
||||
}
|
||||
|
||||
if (server.tls_port || server.tls_replication || server.tls_cluster) {
|
||||
ConnectionType *ct_tls = connectionTypeTls();
|
||||
if (!ct_tls) {
|
||||
serverLog(LL_WARNING, "Failed finding TLS support.");
|
||||
exit(1);
|
||||
}
|
||||
if (connTypeConfigure(ct_tls, &server.tls_ctx_config, 1) == C_ERR) {
|
||||
serverLog(LL_WARNING, "Failed to configure TLS. Check logs for more info.");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (server.tls_port != 0) {
|
||||
conn_index = connectionIndexByType(CONN_TYPE_TLS);
|
||||
if (conn_index < 0)
|
||||
serverPanic("Failed finding connection listener of %s", CONN_TYPE_TLS);
|
||||
listener = &server.listeners[conn_index];
|
||||
listener->bindaddr = server.bindaddr;
|
||||
listener->bindaddr_count = server.bindaddr_count;
|
||||
listener->port = server.tls_port;
|
||||
listener->ct = connectionByType(CONN_TYPE_TLS);
|
||||
}
|
||||
if (server.unixsocket != NULL) {
|
||||
conn_index = connectionIndexByType(CONN_TYPE_UNIX);
|
||||
if (conn_index < 0)
|
||||
serverPanic("Failed finding connection listener of %s", CONN_TYPE_UNIX);
|
||||
listener = &server.listeners[conn_index];
|
||||
listener->bindaddr = &server.unixsocket;
|
||||
listener->bindaddr_count = 1;
|
||||
listener->ct = connectionByType(CONN_TYPE_UNIX);
|
||||
listener->priv = &server.unixsocketperm; /* Unix socket specified */
|
||||
}
|
||||
|
||||
/* create all the configured listener, and add handler to start to accept */
|
||||
int listen_fds = 0;
|
||||
for (int j = 0; j < CONN_TYPE_MAX; j++) {
|
||||
listener = &server.listeners[j];
|
||||
if (listener->ct == NULL)
|
||||
continue;
|
||||
|
||||
if (connListen(listener) == C_ERR) {
|
||||
serverLog(LL_WARNING, "Failed listening on port %u (%s), aborting.", listener->port, listener->ct->get_type(NULL));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (createSocketAcceptHandler(listener, connAcceptHandler(listener->ct)) != C_OK)
|
||||
serverPanic("Unrecoverable error creating %s listener accept handler.", listener->ct->get_type(NULL));
|
||||
|
||||
listen_fds += listener->count;
|
||||
}
|
||||
|
||||
if (listen_fds == 0) {
|
||||
serverLog(LL_WARNING, "Configured to not listen anywhere, exiting.");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Some steps in server initialization need to be done last (after modules
|
||||
* are loaded).
|
||||
* Specifically, creation of threads due to a race bug in ld.so, in which
|
||||
@ -7086,6 +7094,16 @@ int main(int argc, char **argv) {
|
||||
if (server.set_proc_title) redisSetProcTitle(NULL);
|
||||
redisAsciiArt();
|
||||
checkTcpBacklogSettings();
|
||||
if (!server.sentinel_mode) {
|
||||
moduleInitModulesSystemLast();
|
||||
moduleLoadFromQueue();
|
||||
}
|
||||
ACLLoadUsersAtStartup();
|
||||
initListeners();
|
||||
if (server.cluster_enabled) {
|
||||
clusterInit();
|
||||
}
|
||||
InitServerLast();
|
||||
|
||||
if (!server.sentinel_mode) {
|
||||
/* Things not needed when running in Sentinel mode. */
|
||||
@ -7114,10 +7132,6 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
#endif /* __arm64__ */
|
||||
#endif /* __linux__ */
|
||||
moduleInitModulesSystemLast();
|
||||
moduleLoadFromQueue();
|
||||
ACLLoadUsersAtStartup();
|
||||
InitServerLast();
|
||||
aofLoadManifestFromDisk();
|
||||
loadDataFromDisk();
|
||||
aofOpenIfNeededOnServerStart();
|
||||
@ -7148,8 +7162,6 @@ int main(int argc, char **argv) {
|
||||
redisCommunicateSystemd("READY=1\n");
|
||||
}
|
||||
} else {
|
||||
ACLLoadUsersAtStartup();
|
||||
InitServerLast();
|
||||
sentinelIsRunning();
|
||||
if (server.supervised_mode == SUPERVISED_SYSTEMD) {
|
||||
redisCommunicateSystemd("STATUS=Ready to accept connections\n");
|
||||
|
25
src/server.h
25
src/server.h
@ -81,6 +81,7 @@ typedef long long ustime_t; /* microsecond time type. */
|
||||
#include "connection.h" /* Connection abstraction */
|
||||
|
||||
#define REDISMODULE_CORE 1
|
||||
typedef struct redisObject robj;
|
||||
#include "redismodule.h" /* Redis modules API defines. */
|
||||
|
||||
/* Following includes allow test functions to be called from Redis main() */
|
||||
@ -679,9 +680,6 @@ struct RedisModuleIO;
|
||||
struct RedisModuleDigest;
|
||||
struct RedisModuleCtx;
|
||||
struct moduleLoadQueueEntry;
|
||||
struct redisObject;
|
||||
struct RedisModuleDefragCtx;
|
||||
struct RedisModuleInfoCtx;
|
||||
struct RedisModuleKeyOptCtx;
|
||||
struct RedisModuleCommand;
|
||||
|
||||
@ -701,20 +699,12 @@ typedef size_t (*moduleTypeFreeEffortFunc)(struct redisObject *key, const void *
|
||||
typedef void (*moduleTypeUnlinkFunc)(struct redisObject *key, void *value);
|
||||
typedef void *(*moduleTypeCopyFunc)(struct redisObject *fromkey, struct redisObject *tokey, const void *value);
|
||||
typedef int (*moduleTypeDefragFunc)(struct RedisModuleDefragCtx *ctx, struct redisObject *key, void **value);
|
||||
typedef void (*RedisModuleInfoFunc)(struct RedisModuleInfoCtx *ctx, int for_crash_report);
|
||||
typedef void (*RedisModuleDefragFunc)(struct RedisModuleDefragCtx *ctx);
|
||||
typedef size_t (*moduleTypeMemUsageFunc2)(struct RedisModuleKeyOptCtx *ctx, const void *value, size_t sample_size);
|
||||
typedef void (*moduleTypeFreeFunc2)(struct RedisModuleKeyOptCtx *ctx, void *value);
|
||||
typedef size_t (*moduleTypeFreeEffortFunc2)(struct RedisModuleKeyOptCtx *ctx, const void *value);
|
||||
typedef void (*moduleTypeUnlinkFunc2)(struct RedisModuleKeyOptCtx *ctx, void *value);
|
||||
typedef void *(*moduleTypeCopyFunc2)(struct RedisModuleKeyOptCtx *ctx, const void *value);
|
||||
|
||||
/* This callback type is called by moduleNotifyUserChanged() every time
|
||||
* a user authenticated via the module API is associated with a different
|
||||
* user or gets disconnected. This needs to be exposed since you can't cast
|
||||
* a function pointer to (void *). */
|
||||
typedef void (*RedisModuleUserChangedFunc) (uint64_t client_id, void *privdata);
|
||||
|
||||
|
||||
/* The module type, which is referenced in each value of a given type, defines
|
||||
* the methods and links to the module exporting the type. */
|
||||
@ -786,7 +776,7 @@ typedef struct RedisModule RedisModule;
|
||||
/* This is a wrapper for the 'rio' streams used inside rdb.c in Redis, so that
|
||||
* the user does not have to take the total count of the written bytes nor
|
||||
* to care about error conditions. */
|
||||
typedef struct RedisModuleIO {
|
||||
struct RedisModuleIO {
|
||||
size_t bytes; /* Bytes read / written so far. */
|
||||
rio *rio; /* Rio stream. */
|
||||
moduleType *type; /* Module type doing the operation. */
|
||||
@ -794,7 +784,7 @@ typedef struct RedisModuleIO {
|
||||
struct RedisModuleCtx *ctx; /* Optional context, see RM_GetContextFromIO()*/
|
||||
struct redisObject *key; /* Optional name of key processed */
|
||||
int dbid; /* The dbid of the key being processed, -1 when unknown. */
|
||||
} RedisModuleIO;
|
||||
};
|
||||
|
||||
/* Macro to initialize an IO context. Note that the 'ver' field is populated
|
||||
* inside rdb.c according to the version of the value to load. */
|
||||
@ -813,12 +803,12 @@ typedef struct RedisModuleIO {
|
||||
* a data structure, so that a digest can be created in a way that correctly
|
||||
* reflects the values. See the DEBUG DIGEST command implementation for more
|
||||
* background. */
|
||||
typedef struct RedisModuleDigest {
|
||||
struct RedisModuleDigest {
|
||||
unsigned char o[20]; /* Ordered elements. */
|
||||
unsigned char x[20]; /* Xored elements. */
|
||||
struct redisObject *key; /* Optional name of key processed */
|
||||
int dbid; /* The dbid of the key being processed */
|
||||
} RedisModuleDigest;
|
||||
};
|
||||
|
||||
/* Just start with a digest composed of all zero bytes. */
|
||||
#define moduleInitDigestContext(mdvar) do { \
|
||||
@ -849,7 +839,7 @@ typedef struct RedisModuleDigest {
|
||||
#define OBJ_SHARED_REFCOUNT INT_MAX /* Global object never destroyed. */
|
||||
#define OBJ_STATIC_REFCOUNT (INT_MAX-1) /* Object allocated in the stack. */
|
||||
#define OBJ_FIRST_SPECIAL_REFCOUNT OBJ_STATIC_REFCOUNT
|
||||
typedef struct redisObject {
|
||||
struct redisObject {
|
||||
unsigned type:4;
|
||||
unsigned encoding:4;
|
||||
unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or
|
||||
@ -857,7 +847,7 @@ typedef struct redisObject {
|
||||
* and most significant 16 bits access time). */
|
||||
int refcount;
|
||||
void *ptr;
|
||||
} robj;
|
||||
};
|
||||
|
||||
/* The a string name for an object's type as listed above
|
||||
* Native types are checked against the OBJ_STRING, OBJ_LIST, OBJ_* defines,
|
||||
@ -3269,6 +3259,7 @@ void *dictSdsDup(dict *d, const void *key);
|
||||
char *redisGitSHA1(void);
|
||||
char *redisGitDirty(void);
|
||||
uint64_t redisBuildId(void);
|
||||
const char *redisBuildIdRaw(void);
|
||||
char *redisBuildIdString(void);
|
||||
|
||||
/* Commands prototypes */
|
||||
|
62
src/tls.c
62
src/tls.c
@ -27,12 +27,13 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#define REDISMODULE_CORE_MODULE /* A module that's part of the redis core, uses server.h too. */
|
||||
|
||||
#include "server.h"
|
||||
#include "connhelpers.h"
|
||||
#include "adlist.h"
|
||||
|
||||
#ifdef USE_OPENSSL
|
||||
#if (USE_OPENSSL == 1 /* BUILD_YES */ ) || ((USE_OPENSSL == 2 /* BUILD_MODULE */) && (BUILD_TLS_MODULE == 2))
|
||||
|
||||
#include <openssl/conf.h>
|
||||
#include <openssl/ssl.h>
|
||||
@ -56,8 +57,8 @@
|
||||
#define REDIS_TLS_PROTO_DEFAULT (REDIS_TLS_PROTO_TLSv1_2)
|
||||
#endif
|
||||
|
||||
static SSL_CTX *redis_tls_ctx = NULL;
|
||||
static SSL_CTX *redis_tls_client_ctx = NULL;
|
||||
SSL_CTX *redis_tls_ctx = NULL;
|
||||
SSL_CTX *redis_tls_client_ctx = NULL;
|
||||
|
||||
static int parseProtocolsConfig(const char *str) {
|
||||
int i, count = 0;
|
||||
@ -1087,14 +1088,6 @@ static sds connTLSGetPeerCert(connection *conn_) {
|
||||
return cert_pem;
|
||||
}
|
||||
|
||||
static void *tlsGetCtx(void) {
|
||||
return redis_tls_ctx;
|
||||
}
|
||||
|
||||
static void *tlsGetClientCtx(void) {
|
||||
return redis_tls_client_ctx;
|
||||
}
|
||||
|
||||
static ConnectionType CT_TLS = {
|
||||
/* connection type */
|
||||
.get_type = connTLSGetType,
|
||||
@ -1137,20 +1130,55 @@ static ConnectionType CT_TLS = {
|
||||
|
||||
/* TLS specified methods */
|
||||
.get_peer_cert = connTLSGetPeerCert,
|
||||
.get_ctx = tlsGetCtx,
|
||||
.get_client_ctx = tlsGetClientCtx
|
||||
};
|
||||
|
||||
int RedisRegisterConnectionTypeTLS()
|
||||
{
|
||||
int RedisRegisterConnectionTypeTLS() {
|
||||
return connTypeRegister(&CT_TLS);
|
||||
}
|
||||
|
||||
#else /* USE_OPENSSL */
|
||||
|
||||
int RedisRegisterConnectionTypeTLS()
|
||||
{
|
||||
int RedisRegisterConnectionTypeTLS() {
|
||||
serverLog(LL_VERBOSE, "Connection type %s not builtin", CONN_TYPE_TLS);
|
||||
return C_ERR;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if BUILD_TLS_MODULE == 2 /* BUILD_MODULE */
|
||||
|
||||
#include "release.h"
|
||||
|
||||
int RedisModule_OnLoad(void *ctx, RedisModuleString **argv, int argc) {
|
||||
UNUSED(argv);
|
||||
UNUSED(argc);
|
||||
|
||||
/* Connection modules must be part of the same build as redis. */
|
||||
if (strcmp(REDIS_BUILD_ID_RAW, redisBuildIdRaw())) {
|
||||
serverLog(LL_NOTICE, "Connection type %s was not built together with the redis-server used.", CONN_TYPE_TLS);
|
||||
return REDISMODULE_ERR;
|
||||
}
|
||||
|
||||
if (RedisModule_Init(ctx,"tls",1,REDISMODULE_APIVER_1) == REDISMODULE_ERR)
|
||||
return REDISMODULE_ERR;
|
||||
|
||||
/* Connection modules is available only bootup. */
|
||||
if ((RedisModule_GetContextFlags(ctx) & REDISMODULE_CTX_FLAGS_SERVER_STARTUP) == 0) {
|
||||
serverLog(LL_NOTICE, "Connection type %s can be loaded only during bootup", CONN_TYPE_TLS);
|
||||
return REDISMODULE_ERR;
|
||||
}
|
||||
|
||||
RedisModule_SetModuleOptions(ctx, REDISMODULE_OPTIONS_HANDLE_REPL_ASYNC_LOAD);
|
||||
|
||||
if(connTypeRegister(&CT_TLS) != C_OK)
|
||||
return REDISMODULE_ERR;
|
||||
|
||||
return REDISMODULE_OK;
|
||||
}
|
||||
|
||||
int RedisModule_OnUnload(void *arg) {
|
||||
UNUSED(arg);
|
||||
serverLog(LL_NOTICE, "Connection type %s can not be unloaded", CONN_TYPE_TLS);
|
||||
return REDISMODULE_ERR;
|
||||
}
|
||||
#endif
|
||||
|
@ -19,6 +19,7 @@ source ../support/test.tcl
|
||||
set ::verbose 0
|
||||
set ::valgrind 0
|
||||
set ::tls 0
|
||||
set ::tls_module 0
|
||||
set ::pause_on_error 0
|
||||
set ::dont_clean 0
|
||||
set ::simulate_error 0
|
||||
@ -85,6 +86,10 @@ proc spawn_instance {type base_port count {conf {}} {base_conf_file ""}} {
|
||||
}
|
||||
|
||||
if {$::tls} {
|
||||
if {$::tls_module} {
|
||||
puts $cfg [format "loadmodule %s/../../../src/redis-tls.so" [pwd]]
|
||||
}
|
||||
|
||||
puts $cfg "tls-port $port"
|
||||
puts $cfg "tls-replication yes"
|
||||
puts $cfg "tls-cluster yes"
|
||||
@ -271,13 +276,16 @@ proc parse_options {} {
|
||||
} elseif {$opt eq {--host}} {
|
||||
incr j
|
||||
set ::host ${val}
|
||||
} elseif {$opt eq {--tls}} {
|
||||
} elseif {$opt eq {--tls} || $opt eq {--tls-module}} {
|
||||
package require tls 1.6
|
||||
::tls::init \
|
||||
-cafile "$::tlsdir/ca.crt" \
|
||||
-certfile "$::tlsdir/client.crt" \
|
||||
-keyfile "$::tlsdir/client.key"
|
||||
set ::tls 1
|
||||
if {$opt eq {--tls-module}} {
|
||||
set ::tls_module 1
|
||||
}
|
||||
} elseif {$opt eq {--config}} {
|
||||
set val2 [lindex $::argv [expr $j+2]]
|
||||
dict set ::global_config $val $val2
|
||||
@ -293,6 +301,7 @@ proc parse_options {} {
|
||||
puts "--fail Simulate a test failure."
|
||||
puts "--valgrind Run with valgrind."
|
||||
puts "--tls Run tests in TLS mode."
|
||||
puts "--tls-module Run tests in TLS mode with Redis module."
|
||||
puts "--host <host> Use hostname instead of 127.0.0.1."
|
||||
puts "--config <k> <v> Extra config argument(s)."
|
||||
puts "--stop Blocks once the first test fails."
|
||||
|
@ -35,7 +35,7 @@ static void createGlobalStrings(RedisModuleCtx *ctx, int count)
|
||||
}
|
||||
}
|
||||
|
||||
static int defragGlobalStrings(RedisModuleDefragCtx *ctx)
|
||||
static void defragGlobalStrings(RedisModuleDefragCtx *ctx)
|
||||
{
|
||||
for (int i = 0; i < global_strings_len; i++) {
|
||||
RedisModuleString *new = RedisModule_DefragRedisModuleString(ctx, global_strings[i]);
|
||||
@ -45,8 +45,6 @@ static int defragGlobalStrings(RedisModuleDefragCtx *ctx)
|
||||
global_defragged++;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void FragInfo(RedisModuleInfoCtx *ctx, int for_crash_report) {
|
||||
|
@ -300,7 +300,7 @@ proc wait_server_started {config_file stdout pid} {
|
||||
set maxiter [expr {120*1000/$checkperiod}] ; # Wait up to 2 minutes.
|
||||
set port_busy 0
|
||||
while 1 {
|
||||
if {[regexp -- " PID: $pid" [exec cat $stdout]]} {
|
||||
if {[regexp -- " PID: $pid.*Server initialized" [exec cat $stdout]]} {
|
||||
break
|
||||
}
|
||||
after $checkperiod
|
||||
@ -464,6 +464,9 @@ proc start_server {options {code undefined}} {
|
||||
set data [split [exec cat "tests/assets/$baseconfig"] "\n"]
|
||||
set config {}
|
||||
if {$::tls} {
|
||||
if {$::tls_module} {
|
||||
lappend config_lines [list "loadmodule" [format "%s/src/redis-tls.so" [pwd]]]
|
||||
}
|
||||
dict set config "tls-cert-file" [format "%s/tests/tls/server.crt" [pwd]]
|
||||
dict set config "tls-key-file" [format "%s/tests/tls/server.key" [pwd]]
|
||||
dict set config "tls-client-cert-file" [format "%s/tests/tls/client.crt" [pwd]]
|
||||
|
@ -1039,3 +1039,25 @@ proc memory_usage {key} {
|
||||
}
|
||||
return $usage
|
||||
}
|
||||
|
||||
# forward compatibility, lmap missing in TCL 8.5
|
||||
proc lmap args {
|
||||
set body [lindex $args end]
|
||||
set args [lrange $args 0 end-1]
|
||||
set n 0
|
||||
set pairs [list]
|
||||
foreach {varnames listval} $args {
|
||||
set varlist [list]
|
||||
foreach varname $varnames {
|
||||
upvar 1 $varname var$n
|
||||
lappend varlist var$n
|
||||
incr n
|
||||
}
|
||||
lappend pairs $varlist $listval
|
||||
}
|
||||
set temp [list]
|
||||
foreach {*}$pairs {
|
||||
lappend temp [uplevel 1 $body]
|
||||
}
|
||||
set temp
|
||||
}
|
||||
|
@ -111,6 +111,7 @@ set ::traceleaks 0
|
||||
set ::valgrind 0
|
||||
set ::durable 0
|
||||
set ::tls 0
|
||||
set ::tls_module 0
|
||||
set ::stack_logging 0
|
||||
set ::verbose 0
|
||||
set ::quiet 0
|
||||
@ -611,6 +612,7 @@ proc print_help_screen {} {
|
||||
"--wait-server Wait after server is started (so that you can attach a debugger)."
|
||||
"--dump-logs Dump server log on test failure."
|
||||
"--tls Run tests in TLS mode."
|
||||
"--tls-module Run tests in TLS mode with Redis module."
|
||||
"--host <addr> Run tests against an external host."
|
||||
"--port <port> TCP port to use against external host."
|
||||
"--baseport <port> Initial port number for spawned redis servers."
|
||||
@ -659,13 +661,16 @@ for {set j 0} {$j < [llength $argv]} {incr j} {
|
||||
}
|
||||
} elseif {$opt eq {--quiet}} {
|
||||
set ::quiet 1
|
||||
} elseif {$opt eq {--tls}} {
|
||||
} elseif {$opt eq {--tls} || $opt eq {--tls-module}} {
|
||||
package require tls 1.6
|
||||
set ::tls 1
|
||||
::tls::init \
|
||||
-cafile "$::tlsdir/ca.crt" \
|
||||
-certfile "$::tlsdir/client.crt" \
|
||||
-keyfile "$::tlsdir/client.key"
|
||||
if {$opt eq {--tls-module}} {
|
||||
set ::tls_module 1
|
||||
}
|
||||
} elseif {$opt eq {--host}} {
|
||||
set ::external 1
|
||||
set ::host $arg
|
||||
|
@ -5,18 +5,21 @@ test {modules config rewrite} {
|
||||
start_server {tags {"modules"}} {
|
||||
r module load $testmodule
|
||||
|
||||
assert_equal [lindex [lindex [r module list] 0] 1] infotest
|
||||
set modules [lmap x [r module list] {dict get $x name}]
|
||||
assert_not_equal [lsearch $modules infotest] -1
|
||||
|
||||
r config rewrite
|
||||
restart_server 0 true false
|
||||
|
||||
assert_equal [lindex [lindex [r module list] 0] 1] infotest
|
||||
set modules [lmap x [r module list] {dict get $x name}]
|
||||
assert_not_equal [lsearch $modules infotest] -1
|
||||
|
||||
assert_equal {OK} [r module unload infotest]
|
||||
|
||||
r config rewrite
|
||||
restart_server 0 true false
|
||||
|
||||
assert_equal [llength [r module list]] 0
|
||||
set modules [lmap x [r module list] {dict get $x name}]
|
||||
assert_equal [lsearch $modules infotest] -1
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ start_server {tags {"modules"}} {
|
||||
r module load $testmodule
|
||||
test {Config get commands work} {
|
||||
# Make sure config get module config works
|
||||
assert_equal [lindex [lindex [r module list] 0] 1] moduleconfigs
|
||||
assert_not_equal [lsearch [lmap x [r module list] {dict get $x name}] moduleconfigs] -1
|
||||
assert_equal [r config get moduleconfigs.mutable_bool] "moduleconfigs.mutable_bool yes"
|
||||
assert_equal [r config get moduleconfigs.immutable_bool] "moduleconfigs.immutable_bool no"
|
||||
assert_equal [r config get moduleconfigs.memory_numeric] "moduleconfigs.memory_numeric 1024"
|
||||
@ -94,7 +94,7 @@ start_server {tags {"modules"}} {
|
||||
|
||||
test {test loadex functionality} {
|
||||
r module loadex $testmodule CONFIG moduleconfigs.mutable_bool no CONFIG moduleconfigs.immutable_bool yes CONFIG moduleconfigs.memory_numeric 2mb CONFIG moduleconfigs.string tclortickle
|
||||
assert_equal [lindex [lindex [r module list] 0] 1] moduleconfigs
|
||||
assert_not_equal [lsearch [lmap x [r module list] {dict get $x name}] moduleconfigs] -1
|
||||
assert_equal [r config get moduleconfigs.mutable_bool] "moduleconfigs.mutable_bool no"
|
||||
assert_equal [r config get moduleconfigs.immutable_bool] "moduleconfigs.immutable_bool yes"
|
||||
assert_equal [r config get moduleconfigs.memory_numeric] "moduleconfigs.memory_numeric 2097152"
|
||||
@ -157,7 +157,7 @@ start_server {tags {"modules"}} {
|
||||
test {test config rewrite with dynamic load} {
|
||||
#translates to: super \0secret password
|
||||
r module loadex $testmodule CONFIG moduleconfigs.string \x73\x75\x70\x65\x72\x20\x00\x73\x65\x63\x72\x65\x74\x20\x70\x61\x73\x73\x77\x6f\x72\x64 ARGS
|
||||
assert_equal [lindex [lindex [r module list] 0] 1] moduleconfigs
|
||||
assert_not_equal [lsearch [lmap x [r module list] {dict get $x name}] moduleconfigs] -1
|
||||
assert_equal [r config get moduleconfigs.string] "moduleconfigs.string {super \0secret password}"
|
||||
r config set moduleconfigs.mutable_bool yes
|
||||
r config set moduleconfigs.memory_numeric 750
|
||||
@ -207,7 +207,7 @@ start_server {tags {"modules"}} {
|
||||
test {test 1.module load 2.config rewrite 3.module unload 4.config rewrite works} {
|
||||
# Configs need to be removed from the old config file in this case.
|
||||
r module loadex $testmodule CONFIG moduleconfigs.memory_numeric 500 ARGS
|
||||
assert_equal [lindex [lindex [r module list] 0] 1] moduleconfigs
|
||||
assert_not_equal [lsearch [lmap x [r module list] {dict get $x name}] moduleconfigs] -1
|
||||
r config rewrite
|
||||
r module unload moduleconfigs
|
||||
r config rewrite
|
||||
@ -217,34 +217,18 @@ start_server {tags {"modules"}} {
|
||||
}
|
||||
test {startup moduleconfigs} {
|
||||
# No loadmodule directive
|
||||
set nomodload [start_server [list overrides [list moduleconfigs.string "hello"]]]
|
||||
wait_for_condition 100 50 {
|
||||
! [is_alive $nomodload]
|
||||
} else {
|
||||
fail "startup should've failed with no load and module configs supplied"
|
||||
}
|
||||
set stdout [dict get $nomodload stdout]
|
||||
assert_equal [count_message_lines $stdout "Module Configuration detected without loadmodule directive or no ApplyConfig call: aborting"] 1
|
||||
catch {exec src/redis-server --moduleconfigs.string "hello"} err
|
||||
assert_match {*Module Configuration detected without loadmodule directive or no ApplyConfig call: aborting*} $err
|
||||
|
||||
# Bad config value
|
||||
set badconfig [start_server [list overrides [list loadmodule "$testmodule" moduleconfigs.string "rejectisfreed"]]]
|
||||
wait_for_condition 100 50 {
|
||||
! [is_alive $badconfig]
|
||||
} else {
|
||||
fail "startup with bad moduleconfigs should've failed"
|
||||
}
|
||||
set stdout [dict get $badconfig stdout]
|
||||
assert_equal [count_message_lines $stdout "Issue during loading of configuration moduleconfigs.string : Cannot set string to 'rejectisfreed'"] 1
|
||||
catch {exec src/redis-server --loadmodule "$testmodule" --moduleconfigs.string "rejectisfreed"} err
|
||||
assert_match {*Issue during loading of configuration moduleconfigs.string : Cannot set string to 'rejectisfreed'*} $err
|
||||
|
||||
set noload [start_server [list overrides [list loadmodule "$testmodule noload" moduleconfigs.string "hello"]]]
|
||||
wait_for_condition 100 50 {
|
||||
! [is_alive $noload]
|
||||
} else {
|
||||
fail "startup with moduleconfigs and no loadconfigs call should've failed"
|
||||
}
|
||||
set stdout [dict get $noload stdout]
|
||||
assert_equal [count_message_lines $stdout "Module Configurations were not set, likely a missing LoadConfigs call. Unloading the module."] 1
|
||||
# missing LoadConfigs call
|
||||
catch {exec src/redis-server --loadmodule "$testmodule" noload --moduleconfigs.string "hello"} err
|
||||
assert_match {*Module Configurations were not set, likely a missing LoadConfigs call. Unloading the module.*} $err
|
||||
|
||||
# successful
|
||||
start_server [list overrides [list loadmodule "$testmodule" moduleconfigs.string "bootedup" moduleconfigs.enum two moduleconfigs.flags "two four"]] {
|
||||
assert_equal [r config get moduleconfigs.string] "moduleconfigs.string bootedup"
|
||||
assert_equal [r config get moduleconfigs.mutable_bool] "moduleconfigs.mutable_bool yes"
|
||||
|
Loading…
Reference in New Issue
Block a user