]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- Implement cachedb-check-when-serve-expired: yes option, default
authorW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Wed, 10 Apr 2024 09:21:28 +0000 (11:21 +0200)
committerW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Wed, 10 Apr 2024 09:21:28 +0000 (11:21 +0200)
  is enabled. When serve expired is enabled with cachedb, it first
  checks cachedb before serving the expired response.

14 files changed:
cachedb/cachedb.c
cachedb/cachedb.h
daemon/daemon.c
daemon/worker.c
doc/Changelog
doc/example.conf.in
doc/unbound.conf.5.in
services/mesh.c
services/mesh.h
util/config_file.c
util/config_file.h
util/configlexer.lex
util/configparser.y
util/module.h

index b912be8ed54f18ae448f8f650ff6265dfa77697b..f07535240db27b6c2de7f38d16703004e7abedcb 100644 (file)
@@ -50,6 +50,8 @@
 #include "util/data/msgreply.h"
 #include "util/data/msgencode.h"
 #include "services/cache/dns.h"
+#include "services/mesh.h"
+#include "services/modstack.h"
 #include "validator/val_neg.h"
 #include "validator/val_secalgo.h"
 #include "iterator/iter_utils.h"
@@ -791,6 +793,13 @@ cachedb_handle_query(struct module_qstate* qstate,
                return;
        }
 
+       if(qstate->serve_expired_data &&
+               qstate->env->cfg->cachedb_check_when_serve_expired) {
+               /* Reply with expired data if any to client, because cachedb
+                * also has no useful, current data */
+               mesh_respond_serve_expired(qstate->mesh_info);
+       }
+
        /* no cache fetches */
        /* pass request to next module */
        qstate->ext_state[id] = module_wait_module;
@@ -923,4 +932,17 @@ cachedb_get_funcblock(void)
 {
        return &cachedb_block;
 }
+
+int
+cachedb_is_enabled(struct module_stack* mods, struct module_env* env)
+{
+       struct cachedb_env* ie;
+       int id = modstack_find(mods, "cachedb");
+       if(id == -1)
+               return 0;
+       ie = (struct cachedb_env*)env->modinfo[id];
+       if(ie && ie->enabled)
+               return 1;
+       return 0;
+}
 #endif /* USE_CACHEDB */
index 05c4368e60b433337eb2eb6223ec351f9c0fe648..9ca132de22c7a6adac0db9d5504aea284ceda420 100644 (file)
@@ -41,6 +41,7 @@
  */
 #include "util/module.h"
 struct cachedb_backend;
+struct module_stack;
 
 /**
  * The global variable environment contents for the cachedb
@@ -110,3 +111,10 @@ size_t cachedb_get_mem(struct module_env* env, int id);
  */
 struct module_func_block* cachedb_get_funcblock(void);
 
+/**
+ * See if the cachedb is enabled.
+ * @param mods: module stack. It finds the cachedb module environment.
+ * @param env: module environment.
+ * @return true if exists and enabled.
+ */
+int cachedb_is_enabled(struct module_stack* mods, struct module_env* env);
index 4870089a7320bca84432f649a66266b59068f6bd..e1bcc3f23509bb53b1dec71725f796578a227af4 100644 (file)
@@ -99,6 +99,9 @@
 #ifdef HAVE_NETDB_H
 #include <netdb.h>
 #endif
+#ifdef USE_CACHEDB
+#include "cachedb/cachedb.h"
+#endif
 
 /** How many quit requests happened. */
 static int sig_record_quit = 0;
@@ -738,6 +741,10 @@ daemon_fork(struct daemon* daemon)
        if(!edns_strings_apply_cfg(daemon->env->edns_strings, daemon->cfg))
                fatal_exit("Could not set up EDNS strings");
 
+#ifdef USE_CACHEDB
+       daemon->env->cachedb_enabled = cachedb_is_enabled(&daemon->mods,
+               daemon->env);
+#endif
        /* response-ip-xxx options don't work as expected without the respip
         * module.  To avoid run-time operational surprise we reject such
         * configuration. */
index 0d240db142a4a0af440d607fedd267f0f8838fcd..80f48f7f813eada26373bc7698352894319410fe 100644 (file)
@@ -659,7 +659,9 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo,
        if(rep->ttl < timenow) {
                /* Check if we need to serve expired now */
                if(worker->env.cfg->serve_expired &&
-                       !worker->env.cfg->serve_expired_client_timeout) {
+                       !worker->env.cfg->serve_expired_client_timeout &&
+                       !(worker->env.cachedb_enabled &&
+                         worker->env.cfg->cachedb_check_when_serve_expired)) {
                                if(worker->env.cfg->serve_expired_ttl &&
                                        rep->serve_expired_ttl < timenow)
                                        return 0;
index 12ed16446547194cc2ecebb7b7da2ddde50e2085..d40e246e823991acdfaf82c30265e80981588e25 100644 (file)
@@ -1,3 +1,8 @@
+10 April 2024: Wouter
+       - Implement cachedb-check-when-serve-expired: yes option, default
+         is enabled. When serve expired is enabled with cachedb, it first
+         checks cachedb before serving the expired response.
+
 9 April 2024: Yorgos
        - Merge #1043 from xiaoxiaoafeifei: Add loongarch support; updates
          config.guess(2024-01-01) and config.sub(2024-01-01), verified
index 8a67f637509498d2f8323c22c73cc81989095b4b..ea211e97d0f3fb2d24a0cffc2e7d94796736376e 100644 (file)
@@ -1253,6 +1253,9 @@ remote-control:
 #     secret-seed: "default"
 #     # if the backend should be read from, but not written to.
 #     cachedb-no-store: no
+#     # if the cachedb should be checked before a serve-expired response is
+#     # given, when serve-expired is enabled.
+#     cachedb-check-when-serve-expired: yes
 #
 #     # For "redis" backend:
 #     # (to enable, use --with-libhiredis to configure before compiling)
index 362ab4b0299e57df5edd6f160356c5d56cb855f0..2420712e74606af495c9861b992b79f3568f2aae 100644 (file)
@@ -2700,6 +2700,14 @@ This option defaults to "default".
 If the backend should be read from, but not written to. This makes this
 instance not store dns messages in the backend. But if data is available it
 is retrieved. The default is no.
+.TP
+.B cachedb-check-when-serve-expired: \fI<yes or no>\fR
+If enabled, the cachedb is checked before an expired response is returned.
+When serve-expired is enabled, without serve-expired-client-timeout, it then
+does not immediately respond with an expired response from cache, but instead
+first checks the cachedb for valid contents, and if so returns it. If the
+cachedb also has no valid contents, the serve expired response is sent.
+The default is yes.
 .P
 The following
 .B cachedb
index 3c2b536b45eea05f4e96e2053a913b89b8c2615b..5bbff299589e606b443b3858547258fe86a2a7a8 100644 (file)
@@ -385,7 +385,7 @@ mesh_serve_expired_init(struct mesh_state* mstate, int timeout)
                &mesh_serve_expired_lookup;
 
        /* In case this timer already popped, start it again */
-       if(!mstate->s.serve_expired_data->timer) {
+       if(!mstate->s.serve_expired_data->timer && timeout != -1) {
                mstate->s.serve_expired_data->timer = comm_timer_create(
                        mstate->s.env->worker_base, mesh_serve_expired_callback, mstate);
                if(!mstate->s.serve_expired_data->timer)
@@ -511,6 +511,15 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
                log_err("mesh_new_client: out of memory initializing serve expired");
                goto servfail_mem;
        }
+       if(!timeout && mesh->env->cfg->serve_expired &&
+               !mesh->env->cfg->serve_expired_client_timeout &&
+               (mesh->env->cachedb_enabled &&
+                mesh->env->cfg->cachedb_check_when_serve_expired)) {
+               if(!mesh_serve_expired_init(s, -1)) {
+                       log_err("mesh_new_client: out of memory initializing serve expired");
+                       goto servfail_mem;
+               }
+       }
        /* update statistics */
        if(was_detached) {
                log_assert(mesh->num_detached_states > 0);
@@ -616,6 +625,16 @@ mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
                        mesh_state_delete(&s->s);
                return 0;
        }
+       if(!timeout && mesh->env->cfg->serve_expired &&
+               !mesh->env->cfg->serve_expired_client_timeout &&
+               (mesh->env->cachedb_enabled &&
+                mesh->env->cfg->cachedb_check_when_serve_expired)) {
+               if(!mesh_serve_expired_init(s, -1)) {
+                       if(added)
+                               mesh_state_delete(&s->s);
+                       return 0;
+               }
+       }
        /* update statistics */
        if(was_detached) {
                log_assert(mesh->num_detached_states > 0);
@@ -2238,6 +2257,12 @@ mesh_serve_expired_callback(void* arg)
        }
 }
 
+void
+mesh_respond_serve_expired(struct mesh_state* mstate)
+{
+       mesh_serve_expired_callback(mstate);
+}
+
 int mesh_jostle_exceeded(struct mesh_area* mesh)
 {
        if(mesh->all.count < mesh->max_reply_states)
index d926cfc9dec364bd3e74c3a30c4df9dc5e6a77fe..5bd53e065e8f4f8de0eddd797f39e5ed08ee5a78 100644 (file)
@@ -690,4 +690,10 @@ mesh_serve_expired_lookup(struct module_qstate* qstate,
  */
 int mesh_jostle_exceeded(struct mesh_area* mesh);
 
+/**
+ * Give the serve expired responses.
+ * @param mstate: mesh state for query that has serve_expired_data.
+ */
+void mesh_respond_serve_expired(struct mesh_state* mstate);
+
 #endif /* SERVICES_MESH_H */
index 07a1a381199dfe6c2c2c3261454292957e6d984c..e4fe3ee9fc6bda0bcee186559f920e9d6ea35862 100644 (file)
@@ -385,6 +385,7 @@ config_create(void)
        if(!(cfg->cachedb_backend = strdup("testframe"))) goto error_exit;
        if(!(cfg->cachedb_secret = strdup("default"))) goto error_exit;
        cfg->cachedb_no_store = 0;
+       cfg->cachedb_check_when_serve_expired = 1;
 #ifdef USE_REDIS
        if(!(cfg->redis_server_host = strdup("127.0.0.1"))) goto error_exit;
        cfg->redis_server_path = NULL;
@@ -830,6 +831,7 @@ int config_set_option(struct config_file* cfg, const char* opt,
 #endif
 #ifdef USE_CACHEDB
        else S_YNO("cachedb-no-store:", cachedb_no_store)
+       else S_YNO("cachedb-check-when-serve-expired:", cachedb_check_when_serve_expired)
 #endif /* USE_CACHEDB */
        else if(strcmp(opt, "define-tag:") ==0) {
                return config_add_tag(cfg, val);
@@ -1322,6 +1324,7 @@ config_get_option(struct config_file* cfg, const char* opt,
        else O_STR(opt, "backend", cachedb_backend)
        else O_STR(opt, "secret-seed", cachedb_secret)
        else O_YNO(opt, "cachedb-no-store", cachedb_no_store)
+       else O_YNO(opt, "cachedb-check-when-serve-expired", cachedb_check_when_serve_expired)
 #ifdef USE_REDIS
        else O_STR(opt, "redis-server-host", redis_server_host)
        else O_DEC(opt, "redis-server-port", redis_server_port)
index faed1071e55f30ab8944dc465083955353e381c2..42836796e4bc6e45b387146f2607285d5db8418e 100644 (file)
@@ -707,6 +707,8 @@ struct config_file {
        char* cachedb_secret;
        /** cachedb that does not store, but only reads from database, if on */
        int cachedb_no_store;
+       /** cachedb check before serving serve-expired response */
+       int cachedb_check_when_serve_expired;
 #ifdef USE_REDIS
        /** redis server's IP address or host name */
        char* redis_server_host;
index 65abfc5e0b9a495455d0163e39f532ccaa0b2145..51bf0660112fb49f5241b580a025d90d38e12762 100644 (file)
@@ -561,6 +561,7 @@ cachedb{COLON}                      { YDVAR(0, VAR_CACHEDB) }
 backend{COLON}                 { YDVAR(1, VAR_CACHEDB_BACKEND) }
 secret-seed{COLON}             { YDVAR(1, VAR_CACHEDB_SECRETSEED) }
 cachedb-no-store{COLON}                { YDVAR(1, VAR_CACHEDB_NO_STORE) }
+cachedb-check-when-serve-expired{COLON}                { YDVAR(1, VAR_CACHEDB_CHECK_WHEN_SERVE_EXPIRED) }
 redis-server-host{COLON}       { YDVAR(1, VAR_CACHEDB_REDISHOST) }
 redis-server-port{COLON}       { YDVAR(1, VAR_CACHEDB_REDISPORT) }
 redis-server-path{COLON}       { YDVAR(1, VAR_CACHEDB_REDISPATH) }
index 4ca28d747eb06478f2406ba57612937cd3480156..2adbf92cccea4b4f90b274ff2ad0701423d33ec3 100644 (file)
@@ -201,7 +201,7 @@ extern struct config_parser_state* cfg_parser;
 %token VAR_INTERFACE_TAG_ACTION VAR_INTERFACE_TAG_DATA
 %token VAR_PROXY_PROTOCOL_PORT VAR_STATISTICS_INHIBIT_ZERO
 %token VAR_HARDEN_UNKNOWN_ADDITIONAL VAR_DISABLE_EDNS_DO VAR_CACHEDB_NO_STORE
-%token VAR_LOG_DESTADDR
+%token VAR_LOG_DESTADDR VAR_CACHEDB_CHECK_WHEN_SERVE_EXPIRED
 
 %%
 toplevelvars: /* empty */ | toplevelvars toplevelvar ;
@@ -3734,7 +3734,7 @@ 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 |
-       cachedb_no_store | redis_logical_db
+       cachedb_no_store | redis_logical_db | cachedb_check_when_serve_expired
        ;
 cachedb_backend_name: VAR_CACHEDB_BACKEND STRING_ARG
        {
@@ -3773,6 +3773,19 @@ cachedb_no_store: VAR_CACHEDB_NO_STORE STRING_ARG
                free($2);
        }
        ;
+cachedb_check_when_serve_expired: VAR_CACHEDB_CHECK_WHEN_SERVE_EXPIRED STRING_ARG
+       {
+       #ifdef USE_CACHEDB
+               OUTYY(("P(cachedb_check_when_serve_expired:%s)\n", $2));
+               if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+                       yyerror("expected yes or no.");
+               else cfg_parser->cfg->cachedb_check_when_serve_expired = (strcmp($2, "yes")==0);
+       #else
+               OUTYY(("P(Compiled without cachedb, ignoring)\n"));
+       #endif
+               free($2);
+       }
+       ;
 redis_server_host: VAR_CACHEDB_REDISHOST STRING_ARG
        {
        #if defined(USE_CACHEDB) && defined(USE_REDIS)
index 8a9da3f931a128c6cbff0eac7a4876dca4aea21e..8913f65266d2c777d659fb2abddfb607b3cc9523 100644 (file)
@@ -537,6 +537,10 @@ struct module_env {
        /** EDNS client string information */
        struct edns_strings* edns_strings;
 
+#ifdef USE_CACHEDB
+       /** the cachedb enabled value, copied and stored here. */
+       int cachedb_enabled;
+#endif
        /* Make every mesh state unique, do not aggregate mesh states. */
        int unique_mesh;
 };