From: W.C.A. Wijngaards Date: Wed, 10 Apr 2024 09:21:28 +0000 (+0200) Subject: - Implement cachedb-check-when-serve-expired: yes option, default X-Git-Tag: release-1.20.0rc1~34 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d98c7b9ae3d09b90a05087e349cd9c320296a8ad;p=thirdparty%2Funbound.git - 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. --- diff --git a/cachedb/cachedb.c b/cachedb/cachedb.c index b912be8ed..f07535240 100644 --- a/cachedb/cachedb.c +++ b/cachedb/cachedb.c @@ -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 */ diff --git a/cachedb/cachedb.h b/cachedb/cachedb.h index 05c4368e6..9ca132de2 100644 --- a/cachedb/cachedb.h +++ b/cachedb/cachedb.h @@ -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); diff --git a/daemon/daemon.c b/daemon/daemon.c index 4870089a7..e1bcc3f23 100644 --- a/daemon/daemon.c +++ b/daemon/daemon.c @@ -99,6 +99,9 @@ #ifdef HAVE_NETDB_H #include #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. */ diff --git a/daemon/worker.c b/daemon/worker.c index 0d240db14..80f48f7f8 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -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; diff --git a/doc/Changelog b/doc/Changelog index 12ed16446..d40e246e8 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -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 diff --git a/doc/example.conf.in b/doc/example.conf.in index 8a67f6375..ea211e97d 100644 --- a/doc/example.conf.in +++ b/doc/example.conf.in @@ -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) diff --git a/doc/unbound.conf.5.in b/doc/unbound.conf.5.in index 362ab4b02..2420712e7 100644 --- a/doc/unbound.conf.5.in +++ b/doc/unbound.conf.5.in @@ -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\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 diff --git a/services/mesh.c b/services/mesh.c index 3c2b536b4..5bbff2995 100644 --- a/services/mesh.c +++ b/services/mesh.c @@ -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) diff --git a/services/mesh.h b/services/mesh.h index d926cfc9d..5bd53e065 100644 --- a/services/mesh.h +++ b/services/mesh.h @@ -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 */ diff --git a/util/config_file.c b/util/config_file.c index 07a1a3811..e4fe3ee9f 100644 --- a/util/config_file.c +++ b/util/config_file.c @@ -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) diff --git a/util/config_file.h b/util/config_file.h index faed1071e..42836796e 100644 --- a/util/config_file.h +++ b/util/config_file.h @@ -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; diff --git a/util/configlexer.lex b/util/configlexer.lex index 65abfc5e0..51bf06601 100644 --- a/util/configlexer.lex +++ b/util/configlexer.lex @@ -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) } diff --git a/util/configparser.y b/util/configparser.y index 4ca28d747..2adbf92cc 100644 --- a/util/configparser.y +++ b/util/configparser.y @@ -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) diff --git a/util/module.h b/util/module.h index 8a9da3f93..8913f6526 100644 --- a/util/module.h +++ b/util/module.h @@ -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; };