Introduce connection layer framework

Use connTypeRegister() to register a connection type into redis, and
query connection by connectionByType() via type.

With this change, we can hide TLS specified methods into connection
type:
- void tlsInit(void);
- void tlsCleanup(void);
- int tlsConfigure(redisTLSContextConfig *ctx_config);
- int isTlsConfigured(void);

Merge isTlsConfigured & tlsConfigure, use an argument *reconfigure*
to distinguish:
   tlsConfigure(&server.tls_ctx_config)
-> onnTypeConfigure(CONN_TYPE_TLS, &server.tls_ctx_config, 1)

   isTlsConfigured() && tlsConfigure(&server.tls_ctx_config)
-> connTypeConfigure(CONN_TYPE_TLS, &server.tls_ctx_config, 0)

Finally, we can remove USE_OPENSSL from config.c. If redis is built
without TLS, and still run redis with TLS, then redis reports:
 # Missing implement of connection type 1
 # Failed to configure TLS. Check logs for more info.

The log can be optimised, let's leave it in the future. Maybe we can
use connection type as a string.

Although uninitialized fields of a static struct are zero, we still
set them as NULL explicitly in socket.c, let them clear to read & maintain:
    .init = NULL,
    .cleanup = NULL,
    .configure = NULL,

Signed-off-by: zhenwei pi <pizhenwei@bytedance.com>
This commit is contained in:
zhenwei pi 2022-08-22 15:09:59 +08:00
parent bff7ecc786
commit 8234a5123d
9 changed files with 170 additions and 37 deletions

View File

@ -316,7 +316,7 @@ endif
REDIS_SERVER_NAME=redis-server$(PROG_SUFFIX)
REDIS_SENTINEL_NAME=redis-sentinel$(PROG_SUFFIX)
REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o eval.o bio.o rio.o rand.o memtest.o syscheck.o crcspeed.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o redis-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o lolwut.o lolwut5.o lolwut6.o acl.o tracking.o socket.o tls.o sha256.o timeout.o setcpuaffinity.o monotonic.o mt19937-64.o resp_parser.o call_reply.o script_lua.o script.o functions.o function_lua.o commands.o strl.o
REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o eval.o bio.o rio.o rand.o memtest.o syscheck.o crcspeed.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o redis-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o lolwut.o lolwut5.o lolwut6.o acl.o tracking.o socket.o tls.o sha256.o timeout.o setcpuaffinity.o monotonic.o mt19937-64.o resp_parser.o call_reply.o script_lua.o script.o functions.o function_lua.o commands.o strl.o connection.o
REDIS_CLI_NAME=redis-cli$(PROG_SUFFIX)
REDIS_CLI_OBJ=anet.o adlist.o dict.o redis-cli.o zmalloc.o release.o ae.o redisassert.o crcspeed.o crc64.o siphash.o crc16.o monotonic.o cli_common.o mt19937-64.o strl.o
REDIS_BENCHMARK_NAME=redis-benchmark$(PROG_SUFFIX)

View File

@ -30,6 +30,7 @@
#include "server.h"
#include "cluster.h"
#include "connection.h"
#include <fcntl.h>
#include <sys/stat.h>
@ -2571,13 +2572,12 @@ int updateClusterHostname(const char **err) {
return 1;
}
#ifdef USE_OPENSSL
static int applyTlsCfg(const char **err) {
UNUSED(err);
/* If TLS is enabled, try to configure OpenSSL. */
if ((server.tls_port || server.tls_replication || server.tls_cluster)
&& tlsConfigure(&server.tls_ctx_config) == C_ERR) {
&& connTypeConfigure(CONN_TYPE_TLS, &server.tls_ctx_config, 1) == C_ERR) {
*err = "Unable to update TLS configuration. Check server logs.";
return 0;
}
@ -2586,7 +2586,7 @@ static int applyTlsCfg(const char **err) {
static int applyTLSPort(const char **err) {
/* Configure TLS in case it wasn't enabled */
if (!isTlsConfigured() && tlsConfigure(&server.tls_ctx_config) == C_ERR) {
if (connTypeConfigure(CONN_TYPE_TLS, &server.tls_ctx_config, 0) == C_ERR) {
*err = "Unable to update TLS configuration. Check server logs.";
return 0;
}
@ -2599,8 +2599,6 @@ static int applyTLSPort(const char **err) {
return 1;
}
#endif /* USE_OPENSSL */
static int setConfigDirOption(standardConfig *config, sds *argv, int argc, const char **err) {
UNUSED(config);
if (argc != 1) {
@ -3109,7 +3107,6 @@ standardConfig static_configs[] = {
createOffTConfig("auto-aof-rewrite-min-size", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, server.aof_rewrite_min_size, 64*1024*1024, MEMORY_CONFIG, NULL, NULL),
createOffTConfig("loading-process-events-interval-bytes", NULL, MODIFIABLE_CONFIG | HIDDEN_CONFIG, 1024, INT_MAX, server.loading_process_events_interval_bytes, 1024*1024*2, INTEGER_CONFIG, NULL, NULL),
#ifdef USE_OPENSSL
createIntConfig("tls-port", NULL, MODIFIABLE_CONFIG, 0, 65535, server.tls_port, 0, INTEGER_CONFIG, NULL, applyTLSPort), /* TCP port. */
createIntConfig("tls-session-cache-size", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.tls_ctx_config.session_cache_size, 20*1024, INTEGER_CONFIG, NULL, applyTlsCfg),
createIntConfig("tls-session-cache-timeout", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.tls_ctx_config.session_cache_timeout, 300, INTEGER_CONFIG, NULL, applyTlsCfg),
@ -3130,7 +3127,6 @@ standardConfig static_configs[] = {
createStringConfig("tls-protocols", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.protocols, NULL, NULL, applyTlsCfg),
createStringConfig("tls-ciphers", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ciphers, NULL, NULL, applyTlsCfg),
createStringConfig("tls-ciphersuites", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ciphersuites, NULL, NULL, applyTlsCfg),
#endif
/* Special configs */
createSpecialConfig("dir", NULL, MODIFIABLE_CONFIG | PROTECTED_CONFIG | DENY_LOADING_CONFIG, setConfigDirOption, getConfigDirOption, rewriteConfigDirOption, NULL),

102
src/connection.c Normal file
View File

@ -0,0 +1,102 @@
/* ==========================================================================
* connection.c - connection layer framework
* --------------------------------------------------------------------------
* Copyright (C) 2022 zhenwei pi
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit
* persons to whom the Software is furnished to do so, subject to the
* following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE.
* ==========================================================================
*/
#include "server.h"
#include "connection.h"
static ConnectionType *connTypes[CONN_TYPE_MAX];
int connTypeRegister(ConnectionType *ct) {
int type = ct->get_type(NULL);
/* unknown connection type as a fatal error */
if (type >= CONN_TYPE_MAX) {
serverPanic("Unsupported connection type %d", type);
}
if (connTypes[type] == ct) {
serverLog(LL_WARNING, "Connection type %d already registered", type);
return C_OK;
}
serverLog(LL_VERBOSE, "Connection type %d registered", type);
connTypes[type] = ct;
if (ct->init) {
ct->init();
}
return C_OK;
}
int connTypeInitialize() {
/* currently socket connection type is necessary */
serverAssert(RedisRegisterConnectionTypeSocket() == C_OK);
/* may fail if without BUILD_TLS=yes */
RedisRegisterConnectionTypeTLS();
return C_OK;
}
ConnectionType *connectionByType(int type) {
ConnectionType *ct;
serverAssert(type < CONN_TYPE_MAX);
ct = connTypes[type];
if (!ct) {
serverLog(LL_WARNING, "Missing implement of connection type %d", type);
}
return ct;
}
void connTypeCleanup(int type) {
ConnectionType *ct = connectionByType(type);
if (ct && ct->cleanup) {
ct->cleanup();
}
}
void connTypeCleanupAll() {
int type;
for (type = 0; type < CONN_TYPE_MAX; type++) {
connTypeCleanup(type);
}
}
int connTypeConfigure(int type, void *priv, int reconfigure) {
ConnectionType *ct = connectionByType(type);
if (ct && ct->configure) {
return ct->configure(priv, reconfigure);
}
return C_ERR;
}

View File

@ -54,8 +54,9 @@ typedef enum {
#define CONN_FLAG_CLOSE_SCHEDULED (1<<0) /* Closed scheduled by a handler */
#define CONN_FLAG_WRITE_BARRIER (1<<1) /* Write barrier requested */
#define CONN_TYPE_SOCKET 1
#define CONN_TYPE_TLS 2
#define CONN_TYPE_SOCKET 0
#define CONN_TYPE_TLS 1
#define CONN_TYPE_MAX 2
typedef void (*ConnectionCallbackFunc)(struct connection *conn);
@ -63,6 +64,11 @@ typedef struct ConnectionType {
/* connection type */
int (*get_type)(struct connection *conn);
/* connection type initialize & finalize & configure */
void (*init)(void); /* auto-call during register */
void (*cleanup)(void);
int (*configure)(void *priv, int reconfigure);
/* ae & accept & listen & error & address handler */
void (*ae_handler)(struct aeEventLoop *el, int fd, void *clientData, int mask);
int (*addr)(connection *conn, char *ip, size_t ip_len, int *port, int remote);
@ -329,4 +335,24 @@ sds connTLSGetPeerCert(connection *conn);
int tlsHasPendingData();
int tlsProcessPendingData();
/* Initialize the redis connection framework */
int connTypeInitialize();
/* Register a connection type into redis connection framework */
int connTypeRegister(ConnectionType *ct);
/* Configure a connection type. A typical case is to configure TLS.
* @priv is connection type specified,
* @reconfigure is boolean type to specify if overwrite the original config */
int connTypeConfigure(int type, void *priv, int reconfigure);
/* Cleanup a connection type. A typical case is to cleanup TLS. */
void connTypeCleanup(int type);
/* Walk all the connection type, and cleanup them all if possible */
void connTypeCleanupAll();
int RedisRegisterConnectionTypeSocket();
int RedisRegisterConnectionTypeTLS();
#endif /* __REDIS_CONNECTION_H */

View File

@ -848,7 +848,7 @@ void sentinelRunPendingScripts(void) {
sj->pid = 0;
} else if (pid == 0) {
/* Child */
tlsCleanup();
connTypeCleanupAll();
execve(sj->argv[0],sj->argv,environ);
/* If we are here an error occurred. */
_exit(2); /* Don't retry execution. */

View File

@ -2447,7 +2447,7 @@ void initServer(void) {
}
if ((server.tls_port || server.tls_replication || server.tls_cluster)
&& tlsConfigure(&server.tls_ctx_config) == C_ERR) {
&& connTypeConfigure(CONN_TYPE_TLS, &server.tls_ctx_config, 1) == C_ERR) {
serverLog(LL_WARNING, "Failed to configure TLS. Check logs for more info.");
exit(1);
}
@ -6944,7 +6944,7 @@ int main(int argc, char **argv) {
ACLInit(); /* The ACL subsystem must be initialized ASAP because the
basic networking code and client creation depends on it. */
moduleInitModulesSystem();
tlsInit();
connTypeInitialize();
/* Store the executable path and arguments in a safe place in order
* to be able to restart the server later. */

View File

@ -3579,12 +3579,6 @@ void swapMainDbWithTempDb(redisDb *tempDb);
_serverLog(level, __VA_ARGS__);\
} while(0)
/* TLS stuff */
void tlsInit(void);
void tlsCleanup(void);
int tlsConfigure(redisTLSContextConfig *ctx_config);
int isTlsConfigured(void);
#define redisDebug(fmt, ...) \
printf("DEBUG %s:%d > " fmt "\n", __FILE__, __LINE__, __VA_ARGS__)
#define redisDebugMark() \

View File

@ -352,6 +352,11 @@ ConnectionType CT_Socket = {
/* connection type */
.get_type = connSocketGetType,
/* connection type initialize & finalize & configure */
.init = NULL,
.cleanup = NULL,
.configure = NULL,
/* ae & accept & listen & error & address handler */
.ae_handler = connSocketEventHandler,
.addr = connSocketAddr,
@ -409,3 +414,7 @@ int connRecvTimeout(connection *conn, long long ms) {
return anetRecvTimeout(NULL, conn->fd, ms);
}
int RedisRegisterConnectionTypeSocket()
{
return connTypeRegister(&CT_Socket);
}

View File

@ -141,7 +141,7 @@ static void initCryptoLocks(void) {
}
#endif /* USE_CRYPTO_LOCKS */
void tlsInit(void) {
static void tlsInit(void) {
/* Enable configuring OpenSSL using the standard openssl.cnf
* OPENSSL_config()/OPENSSL_init_crypto() should be the first
* call to the OpenSSL* library.
@ -169,7 +169,7 @@ void tlsInit(void) {
pending_list = listCreate();
}
void tlsCleanup(void) {
static void tlsCleanup(void) {
if (redis_tls_ctx) {
SSL_CTX_free(redis_tls_ctx);
redis_tls_ctx = NULL;
@ -281,12 +281,20 @@ error:
/* Attempt to configure/reconfigure TLS. This operation is atomic and will
* leave the SSL_CTX unchanged if fails.
* @priv: config of redisTLSContextConfig.
* @reconfigure: if true, ignore the previous configure; if false, only
* configure from @ctx_config if redis_tls_ctx is NULL.
*/
int tlsConfigure(redisTLSContextConfig *ctx_config) {
static int tlsConfigure(void *priv, int reconfigure) {
redisTLSContextConfig *ctx_config = (redisTLSContextConfig *)priv;
char errbuf[256];
SSL_CTX *ctx = NULL;
SSL_CTX *client_ctx = NULL;
if (!reconfigure && redis_tls_ctx) {
return C_OK;
}
if (!ctx_config->cert_file) {
serverLog(LL_WARNING, "No tls-cert-file configured!");
goto error;
@ -406,12 +414,6 @@ error:
return C_ERR;
}
/* Return 1 if TLS was already configured, 0 otherwise.
*/
int isTlsConfigured(void) {
return redis_tls_ctx != NULL;
}
#ifdef TLS_DEBUGGING
#define TLSCONN_DEBUG(fmt, ...) \
serverLog(LL_DEBUG, "TLSCONN: " fmt, __VA_ARGS__)
@ -1066,6 +1068,11 @@ ConnectionType CT_TLS = {
/* connection type */
.get_type = connTLSGetType,
/* connection type initialize & finalize & configure */
.init = tlsInit,
.cleanup = tlsCleanup,
.configure = tlsConfigure,
/* ae & accept & listen & error & address handler */
.ae_handler = tlsEventHandler,
.addr = connTLSAddr,
@ -1090,17 +1097,16 @@ ConnectionType CT_TLS = {
.sync_readline = connTLSSyncReadLine,
};
int RedisRegisterConnectionTypeTLS()
{
return connTypeRegister(&CT_TLS);
}
#else /* USE_OPENSSL */
void tlsInit(void) {
}
void tlsCleanup(void) {
}
int tlsConfigure(redisTLSContextConfig *ctx_config) {
UNUSED(ctx_config);
return C_OK;
int RedisRegisterConnectionTypeTLS()
{
return C_ERR;
}
connection *connCreateTLS(void) {