Skip to content

Commit

Permalink
- Fix #850: [FR] Ability to use specific database in Redis, with new
Browse files Browse the repository at this point in the history
  redis-logical-db configuration option.
  • Loading branch information
gthess committed Oct 11, 2023
1 parent 516f90a commit e98b896
Show file tree
Hide file tree
Showing 8 changed files with 87 additions and 22 deletions.
68 changes: 47 additions & 21 deletions cachedb/redis.c
Expand Up @@ -59,11 +59,28 @@ struct redis_moddata {
const char* server_path; /* server's unix path, or "", NULL if unused */
const char* server_password; /* server's AUTH password, or "", NULL if unused */
struct timeval timeout; /* timeout for connection setup and commands */
int logical_db; /* the redis logical database to use */
};

static redisReply* redis_command(struct module_env*, struct cachedb_env*,
const char*, const uint8_t*, size_t);

static void
moddata_clean(struct redis_moddata** moddata) {
if(!moddata || !*moddata)
return;
if((*moddata)->ctxs) {
int i;
for(i = 0; i < (*moddata)->numctxs; i++) {
if((*moddata)->ctxs[i])
redisFree((*moddata)->ctxs[i]);
}
free((*moddata)->ctxs);
}
free(*moddata);
*moddata = NULL;
}

static redisContext*
redis_connect(const struct redis_moddata* moddata)
{
Expand Down Expand Up @@ -97,10 +114,21 @@ redis_connect(const struct redis_moddata* moddata)
}
freeReplyObject(rep);
}
if(moddata->logical_db > 0) {
redisReply* rep;
rep = redisCommand(ctx, "SELECT %d", moddata->logical_db);
if(!rep || rep->type == REDIS_REPLY_ERROR) {
log_err("failed to set logical database (%d)",
moddata->logical_db);
freeReplyObject(rep);
goto fail;
}
freeReplyObject(rep);
}
verbose(VERB_OPS, "Connection to Redis established");
return ctx;

fail:
fail:
if(ctx)
redisFree(ctx);
return NULL;
Expand All @@ -117,14 +145,13 @@ redis_init(struct module_env* env, struct cachedb_env* cachedb_env)
moddata = calloc(1, sizeof(struct redis_moddata));
if(!moddata) {
log_err("out of memory");
return 0;
goto fail;
}
moddata->numctxs = env->cfg->num_threads;
moddata->ctxs = calloc(env->cfg->num_threads, sizeof(redisContext*));
if(!moddata->ctxs) {
log_err("out of memory");
free(moddata);
return 0;
goto fail;
}
/* note: server_host is a shallow reference to configured string.
* we don't have to free it in this module. */
Expand All @@ -134,8 +161,15 @@ redis_init(struct module_env* env, struct cachedb_env* cachedb_env)
moddata->server_password = env->cfg->redis_server_password;
moddata->timeout.tv_sec = env->cfg->redis_timeout / 1000;
moddata->timeout.tv_usec = (env->cfg->redis_timeout % 1000) * 1000;
for(i = 0; i < moddata->numctxs; i++)
moddata->ctxs[i] = redis_connect(moddata);
moddata->logical_db = env->cfg->redis_logical_db;
for(i = 0; i < moddata->numctxs; i++) {
redisContext* ctx = redis_connect(moddata);
if(!ctx) {
log_err("redis_init: failed to init redis");
goto fail;
}
moddata->ctxs[i] = ctx;
}
cachedb_env->backend_data = moddata;
if(env->cfg->redis_expire_records) {
redisReply* rep = NULL;
Expand All @@ -148,7 +182,7 @@ redis_init(struct module_env* env, struct cachedb_env* cachedb_env)
log_err("redis_init: failed to init redis, the "
"redis-expire-records option requires the SETEX command "
"(redis >= 2.0.0)");
return 0;
goto fail;
}
redis_reply_type = rep->type;
freeReplyObject(rep);
Expand All @@ -160,11 +194,14 @@ redis_init(struct module_env* env, struct cachedb_env* cachedb_env)
log_err("redis_init: failed to init redis, the "
"redis-expire-records option requires the SETEX command "
"(redis >= 2.0.0)");
return 0;
goto fail;
}
}

return 1;

fail:
moddata_clean(&moddata);
return 0;
}

static void
Expand All @@ -175,18 +212,7 @@ redis_deinit(struct module_env* env, struct cachedb_env* cachedb_env)
(void)env;

verbose(VERB_OPS, "Redis deinitialization");

if(!moddata)
return;
if(moddata->ctxs) {
int i;
for(i = 0; i < moddata->numctxs; i++) {
if(moddata->ctxs[i])
redisFree(moddata->ctxs[i]);
}
free(moddata->ctxs);
}
free(moddata);
moddata_clean(&moddata);
}

/*
Expand Down
4 changes: 4 additions & 0 deletions doc/Changelog
@@ -1,3 +1,7 @@
11 October 2023: George
- Fix #850: [FR] Ability to use specific database in Redis, with new
redis-logical-db configuration option.

10 October 2023: George
- Fix infinite loop when reading multiple lines of input on a broken
remote control socket. Addesses #947 and #948.
Expand Down
2 changes: 2 additions & 0 deletions doc/example.conf.in
Expand Up @@ -1236,6 +1236,8 @@ remote-control:
# redis-timeout: 100
# # set timeout on redis records based on DNS response TTL
# redis-expire-records: no
# # redis logical database to use, 0 is the default database.
# redis-logical-db: 0

# IPSet
# Add specify domain into set via ipset.
Expand Down
11 changes: 11 additions & 0 deletions doc/unbound.conf.5.in
Expand Up @@ -2707,6 +2707,17 @@ Unbound is configured with \fBserve-expired\fR and \fBserve-expired-ttl\fR is 0,
this option is internally reverted to "no". Redis SETEX support is required
for this option (Redis >= 2.0.0).
This option defaults to no.
.TP
.B redis-logical-db: \fI<logical database index>
The logical database in Redis to use.
These are databases in the same Redis instance sharing the same configuration
and persisted in the same RDB/AOF file.
If unsure about using this option, Redis documentation
(https://redis.io/commands/select/) suggests not to use a single Redis instance
for multiple unrelated applications.
The default database in Redis is 0 while other logical databases need to be
explicitly SELECT'ed upon connecting.
This option defaults to 0.
.SS DNSTAP Logging Options
DNSTAP support, when compiled in by using \fB\-\-enable\-dnstap\fR, is enabled
in the \fBdnstap:\fR section.
Expand Down
2 changes: 2 additions & 0 deletions util/config_file.c
Expand Up @@ -388,6 +388,7 @@ config_create(void)
cfg->redis_timeout = 100;
cfg->redis_server_port = 6379;
cfg->redis_expire_records = 0;
cfg->redis_logical_db = 0;
#endif /* USE_REDIS */
#endif /* USE_CACHEDB */
#ifdef USE_IPSET
Expand Down Expand Up @@ -1313,6 +1314,7 @@ config_get_option(struct config_file* cfg, const char* opt,
else O_STR(opt, "redis-server-password", redis_server_password)
else O_DEC(opt, "redis-timeout", redis_timeout)
else O_YNO(opt, "redis-expire-records", redis_expire_records)
else O_DEC(opt, "redis-logical-db", redis_logical_db)
#endif /* USE_REDIS */
#endif /* USE_CACHEDB */
#ifdef USE_IPSET
Expand Down
2 changes: 2 additions & 0 deletions util/config_file.h
Expand Up @@ -712,6 +712,8 @@ struct config_file {
int redis_timeout;
/** set timeout on redis records based on DNS response ttl */
int redis_expire_records;
/** set the redis logical database upon connection */
int redis_logical_db;
#endif
#endif
/** Downstream DNS Cookies */
Expand Down
1 change: 1 addition & 0 deletions util/configlexer.lex
Expand Up @@ -563,6 +563,7 @@ redis-server-path{COLON} { YDVAR(1, VAR_CACHEDB_REDISPATH) }
redis-server-password{COLON} { YDVAR(1, VAR_CACHEDB_REDISPASSWORD) }
redis-timeout{COLON} { YDVAR(1, VAR_CACHEDB_REDISTIMEOUT) }
redis-expire-records{COLON} { YDVAR(1, VAR_CACHEDB_REDISEXPIRERECORDS) }
redis-logical-db{COLON} { YDVAR(1, VAR_CACHEDB_REDISLOGICALDB) }
ipset{COLON} { YDVAR(0, VAR_IPSET) }
name-v4{COLON} { YDVAR(1, VAR_IPSET_NAME_V4) }
name-v6{COLON} { YDVAR(1, VAR_IPSET_NAME_V6) }
Expand Down
19 changes: 18 additions & 1 deletion util/configparser.y
Expand Up @@ -179,6 +179,7 @@ extern struct config_parser_state* cfg_parser;
%token VAR_CACHEDB VAR_CACHEDB_BACKEND VAR_CACHEDB_SECRETSEED
%token VAR_CACHEDB_REDISHOST VAR_CACHEDB_REDISPORT VAR_CACHEDB_REDISTIMEOUT
%token VAR_CACHEDB_REDISEXPIRERECORDS VAR_CACHEDB_REDISPATH VAR_CACHEDB_REDISPASSWORD
%token VAR_CACHEDB_REDISLOGICALDB
%token VAR_UDP_UPSTREAM_WITHOUT_DOWNSTREAM VAR_FOR_UPSTREAM
%token VAR_AUTH_ZONE VAR_ZONEFILE VAR_MASTER VAR_URL VAR_FOR_DOWNSTREAM
%token VAR_FALLBACK_ENABLED VAR_TLS_ADDITIONAL_PORT VAR_LOW_RTT VAR_LOW_RTT_PERMIL
Expand Down Expand Up @@ -3701,7 +3702,8 @@ contents_cachedb: contents_cachedb content_cachedb
| ;
content_cachedb: cachedb_backend_name | cachedb_secret_seed |
redis_server_host | redis_server_port | redis_timeout |
redis_expire_records | redis_server_path | redis_server_password
redis_expire_records | redis_server_path | redis_server_password |
redis_logical_db
;
cachedb_backend_name: VAR_CACHEDB_BACKEND STRING_ARG
{
Expand Down Expand Up @@ -3804,6 +3806,21 @@ redis_expire_records: VAR_CACHEDB_REDISEXPIRERECORDS STRING_ARG
free($2);
}
;
redis_logical_db: VAR_CACHEDB_REDISLOGICALDB STRING_ARG
{
#if defined(USE_CACHEDB) && defined(USE_REDIS)
int db;
OUTYY(("P(redis_logical_db:%s)\n", $2));
db = atoi($2);
if((db == 0 && strcmp($2, "0") != 0) || db < 0)
yyerror("valid redis logical database index expected");
else cfg_parser->cfg->redis_logical_db = db;
#else
OUTYY(("P(Compiled without cachedb or redis, ignoring)\n"));
#endif
free($2);
}
;
server_tcp_connection_limit: VAR_TCP_CONNECTION_LIMIT STRING_ARG STRING_ARG
{
OUTYY(("P(server_tcp_connection_limit:%s %s)\n", $2, $3));
Expand Down

0 comments on commit e98b896

Please sign in to comment.