]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- Fix #850: [FR] Ability to use specific database in Redis, with new
authorGeorge Thessalonikefs <yorgos@nlnetlabs.nl>
Wed, 11 Oct 2023 09:44:55 +0000 (11:44 +0200)
committerGeorge Thessalonikefs <yorgos@nlnetlabs.nl>
Wed, 11 Oct 2023 09:44:55 +0000 (11:44 +0200)
  redis-logical-db configuration option.

cachedb/redis.c
doc/Changelog
doc/example.conf.in
doc/unbound.conf.5.in
util/config_file.c
util/config_file.h
util/configlexer.lex
util/configparser.y

index 93a575a4c6d2a9912c914df0c2e2ad8a386d264f..6cc975901df2a3d18e2bf502dd771eb274fdd621 100644 (file)
@@ -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)
 {
@@ -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;
@@ -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. */
@@ -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;
@@ -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);
@@ -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
@@ -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);
 }
 
 /*
index a943cce5758a8ac4c21ae0389eca126eec24ef92..40da8e55c101ac63e37f76aab4ecd8fe21177a25 100644 (file)
@@ -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.
index 0980212e123dd32649b49c001e25bb9ef435b05c..ca8f929e4f0a53a35ef2716261b944604eb1b4a9 100644 (file)
@@ -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.
index 84b903f49ddb479c9e4996a84a5c37c7784b7070..2992e0a4afda8bd444394a54c03dc256dad23d56 100644 (file)
@@ -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.
index 45409634232634626c08365deafc2d9e63fdaaaa..80003e63204bddcdcb1e3c1b845b28a9d322078a 100644 (file)
@@ -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
@@ -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
index 452f3c6a78fb9361645b1c47516204579ec7ba29..588012f823ca3ec71f5434d13c9c26a0fd7f94d1 100644 (file)
@@ -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 */
index 3fcdfa62e03351bb3706a38b57d47e03a4c74da1..142810a2f1dff1051817ef1e5ccf94ea15aa2807 100644 (file)
@@ -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) }
index d8f25a67ebbfded22d1a5f307a859f041ea29332..6237d0c081561842e7030842a7494a0c4a3258ff 100644 (file)
@@ -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
@@ -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
        {
@@ -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));