From c96e4ca121f4b932701d5158d4d56f9fdef61757 Mon Sep 17 00:00:00 2001 From: Christopher Zimmermann Date: Sat, 11 Jan 2020 15:49:04 +0100 Subject: [PATCH 01/66] allow privileged initialisation of modules --- cachedb/cachedb.c | 6 ++-- cachedb/cachedb.h | 4 +-- daemon/daemon.c | 20 ++++++++++-- daemon/daemon.h | 5 +-- daemon/unbound.c | 4 +-- dns64/dns64.c | 8 ++--- dns64/dns64.h | 4 +-- edns-subnet/subnetmod.c | 10 +++--- edns-subnet/subnetmod.h | 4 +-- ipsecmod/ipsecmod.c | 9 +++--- ipsecmod/ipsecmod.h | 4 +-- ipset/ipset.c | 6 ++-- ipset/ipset.h | 4 +-- iterator/iterator.c | 8 ++--- iterator/iterator.h | 4 +-- libunbound/context.c | 2 ++ libunbound/libunbound.c | 5 ++- respip/respip.c | 8 ++--- respip/respip.h | 4 +-- services/modstack.c | 61 ++++++++++++++++++++++++++---------- services/modstack.h | 29 ++++++++++++----- smallapp/unbound-checkconf.c | 6 ++-- util/fptr_wlist.c | 60 +++++++++++++++++++++++------------ util/fptr_wlist.h | 12 +++++-- util/module.c | 5 +++ util/module.h | 31 +++++++++++++++--- validator/validator.c | 8 ++--- validator/validator.h | 4 +-- 28 files changed, 229 insertions(+), 106 deletions(-) diff --git a/cachedb/cachedb.c b/cachedb/cachedb.c index eed4d5fd9..06738972f 100644 --- a/cachedb/cachedb.c +++ b/cachedb/cachedb.c @@ -229,7 +229,7 @@ cachedb_apply_cfg(struct cachedb_env* cachedb_env, struct config_file* cfg) } int -cachedb_init(struct module_env* env, int id) +cachedb_setup(struct module_env* env, int id) { struct cachedb_env* cachedb_env = (struct cachedb_env*)calloc(1, sizeof(struct cachedb_env)); @@ -268,7 +268,7 @@ cachedb_init(struct module_env* env, int id) } void -cachedb_deinit(struct module_env* env, int id) +cachedb_desetup(struct module_env* env, int id) { struct cachedb_env* cachedb_env; if(!env || !env->modinfo[id]) @@ -846,7 +846,7 @@ cachedb_get_mem(struct module_env* env, int id) */ static struct module_func_block cachedb_block = { "cachedb", - &cachedb_init, &cachedb_deinit, &cachedb_operate, + &module_dummy_init, &module_dummy_init, &cachedb_setup, &cachedb_desetup, &cachedb_operate, &cachedb_inform_super, &cachedb_clear, &cachedb_get_mem }; diff --git a/cachedb/cachedb.h b/cachedb/cachedb.h index 05c4368e6..117916e4b 100644 --- a/cachedb/cachedb.h +++ b/cachedb/cachedb.h @@ -90,9 +90,9 @@ struct cachedb_backend { #define CACHEDB_HASHSIZE 256 /* bit hash */ /** Init the cachedb module */ -int cachedb_init(struct module_env* env, int id); +int cachedb_setup(struct module_env* env, int id); /** Deinit the cachedb module */ -void cachedb_deinit(struct module_env* env, int id); +void cachedb_desetup(struct module_env* env, int id); /** Operate on an event on a query (in qstate). */ void cachedb_operate(struct module_qstate* qstate, enum module_ev event, int id, struct outbound_entry* outbound); diff --git a/daemon/daemon.c b/daemon/daemon.c index 5d4279259..05f16cc60 100644 --- a/daemon/daemon.c +++ b/daemon/daemon.c @@ -251,7 +251,7 @@ daemon_init(void) tzset(); #endif daemon->need_to_exit = 0; - modstack_init(&daemon->mods); + memset(&daemon->mods, 0, sizeof(daemon->mods)); if(!(daemon->env = (struct module_env*)calloc(1, sizeof(*daemon->env)))) { free(daemon); @@ -293,7 +293,7 @@ daemon_init(void) return daemon; } -int +int daemon_open_shared_ports(struct daemon* daemon) { log_assert(daemon); @@ -365,6 +365,22 @@ daemon_open_shared_ports(struct daemon* daemon) return 1; } +int +daemon_privileged(struct daemon* daemon) +{ + if(!daemon_open_shared_ports(daemon)) + fatal_exit("could not open ports"); + + daemon->env->cfg = daemon->cfg; + daemon->env->alloc = &daemon->superalloc; + daemon->env->worker = NULL; + if(!modstack_init(&daemon->mods, daemon->cfg->module_conf, + daemon->env)) { + fatal_exit("failed to init modules"); + } + return 1; +} + /** * Setup modules. setup module stack. * @param daemon: the daemon diff --git a/daemon/daemon.h b/daemon/daemon.h index 3effbafb7..7d8b99fc4 100644 --- a/daemon/daemon.h +++ b/daemon/daemon.h @@ -147,12 +147,13 @@ struct daemon { struct daemon* daemon_init(void); /** - * Open shared listening ports (if needed). + * Do daemon setup that needs privileges + * like opening privileged ports or opening device files. * The cfg member pointer must have been set for the daemon. * @param daemon: the daemon. * @return: false on error. */ -int daemon_open_shared_ports(struct daemon* daemon); +int daemon_privileged(struct daemon* daemon); /** * Fork workers and start service. diff --git a/daemon/unbound.c b/daemon/unbound.c index ceb3da6f5..32604728d 100644 --- a/daemon/unbound.c +++ b/daemon/unbound.c @@ -668,8 +668,8 @@ run_daemon(const char* cfgfile, int cmdline_verbose, int debug_mode, int need_pi config_lookup_uid(cfg); /* prepare */ - if(!daemon_open_shared_ports(daemon)) - fatal_exit("could not open ports"); + if(!daemon_privileged(daemon)) + fatal_exit("could not do privileged setup"); if(!done_setup) { perform_setup(daemon, cfg, debug_mode, &cfgfile, need_pidfile); done_setup = 1; diff --git a/dns64/dns64.c b/dns64/dns64.c index 5c70119a5..97ecce4b0 100644 --- a/dns64/dns64.c +++ b/dns64/dns64.c @@ -394,7 +394,7 @@ dns64_apply_cfg(struct dns64_env* dns64_env, struct config_file* cfg) * \param id This instance's ID number. */ int -dns64_init(struct module_env* env, int id) +dns64_setup(struct module_env* env, int id) { struct dns64_env* dns64_env = (struct dns64_env*)calloc(1, sizeof(struct dns64_env)); @@ -428,7 +428,7 @@ free_ignore_aaaa_node(rbnode_type* node, void* ATTR_UNUSED(arg)) * \param id This instance's ID number. */ void -dns64_deinit(struct module_env* env, int id) +dns64_desetup(struct module_env* env, int id) { struct dns64_env* dns64_env; if (!env) @@ -1019,8 +1019,8 @@ dns64_get_mem(struct module_env* env, int id) */ static struct module_func_block dns64_block = { "dns64", - &dns64_init, &dns64_deinit, &dns64_operate, &dns64_inform_super, - &dns64_clear, &dns64_get_mem + &module_dummy_init, &module_dummy_init, &dns64_setup, &dns64_desetup, + &dns64_operate, &dns64_inform_super, &dns64_clear, &dns64_get_mem }; /** diff --git a/dns64/dns64.h b/dns64/dns64.h index 2f0c01a22..532a5bed3 100644 --- a/dns64/dns64.h +++ b/dns64/dns64.h @@ -50,10 +50,10 @@ struct module_func_block *dns64_get_funcblock(void); /** dns64 init */ -int dns64_init(struct module_env* env, int id); +int dns64_setup(struct module_env* env, int id); /** dns64 deinit */ -void dns64_deinit(struct module_env* env, int id); +void dns64_desetup(struct module_env* env, int id); /** dns64 operate on a query */ void dns64_operate(struct module_qstate* qstate, enum module_ev event, int id, diff --git a/edns-subnet/subnetmod.c b/edns-subnet/subnetmod.c index 37dc550cd..cb2969352 100644 --- a/edns-subnet/subnetmod.c +++ b/edns-subnet/subnetmod.c @@ -188,7 +188,7 @@ subnet_markdel(void* key) } int -subnetmod_init(struct module_env *env, int id) +subnetmod_setup(struct module_env *env, int id) { struct subnet_env *sn_env = (struct subnet_env*)calloc(1, sizeof(struct subnet_env)); @@ -246,7 +246,7 @@ subnetmod_init(struct module_env *env, int id) } void -subnetmod_deinit(struct module_env *env, int id) +subnetmod_desetup(struct module_env *env, int id) { struct subnet_env *sn_env; if(!env || !env->modinfo[id]) @@ -846,8 +846,10 @@ subnetmod_get_mem(struct module_env *env, int id) * The module function block */ static struct module_func_block subnetmod_block = { - "subnet", &subnetmod_init, &subnetmod_deinit, &subnetmod_operate, - &subnetmod_inform_super, &subnetmod_clear, &subnetmod_get_mem + "subnet", + &module_dummy_init, &module_dummy_init, &subnetmod_setup, + &subnetmod_desetup, &subnetmod_operate, &subnetmod_inform_super, + &subnetmod_clear, &subnetmod_get_mem }; struct module_func_block* diff --git a/edns-subnet/subnetmod.h b/edns-subnet/subnetmod.h index e408627b0..7936d889e 100644 --- a/edns-subnet/subnetmod.h +++ b/edns-subnet/subnetmod.h @@ -97,10 +97,10 @@ size_t msg_cache_sizefunc(void* k, void* d); struct module_func_block* subnetmod_get_funcblock(void); /** subnet module init */ -int subnetmod_init(struct module_env* env, int id); +int subnetmod_setup(struct module_env* env, int id); /** subnet module deinit */ -void subnetmod_deinit(struct module_env* env, int id); +void subnetmod_desetup(struct module_env* env, int id); /** subnet module operate on a query */ void subnetmod_operate(struct module_qstate* qstate, enum module_ev event, diff --git a/ipsecmod/ipsecmod.c b/ipsecmod/ipsecmod.c index a1f40a512..ce461f00a 100644 --- a/ipsecmod/ipsecmod.c +++ b/ipsecmod/ipsecmod.c @@ -67,7 +67,7 @@ ipsecmod_apply_cfg(struct ipsecmod_env* ipsecmod_env, struct config_file* cfg) } int -ipsecmod_init(struct module_env* env, int id) +ipsecmod_setup(struct module_env* env, int id) { struct ipsecmod_env* ipsecmod_env = (struct ipsecmod_env*)calloc(1, sizeof(struct ipsecmod_env)); @@ -85,7 +85,7 @@ ipsecmod_init(struct module_env* env, int id) } void -ipsecmod_deinit(struct module_env* env, int id) +ipsecmod_desetup(struct module_env* env, int id) { struct ipsecmod_env* ipsecmod_env; if(!env || !env->modinfo[id]) @@ -598,8 +598,9 @@ ipsecmod_get_mem(struct module_env* env, int id) */ static struct module_func_block ipsecmod_block = { "ipsecmod", - &ipsecmod_init, &ipsecmod_deinit, &ipsecmod_operate, - &ipsecmod_inform_super, &ipsecmod_clear, &ipsecmod_get_mem + &module_dummy_init, &module_dummy_init, &ipsecmod_setup, + &ipsecmod_desetup, &ipsecmod_operate, &ipsecmod_inform_super, + &ipsecmod_clear, &ipsecmod_get_mem }; struct module_func_block* diff --git a/ipsecmod/ipsecmod.h b/ipsecmod/ipsecmod.h index e00816d4b..96d28717b 100644 --- a/ipsecmod/ipsecmod.h +++ b/ipsecmod/ipsecmod.h @@ -74,9 +74,9 @@ struct ipsecmod_qstate { }; /** Init the ipsecmod module */ -int ipsecmod_init(struct module_env* env, int id); +int ipsecmod_setup(struct module_env* env, int id); /** Deinit the ipsecmod module */ -void ipsecmod_deinit(struct module_env* env, int id); +void ipsecmod_desetup(struct module_env* env, int id); /** Operate on an event on a query (in qstate). */ void ipsecmod_operate(struct module_qstate* qstate, enum module_ev event, int id, struct outbound_entry* outbound); diff --git a/ipset/ipset.c b/ipset/ipset.c index f6e2c4a9d..bf4cbea47 100755 --- a/ipset/ipset.c +++ b/ipset/ipset.c @@ -223,7 +223,7 @@ static int ipset_update(struct module_env *env, struct dns_msg *return_msg, stru return 0; } -int ipset_init(struct module_env* env, int id) { +int ipset_setup(struct module_env* env, int id) { struct ipset_env *ipset_env; ipset_env = (struct ipset_env *)calloc(1, sizeof(struct ipset_env)); @@ -250,7 +250,7 @@ int ipset_init(struct module_env* env, int id) { return 1; } -void ipset_deinit(struct module_env *env, int id) { +void ipset_desetup(struct module_env *env, int id) { struct mnl_socket *mnl; struct ipset_env *ipset_env; @@ -373,7 +373,7 @@ size_t ipset_get_mem(struct module_env *env, int id) { */ static struct module_func_block ipset_block = { "ipset", - &ipset_init, &ipset_deinit, &ipset_operate, + &module_dummy_init, &module_dummy_init, &ipset_setup, &ipset_desetup, &ipset_operate, &ipset_inform_super, &ipset_clear, &ipset_get_mem }; diff --git a/ipset/ipset.h b/ipset/ipset.h index f60a8be8c..6273ec789 100755 --- a/ipset/ipset.h +++ b/ipset/ipset.h @@ -51,9 +51,9 @@ struct ipset_qstate { }; /** Init the ipset module */ -int ipset_init(struct module_env* env, int id); +int ipset_setup(struct module_env* env, int id); /** Deinit the ipset module */ -void ipset_deinit(struct module_env* env, int id); +void ipset_desetup(struct module_env* env, int id); /** Operate on an event on a query (in qstate). */ void ipset_operate(struct module_qstate* qstate, enum module_ev event, int id, struct outbound_entry* outbound); diff --git a/iterator/iterator.c b/iterator/iterator.c index eea2f2fb2..af209f84d 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -73,7 +73,7 @@ int UNKNOWN_SERVER_NICENESS = 376; int -iter_init(struct module_env* env, int id) +iter_setup(struct module_env* env, int id) { struct iter_env* iter_env = (struct iter_env*)calloc(1, sizeof(struct iter_env)); @@ -107,7 +107,7 @@ caps_free(struct rbnode_type* n, void* ATTR_UNUSED(d)) } void -iter_deinit(struct module_env* env, int id) +iter_desetup(struct module_env* env, int id) { struct iter_env* iter_env; if(!env || !env->modinfo[id]) @@ -3849,8 +3849,8 @@ iter_get_mem(struct module_env* env, int id) */ static struct module_func_block iter_block = { "iterator", - &iter_init, &iter_deinit, &iter_operate, &iter_inform_super, - &iter_clear, &iter_get_mem + &module_dummy_init, &module_dummy_init, &iter_setup, &iter_desetup, + &iter_operate, &iter_inform_super, &iter_clear, &iter_get_mem }; struct module_func_block* diff --git a/iterator/iterator.h b/iterator/iterator.h index 26ff39559..30bc47289 100644 --- a/iterator/iterator.h +++ b/iterator/iterator.h @@ -429,10 +429,10 @@ const char* iter_state_to_string(enum iter_state state); int iter_state_is_responsestate(enum iter_state s); /** iterator init */ -int iter_init(struct module_env* env, int id); +int iter_setup(struct module_env* env, int id); /** iterator deinit */ -void iter_deinit(struct module_env* env, int id); +void iter_desetup(struct module_env* env, int id); /** iterator operate on a query */ void iter_operate(struct module_qstate* qstate, enum module_ev event, int id, diff --git a/libunbound/context.c b/libunbound/context.c index 6d62e32b5..e04cd653d 100644 --- a/libunbound/context.c +++ b/libunbound/context.c @@ -69,6 +69,8 @@ context_finalize(struct ub_ctx* ctx) log_init(cfg->logfile, cfg->use_syslog, NULL); } config_apply(cfg); + if(!modstack_init(&ctx->mods, cfg->module_conf, ctx->env)) + return UB_INITFAIL; if(!modstack_setup(&ctx->mods, cfg->module_conf, ctx->env)) return UB_INITFAIL; log_edns_known_options(VERB_ALGO, ctx->env); diff --git a/libunbound/libunbound.c b/libunbound/libunbound.c index 3b30419b3..e1abfe64a 100644 --- a/libunbound/libunbound.c +++ b/libunbound/libunbound.c @@ -156,7 +156,7 @@ static struct ub_ctx* ub_ctx_create_nopipe(void) ctx->env->alloc = &ctx->superalloc; ctx->env->worker = NULL; ctx->env->need_to_validate = 0; - modstack_init(&ctx->mods); + memset(&ctx->mods, 0, sizeof(ctx->mods)); rbtree_init(&ctx->queries, &context_query_cmp); return ctx; } @@ -172,6 +172,7 @@ ub_ctx_create(void) ub_randfree(ctx->seed_rnd); config_delete(ctx->env->cfg); modstack_desetup(&ctx->mods, ctx->env); + modstack_deinit(&ctx->mods, ctx->env); edns_known_options_delete(ctx->env); free(ctx->env); free(ctx); @@ -184,6 +185,7 @@ ub_ctx_create(void) ub_randfree(ctx->seed_rnd); config_delete(ctx->env->cfg); modstack_desetup(&ctx->mods, ctx->env); + modstack_deinit(&ctx->mods, ctx->env); edns_known_options_delete(ctx->env); free(ctx->env); free(ctx); @@ -303,6 +305,7 @@ ub_ctx_delete(struct ub_ctx* ctx) libworker_delete_event(ctx->event_worker); modstack_desetup(&ctx->mods, ctx->env); + modstack_deinit(&ctx->mods, ctx->env); a = ctx->alloc_list; while(a) { na = a->super; diff --git a/respip/respip.c b/respip/respip.c index 6fa4f1885..44aea841c 100644 --- a/respip/respip.c +++ b/respip/respip.c @@ -547,7 +547,7 @@ copy_rrset(const struct ub_packed_rrset_key* key, struct regional* region) } int -respip_init(struct module_env* env, int id) +respip_setup(struct module_env* env, int id) { (void)env; (void)id; @@ -555,7 +555,7 @@ respip_init(struct module_env* env, int id) } void -respip_deinit(struct module_env* env, int id) +respip_desetup(struct module_env* env, int id) { (void)env; (void)id; @@ -1273,8 +1273,8 @@ respip_get_mem(struct module_env* env, int id) */ static struct module_func_block respip_block = { "respip", - &respip_init, &respip_deinit, &respip_operate, &respip_inform_super, - &respip_clear, &respip_get_mem + &module_dummy_init, &module_dummy_init, &respip_setup, &respip_desetup, &respip_operate, + &respip_inform_super, &respip_clear, &respip_get_mem }; struct module_func_block* diff --git a/respip/respip.h b/respip/respip.h index bbd471421..d26014766 100644 --- a/respip/respip.h +++ b/respip/respip.h @@ -192,10 +192,10 @@ int respip_rewrite_reply(const struct query_info* qinfo, struct module_func_block* respip_get_funcblock(void); /** response-ip init */ -int respip_init(struct module_env* env, int id); +int respip_setup(struct module_env* env, int id); /** response-ip deinit */ -void respip_deinit(struct module_env* env, int id); +void respip_desetup(struct module_env* env, int id); /** response-ip operate on a query */ void respip_operate(struct module_qstate* qstate, enum module_ev event, int id, diff --git a/services/modstack.c b/services/modstack.c index 68e592814..5a57ecd2d 100644 --- a/services/modstack.c +++ b/services/modstack.c @@ -85,14 +85,7 @@ count_modules(const char* s) return num; } -void -modstack_init(struct module_stack* stack) -{ - stack->num = 0; - stack->mod = NULL; -} - -int +int modstack_config(struct module_stack* stack, const char* module_conf) { int i; @@ -210,18 +203,17 @@ module_func_block* module_factory(const char** str) return NULL; } -int -modstack_setup(struct module_stack* stack, const char* module_conf, +int +modstack_init(struct module_stack* stack, const char* module_conf, struct module_env* env) { - int i; - if(stack->num != 0) - modstack_desetup(stack, env); + int i; + if (stack->num != 0) + fatal_exit("unexpected already initialised modules"); /* fixed setup of the modules */ if(!modstack_config(stack, module_conf)) { return 0; } - env->need_to_validate = 0; /* set by module init below */ for(i=0; inum; i++) { verbose(VERB_OPS, "init module %d: %s", i, stack->mod[i]->name); @@ -235,8 +227,45 @@ modstack_setup(struct module_stack* stack, const char* module_conf, return 1; } -void +int +modstack_setup(struct module_stack* stack, const char* module_conf, + struct module_env* env) +{ + int i; + env->need_to_validate = 0; /* set by module setup below */ + for(i=0; inum; i++) { + while(*module_conf && isspace(*module_conf)) + module_conf++; + if(strncmp(stack->mod[i]->name, module_conf, + strlen(stack->mod[i]->name))) { + log_err("changed module ordering during reload not supported"); + return 0; + } + module_conf += strlen(stack->mod[i]->name); + verbose(VERB_OPS, "setup module %d: %s", + i, stack->mod[i]->name); + fptr_ok(fptr_whitelist_mod_setup(stack->mod[i]->setup)); + if(!(*stack->mod[i]->setup)(env, i)) { + log_err("module setup for module %s failed", + stack->mod[i]->name); + return 0; + } + } + return 1; +} + +void modstack_desetup(struct module_stack* stack, struct module_env* env) +{ + int i; + for(i=0; inum; i++) { + fptr_ok(fptr_whitelist_mod_desetup(stack->mod[i]->desetup)); + (*stack->mod[i]->desetup)(env, i); + } +} + +void +modstack_deinit(struct module_stack* stack, struct module_env* env) { int i; for(i=0; inum; i++) { @@ -248,7 +277,7 @@ modstack_desetup(struct module_stack* stack, struct module_env* env) stack->mod = NULL; } -int +int modstack_find(struct module_stack* stack, const char* name) { int i; diff --git a/services/modstack.h b/services/modstack.h index 3ff01b54d..3e79595ed 100644 --- a/services/modstack.h +++ b/services/modstack.h @@ -55,10 +55,16 @@ struct module_stack { }; /** - * Init a stack of modules - * @param stack: initialised as empty. + * Initialises modules and assignes ids. + * @param stack: Expected empty, filled according to module_conf + * @param module_conf: string what modules to initialize + * @param env: module environment which is inited by the modules. + * environment should have a superalloc, cfg, + * env.need_to_validate is set by the modules. + * @return on false a module init failed. */ -void modstack_init(struct module_stack* stack); +int modstack_init(struct module_stack* stack, const char* module_conf, + struct module_env* env); /** * Read config file module settings and set up the modfunc block @@ -83,10 +89,10 @@ struct module_func_block* module_factory(const char** str); const char** module_list_avail(void); /** - * Setup modules. Assigns ids and calls module_init. - * @param stack: if not empty beforehand, it will be desetup()ed. - * It is then modstack_configged(). - * @param module_conf: string what modules to insert. + * Setup modules. Calls module_setup(). + * @param stack: It is modstack_setupped(). + * @param module_conf: module ordering to check against the ordering in stack. + * fails on changed ordering. * @param env: module environment which is inited by the modules. * environment should have a superalloc, cfg, * env.need_to_validate is set by the modules. @@ -96,12 +102,19 @@ int modstack_setup(struct module_stack* stack, const char* module_conf, struct module_env* env); /** - * Desetup the modules, deinit, delete. + * Desetup the modules * @param stack: made empty. * @param env: module env for module deinit() calls. */ void modstack_desetup(struct module_stack* stack, struct module_env* env); +/** + * Deinit the modules, deinit, delete. + * @param stack: made empty. + * @param env: module env for module deinit() calls. + */ +void modstack_deinit(struct module_stack* stack, struct module_env* env); + /** * Find index of module by name. * @param stack: to look in diff --git a/smallapp/unbound-checkconf.c b/smallapp/unbound-checkconf.c index 3fc638cae..a1f70a8e6 100644 --- a/smallapp/unbound-checkconf.c +++ b/smallapp/unbound-checkconf.c @@ -137,9 +137,11 @@ check_mod(struct config_file* cfg, struct module_func_block* fb) fatal_exit("out of memory"); if(!edns_known_options_init(&env)) fatal_exit("out of memory"); - if(!(*fb->init)(&env, 0)) { + if(!(*fb->setup)(&env, 0)) fatal_exit("bad config for %s module", fb->name); - } + if(!(*fb->setup)(&env, 0)) + fatal_exit("bad config for %s module", fb->name); + (*fb->desetup)(&env, 0); (*fb->deinit)(&env, 0); sldns_buffer_free(env.scratch_buffer); regional_destroy(env.scratch); diff --git a/util/fptr_wlist.c b/util/fptr_wlist.c index b124e7169..35d8bfaed 100644 --- a/util/fptr_wlist.c +++ b/util/fptr_wlist.c @@ -382,52 +382,72 @@ fptr_whitelist_modenv_detect_cycle(int (*fptr)( return 0; } -int +int fptr_whitelist_mod_init(int (*fptr)(struct module_env* env, int id)) { - if(fptr == &iter_init) return 1; - else if(fptr == &val_init) return 1; - else if(fptr == &dns64_init) return 1; - else if(fptr == &respip_init) return 1; + if(fptr == &module_dummy_init) return 1; +#ifdef USE_IPSET + else if(fptr == &ipset_init) return 1; +#endif + return 0; +} + +int +fptr_whitelist_mod_deinit(int (*fptr)(struct module_env* env, int id)) +{ + if(fptr == &module_dummy_init) return 1; +#ifdef USE_IPSET + else if(fptr == &ipset_deinit) return 1; +#endif + return 0; +} + +int +fptr_whitelist_mod_setup(int (*fptr)(struct module_env* env, int id)) +{ + if(fptr == &iter_setup) return 1; + else if(fptr == &val_setup) return 1; + else if(fptr == &dns64_setup) return 1; + else if(fptr == &respip_setup) return 1; #ifdef WITH_PYTHONMODULE - else if(fptr == &pythonmod_init) return 1; + else if(fptr == &pythonmod_setup) return 1; #endif #ifdef USE_CACHEDB - else if(fptr == &cachedb_init) return 1; + else if(fptr == &cachedb_setup) return 1; #endif #ifdef USE_IPSECMOD - else if(fptr == &ipsecmod_init) return 1; + else if(fptr == &ipsecmod_setup) return 1; #endif #ifdef CLIENT_SUBNET - else if(fptr == &subnetmod_init) return 1; + else if(fptr == &subnetmod_setup) return 1; #endif #ifdef USE_IPSET - else if(fptr == &ipset_init) return 1; + else if(fptr == &ipset_setup) return 1; #endif return 0; } int -fptr_whitelist_mod_deinit(void (*fptr)(struct module_env* env, int id)) +fptr_whitelist_mod_desetup(void (*fptr)(struct module_env* env, int id)) { - if(fptr == &iter_deinit) return 1; - else if(fptr == &val_deinit) return 1; - else if(fptr == &dns64_deinit) return 1; - else if(fptr == &respip_deinit) return 1; + if(fptr == &iter_desetup) return 1; + else if(fptr == &val_desetup) return 1; + else if(fptr == &dns64_desetup) return 1; + else if(fptr == &respip_desetup) return 1; #ifdef WITH_PYTHONMODULE - else if(fptr == &pythonmod_deinit) return 1; + else if(fptr == &pythonmod_desetup) return 1; #endif #ifdef USE_CACHEDB - else if(fptr == &cachedb_deinit) return 1; + else if(fptr == &cachedb_desetup) return 1; #endif #ifdef USE_IPSECMOD - else if(fptr == &ipsecmod_deinit) return 1; + else if(fptr == &ipsecmod_desetup) return 1; #endif #ifdef CLIENT_SUBNET - else if(fptr == &subnetmod_deinit) return 1; + else if(fptr == &subnetmod_desetup) return 1; #endif #ifdef USE_IPSET - else if(fptr == &ipset_deinit) return 1; + else if(fptr == &ipset_desetup) return 1; #endif return 0; } diff --git a/util/fptr_wlist.h b/util/fptr_wlist.h index cd331febb..3f74fc8b0 100644 --- a/util/fptr_wlist.h +++ b/util/fptr_wlist.h @@ -270,12 +270,20 @@ int fptr_whitelist_modenv_detect_cycle(int (*fptr)( int fptr_whitelist_mod_init(int (*fptr)(struct module_env* env, int id)); /** - * Check function pointer whitelist for module deinit call values. + * Check function pointer whitelist for module setup call values. * * @param fptr: function pointer to check. * @return false if not in whitelist. */ -int fptr_whitelist_mod_deinit(void (*fptr)(struct module_env* env, int id)); +int fptr_whitelist_mod_setup(int (*fptr)(struct module_env* env, int id)); + +/** + * Check function pointer whitelist for module desetup call values. + * + * @param fptr: function pointer to check. + * @return false if not in whitelist. + */ +int fptr_whitelist_mod_desetup(void (*fptr)(struct module_env* env, int id)); /** * Check function pointer whitelist for module operate call values. diff --git a/util/module.c b/util/module.c index f16583183..a94b0fffa 100644 --- a/util/module.c +++ b/util/module.c @@ -246,3 +246,8 @@ copy_state_to_super(struct module_qstate* qstate, int ATTR_UNUSED(id), super->was_ratelimited = qstate->was_ratelimited; } } + +int module_dummy_init(struct module_env* env, int id) +{ + return 1; +} diff --git a/util/module.h b/util/module.h index fa89c647e..e7b1f33e5 100644 --- a/util/module.h +++ b/util/module.h @@ -672,21 +672,40 @@ struct module_func_block { /** text string name of module */ const char* name; - /** - * init the module. Called once for the global state. + /** + * initialise the module. This is called only once at startup. + * Privileged operations like opening device files may be done here. + * @param id: module id number. + * return: 0 on error + */ + int (*init)(struct module_env* env, int id); + + /** + * deinitialise the module. This is called only once before shutdown to + * free resources allocated during init(). + * Closing privileged ports or files must be done here. + * @param id: module id number. + * return: 0 on error + */ + int (*deinit)(struct module_env* env, int id); + + /** + * setup the module. Called when restarting or reloading the + * daemon. * This is the place to apply settings from the config file. * @param env: module environment. * @param id: module id number. * return: 0 on error */ - int (*init)(struct module_env* env, int id); + int (*setup)(struct module_env* env, int id); /** - * de-init, delete, the module. Called once for the global state. + * de-setup, undo stuff done during setup(). + * Called before reloading the daemon. * @param env: module environment. * @param id: module id number. */ - void (*deinit)(struct module_env* env, int id); + void (*desetup)(struct module_env* env, int id); /** * accept a new query, or work further on existing query. @@ -858,4 +877,6 @@ void log_edns_known_options(enum verbosity_value level, void copy_state_to_super(struct module_qstate* qstate, int id, struct module_qstate* super); +int module_dummy_init(struct module_env* env, int id); + #endif /* UTIL_MODULE_H */ diff --git a/validator/validator.c b/validator/validator.c index c3ca0a27d..fa5fe4c0e 100644 --- a/validator/validator.c +++ b/validator/validator.c @@ -165,7 +165,7 @@ val_apply_cfg(struct module_env* env, struct val_env* val_env, void ecdsa_evp_workaround_init(void); #endif int -val_init(struct module_env* env, int id) +val_setup(struct module_env* env, int id) { struct val_env* val_env = (struct val_env*)calloc(1, sizeof(struct val_env)); @@ -190,7 +190,7 @@ val_init(struct module_env* env, int id) } void -val_deinit(struct module_env* env, int id) +val_desetup(struct module_env* env, int id) { struct val_env* val_env; if(!env || !env->modinfo[id]) @@ -3266,8 +3266,8 @@ val_get_mem(struct module_env* env, int id) */ static struct module_func_block val_block = { "validator", - &val_init, &val_deinit, &val_operate, &val_inform_super, &val_clear, - &val_get_mem + &module_dummy_init, &module_dummy_init, &val_setup, &val_desetup, + &val_operate, &val_inform_super, &val_clear, &val_get_mem }; struct module_func_block* diff --git a/validator/validator.h b/validator/validator.h index 9e4c8a941..caa18d7fd 100644 --- a/validator/validator.h +++ b/validator/validator.h @@ -254,10 +254,10 @@ struct module_func_block* val_get_funcblock(void); const char* val_state_to_string(enum val_state state); /** validator init */ -int val_init(struct module_env* env, int id); +int val_setup(struct module_env* env, int id); /** validator deinit */ -void val_deinit(struct module_env* env, int id); +void val_desetup(struct module_env* env, int id); /** validator operate on a query */ void val_operate(struct module_qstate* qstate, enum module_ev event, int id, From 7d76e84953a1753529219bd8e33412f26ba721d8 Mon Sep 17 00:00:00 2001 From: Christopher Zimmermann Date: Sat, 11 Jan 2020 15:58:55 +0100 Subject: [PATCH 02/66] Port ipset to BSD pf tables --- config.h.in | 3 + configure | 63 +++++++++++------ configure.ac | 57 ++++++++------- ipset/ipset.c | 188 ++++++++++++++++++++++++++++++++++++++++---------- ipset/ipset.h | 8 ++- 5 files changed, 234 insertions(+), 85 deletions(-) diff --git a/config.h.in b/config.h.in index bd9b38bc0..0b1f8053a 100644 --- a/config.h.in +++ b/config.h.in @@ -371,6 +371,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_NETTLE_EDDSA_H +/* Define to 1 if you have the header file. */ +#undef HAVE_NET_PFVAR_H + /* Use libnss for crypto */ #undef HAVE_NSS diff --git a/configure b/configure index fb1ce374e..6e40fa0f8 100755 --- a/configure +++ b/configure @@ -21376,7 +21376,22 @@ $as_echo "#define USE_IPSET 1" >>confdefs.h IPSET_OBJ="ipset.lo" - # mnl + # BSD's pf + for ac_header in net/pfvar.h +do : + ac_fn_c_check_header_compile "$LINENO" "net/pfvar.h" "ac_cv_header_net_pfvar_h" " + #include + #include + +" +if test "x$ac_cv_header_net_pfvar_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_NET_PFVAR_H 1 +_ACEOF + +else + + # mnl # Check whether --with-libmnl was given. if test "${with_libmnl+set}" = set; then : @@ -21385,28 +21400,34 @@ else withval="yes" fi - found_libmnl="no" - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libmnl" >&5 + found_libmnl="no" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libmnl" >&5 $as_echo_n "checking for libmnl... " >&6; } - if test x_$withval = x_ -o x_$withval = x_yes; then - withval="/usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr" - fi - for dir in $withval ; do - if test -f "$dir/include/libmnl/libmnl.h"; then - found_libmnl="yes" - if test "$dir" != "/usr"; then - CPPFLAGS="$CPPFLAGS -I$dir/include" - LDFLAGS="$LDFLAGS -L$dir/lib" - fi - { $as_echo "$as_me:${as_lineno-$LINENO}: result: found in $dir" >&5 + if test x_$withval = x_ -o x_$withval = x_yes; then + withval="/usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr" + fi + for dir in $withval ; do + if test -f "$dir/include/libmnl/libmnl.h"; then + found_libmnl="yes" + if test "$dir" != "/usr"; then + CPPFLAGS="$CPPFLAGS -I$dir/include" + LDFLAGS="$LDFLAGS -L$dir/lib" + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: found in $dir" >&5 $as_echo "found in $dir" >&6; } - LIBS="$LIBS -lmnl" - break; - fi - done - if test x_$found_libmnl != x_yes; then - as_fn_error $? "Could not find libmnl, libmnl.h" "$LINENO" 5 - fi + LIBS="$LIBS -lmnl" + break; + fi + done + if test x_$found_libmnl != x_yes + then + as_fn_error $? "Could not find libmnl, libmnl.h" "$LINENO" 5 + fi + +fi + +done + ;; no|*) # nothing diff --git a/configure.ac b/configure.ac index f96a24ef2..09bdf7c57 100644 --- a/configure.ac +++ b/configure.ac @@ -1756,31 +1756,38 @@ case "$enable_ipset" in IPSET_OBJ="ipset.lo" AC_SUBST(IPSET_OBJ) - # mnl - AC_ARG_WITH(libmnl, AC_HELP_STRING([--with-libmnl=path], - [specify explicit path for libmnl.]), - [ ],[ withval="yes" ]) - found_libmnl="no" - AC_MSG_CHECKING(for libmnl) - if test x_$withval = x_ -o x_$withval = x_yes; then - withval="/usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr" - fi - for dir in $withval ; do - if test -f "$dir/include/libmnl/libmnl.h"; then - found_libmnl="yes" - dnl assume /usr is in default path. - if test "$dir" != "/usr"; then - CPPFLAGS="$CPPFLAGS -I$dir/include" - LDFLAGS="$LDFLAGS -L$dir/lib" - fi - AC_MSG_RESULT(found in $dir) - LIBS="$LIBS -lmnl" - break; - fi - done - if test x_$found_libmnl != x_yes; then - AC_ERROR([Could not find libmnl, libmnl.h]) - fi + # BSD's pf + AC_CHECK_HEADERS([net/pfvar.h], [], [ + # mnl + AC_ARG_WITH(libmnl, AC_HELP_STRING([--with-libmnl=path], + [specify explicit path for libmnl.]), + [ ],[ withval="yes" ]) + found_libmnl="no" + AC_MSG_CHECKING(for libmnl) + if test x_$withval = x_ -o x_$withval = x_yes; then + withval="/usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr" + fi + for dir in $withval ; do + if test -f "$dir/include/libmnl/libmnl.h"; then + found_libmnl="yes" + dnl assume /usr is in default path. + if test "$dir" != "/usr"; then + CPPFLAGS="$CPPFLAGS -I$dir/include" + LDFLAGS="$LDFLAGS -L$dir/lib" + fi + AC_MSG_RESULT(found in $dir) + LIBS="$LIBS -lmnl" + break; + fi + done + if test x_$found_libmnl != x_yes + then + AC_ERROR([Could not find libmnl, libmnl.h]) + fi + ], [ + #include + #include + ]) ;; no|*) # nothing diff --git a/ipset/ipset.c b/ipset/ipset.c index bf4cbea47..347512406 100755 --- a/ipset/ipset.c +++ b/ipset/ipset.c @@ -17,9 +17,21 @@ #include "sldns/wire2str.h" #include "sldns/parseutil.h" +#include + +#ifdef HAVE_NET_PFVAR_H +#include +#include +#include +#include +#include +typedef intptr_t filter_dev; +#else #include #include #include +typedef struct mnl_socket * filter_dev; +#endif #define BUFF_LEN 256 @@ -41,24 +53,95 @@ static int error_response(struct module_qstate* qstate, int id, int rcode) { return 0; } -static struct mnl_socket * open_mnl_socket() { - struct mnl_socket *mnl; +#ifdef HAVE_NET_PFVAR_H +static void * open_filter() { + filter_dev dev; - mnl = mnl_socket_open(NETLINK_NETFILTER); - if (!mnl) { + dev = open("/dev/pf", O_RDWR); + if (dev == -1) { + log_err("open(\"/dev/pf\") failed"); + return NULL; + } + else + return (void *)dev; +} +#else +static void * open_filter() { + filter_dev dev; + + dev = mnl_socket_open(NETLINK_NETFILTER); + if (!dev) { log_err("ipset: could not open netfilter."); return NULL; } - if (mnl_socket_bind(mnl, 0, MNL_SOCKET_AUTOPID) < 0) { - mnl_socket_close(mnl); + if (mnl_socket_bind(dev, 0, MNL_SOCKET_AUTOPID) < 0) { + mnl_socket_close(dev); log_err("ipset: could not bind netfilter."); return NULL; } - return mnl; + return dev; } +#endif + +#ifdef HAVE_NET_PFVAR_H +static int add_to_ipset(filter_dev dev, const char *setname, const void *ipaddr, int af) { + struct pfioc_table io; + struct pfr_addr addr; + const char *p; + int i; + + bzero(&io, sizeof(io)); + bzero(&addr, sizeof(addr)); + + p = strrchr(setname, '/'); + if (p) { + i = p - setname; + if (i >= PATH_MAX) { + errno = ENAMETOOLONG; + return -1; + } + memcpy(io.pfrio_table.pfrt_anchor, setname, i); + if (i < PATH_MAX) + io.pfrio_table.pfrt_anchor[i] = '\0'; + p++; + } + else + p = setname; -static int add_to_ipset(struct mnl_socket *mnl, const char *setname, const void *ipaddr, int af) { + if (strlen(p) >= PF_TABLE_NAME_SIZE) { + errno = ENAMETOOLONG; + return -1; + } + strlcpy(io.pfrio_table.pfrt_name, p, PF_TABLE_NAME_SIZE); + + io.pfrio_buffer = &addr; + io.pfrio_size = 1; + io.pfrio_esize = sizeof(addr); + + switch (af) { + case AF_INET: + addr.pfra_ip4addr = *(struct in_addr *)ipaddr; + addr.pfra_net = 32; + break; + case AF_INET6: + addr.pfra_ip6addr = *(struct in6_addr *)ipaddr; + addr.pfra_net = 128; + break; + default: + errno = EAFNOSUPPORT; + return -1; + } + addr.pfra_af = af; + + if (ioctl(dev, DIOCRADDADDRS, &io) == -1) { + log_err("ioctl failed: %s", strerror(errno)); + return -1; + } + return 0; +} +#else +static int add_to_ipset(filter_dev dev, const char *setname, const void *ipaddr, int af) { struct nlmsghdr *nlh; struct nfgenmsg *nfg; struct nlattr *nested[2]; @@ -91,14 +174,15 @@ static int add_to_ipset(struct mnl_socket *mnl, const char *setname, const void mnl_attr_nest_end(nlh, nested[1]); mnl_attr_nest_end(nlh, nested[0]); - if (mnl_socket_sendto(mnl, nlh, nlh->nlmsg_len) < 0) { + if (mnl_socket_sendto(dev, nlh, nlh->nlmsg_len) < 0) { return -1; } return 0; } +#endif static void -ipset_add_rrset_data(struct ipset_env *ie, struct mnl_socket *mnl, +ipset_add_rrset_data(struct ipset_env *ie, struct packed_rrset_data *d, const char* setname, int af, const char* dname) { @@ -123,12 +207,16 @@ ipset_add_rrset_data(struct ipset_env *ie, struct mnl_socket *mnl, snprintf(ip, sizeof(ip), "(inet_ntop_error)"); verbose(VERB_QUERY, "ipset: add %s to %s for %s", ip, setname, dname); } - ret = add_to_ipset(mnl, setname, rr_data + 2, af); + ret = add_to_ipset((filter_dev)ie->dev, setname, rr_data + 2, af); if (ret < 0) { log_err("ipset: could not add %s into %s", dname, setname); - mnl_socket_close(mnl); - ie->mnl = NULL; +#if HAVE_NET_PFVAR_H + /* don't close as we might not be able to open again due to dropped privs */ +#else + mnl_socket_close((filter_dev)ie->dev); + ie->dev = NULL; +#endif break; } } @@ -137,7 +225,7 @@ ipset_add_rrset_data(struct ipset_env *ie, struct mnl_socket *mnl, static int ipset_check_zones_for_rrset(struct module_env *env, struct ipset_env *ie, - struct mnl_socket *mnl, struct ub_packed_rrset_key *rrset, + struct ub_packed_rrset_key *rrset, const char *setname, int af) { static char dname[BUFF_LEN]; @@ -158,13 +246,16 @@ ipset_check_zones_for_rrset(struct module_env *env, struct ipset_env *ie, for (p = env->cfg->local_zones_ipset; p; p = p->next) { plen = strlen(p->str); + if (p->str[plen - 1] == '.') { + plen--; + } if (dlen >= plen) { s = dname + (dlen - plen); if (strncasecmp(p->str, s, plen) == 0) { d = (struct packed_rrset_data*)rrset->entry.data; - ipset_add_rrset_data(ie, mnl, d, setname, + ipset_add_rrset_data(ie, d, setname, af, dname); break; } @@ -174,8 +265,6 @@ ipset_check_zones_for_rrset(struct module_env *env, struct ipset_env *ie, } static int ipset_update(struct module_env *env, struct dns_msg *return_msg, struct ipset_env *ie) { - struct mnl_socket *mnl; - size_t i; const char *setname; @@ -184,17 +273,17 @@ static int ipset_update(struct module_env *env, struct dns_msg *return_msg, stru int af; - - mnl = (struct mnl_socket *)ie->mnl; - if (!mnl) { +#ifdef HAVE_NET_PFVAR_H +#else + if (!ie->dev) { // retry to create mnl socket - mnl = open_mnl_socket(); - if (!mnl) { + ie->dev = open_filter(); + if (!ie->dev) { + log_warn("ipset open_filter failed"); return -1; } - - ie->mnl = mnl; } +#endif for (i = 0; i < return_msg->rep->rrset_count; ++i) { setname = NULL; @@ -203,18 +292,18 @@ static int ipset_update(struct module_env *env, struct dns_msg *return_msg, stru if (rrset->rk.type == htons(LDNS_RR_TYPE_A)) { af = AF_INET; - if ((ie->v4_enabled == 1)) { + if (ie->v4_enabled == 1) { setname = ie->name_v4; } } else { af = AF_INET6; - if ((ie->v6_enabled == 1)) { + if (ie->v6_enabled == 1) { setname = ie->name_v6; } } if (setname) { - if(ipset_check_zones_for_rrset(env, ie, mnl, rrset, + if(ipset_check_zones_for_rrset(env, ie, rrset, setname, af) == -1) return -1; } @@ -223,10 +312,10 @@ static int ipset_update(struct module_env *env, struct dns_msg *return_msg, stru return 0; } -int ipset_setup(struct module_env* env, int id) { +int ipset_init(struct module_env* env, int id) { struct ipset_env *ipset_env; - ipset_env = (struct ipset_env *)calloc(1, sizeof(struct ipset_env)); + ipset_env = (struct ipset_env *)malloc(sizeof(struct ipset_env)); if (!ipset_env) { log_err("malloc failure"); return 0; @@ -234,7 +323,28 @@ int ipset_setup(struct module_env* env, int id) { env->modinfo[id] = (void *)ipset_env; - ipset_env->mnl = NULL; +#ifdef HAVE_NET_PFVAR_H + ipset_env->dev = open_filter(); + if (!ipset_env->dev) { + log_err("ipset open_filter failed"); + return 0; + } +#else + ipset_env->dev = NULL; +#endif + return 1; +} + +int ipset_deinit(struct module_env* env, int id) { + struct ipset_env *ipset_env = env->modinfo[id]; + close((filter_dev)ipset_env->dev); + free(ipset_env); + env->modinfo[id] = NULL; + return 1; +} + +int ipset_setup(struct module_env* env, int id) { + struct ipset_env *ipset_env = env->modinfo[id]; ipset_env->name_v4 = env->cfg->ipset_name_v4; ipset_env->name_v6 = env->cfg->ipset_name_v6; @@ -251,7 +361,7 @@ int ipset_setup(struct module_env* env, int id) { } void ipset_desetup(struct module_env *env, int id) { - struct mnl_socket *mnl; + filter_dev dev; struct ipset_env *ipset_env; if (!env || !env->modinfo[id]) { @@ -260,10 +370,14 @@ void ipset_desetup(struct module_env *env, int id) { ipset_env = (struct ipset_env *)env->modinfo[id]; - mnl = (struct mnl_socket *)ipset_env->mnl; - if (mnl) { - mnl_socket_close(mnl); - ipset_env->mnl = NULL; + dev = (filter_dev)ipset_env->dev; + if (dev) { +#if HAVE_NET_PFVAR_H + close(dev); +#else + mnl_socket_close(dev); +#endif + ipset_env->dev = NULL; } free(ipset_env); @@ -373,8 +487,8 @@ size_t ipset_get_mem(struct module_env *env, int id) { */ static struct module_func_block ipset_block = { "ipset", - &module_dummy_init, &module_dummy_init, &ipset_setup, &ipset_desetup, &ipset_operate, - &ipset_inform_super, &ipset_clear, &ipset_get_mem + &ipset_init, &ipset_deinit, &ipset_setup, &ipset_desetup, + &ipset_operate, &ipset_inform_super, &ipset_clear, &ipset_get_mem }; struct module_func_block * ipset_get_funcblock(void) { diff --git a/ipset/ipset.h b/ipset/ipset.h index 6273ec789..66a4378b6 100755 --- a/ipset/ipset.h +++ b/ipset/ipset.h @@ -37,7 +37,7 @@ extern "C" { #endif struct ipset_env { - void* mnl; + void* dev; int v4_enabled; int v6_enabled; @@ -51,8 +51,12 @@ struct ipset_qstate { }; /** Init the ipset module */ -int ipset_setup(struct module_env* env, int id); +int ipset_init(struct module_env* env, int id); /** Deinit the ipset module */ +int ipset_deinit(struct module_env* env, int id); +/** Setup the ipset module */ +int ipset_setup(struct module_env* env, int id); +/** Desetup the ipset module */ void ipset_desetup(struct module_env* env, int id); /** Operate on an event on a query (in qstate). */ void ipset_operate(struct module_qstate* qstate, enum module_ev event, From ad51795314b9bc89f78e4c2a2efe9feec871367c Mon Sep 17 00:00:00 2001 From: Christopher Zimmermann Date: Tue, 14 Jan 2020 23:45:21 +0100 Subject: [PATCH 03/66] Don't try to run daemon_privileged on reload. --- daemon/unbound.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/daemon/unbound.c b/daemon/unbound.c index 32604728d..358d57bf1 100644 --- a/daemon/unbound.c +++ b/daemon/unbound.c @@ -668,9 +668,9 @@ run_daemon(const char* cfgfile, int cmdline_verbose, int debug_mode, int need_pi config_lookup_uid(cfg); /* prepare */ - if(!daemon_privileged(daemon)) - fatal_exit("could not do privileged setup"); if(!done_setup) { + if(!daemon_privileged(daemon)) + fatal_exit("could not do privileged setup"); perform_setup(daemon, cfg, debug_mode, &cfgfile, need_pidfile); done_setup = 1; } else { From fd11cd91824bf6d253412aa2a955dbc5792fd04f Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 1 Jul 2024 14:54:45 +0200 Subject: [PATCH 04/66] ipset-pf-support, fix compilation, close of pf socket. --- ipset/ipset.c | 3 +++ pythonmod/pythonmod.c | 2 +- util/fptr_wlist.c | 4 ++-- util/fptr_wlist.h | 8 ++++++++ 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/ipset/ipset.c b/ipset/ipset.c index 59fd9ce9f..f7cf2082e 100644 --- a/ipset/ipset.c +++ b/ipset/ipset.c @@ -270,6 +270,7 @@ ipset_check_zones_for_rrset(struct module_env *env, struct ipset_env *ie, static int ipset_update(struct module_env *env, struct dns_msg *return_msg, struct query_info qinfo, struct ipset_env *ie) +{ size_t i; const char *setname; struct ub_packed_rrset_key *rrset; @@ -344,7 +345,9 @@ int ipset_init(struct module_env* env, int id) { int ipset_deinit(struct module_env* env, int id) { struct ipset_env *ipset_env = env->modinfo[id]; +#ifdef HAVE_NET_PFVAR_H close((filter_dev)ipset_env->dev); +#endif free(ipset_env); env->modinfo[id] = NULL; return 1; diff --git a/pythonmod/pythonmod.c b/pythonmod/pythonmod.c index b8f2d62fb..6bc6752e2 100644 --- a/pythonmod/pythonmod.c +++ b/pythonmod/pythonmod.c @@ -777,7 +777,7 @@ size_t pythonmod_get_mem(struct module_env* env, int id) */ static struct module_func_block pythonmod_block = { "python", - &pythonmod_init, &pythonmod_deinit, &pythonmod_operate, &pythonmod_inform_super, + &module_dummy_init, &module_dummy_init, &pythonmod_init, &pythonmod_deinit, &pythonmod_operate, &pythonmod_inform_super, &pythonmod_clear, &pythonmod_get_mem }; diff --git a/util/fptr_wlist.c b/util/fptr_wlist.c index 3ea258358..d24941321 100644 --- a/util/fptr_wlist.c +++ b/util/fptr_wlist.c @@ -420,7 +420,7 @@ fptr_whitelist_mod_setup(int (*fptr)(struct module_env* env, int id)) else if(fptr == &dns64_setup) return 1; else if(fptr == &respip_setup) return 1; #ifdef WITH_PYTHONMODULE - else if(fptr == &pythonmod_setup) return 1; + else if(fptr == &pythonmod_init) return 1; #endif #ifdef WITH_DYNLIBMODULE else if(fptr == &dynlibmod_init) return 1; @@ -448,7 +448,7 @@ fptr_whitelist_mod_desetup(void (*fptr)(struct module_env* env, int id)) else if(fptr == &dns64_desetup) return 1; else if(fptr == &respip_desetup) return 1; #ifdef WITH_PYTHONMODULE - else if(fptr == &pythonmod_desetup) return 1; + else if(fptr == &pythonmod_deinit) return 1; #endif #ifdef WITH_DYNLIBMODULE else if(fptr == &dynlibmod_deinit) return 1; diff --git a/util/fptr_wlist.h b/util/fptr_wlist.h index cf1a4fbc3..cf70c7066 100644 --- a/util/fptr_wlist.h +++ b/util/fptr_wlist.h @@ -270,6 +270,14 @@ int fptr_whitelist_modenv_detect_cycle(int (*fptr)( */ int fptr_whitelist_mod_init(int (*fptr)(struct module_env* env, int id)); +/** + * Check function pointer whitelist for module deinit call values. + * + * @param fptr: function pointer to check. + * @return false if not in whitelist. + */ +int fptr_whitelist_mod_deinit(int (*fptr)(struct module_env* env, int id)); + /** * Check function pointer whitelist for module setup call values. * From ff653a7ef83e1ed147f21522a21ec9d1dbf6bbf5 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 1 Jul 2024 16:10:07 +0200 Subject: [PATCH 05/66] Call module init init again, and new function startup and destartup. NULL can be used if the function is not used. Open shared ports during reload. Deinit is called during reload. --- cachedb/cachedb.c | 6 +-- cachedb/cachedb.h | 4 +- daemon/daemon.c | 16 ++++---- daemon/daemon.h | 8 ++++ daemon/unbound.c | 8 +++- dns64/dns64.c | 8 ++-- dns64/dns64.h | 4 +- dynlibmod/dynlibmod.c | 4 +- edns-subnet/subnetmod.c | 9 ++--- edns-subnet/subnetmod.h | 4 +- ipsecmod/ipsecmod.c | 9 ++--- ipsecmod/ipsecmod.h | 4 +- ipset/ipset.c | 13 +++---- ipset/ipset.h | 10 ++--- iterator/iterator.c | 8 ++-- iterator/iterator.h | 4 +- libunbound/context.c | 12 ++++-- libunbound/context.h | 3 ++ libunbound/libunbound.c | 8 ++-- pythonmod/pythonmod.c | 4 +- respip/respip.c | 6 +-- respip/respip.h | 4 +- services/modstack.c | 36 +++++++++-------- services/modstack.h | 19 +++++---- smallapp/unbound-checkconf.c | 11 +++--- testcode/unitzonemd.c | 11 ++++-- util/fptr_wlist.c | 75 ++++++++++++++++++------------------ util/fptr_wlist.h | 10 ++--- util/module.c | 5 --- util/module.h | 33 ++++++++-------- validator/validator.c | 8 ++-- validator/validator.h | 4 +- 32 files changed, 195 insertions(+), 173 deletions(-) diff --git a/cachedb/cachedb.c b/cachedb/cachedb.c index d610c4f19..aca68d7ed 100644 --- a/cachedb/cachedb.c +++ b/cachedb/cachedb.c @@ -241,7 +241,7 @@ cachedb_apply_cfg(struct cachedb_env* cachedb_env, struct config_file* cfg) } int -cachedb_setup(struct module_env* env, int id) +cachedb_init(struct module_env* env, int id) { struct cachedb_env* cachedb_env = (struct cachedb_env*)calloc(1, sizeof(struct cachedb_env)); @@ -271,7 +271,7 @@ cachedb_setup(struct module_env* env, int id) } void -cachedb_desetup(struct module_env* env, int id) +cachedb_deinit(struct module_env* env, int id) { struct cachedb_env* cachedb_env; if(!env || !env->modinfo[id]) @@ -983,7 +983,7 @@ cachedb_get_mem(struct module_env* env, int id) */ static struct module_func_block cachedb_block = { "cachedb", - &module_dummy_init, &module_dummy_init, &cachedb_setup, &cachedb_desetup, &cachedb_operate, + &cachedb_init, &cachedb_deinit, NULL, NULL, &cachedb_operate, &cachedb_inform_super, &cachedb_clear, &cachedb_get_mem }; diff --git a/cachedb/cachedb.h b/cachedb/cachedb.h index 3d3e101d7..482c5db6c 100644 --- a/cachedb/cachedb.h +++ b/cachedb/cachedb.h @@ -91,9 +91,9 @@ struct cachedb_backend { #define CACHEDB_HASHSIZE 256 /* bit hash */ /** Init the cachedb module */ -int cachedb_setup(struct module_env* env, int id); +int cachedb_init(struct module_env* env, int id); /** Deinit the cachedb module */ -void cachedb_desetup(struct module_env* env, int id); +void cachedb_deinit(struct module_env* env, int id); /** Operate on an event on a query (in qstate). */ void cachedb_operate(struct module_qstate* qstate, enum module_ev event, int id, struct outbound_entry* outbound); diff --git a/daemon/daemon.c b/daemon/daemon.c index 15102b9d2..eb933cc93 100644 --- a/daemon/daemon.c +++ b/daemon/daemon.c @@ -447,15 +447,12 @@ daemon_open_shared_ports(struct daemon* daemon) int daemon_privileged(struct daemon* daemon) { - if(!daemon_open_shared_ports(daemon)) - fatal_exit("could not open ports"); - daemon->env->cfg = daemon->cfg; daemon->env->alloc = &daemon->superalloc; daemon->env->worker = NULL; - if(!modstack_init(&daemon->mods, daemon->cfg->module_conf, + if(!modstack_startup(&daemon->mods, daemon->cfg->module_conf, daemon->env)) { - fatal_exit("failed to init modules"); + fatal_exit("failed to startup modules"); } return 1; } @@ -470,7 +467,9 @@ static void daemon_setup_modules(struct daemon* daemon) daemon->env->alloc = &daemon->superalloc; daemon->env->worker = NULL; daemon->env->need_to_validate = 0; /* set by module init below */ - if(!modstack_setup(&daemon->mods, daemon->cfg->module_conf, + if(daemon->mods.num != 0) + modstack_deinit(&daemon->mods, daemon->env); + if(!modstack_call_init(&daemon->mods, daemon->cfg->module_conf, daemon->env)) { fatal_exit("failed to setup modules"); } @@ -877,7 +876,7 @@ daemon_cleanup(struct daemon* daemon) daemon->views = NULL; if(daemon->env->auth_zones) auth_zones_cleanup(daemon->env->auth_zones); - /* key cache is cleared by module desetup during next daemon_fork() */ + /* key cache is cleared by module deinit during next daemon_fork() */ daemon_remote_clear(daemon->rc); for(i=0; inum; i++) worker_delete(daemon->workers[i]); @@ -907,7 +906,8 @@ daemon_delete(struct daemon* daemon) size_t i; if(!daemon) return; - modstack_desetup(&daemon->mods, daemon->env); + modstack_deinit(&daemon->mods, daemon->env); + modstack_destartup(&daemon->mods, daemon->env); daemon_remote_delete(daemon->rc); for(i = 0; i < daemon->num_ports; i++) listening_ports_free(daemon->ports[i]); diff --git a/daemon/daemon.h b/daemon/daemon.h index 0f6c260a8..200149745 100644 --- a/daemon/daemon.h +++ b/daemon/daemon.h @@ -154,6 +154,14 @@ struct daemon { */ struct daemon* daemon_init(void); +/** + * Open shared listening ports (if needed). + * The cfg member pointer must have been set for the daemon. + * @param daemon: the daemon. + * @return: false on error. + */ +int daemon_open_shared_ports(struct daemon* daemon); + /** * Do daemon setup that needs privileges * like opening privileged ports or opening device files. diff --git a/daemon/unbound.c b/daemon/unbound.c index 3644709d0..306fe6caf 100644 --- a/daemon/unbound.c +++ b/daemon/unbound.c @@ -473,7 +473,11 @@ perform_setup(struct daemon* daemon, struct config_file* cfg, int debug_mode, #endif #ifdef HAVE_GETPWNAM struct passwd *pwd = NULL; +#endif + if(!daemon_privileged(daemon)) + fatal_exit("could not do privileged setup"); +#ifdef HAVE_GETPWNAM if(cfg->username && cfg->username[0]) { if((pwd = getpwnam(cfg->username)) == NULL) fatal_exit("user '%s' does not exist.", cfg->username); @@ -717,9 +721,9 @@ run_daemon(const char* cfgfile, int cmdline_verbose, int debug_mode, int need_pi config_lookup_uid(cfg); /* prepare */ + if(!daemon_open_shared_ports(daemon)) + fatal_exit("could not open ports"); if(!done_setup) { - if(!daemon_privileged(daemon)) - fatal_exit("could not do privileged setup"); perform_setup(daemon, cfg, debug_mode, &cfgfile, need_pidfile); done_setup = 1; } else { diff --git a/dns64/dns64.c b/dns64/dns64.c index 3f2994fa6..d34eafb9f 100644 --- a/dns64/dns64.c +++ b/dns64/dns64.c @@ -399,7 +399,7 @@ dns64_apply_cfg(struct dns64_env* dns64_env, struct config_file* cfg) * \param id This instance's ID number. */ int -dns64_setup(struct module_env* env, int id) +dns64_init(struct module_env* env, int id) { struct dns64_env* dns64_env = (struct dns64_env*)calloc(1, sizeof(struct dns64_env)); @@ -433,7 +433,7 @@ free_ignore_aaaa_node(rbnode_type* node, void* ATTR_UNUSED(arg)) * \param id This instance's ID number. */ void -dns64_desetup(struct module_env* env, int id) +dns64_deinit(struct module_env* env, int id) { struct dns64_env* dns64_env; if (!env) @@ -1044,8 +1044,8 @@ dns64_get_mem(struct module_env* env, int id) */ static struct module_func_block dns64_block = { "dns64", - &module_dummy_init, &module_dummy_init, &dns64_setup, &dns64_desetup, - &dns64_operate, &dns64_inform_super, &dns64_clear, &dns64_get_mem + &dns64_init, &dns64_deinit, NULL, NULL, &dns64_operate, + &dns64_inform_super, &dns64_clear, &dns64_get_mem }; /** diff --git a/dns64/dns64.h b/dns64/dns64.h index 532a5bed3..2f0c01a22 100644 --- a/dns64/dns64.h +++ b/dns64/dns64.h @@ -50,10 +50,10 @@ struct module_func_block *dns64_get_funcblock(void); /** dns64 init */ -int dns64_setup(struct module_env* env, int id); +int dns64_init(struct module_env* env, int id); /** dns64 deinit */ -void dns64_desetup(struct module_env* env, int id); +void dns64_deinit(struct module_env* env, int id); /** dns64 operate on a query */ void dns64_operate(struct module_qstate* qstate, enum module_ev event, int id, diff --git a/dynlibmod/dynlibmod.c b/dynlibmod/dynlibmod.c index 1e040a30e..06c4f08c1 100644 --- a/dynlibmod/dynlibmod.c +++ b/dynlibmod/dynlibmod.c @@ -297,8 +297,8 @@ inplace_cb_delete_wrapped(struct module_env* env, enum inplace_cb_list_type type */ static struct module_func_block dynlibmod_block = { "dynlib", - &dynlibmod_init, &dynlibmod_deinit, &dynlibmod_operate, &dynlibmod_inform_super, - &dynlibmod_clear, &dynlibmod_get_mem + &dynlibmod_init, &dynlibmod_deinit, NULL, NULL, &dynlibmod_operate, + &dynlibmod_inform_super, &dynlibmod_clear, &dynlibmod_get_mem }; struct module_func_block* dynlibmod_get_funcblock(void) diff --git a/edns-subnet/subnetmod.c b/edns-subnet/subnetmod.c index 61672be59..8e6db02e7 100644 --- a/edns-subnet/subnetmod.c +++ b/edns-subnet/subnetmod.c @@ -206,7 +206,7 @@ subnet_markdel(void* key) } int -subnetmod_setup(struct module_env *env, int id) +subnetmod_init(struct module_env *env, int id) { struct subnet_env *sn_env = (struct subnet_env*)calloc(1, sizeof(struct subnet_env)); @@ -275,7 +275,7 @@ subnetmod_setup(struct module_env *env, int id) } void -subnetmod_desetup(struct module_env *env, int id) +subnetmod_deinit(struct module_env *env, int id) { struct subnet_env *sn_env; if(!env || !env->modinfo[id]) @@ -996,9 +996,8 @@ subnetmod_get_mem(struct module_env *env, int id) */ static struct module_func_block subnetmod_block = { "subnetcache", - &module_dummy_init, &module_dummy_init, &subnetmod_setup, - &subnetmod_desetup, &subnetmod_operate, &subnetmod_inform_super, - &subnetmod_clear, &subnetmod_get_mem + &subnetmod_init, &subnetmod_deinit, NULL, NULL, &subnetmod_operate, + &subnetmod_inform_super, &subnetmod_clear, &subnetmod_get_mem }; struct module_func_block* diff --git a/edns-subnet/subnetmod.h b/edns-subnet/subnetmod.h index 8999aa632..1ff8a23ec 100644 --- a/edns-subnet/subnetmod.h +++ b/edns-subnet/subnetmod.h @@ -114,10 +114,10 @@ size_t msg_cache_sizefunc(void* k, void* d); struct module_func_block* subnetmod_get_funcblock(void); /** subnet module init */ -int subnetmod_setup(struct module_env* env, int id); +int subnetmod_init(struct module_env* env, int id); /** subnet module deinit */ -void subnetmod_desetup(struct module_env* env, int id); +void subnetmod_deinit(struct module_env* env, int id); /** subnet module operate on a query */ void subnetmod_operate(struct module_qstate* qstate, enum module_ev event, diff --git a/ipsecmod/ipsecmod.c b/ipsecmod/ipsecmod.c index 83f91702b..137e22c33 100644 --- a/ipsecmod/ipsecmod.c +++ b/ipsecmod/ipsecmod.c @@ -67,7 +67,7 @@ ipsecmod_apply_cfg(struct ipsecmod_env* ipsecmod_env, struct config_file* cfg) } int -ipsecmod_setup(struct module_env* env, int id) +ipsecmod_init(struct module_env* env, int id) { struct ipsecmod_env* ipsecmod_env = (struct ipsecmod_env*)calloc(1, sizeof(struct ipsecmod_env)); @@ -85,7 +85,7 @@ ipsecmod_setup(struct module_env* env, int id) } void -ipsecmod_desetup(struct module_env* env, int id) +ipsecmod_deinit(struct module_env* env, int id) { struct ipsecmod_env* ipsecmod_env; if(!env || !env->modinfo[id]) @@ -615,9 +615,8 @@ ipsecmod_get_mem(struct module_env* env, int id) */ static struct module_func_block ipsecmod_block = { "ipsecmod", - &module_dummy_init, &module_dummy_init, &ipsecmod_setup, - &ipsecmod_desetup, &ipsecmod_operate, &ipsecmod_inform_super, - &ipsecmod_clear, &ipsecmod_get_mem + &ipsecmod_init, &ipsecmod_deinit, NULL, NULL, &ipsecmod_operate, + &ipsecmod_inform_super, &ipsecmod_clear, &ipsecmod_get_mem }; struct module_func_block* diff --git a/ipsecmod/ipsecmod.h b/ipsecmod/ipsecmod.h index 9e77208c4..272f473c2 100644 --- a/ipsecmod/ipsecmod.h +++ b/ipsecmod/ipsecmod.h @@ -74,9 +74,9 @@ struct ipsecmod_qstate { }; /** Init the ipsecmod module */ -int ipsecmod_setup(struct module_env* env, int id); +int ipsecmod_init(struct module_env* env, int id); /** Deinit the ipsecmod module */ -void ipsecmod_desetup(struct module_env* env, int id); +void ipsecmod_deinit(struct module_env* env, int id); /** Operate on an event on a query (in qstate). */ void ipsecmod_operate(struct module_qstate* qstate, enum module_ev event, int id, struct outbound_entry* outbound); diff --git a/ipset/ipset.c b/ipset/ipset.c index f7cf2082e..9d1a8aa43 100644 --- a/ipset/ipset.c +++ b/ipset/ipset.c @@ -320,10 +320,10 @@ static int ipset_update(struct module_env *env, struct dns_msg *return_msg, return 0; } -int ipset_init(struct module_env* env, int id) { +int ipset_startup(struct module_env* env, int id) { struct ipset_env *ipset_env; - ipset_env = (struct ipset_env *)malloc(sizeof(struct ipset_env)); + ipset_env = (struct ipset_env *)calloc(1, sizeof(struct ipset_env)); if (!ipset_env) { log_err("malloc failure"); return 0; @@ -343,17 +343,16 @@ int ipset_init(struct module_env* env, int id) { return 1; } -int ipset_deinit(struct module_env* env, int id) { +void ipset_destartup(struct module_env* env, int id) { struct ipset_env *ipset_env = env->modinfo[id]; #ifdef HAVE_NET_PFVAR_H close((filter_dev)ipset_env->dev); #endif free(ipset_env); env->modinfo[id] = NULL; - return 1; } -int ipset_setup(struct module_env* env, int id) { +int ipset_init(struct module_env* env, int id) { struct ipset_env *ipset_env = env->modinfo[id]; ipset_env->name_v4 = env->cfg->ipset_name_v4; @@ -370,7 +369,7 @@ int ipset_setup(struct module_env* env, int id) { return 1; } -void ipset_desetup(struct module_env *env, int id) { +void ipset_deinit(struct module_env *env, int id) { filter_dev dev; struct ipset_env *ipset_env; @@ -497,7 +496,7 @@ size_t ipset_get_mem(struct module_env *env, int id) { */ static struct module_func_block ipset_block = { "ipset", - &ipset_init, &ipset_deinit, &ipset_setup, &ipset_desetup, + &ipset_init, &ipset_deinit, &ipset_startup, &ipset_destartup, &ipset_operate, &ipset_inform_super, &ipset_clear, &ipset_get_mem }; diff --git a/ipset/ipset.h b/ipset/ipset.h index 66a4378b6..195c7db93 100644 --- a/ipset/ipset.h +++ b/ipset/ipset.h @@ -50,14 +50,14 @@ struct ipset_qstate { int dummy; }; +/** Startup the ipset module */ +int ipset_startup(struct module_env* env, int id); +/** Destartup the ipset module */ +void ipset_destartup(struct module_env* env, int id); /** Init the ipset module */ int ipset_init(struct module_env* env, int id); /** Deinit the ipset module */ -int ipset_deinit(struct module_env* env, int id); -/** Setup the ipset module */ -int ipset_setup(struct module_env* env, int id); -/** Desetup the ipset module */ -void ipset_desetup(struct module_env* env, int id); +void ipset_deinit(struct module_env* env, int id); /** Operate on an event on a query (in qstate). */ void ipset_operate(struct module_qstate* qstate, enum module_ev event, int id, struct outbound_entry* outbound); diff --git a/iterator/iterator.c b/iterator/iterator.c index 5a9729ff0..c99d9504a 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -80,7 +80,7 @@ int BLACKLIST_PENALTY = (120000*4); static void target_count_increase_nx(struct iter_qstate* iq, int num); int -iter_setup(struct module_env* env, int id) +iter_init(struct module_env* env, int id) { struct iter_env* iter_env = (struct iter_env*)calloc(1, sizeof(struct iter_env)); @@ -114,7 +114,7 @@ caps_free(struct rbnode_type* n, void* ATTR_UNUSED(d)) } void -iter_desetup(struct module_env* env, int id) +iter_deinit(struct module_env* env, int id) { struct iter_env* iter_env; if(!env || !env->modinfo[id]) @@ -4489,8 +4489,8 @@ iter_get_mem(struct module_env* env, int id) */ static struct module_func_block iter_block = { "iterator", - &module_dummy_init, &module_dummy_init, &iter_setup, &iter_desetup, - &iter_operate, &iter_inform_super, &iter_clear, &iter_get_mem + &iter_init, &iter_deinit, NULL, NULL, &iter_operate, + &iter_inform_super, &iter_clear, &iter_get_mem }; struct module_func_block* diff --git a/iterator/iterator.h b/iterator/iterator.h index ec093014e..e253f3f7e 100644 --- a/iterator/iterator.h +++ b/iterator/iterator.h @@ -499,10 +499,10 @@ const char* iter_state_to_string(enum iter_state state); int iter_state_is_responsestate(enum iter_state s); /** iterator init */ -int iter_setup(struct module_env* env, int id); +int iter_init(struct module_env* env, int id); /** iterator deinit */ -void iter_desetup(struct module_env* env, int id); +void iter_deinit(struct module_env* env, int id); /** iterator operate on a query */ void iter_operate(struct module_qstate* qstate, enum module_ev event, int id, diff --git a/libunbound/context.c b/libunbound/context.c index 51f4474bb..f55aeb666 100644 --- a/libunbound/context.c +++ b/libunbound/context.c @@ -56,6 +56,9 @@ #include "iterator/iter_fwd.h" #include "iterator/iter_hints.h" +/** If the modules have started, once. */ +int modstack_started = 0; + int context_finalize(struct ub_ctx* ctx) { @@ -75,9 +78,12 @@ context_finalize(struct ub_ctx* ctx) ctx->pipe_pid = getpid(); cfg_apply_local_port_policy(cfg, 65536); config_apply(cfg); - if(!modstack_init(&ctx->mods, cfg->module_conf, ctx->env)) - return UB_INITFAIL; - if(!modstack_setup(&ctx->mods, cfg->module_conf, ctx->env)) + if(!modstack_started) { + modstack_started = 1; + if(!modstack_startup(&ctx->mods, cfg->module_conf, ctx->env)) + return UB_INITFAIL; + } + if(!modstack_call_init(&ctx->mods, cfg->module_conf, ctx->env)) return UB_INITFAIL; listen_setup_locks(); log_edns_known_options(VERB_ALGO, ctx->env); diff --git a/libunbound/context.h b/libunbound/context.h index c0fc80e57..0a48db72d 100644 --- a/libunbound/context.h +++ b/libunbound/context.h @@ -337,4 +337,7 @@ struct ctx_query* context_deserialize_answer(struct ub_ctx* ctx, struct ctx_query* context_deserialize_cancel(struct ub_ctx* ctx, uint8_t* p, uint32_t len); +/** If the modules have started. */ +extern int modstack_started; + #endif /* LIBUNBOUND_CONTEXT_H */ diff --git a/libunbound/libunbound.c b/libunbound/libunbound.c index 8a5e0188f..4b6e60c08 100644 --- a/libunbound/libunbound.c +++ b/libunbound/libunbound.c @@ -188,7 +188,8 @@ ub_ctx_create(void) int e = errno; ub_randfree(ctx->seed_rnd); config_delete(ctx->env->cfg); - modstack_desetup(&ctx->mods, ctx->env); + modstack_deinit(&ctx->mods, ctx->env); + modstack_destartup(&ctx->mods, ctx->env); listen_desetup_locks(); edns_known_options_delete(ctx->env); edns_strings_delete(ctx->env->edns_strings); @@ -202,7 +203,8 @@ ub_ctx_create(void) tube_delete(ctx->qq_pipe); ub_randfree(ctx->seed_rnd); config_delete(ctx->env->cfg); - modstack_desetup(&ctx->mods, ctx->env); + modstack_deinit(&ctx->mods, ctx->env); + modstack_destartup(&ctx->mods, ctx->env); listen_desetup_locks(); edns_known_options_delete(ctx->env); edns_strings_delete(ctx->env->edns_strings); @@ -360,8 +362,8 @@ ub_ctx_delete(struct ub_ctx* ctx) } libworker_delete_event(ctx->event_worker); - modstack_desetup(&ctx->mods, ctx->env); modstack_deinit(&ctx->mods, ctx->env); + modstack_destartup(&ctx->mods, ctx->env); a = ctx->alloc_list; while(a) { na = a->super; diff --git a/pythonmod/pythonmod.c b/pythonmod/pythonmod.c index 6bc6752e2..f397012ac 100644 --- a/pythonmod/pythonmod.c +++ b/pythonmod/pythonmod.c @@ -777,8 +777,8 @@ size_t pythonmod_get_mem(struct module_env* env, int id) */ static struct module_func_block pythonmod_block = { "python", - &module_dummy_init, &module_dummy_init, &pythonmod_init, &pythonmod_deinit, &pythonmod_operate, &pythonmod_inform_super, - &pythonmod_clear, &pythonmod_get_mem + &pythonmod_init, &pythonmod_deinit, NULL, NULL, &pythonmod_operate, + &pythonmod_inform_super, &pythonmod_clear, &pythonmod_get_mem }; struct module_func_block* pythonmod_get_funcblock(void) diff --git a/respip/respip.c b/respip/respip.c index 0dc6b626a..cfc6a4908 100644 --- a/respip/respip.c +++ b/respip/respip.c @@ -548,7 +548,7 @@ respip_copy_rrset(const struct ub_packed_rrset_key* key, struct regional* region } int -respip_setup(struct module_env* env, int id) +respip_init(struct module_env* env, int id) { (void)env; (void)id; @@ -556,7 +556,7 @@ respip_setup(struct module_env* env, int id) } void -respip_desetup(struct module_env* env, int id) +respip_deinit(struct module_env* env, int id) { (void)env; (void)id; @@ -1259,7 +1259,7 @@ respip_get_mem(struct module_env* env, int id) */ static struct module_func_block respip_block = { "respip", - &module_dummy_init, &module_dummy_init, &respip_setup, &respip_desetup, &respip_operate, + &respip_init, &respip_deinit, NULL, NULL, &respip_operate, &respip_inform_super, &respip_clear, &respip_get_mem }; diff --git a/respip/respip.h b/respip/respip.h index 3ebda5ce7..e4ab5cc9c 100644 --- a/respip/respip.h +++ b/respip/respip.h @@ -195,10 +195,10 @@ int respip_rewrite_reply(const struct query_info* qinfo, struct module_func_block* respip_get_funcblock(void); /** response-ip init */ -int respip_setup(struct module_env* env, int id); +int respip_init(struct module_env* env, int id); /** response-ip deinit */ -void respip_desetup(struct module_env* env, int id); +void respip_deinit(struct module_env* env, int id); /** response-ip operate on a query */ void respip_operate(struct module_qstate* qstate, enum module_ev event, int id, diff --git a/services/modstack.c b/services/modstack.c index 77e33263c..85f0afc5d 100644 --- a/services/modstack.c +++ b/services/modstack.c @@ -216,7 +216,7 @@ module_func_block* module_factory(const char** str) } int -modstack_init(struct module_stack* stack, const char* module_conf, +modstack_startup(struct module_stack* stack, const char* module_conf, struct module_env* env) { int i; @@ -227,11 +227,13 @@ modstack_init(struct module_stack* stack, const char* module_conf, return 0; } for(i=0; inum; i++) { - verbose(VERB_OPS, "init module %d: %s", + if(stack->mod[i]->startup == NULL) + continue; + verbose(VERB_OPS, "startup module %d: %s", i, stack->mod[i]->name); - fptr_ok(fptr_whitelist_mod_init(stack->mod[i]->init)); - if(!(*stack->mod[i]->init)(env, i)) { - log_err("module init for module %s failed", + fptr_ok(fptr_whitelist_mod_startup(stack->mod[i]->startup)); + if(!(*stack->mod[i]->startup)(env, i)) { + log_err("module startup for module %s failed", stack->mod[i]->name); return 0; } @@ -240,7 +242,7 @@ modstack_init(struct module_stack* stack, const char* module_conf, } int -modstack_setup(struct module_stack* stack, const char* module_conf, +modstack_call_init(struct module_stack* stack, const char* module_conf, struct module_env* env) { int i; @@ -254,11 +256,11 @@ modstack_setup(struct module_stack* stack, const char* module_conf, return 0; } module_conf += strlen(stack->mod[i]->name); - verbose(VERB_OPS, "setup module %d: %s", + verbose(VERB_OPS, "init module %d: %s", i, stack->mod[i]->name); - fptr_ok(fptr_whitelist_mod_setup(stack->mod[i]->setup)); - if(!(*stack->mod[i]->setup)(env, i)) { - log_err("module setup for module %s failed", + fptr_ok(fptr_whitelist_mod_init(stack->mod[i]->init)); + if(!(*stack->mod[i]->init)(env, i)) { + log_err("module init for module %s failed", stack->mod[i]->name); return 0; } @@ -267,22 +269,24 @@ modstack_setup(struct module_stack* stack, const char* module_conf, } void -modstack_desetup(struct module_stack* stack, struct module_env* env) +modstack_deinit(struct module_stack* stack, struct module_env* env) { int i; for(i=0; inum; i++) { - fptr_ok(fptr_whitelist_mod_desetup(stack->mod[i]->desetup)); - (*stack->mod[i]->desetup)(env, i); + fptr_ok(fptr_whitelist_mod_deinit(stack->mod[i]->deinit)); + (*stack->mod[i]->deinit)(env, i); } } void -modstack_deinit(struct module_stack* stack, struct module_env* env) +modstack_destartup(struct module_stack* stack, struct module_env* env) { int i; for(i=0; inum; i++) { - fptr_ok(fptr_whitelist_mod_deinit(stack->mod[i]->deinit)); - (*stack->mod[i]->deinit)(env, i); + if(stack->mod[i]->destartup == NULL) + continue; + fptr_ok(fptr_whitelist_mod_destartup(stack->mod[i]->destartup)); + (*stack->mod[i]->destartup)(env, i); } stack->num = 0; free(stack->mod); diff --git a/services/modstack.h b/services/modstack.h index 3e79595ed..3f01be619 100644 --- a/services/modstack.h +++ b/services/modstack.h @@ -60,10 +60,9 @@ struct module_stack { * @param module_conf: string what modules to initialize * @param env: module environment which is inited by the modules. * environment should have a superalloc, cfg, - * env.need_to_validate is set by the modules. * @return on false a module init failed. */ -int modstack_init(struct module_stack* stack, const char* module_conf, +int modstack_startup(struct module_stack* stack, const char* module_conf, struct module_env* env); /** @@ -89,31 +88,31 @@ struct module_func_block* module_factory(const char** str); const char** module_list_avail(void); /** - * Setup modules. Calls module_setup(). + * Init modules. Calls module_init(). * @param stack: It is modstack_setupped(). * @param module_conf: module ordering to check against the ordering in stack. - * fails on changed ordering. + * fails on changed ordering. * @param env: module environment which is inited by the modules. * environment should have a superalloc, cfg, * env.need_to_validate is set by the modules. * @return on false a module init failed. */ -int modstack_setup(struct module_stack* stack, const char* module_conf, +int modstack_call_init(struct module_stack* stack, const char* module_conf, struct module_env* env); /** - * Desetup the modules + * Deinint the modules * @param stack: made empty. * @param env: module env for module deinit() calls. */ -void modstack_desetup(struct module_stack* stack, struct module_env* env); +void modstack_deinit(struct module_stack* stack, struct module_env* env); /** - * Deinit the modules, deinit, delete. + * Destartup the modules, close, delete. * @param stack: made empty. - * @param env: module env for module deinit() calls. + * @param env: module env for module destartup() calls. */ -void modstack_deinit(struct module_stack* stack, struct module_env* env); +void modstack_destartup(struct module_stack* stack, struct module_env* env); /** * Find index of module by name. diff --git a/smallapp/unbound-checkconf.c b/smallapp/unbound-checkconf.c index 6271d10cf..6cc5285ec 100644 --- a/smallapp/unbound-checkconf.c +++ b/smallapp/unbound-checkconf.c @@ -140,12 +140,13 @@ check_mod(struct config_file* cfg, struct module_func_block* fb) fatal_exit("out of memory"); if(!edns_known_options_init(&env)) fatal_exit("out of memory"); - if(!(*fb->setup)(&env, 0)) - fatal_exit("bad config for %s module", fb->name); - if(!(*fb->setup)(&env, 0)) - fatal_exit("bad config for %s module", fb->name); - (*fb->desetup)(&env, 0); + if(fb->startup && !(*fb->startup)(&env, 0)) + fatal_exit("bad config during startup for %s module", fb->name); + if(!(*fb->init)(&env, 0)) + fatal_exit("bad config during init for %s module", fb->name); (*fb->deinit)(&env, 0); + if(fb->destartup) + (*fb->destartup)(&env, 0); sldns_buffer_free(env.scratch_buffer); regional_destroy(env.scratch); edns_known_options_delete(&env); diff --git a/testcode/unitzonemd.c b/testcode/unitzonemd.c index 23c9f7010..5336a1224 100644 --- a/testcode/unitzonemd.c +++ b/testcode/unitzonemd.c @@ -287,9 +287,11 @@ static void zonemd_verify_test(char* zname, char* zfile, char* tastr, env.auth_zones = auth_zones_create(); if(!env.auth_zones) fatal_exit("out of memory"); - modstack_init(&mods); - if(!modstack_setup(&mods, env.cfg->module_conf, &env)) - fatal_exit("could not modstack_setup"); + memset(&mods, 0, sizeof(mods)); + if(!modstack_startup(&mods, env.cfg->module_conf, &env)) + fatal_exit("could not modstack_startup"); + if(!modstack_call_init(&mods, env.cfg->module_conf, &env)) + fatal_exit("could not modstack_call_init"); env.mesh = mesh_create(&mods, &env); if(!env.mesh) fatal_exit("out of memory"); @@ -327,7 +329,8 @@ static void zonemd_verify_test(char* zname, char* zfile, char* tastr, /* desetup test harness */ mesh_delete(env.mesh); - modstack_desetup(&mods, &env); + modstack_deinit(&mods, &env); + modstack_destartup(&mods, &env); auth_zones_delete(env.auth_zones); anchors_delete(env.anchors); config_delete(env.cfg); diff --git a/util/fptr_wlist.c b/util/fptr_wlist.c index d24941321..b13988835 100644 --- a/util/fptr_wlist.c +++ b/util/fptr_wlist.c @@ -395,30 +395,10 @@ fptr_whitelist_modenv_detect_cycle(int (*fptr)( int fptr_whitelist_mod_init(int (*fptr)(struct module_env* env, int id)) { - if(fptr == &module_dummy_init) return 1; -#ifdef USE_IPSET - else if(fptr == &ipset_init) return 1; -#endif - return 0; -} - -int -fptr_whitelist_mod_deinit(int (*fptr)(struct module_env* env, int id)) -{ - if(fptr == &module_dummy_init) return 1; -#ifdef USE_IPSET - else if(fptr == &ipset_deinit) return 1; -#endif - return 0; -} - -int -fptr_whitelist_mod_setup(int (*fptr)(struct module_env* env, int id)) -{ - if(fptr == &iter_setup) return 1; - else if(fptr == &val_setup) return 1; - else if(fptr == &dns64_setup) return 1; - else if(fptr == &respip_setup) return 1; + if(fptr == &iter_init) return 1; + else if(fptr == &val_init) return 1; + else if(fptr == &dns64_init) return 1; + else if(fptr == &respip_init) return 1; #ifdef WITH_PYTHONMODULE else if(fptr == &pythonmod_init) return 1; #endif @@ -426,27 +406,28 @@ fptr_whitelist_mod_setup(int (*fptr)(struct module_env* env, int id)) else if(fptr == &dynlibmod_init) return 1; #endif #ifdef USE_CACHEDB - else if(fptr == &cachedb_setup) return 1; + else if(fptr == &cachedb_init) return 1; #endif #ifdef USE_IPSECMOD - else if(fptr == &ipsecmod_setup) return 1; + else if(fptr == &ipsecmod_init) return 1; #endif #ifdef CLIENT_SUBNET - else if(fptr == &subnetmod_setup) return 1; + else if(fptr == &subnetmod_init) return 1; #endif #ifdef USE_IPSET - else if(fptr == &ipset_setup) return 1; + else if(fptr == &ipset_init) return 1; #endif return 0; + } -int -fptr_whitelist_mod_desetup(void (*fptr)(struct module_env* env, int id)) +int +fptr_whitelist_mod_deinit(void (*fptr)(struct module_env* env, int id)) { - if(fptr == &iter_desetup) return 1; - else if(fptr == &val_desetup) return 1; - else if(fptr == &dns64_desetup) return 1; - else if(fptr == &respip_desetup) return 1; + if(fptr == &iter_deinit) return 1; + else if(fptr == &val_deinit) return 1; + else if(fptr == &dns64_deinit) return 1; + else if(fptr == &respip_deinit) return 1; #ifdef WITH_PYTHONMODULE else if(fptr == &pythonmod_deinit) return 1; #endif @@ -454,16 +435,34 @@ fptr_whitelist_mod_desetup(void (*fptr)(struct module_env* env, int id)) else if(fptr == &dynlibmod_deinit) return 1; #endif #ifdef USE_CACHEDB - else if(fptr == &cachedb_desetup) return 1; + else if(fptr == &cachedb_deinit) return 1; #endif #ifdef USE_IPSECMOD - else if(fptr == &ipsecmod_desetup) return 1; + else if(fptr == &ipsecmod_deinit) return 1; #endif #ifdef CLIENT_SUBNET - else if(fptr == &subnetmod_desetup) return 1; + else if(fptr == &subnetmod_deinit) return 1; +#endif +#ifdef USE_IPSET + else if(fptr == &ipset_deinit) return 1; #endif + return 0; +} + +int +fptr_whitelist_mod_startup(int (*fptr)(struct module_env* env, int id)) +{ +#ifdef USE_IPSET + if(fptr == &ipset_startup) return 1; +#endif + return 0; +} + +int +fptr_whitelist_mod_destartup(void (*fptr)(struct module_env* env, int id)) +{ #ifdef USE_IPSET - else if(fptr == &ipset_desetup) return 1; + if(fptr == &ipset_destartup) return 1; #endif return 0; } diff --git a/util/fptr_wlist.h b/util/fptr_wlist.h index cf70c7066..fb2475cce 100644 --- a/util/fptr_wlist.h +++ b/util/fptr_wlist.h @@ -276,23 +276,23 @@ int fptr_whitelist_mod_init(int (*fptr)(struct module_env* env, int id)); * @param fptr: function pointer to check. * @return false if not in whitelist. */ -int fptr_whitelist_mod_deinit(int (*fptr)(struct module_env* env, int id)); +int fptr_whitelist_mod_deinit(void (*fptr)(struct module_env* env, int id)); /** - * Check function pointer whitelist for module setup call values. + * Check function pointer whitelist for module startup call values. * * @param fptr: function pointer to check. * @return false if not in whitelist. */ -int fptr_whitelist_mod_setup(int (*fptr)(struct module_env* env, int id)); +int fptr_whitelist_mod_startup(int (*fptr)(struct module_env* env, int id)); /** - * Check function pointer whitelist for module desetup call values. + * Check function pointer whitelist for module destartup call values. * * @param fptr: function pointer to check. * @return false if not in whitelist. */ -int fptr_whitelist_mod_desetup(void (*fptr)(struct module_env* env, int id)); +int fptr_whitelist_mod_destartup(void (*fptr)(struct module_env* env, int id)); /** * Check function pointer whitelist for module operate call values. diff --git a/util/module.c b/util/module.c index 1f0cdbef3..90a155b5e 100644 --- a/util/module.c +++ b/util/module.c @@ -415,8 +415,3 @@ copy_state_to_super(struct module_qstate* qstate, int ATTR_UNUSED(id), super->was_ratelimited = qstate->was_ratelimited; } } - -int module_dummy_init(struct module_env* env, int id) -{ - return 1; -} diff --git a/util/module.h b/util/module.h index b140fd2bb..702372cb8 100644 --- a/util/module.h +++ b/util/module.h @@ -713,39 +713,42 @@ struct module_func_block { const char* name; /** - * initialise the module. This is called only once at startup. - * Privileged operations like opening device files may be done here. + * Initialise the module. Called when restarting or reloading the + * daemon. + * This is the place to apply settings from the config file. + * @param env: module environment. * @param id: module id number. * return: 0 on error */ int (*init)(struct module_env* env, int id); /** - * deinitialise the module. This is called only once before shutdown to - * free resources allocated during init(). - * Closing privileged ports or files must be done here. + * Deinitialise the module, undo stuff done during init(). + * Called before reloading the daemon. + * @param env: module environment. * @param id: module id number. - * return: 0 on error */ - int (*deinit)(struct module_env* env, int id); + void (*deinit)(struct module_env* env, int id); /** - * setup the module. Called when restarting or reloading the - * daemon. - * This is the place to apply settings from the config file. + * Set up the module for start. This is called only once at startup. + * Privileged operations like opening device files may be done here. + * The function ptr can be NULL, if it is not used. * @param env: module environment. * @param id: module id number. * return: 0 on error */ - int (*setup)(struct module_env* env, int id); + int (*startup)(struct module_env* env, int id); /** - * de-setup, undo stuff done during setup(). - * Called before reloading the daemon. + * Close down the module for stop. This is called only once before + * shutdown to free resources allocated during startup(). + * Closing privileged ports or files must be done here. + * The function ptr can be NULL, if it is not used. * @param env: module environment. * @param id: module id number. */ - void (*desetup)(struct module_env* env, int id); + void (*destartup)(struct module_env* env, int id); /** * accept a new query, or work further on existing query. @@ -983,6 +986,4 @@ void log_edns_known_options(enum verbosity_value level, void copy_state_to_super(struct module_qstate* qstate, int id, struct module_qstate* super); -int module_dummy_init(struct module_env* env, int id); - #endif /* UTIL_MODULE_H */ diff --git a/validator/validator.c b/validator/validator.c index 2a3be7e40..3f62733c7 100644 --- a/validator/validator.c +++ b/validator/validator.c @@ -185,7 +185,7 @@ val_apply_cfg(struct module_env* env, struct val_env* val_env, void ecdsa_evp_workaround_init(void); #endif int -val_setup(struct module_env* env, int id) +val_init(struct module_env* env, int id) { struct val_env* val_env = (struct val_env*)calloc(1, sizeof(struct val_env)); @@ -221,7 +221,7 @@ val_setup(struct module_env* env, int id) } void -val_desetup(struct module_env* env, int id) +val_deinit(struct module_env* env, int id) { struct val_env* val_env; if(!env || !env->modinfo[id]) @@ -3344,8 +3344,8 @@ val_get_mem(struct module_env* env, int id) */ static struct module_func_block val_block = { "validator", - &module_dummy_init, &module_dummy_init, &val_setup, &val_desetup, - &val_operate, &val_inform_super, &val_clear, &val_get_mem + &val_init, &val_deinit, NULL, NULL, &val_operate, &val_inform_super, + &val_clear, &val_get_mem }; struct module_func_block* diff --git a/validator/validator.h b/validator/validator.h index 844dfe7be..72f44b16e 100644 --- a/validator/validator.h +++ b/validator/validator.h @@ -246,10 +246,10 @@ struct module_func_block* val_get_funcblock(void); const char* val_state_to_string(enum val_state state); /** validator init */ -int val_setup(struct module_env* env, int id); +int val_init(struct module_env* env, int id); /** validator deinit */ -void val_desetup(struct module_env* env, int id); +void val_deinit(struct module_env* env, int id); /** validator operate on a query */ void val_operate(struct module_qstate* qstate, enum module_ev event, int id, From 2279cde8f7a4fcb8f2fbea624a16f5713b201775 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 1 Jul 2024 17:02:49 +0200 Subject: [PATCH 06/66] ipset-pf-support, fix to remove unused include, free at end, adjust qname for comparison. --- ipset/ipset.c | 51 +++++++++++++++++++++++---------------------------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/ipset/ipset.c b/ipset/ipset.c index 9d1a8aa43..8645c78b5 100644 --- a/ipset/ipset.c +++ b/ipset/ipset.c @@ -17,8 +17,6 @@ #include "sldns/wire2str.h" #include "sldns/parseutil.h" -#include - #ifdef HAVE_NET_PFVAR_H #include #include @@ -225,7 +223,7 @@ ipset_add_rrset_data(struct ipset_env *ie, static int ipset_check_zones_for_rrset(struct module_env *env, struct ipset_env *ie, - struct ub_packed_rrset_key *rrset, const char *qname, const int qlen, + struct ub_packed_rrset_key *rrset, const char *qname, int qlen, const char *setname, int af) { static char dname[BUFF_LEN]; @@ -243,6 +241,9 @@ ipset_check_zones_for_rrset(struct module_env *env, struct ipset_env *ie, if (dname[dlen - 1] == '.') { dlen--; } + if (qname[qlen - 1] == '.') { + qlen--; + } for (p = env->cfg->local_zones_ipset; p; p = p->next) { ds = NULL; @@ -344,10 +345,24 @@ int ipset_startup(struct module_env* env, int id) { } void ipset_destartup(struct module_env* env, int id) { - struct ipset_env *ipset_env = env->modinfo[id]; -#ifdef HAVE_NET_PFVAR_H - close((filter_dev)ipset_env->dev); + filter_dev dev; + struct ipset_env *ipset_env; + + if (!env || !env->modinfo[id]) { + return; + } + ipset_env = (struct ipset_env*)env->modinfo[id]; + + dev = (filter_dev)ipset_env->dev; + if (dev) { +#if HAVE_NET_PFVAR_H + close(dev); +#else + mnl_socket_close(dev); #endif + ipset_env->dev = NULL; + } + free(ipset_env); env->modinfo[id] = NULL; } @@ -369,28 +384,8 @@ int ipset_init(struct module_env* env, int id) { return 1; } -void ipset_deinit(struct module_env *env, int id) { - filter_dev dev; - struct ipset_env *ipset_env; - - if (!env || !env->modinfo[id]) { - return; - } - - ipset_env = (struct ipset_env *)env->modinfo[id]; - - dev = (filter_dev)ipset_env->dev; - if (dev) { -#if HAVE_NET_PFVAR_H - close(dev); -#else - mnl_socket_close(dev); -#endif - ipset_env->dev = NULL; - } - - free(ipset_env); - env->modinfo[id] = NULL; +void ipset_deinit(struct module_env *ATTR_UNUSED(env), int ATTR_UNUSED(id)) { + /* nothing */ } static int ipset_new(struct module_qstate* qstate, int id) { From 97ad1df34364b8265a9eff626a33166870d008f5 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 1 Jul 2024 17:06:11 +0200 Subject: [PATCH 07/66] ipset-pf-support, fix to log error on failure to open pf. --- ipset/ipset.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipset/ipset.c b/ipset/ipset.c index 8645c78b5..3cc906f32 100644 --- a/ipset/ipset.c +++ b/ipset/ipset.c @@ -57,7 +57,7 @@ static void * open_filter() { dev = open("/dev/pf", O_RDWR); if (dev == -1) { - log_err("open(\"/dev/pf\") failed"); + log_err("open(\"/dev/pf\") failed: %s", strerror(errno)); return NULL; } else From 03ac9022963a0fc401d04fa0eb0df47347d5bab9 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 1 Jul 2024 17:11:20 +0200 Subject: [PATCH 08/66] - ipset-pf-support, fix to skip unit test if no pf dev. --- testdata/ipset.tdir/ipset.pre | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/testdata/ipset.tdir/ipset.pre b/testdata/ipset.tdir/ipset.pre index 42c94fac4..7c61e6468 100644 --- a/testdata/ipset.tdir/ipset.pre +++ b/testdata/ipset.tdir/ipset.pre @@ -8,6 +8,11 @@ PRE="../.." if grep "define USE_IPSET 1" $PRE/config.h; then echo test enabled; else skip_test "test skipped"; fi +if grep "define HAVE_NET_PFVAR_H 1" $PRE/config.h; then + if test ! -f /dev/pf; then + skip_test "no /dev/pf" + fi +fi get_random_port 2 UNBOUND_PORT=$RND_PORT From 65e7253d190ba182dacc2869641e86e94292deaf Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 2 Jul 2024 09:08:27 +0200 Subject: [PATCH 09/66] ipset-pf-support, simplification of code. --- daemon/daemon.c | 8 +++----- libunbound/context.c | 12 +++--------- libunbound/context.h | 3 --- libunbound/libunbound.c | 8 ++++---- services/modstack.c | 19 ++++++++++++++----- services/modstack.h | 12 +++++++++--- testcode/unitzonemd.c | 4 ++-- 7 files changed, 35 insertions(+), 31 deletions(-) diff --git a/daemon/daemon.c b/daemon/daemon.c index eb933cc93..ab3d182c3 100644 --- a/daemon/daemon.c +++ b/daemon/daemon.c @@ -259,7 +259,7 @@ daemon_init(void) tzset(); #endif daemon->need_to_exit = 0; - memset(&daemon->mods, 0, sizeof(daemon->mods)); + modstack_init(&daemon->mods); if(!(daemon->env = (struct module_env*)calloc(1, sizeof(*daemon->env)))) { free(daemon); @@ -467,9 +467,7 @@ static void daemon_setup_modules(struct daemon* daemon) daemon->env->alloc = &daemon->superalloc; daemon->env->worker = NULL; daemon->env->need_to_validate = 0; /* set by module init below */ - if(daemon->mods.num != 0) - modstack_deinit(&daemon->mods, daemon->env); - if(!modstack_call_init(&daemon->mods, daemon->cfg->module_conf, + if(!modstack_setup(&daemon->mods, daemon->cfg->module_conf, daemon->env)) { fatal_exit("failed to setup modules"); } @@ -906,7 +904,7 @@ daemon_delete(struct daemon* daemon) size_t i; if(!daemon) return; - modstack_deinit(&daemon->mods, daemon->env); + modstack_desetup(&daemon->mods, daemon->env); modstack_destartup(&daemon->mods, daemon->env); daemon_remote_delete(daemon->rc); for(i = 0; i < daemon->num_ports; i++) diff --git a/libunbound/context.c b/libunbound/context.c index f55aeb666..5b2d36b2d 100644 --- a/libunbound/context.c +++ b/libunbound/context.c @@ -56,9 +56,6 @@ #include "iterator/iter_fwd.h" #include "iterator/iter_hints.h" -/** If the modules have started, once. */ -int modstack_started = 0; - int context_finalize(struct ub_ctx* ctx) { @@ -78,12 +75,9 @@ context_finalize(struct ub_ctx* ctx) ctx->pipe_pid = getpid(); cfg_apply_local_port_policy(cfg, 65536); config_apply(cfg); - if(!modstack_started) { - modstack_started = 1; - if(!modstack_startup(&ctx->mods, cfg->module_conf, ctx->env)) - return UB_INITFAIL; - } - if(!modstack_call_init(&ctx->mods, cfg->module_conf, ctx->env)) + if(!modstack_startup(&ctx->mods, cfg->module_conf, ctx->env)) + return UB_INITFAIL; + if(!modstack_setup(&ctx->mods, cfg->module_conf, ctx->env)) return UB_INITFAIL; listen_setup_locks(); log_edns_known_options(VERB_ALGO, ctx->env); diff --git a/libunbound/context.h b/libunbound/context.h index 0a48db72d..c0fc80e57 100644 --- a/libunbound/context.h +++ b/libunbound/context.h @@ -337,7 +337,4 @@ struct ctx_query* context_deserialize_answer(struct ub_ctx* ctx, struct ctx_query* context_deserialize_cancel(struct ub_ctx* ctx, uint8_t* p, uint32_t len); -/** If the modules have started. */ -extern int modstack_started; - #endif /* LIBUNBOUND_CONTEXT_H */ diff --git a/libunbound/libunbound.c b/libunbound/libunbound.c index 4b6e60c08..561e41fba 100644 --- a/libunbound/libunbound.c +++ b/libunbound/libunbound.c @@ -172,7 +172,7 @@ static struct ub_ctx* ub_ctx_create_nopipe(void) ctx->env->alloc = &ctx->superalloc; ctx->env->worker = NULL; ctx->env->need_to_validate = 0; - memset(&ctx->mods, 0, sizeof(ctx->mods)); + modstack_init(&ctx->mods); ctx->env->modstack = &ctx->mods; rbtree_init(&ctx->queries, &context_query_cmp); return ctx; @@ -188,7 +188,7 @@ ub_ctx_create(void) int e = errno; ub_randfree(ctx->seed_rnd); config_delete(ctx->env->cfg); - modstack_deinit(&ctx->mods, ctx->env); + modstack_desetup(&ctx->mods, ctx->env); modstack_destartup(&ctx->mods, ctx->env); listen_desetup_locks(); edns_known_options_delete(ctx->env); @@ -203,7 +203,7 @@ ub_ctx_create(void) tube_delete(ctx->qq_pipe); ub_randfree(ctx->seed_rnd); config_delete(ctx->env->cfg); - modstack_deinit(&ctx->mods, ctx->env); + modstack_desetup(&ctx->mods, ctx->env); modstack_destartup(&ctx->mods, ctx->env); listen_desetup_locks(); edns_known_options_delete(ctx->env); @@ -362,7 +362,7 @@ ub_ctx_delete(struct ub_ctx* ctx) } libworker_delete_event(ctx->event_worker); - modstack_deinit(&ctx->mods, ctx->env); + modstack_desetup(&ctx->mods, ctx->env); modstack_destartup(&ctx->mods, ctx->env); a = ctx->alloc_list; while(a) { diff --git a/services/modstack.c b/services/modstack.c index 85f0afc5d..f32f942c8 100644 --- a/services/modstack.c +++ b/services/modstack.c @@ -88,6 +88,13 @@ count_modules(const char* s) return num; } +void +modstack_init(struct module_stack* stack) +{ + stack->num = 0; + stack->mod = NULL; +} + int modstack_config(struct module_stack* stack, const char* module_conf) { @@ -219,8 +226,8 @@ int modstack_startup(struct module_stack* stack, const char* module_conf, struct module_env* env) { - int i; - if (stack->num != 0) + int i; + if(stack->num != 0) fatal_exit("unexpected already initialised modules"); /* fixed setup of the modules */ if(!modstack_config(stack, module_conf)) { @@ -242,11 +249,13 @@ modstack_startup(struct module_stack* stack, const char* module_conf, } int -modstack_call_init(struct module_stack* stack, const char* module_conf, +modstack_setup(struct module_stack* stack, const char* module_conf, struct module_env* env) { int i; - env->need_to_validate = 0; /* set by module setup below */ + if(stack->num != 0) + modstack_desetup(stack, env); + env->need_to_validate = 0; /* set by module init below */ for(i=0; inum; i++) { while(*module_conf && isspace(*module_conf)) module_conf++; @@ -269,7 +278,7 @@ modstack_call_init(struct module_stack* stack, const char* module_conf, } void -modstack_deinit(struct module_stack* stack, struct module_env* env) +modstack_desetup(struct module_stack* stack, struct module_env* env) { int i; for(i=0; inum; i++) { diff --git a/services/modstack.h b/services/modstack.h index 3f01be619..deb8b634b 100644 --- a/services/modstack.h +++ b/services/modstack.h @@ -54,6 +54,12 @@ struct module_stack { struct module_func_block** mod; }; +/** + * Init a stack of modules + * @param stack: initialised as empty. + */ +void modstack_init(struct module_stack* stack); + /** * Initialises modules and assignes ids. * @param stack: Expected empty, filled according to module_conf @@ -97,15 +103,15 @@ const char** module_list_avail(void); * env.need_to_validate is set by the modules. * @return on false a module init failed. */ -int modstack_call_init(struct module_stack* stack, const char* module_conf, +int modstack_setup(struct module_stack* stack, const char* module_conf, struct module_env* env); /** - * Deinint the modules + * Desetup the modules, deinit. * @param stack: made empty. * @param env: module env for module deinit() calls. */ -void modstack_deinit(struct module_stack* stack, struct module_env* env); +void modstack_desetup(struct module_stack* stack, struct module_env* env); /** * Destartup the modules, close, delete. diff --git a/testcode/unitzonemd.c b/testcode/unitzonemd.c index 5336a1224..6ae29cdc4 100644 --- a/testcode/unitzonemd.c +++ b/testcode/unitzonemd.c @@ -290,7 +290,7 @@ static void zonemd_verify_test(char* zname, char* zfile, char* tastr, memset(&mods, 0, sizeof(mods)); if(!modstack_startup(&mods, env.cfg->module_conf, &env)) fatal_exit("could not modstack_startup"); - if(!modstack_call_init(&mods, env.cfg->module_conf, &env)) + if(!modstack_setup(&mods, env.cfg->module_conf, &env)) fatal_exit("could not modstack_call_init"); env.mesh = mesh_create(&mods, &env); if(!env.mesh) @@ -329,7 +329,7 @@ static void zonemd_verify_test(char* zname, char* zfile, char* tastr, /* desetup test harness */ mesh_delete(env.mesh); - modstack_deinit(&mods, &env); + modstack_desetup(&mods, &env); modstack_destartup(&mods, &env); auth_zones_delete(env.auth_zones); anchors_delete(env.anchors); From 538434186e99d1282bc23cc592111668c90298fc Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 2 Jul 2024 09:31:34 +0200 Subject: [PATCH 10/66] - Fix to remove unused include from the readzone test program. --- doc/Changelog | 3 +++ testcode/readzone.c | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/Changelog b/doc/Changelog index a764646e7..b27cc34e0 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,6 @@ +2 July 2024: Wouter + - Fix to remove unused include from the readzone test program. + 17 June 2024: Wouter - Fix ip-ratelimit-cookie setting, it was not applied. diff --git a/testcode/readzone.c b/testcode/readzone.c index 94511e577..f50eea31f 100644 --- a/testcode/readzone.c +++ b/testcode/readzone.c @@ -45,7 +45,6 @@ #include #include -#include #include "sldns/str2wire.h" #include "sldns/wire2str.h" From e54928a628897950677f6608b3fbfa01ab416bd8 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 2 Jul 2024 09:33:22 +0200 Subject: [PATCH 11/66] - Fix unused variable warning in do_cache_remove. --- daemon/remote.c | 2 ++ doc/Changelog | 1 + 2 files changed, 3 insertions(+) diff --git a/daemon/remote.c b/daemon/remote.c index 341e56054..a5db4330c 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -1682,6 +1682,8 @@ do_cache_remove(struct worker* worker, uint8_t* nm, size_t nmlen, #ifdef USE_CACHEDB if(remcachedb && worker->env.cachedb_enabled) cachedb_msg_remove_qinfo(&worker->env, &k); +#else + (void)remcachedb; #endif } diff --git a/doc/Changelog b/doc/Changelog index b27cc34e0..92a5d8913 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,5 +1,6 @@ 2 July 2024: Wouter - Fix to remove unused include from the readzone test program. + - Fix unused variable warning in do_cache_remove. 17 June 2024: Wouter - Fix ip-ratelimit-cookie setting, it was not applied. From 2fe4e2ec3eb1826d943ef90537c1cdefba6cfcce Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 2 Jul 2024 09:44:58 +0200 Subject: [PATCH 12/66] - Fix compile warning in worker pthread id printout. --- daemon/stats.c | 9 ++++++++- doc/Changelog | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/daemon/stats.c b/daemon/stats.c index 4855bf1c1..827110698 100644 --- a/daemon/stats.c +++ b/daemon/stats.c @@ -391,6 +391,13 @@ void server_stats_obtain(struct worker* worker, struct worker* who, else worker_send_cmd(who, worker_cmd_stats_noreset); verbose(VERB_ALGO, "wait for stats reply"); if(tube_wait_timeout(worker->cmd, STATS_THREAD_WAIT) == 0) { +#if defined(HAVE_PTHREAD) && defined(SIZEOF_PTHREAD_T) && defined(SIZEOF_UNSIGNED_LONG) +# if SIZEOF_PTHREAD_T == SIZEOF_UNSIGNED_LONG + unsigned long pthid = 0; + if(verbosity >= VERB_OPS) + memcpy(&pthid, &who->thr_id, sizeof(unsigned long)); +# endif +#endif verbose(VERB_OPS, "no response from thread %d" #ifdef HAVE_GETTID " LWP %u" @@ -407,7 +414,7 @@ void server_stats_obtain(struct worker* worker, struct worker* who, #endif #if defined(HAVE_PTHREAD) && defined(SIZEOF_PTHREAD_T) && defined(SIZEOF_UNSIGNED_LONG) # if SIZEOF_PTHREAD_T == SIZEOF_UNSIGNED_LONG - , (unsigned long)*((unsigned long*)&who->thr_id) + , pthid # endif #endif ); diff --git a/doc/Changelog b/doc/Changelog index 92a5d8913..101e80754 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,6 +1,7 @@ 2 July 2024: Wouter - Fix to remove unused include from the readzone test program. - Fix unused variable warning in do_cache_remove. + - Fix compile warning in worker pthread id printout. 17 June 2024: Wouter - Fix ip-ratelimit-cookie setting, it was not applied. From 96f8a94c19737c928a9ff71e4264c43e4766b9de Mon Sep 17 00:00:00 2001 From: Yorgos Thessalonikefs Date: Wed, 3 Jul 2024 10:08:44 +0200 Subject: [PATCH 13/66] - Fix for repeated use of a DNAME record: first overallocate and then move the exact size of the init value to avoid false positive heap overflow reads from address sanitizers. --- doc/Changelog | 5 +++++ validator/validator.c | 12 +++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/doc/Changelog b/doc/Changelog index 101e80754..c7fe7982f 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,8 @@ +3 July 2024: Yorgos + - Fix for repeated use of a DNAME record: first overallocate and then + move the exact size of the init value to avoid false positive heap + overflow reads from address sanitizers. + 2 July 2024: Wouter - Fix to remove unused include from the readzone test program. - Fix unused variable warning in do_cache_remove. diff --git a/validator/validator.c b/validator/validator.c index ec656db12..e608b9a0e 100644 --- a/validator/validator.c +++ b/validator/validator.c @@ -273,11 +273,17 @@ val_new_getmsg(struct module_qstate* qstate, struct val_qstate* vq) return NULL; if(vq->orig_msg->rep->rrset_count > RR_COUNT_MAX) return NULL; /* protect against integer overflow */ - vq->chase_reply->rrsets = regional_alloc_init(qstate->region, - vq->orig_msg->rep->rrsets, sizeof(struct ub_packed_rrset_key*) - * (vq->orig_msg->rep->rrset_count + vq->orig_msg->rep->an_numrrsets /* for extra DNAME records for unsigned CNAME repetitions*/) ); + /* Over allocate (+an_numrrsets) in case we need to put extra DNAME + * records for unsigned CNAME repetitions */ + vq->chase_reply->rrsets = regional_alloc(qstate->region, + sizeof(struct ub_packed_rrset_key*) * + (vq->orig_msg->rep->rrset_count + + vq->orig_msg->rep->an_numrrsets)); if(!vq->chase_reply->rrsets) return NULL; + memmove(vq->chase_reply->rrsets, vq->orig_msg->rep->rrsets, + sizeof(struct ub_packed_rrset_key*) * + vq->orig_msg->rep->rrset_count); vq->rrset_skip = 0; return vq; } From a19009df1dac8a2b30ee74d418efc816b2f7d55e Mon Sep 17 00:00:00 2001 From: Wouter Wijngaards Date: Wed, 3 Jul 2024 13:08:51 +0200 Subject: [PATCH 14/66] Apply suggestions from code review Co-authored-by: Yorgos Thessalonikefs --- ipset/ipset.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ipset/ipset.c b/ipset/ipset.c index 3cc906f32..d416f34fd 100644 --- a/ipset/ipset.c +++ b/ipset/ipset.c @@ -78,7 +78,7 @@ static void * open_filter() { log_err("ipset: could not bind netfilter."); return NULL; } - return dev; + return (void *)dev; } #endif @@ -282,7 +282,7 @@ static int ipset_update(struct module_env *env, struct dns_msg *return_msg, #ifdef HAVE_NET_PFVAR_H #else if (!ie->dev) { - // retry to create mnl socket + /* retry to create mnl socket */ ie->dev = open_filter(); if (!ie->dev) { log_warn("ipset open_filter failed"); From a335e601e42ea90d0bb9a028734ab45d263efd45 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 3 Jul 2024 13:53:44 +0200 Subject: [PATCH 15/66] ipset-pf-support, move startup and destartup to the front of the module func block functions, modstack call deinit function names, and detect module change when no startup functions are needed. --- cachedb/cachedb.c | 2 +- daemon/daemon.c | 15 ++++++++++----- daemon/daemon.h | 2 ++ dns64/dns64.c | 2 +- dynlibmod/dynlibmod.c | 2 +- edns-subnet/subnetmod.c | 2 +- ipsecmod/ipsecmod.c | 2 +- ipset/ipset.c | 2 +- iterator/iterator.c | 2 +- libunbound/context.c | 4 ++-- libunbound/libunbound.c | 15 +++++++++------ pythonmod/pythonmod.c | 2 +- respip/respip.c | 2 +- services/modstack.c | 42 +++++++++++++++++++++++++++++------------ services/modstack.h | 18 ++++++++++++------ testcode/unitzonemd.c | 9 +++++---- util/module.h | 32 +++++++++++++++---------------- validator/validator.c | 2 +- 18 files changed, 96 insertions(+), 61 deletions(-) diff --git a/cachedb/cachedb.c b/cachedb/cachedb.c index aca68d7ed..7a07b9976 100644 --- a/cachedb/cachedb.c +++ b/cachedb/cachedb.c @@ -983,7 +983,7 @@ cachedb_get_mem(struct module_env* env, int id) */ static struct module_func_block cachedb_block = { "cachedb", - &cachedb_init, &cachedb_deinit, NULL, NULL, &cachedb_operate, + NULL, NULL, &cachedb_init, &cachedb_deinit, &cachedb_operate, &cachedb_inform_super, &cachedb_clear, &cachedb_get_mem }; diff --git a/daemon/daemon.c b/daemon/daemon.c index ab3d182c3..d81bec844 100644 --- a/daemon/daemon.c +++ b/daemon/daemon.c @@ -450,7 +450,7 @@ daemon_privileged(struct daemon* daemon) daemon->env->cfg = daemon->cfg; daemon->env->alloc = &daemon->superalloc; daemon->env->worker = NULL; - if(!modstack_startup(&daemon->mods, daemon->cfg->module_conf, + if(!modstack_call_startup(&daemon->mods, daemon->cfg->module_conf, daemon->env)) { fatal_exit("failed to startup modules"); } @@ -466,11 +466,15 @@ static void daemon_setup_modules(struct daemon* daemon) daemon->env->cfg = daemon->cfg; daemon->env->alloc = &daemon->superalloc; daemon->env->worker = NULL; + if(daemon->mods_inited) { + modstack_call_deinit(&daemon->mods, daemon->env); + } daemon->env->need_to_validate = 0; /* set by module init below */ - if(!modstack_setup(&daemon->mods, daemon->cfg->module_conf, + if(!modstack_call_init(&daemon->mods, daemon->cfg->module_conf, daemon->env)) { - fatal_exit("failed to setup modules"); + fatal_exit("failed to init modules"); } + daemon->mods_inited = 1; log_edns_known_options(VERB_ALGO, daemon->env); } @@ -904,8 +908,9 @@ daemon_delete(struct daemon* daemon) size_t i; if(!daemon) return; - modstack_desetup(&daemon->mods, daemon->env); - modstack_destartup(&daemon->mods, daemon->env); + modstack_call_deinit(&daemon->mods, daemon->env); + modstack_call_destartup(&daemon->mods, daemon->env); + modstack_free(&daemon->mods); daemon_remote_delete(daemon->rc); for(i = 0; i < daemon->num_ports; i++) listening_ports_free(daemon->ports[i]); diff --git a/daemon/daemon.h b/daemon/daemon.h index 200149745..a6b6391cc 100644 --- a/daemon/daemon.h +++ b/daemon/daemon.h @@ -115,6 +115,8 @@ struct daemon { struct module_env* env; /** stack of module callbacks */ struct module_stack mods; + /** The module stack has been inited */ + int mods_inited; /** access control, which client IPs are allowed to connect */ struct acl_list* acl; /** access control, which interfaces are allowed to connect */ diff --git a/dns64/dns64.c b/dns64/dns64.c index d34eafb9f..3a43698a8 100644 --- a/dns64/dns64.c +++ b/dns64/dns64.c @@ -1044,7 +1044,7 @@ dns64_get_mem(struct module_env* env, int id) */ static struct module_func_block dns64_block = { "dns64", - &dns64_init, &dns64_deinit, NULL, NULL, &dns64_operate, + NULL, NULL, &dns64_init, &dns64_deinit, &dns64_operate, &dns64_inform_super, &dns64_clear, &dns64_get_mem }; diff --git a/dynlibmod/dynlibmod.c b/dynlibmod/dynlibmod.c index 06c4f08c1..c94115492 100644 --- a/dynlibmod/dynlibmod.c +++ b/dynlibmod/dynlibmod.c @@ -297,7 +297,7 @@ inplace_cb_delete_wrapped(struct module_env* env, enum inplace_cb_list_type type */ static struct module_func_block dynlibmod_block = { "dynlib", - &dynlibmod_init, &dynlibmod_deinit, NULL, NULL, &dynlibmod_operate, + NULL, NULL, &dynlibmod_init, &dynlibmod_deinit, &dynlibmod_operate, &dynlibmod_inform_super, &dynlibmod_clear, &dynlibmod_get_mem }; diff --git a/edns-subnet/subnetmod.c b/edns-subnet/subnetmod.c index 8e6db02e7..ead720f34 100644 --- a/edns-subnet/subnetmod.c +++ b/edns-subnet/subnetmod.c @@ -996,7 +996,7 @@ subnetmod_get_mem(struct module_env *env, int id) */ static struct module_func_block subnetmod_block = { "subnetcache", - &subnetmod_init, &subnetmod_deinit, NULL, NULL, &subnetmod_operate, + NULL, NULL, &subnetmod_init, &subnetmod_deinit, &subnetmod_operate, &subnetmod_inform_super, &subnetmod_clear, &subnetmod_get_mem }; diff --git a/ipsecmod/ipsecmod.c b/ipsecmod/ipsecmod.c index 137e22c33..76f9b1965 100644 --- a/ipsecmod/ipsecmod.c +++ b/ipsecmod/ipsecmod.c @@ -615,7 +615,7 @@ ipsecmod_get_mem(struct module_env* env, int id) */ static struct module_func_block ipsecmod_block = { "ipsecmod", - &ipsecmod_init, &ipsecmod_deinit, NULL, NULL, &ipsecmod_operate, + NULL, NULL, &ipsecmod_init, &ipsecmod_deinit, &ipsecmod_operate, &ipsecmod_inform_super, &ipsecmod_clear, &ipsecmod_get_mem }; diff --git a/ipset/ipset.c b/ipset/ipset.c index d416f34fd..1ad2c09f4 100644 --- a/ipset/ipset.c +++ b/ipset/ipset.c @@ -491,7 +491,7 @@ size_t ipset_get_mem(struct module_env *env, int id) { */ static struct module_func_block ipset_block = { "ipset", - &ipset_init, &ipset_deinit, &ipset_startup, &ipset_destartup, + &ipset_startup, &ipset_destartup, &ipset_init, &ipset_deinit, &ipset_operate, &ipset_inform_super, &ipset_clear, &ipset_get_mem }; diff --git a/iterator/iterator.c b/iterator/iterator.c index c99d9504a..cddb02717 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -4489,7 +4489,7 @@ iter_get_mem(struct module_env* env, int id) */ static struct module_func_block iter_block = { "iterator", - &iter_init, &iter_deinit, NULL, NULL, &iter_operate, + NULL, NULL, &iter_init, &iter_deinit, &iter_operate, &iter_inform_super, &iter_clear, &iter_get_mem }; diff --git a/libunbound/context.c b/libunbound/context.c index 5b2d36b2d..05f57987a 100644 --- a/libunbound/context.c +++ b/libunbound/context.c @@ -75,9 +75,9 @@ context_finalize(struct ub_ctx* ctx) ctx->pipe_pid = getpid(); cfg_apply_local_port_policy(cfg, 65536); config_apply(cfg); - if(!modstack_startup(&ctx->mods, cfg->module_conf, ctx->env)) + if(!modstack_call_startup(&ctx->mods, cfg->module_conf, ctx->env)) return UB_INITFAIL; - if(!modstack_setup(&ctx->mods, cfg->module_conf, ctx->env)) + if(!modstack_call_init(&ctx->mods, cfg->module_conf, ctx->env)) return UB_INITFAIL; listen_setup_locks(); log_edns_known_options(VERB_ALGO, ctx->env); diff --git a/libunbound/libunbound.c b/libunbound/libunbound.c index 561e41fba..9c6a3e309 100644 --- a/libunbound/libunbound.c +++ b/libunbound/libunbound.c @@ -188,8 +188,9 @@ ub_ctx_create(void) int e = errno; ub_randfree(ctx->seed_rnd); config_delete(ctx->env->cfg); - modstack_desetup(&ctx->mods, ctx->env); - modstack_destartup(&ctx->mods, ctx->env); + modstack_call_deinit(&ctx->mods, ctx->env); + modstack_call_destartup(&ctx->mods, ctx->env); + modstack_free(&ctx->mods); listen_desetup_locks(); edns_known_options_delete(ctx->env); edns_strings_delete(ctx->env->edns_strings); @@ -203,8 +204,9 @@ ub_ctx_create(void) tube_delete(ctx->qq_pipe); ub_randfree(ctx->seed_rnd); config_delete(ctx->env->cfg); - modstack_desetup(&ctx->mods, ctx->env); - modstack_destartup(&ctx->mods, ctx->env); + modstack_call_deinit(&ctx->mods, ctx->env); + modstack_call_destartup(&ctx->mods, ctx->env); + modstack_free(&ctx->mods); listen_desetup_locks(); edns_known_options_delete(ctx->env); edns_strings_delete(ctx->env->edns_strings); @@ -362,8 +364,9 @@ ub_ctx_delete(struct ub_ctx* ctx) } libworker_delete_event(ctx->event_worker); - modstack_desetup(&ctx->mods, ctx->env); - modstack_destartup(&ctx->mods, ctx->env); + modstack_call_deinit(&ctx->mods, ctx->env); + modstack_call_destartup(&ctx->mods, ctx->env); + modstack_free(&ctx->mods); a = ctx->alloc_list; while(a) { na = a->super; diff --git a/pythonmod/pythonmod.c b/pythonmod/pythonmod.c index f397012ac..e231ad079 100644 --- a/pythonmod/pythonmod.c +++ b/pythonmod/pythonmod.c @@ -777,7 +777,7 @@ size_t pythonmod_get_mem(struct module_env* env, int id) */ static struct module_func_block pythonmod_block = { "python", - &pythonmod_init, &pythonmod_deinit, NULL, NULL, &pythonmod_operate, + NULL, NULL, &pythonmod_init, &pythonmod_deinit, &pythonmod_operate, &pythonmod_inform_super, &pythonmod_clear, &pythonmod_get_mem }; diff --git a/respip/respip.c b/respip/respip.c index cfc6a4908..db48f176e 100644 --- a/respip/respip.c +++ b/respip/respip.c @@ -1259,7 +1259,7 @@ respip_get_mem(struct module_env* env, int id) */ static struct module_func_block respip_block = { "respip", - &respip_init, &respip_deinit, NULL, NULL, &respip_operate, + NULL, NULL, &respip_init, &respip_deinit, &respip_operate, &respip_inform_super, &respip_clear, &respip_get_mem }; diff --git a/services/modstack.c b/services/modstack.c index f32f942c8..6c8af0505 100644 --- a/services/modstack.c +++ b/services/modstack.c @@ -95,6 +95,16 @@ modstack_init(struct module_stack* stack) stack->mod = NULL; } +void +modstack_free(struct module_stack* stack) +{ + if(!stack) + return; + stack->num = 0; + free(stack->mod); + stack->mod = NULL; +} + int modstack_config(struct module_stack* stack, const char* module_conf) { @@ -223,7 +233,7 @@ module_func_block* module_factory(const char** str) } int -modstack_startup(struct module_stack* stack, const char* module_conf, +modstack_call_startup(struct module_stack* stack, const char* module_conf, struct module_env* env) { int i; @@ -249,22 +259,33 @@ modstack_startup(struct module_stack* stack, const char* module_conf, } int -modstack_setup(struct module_stack* stack, const char* module_conf, +modstack_call_init(struct module_stack* stack, const char* module_conf, struct module_env* env) { - int i; - if(stack->num != 0) - modstack_desetup(stack, env); + int i, changed = 0; env->need_to_validate = 0; /* set by module init below */ for(i=0; inum; i++) { while(*module_conf && isspace(*module_conf)) module_conf++; if(strncmp(stack->mod[i]->name, module_conf, strlen(stack->mod[i]->name))) { - log_err("changed module ordering during reload not supported"); - return 0; + if(stack->mod[i]->startup || stack->mod[i]->destartup) { + log_err("changed module ordering during reload not supported, for module that needs startup"); + return 0; + } else { + changed = 1; + } } module_conf += strlen(stack->mod[i]->name); + } + if(changed) { + modstack_free(stack); + if(!modstack_config(stack, module_conf)) { + return 0; + } + } + + for(i=0; inum; i++) { verbose(VERB_OPS, "init module %d: %s", i, stack->mod[i]->name); fptr_ok(fptr_whitelist_mod_init(stack->mod[i]->init)); @@ -278,7 +299,7 @@ modstack_setup(struct module_stack* stack, const char* module_conf, } void -modstack_desetup(struct module_stack* stack, struct module_env* env) +modstack_call_deinit(struct module_stack* stack, struct module_env* env) { int i; for(i=0; inum; i++) { @@ -288,7 +309,7 @@ modstack_desetup(struct module_stack* stack, struct module_env* env) } void -modstack_destartup(struct module_stack* stack, struct module_env* env) +modstack_call_destartup(struct module_stack* stack, struct module_env* env) { int i; for(i=0; inum; i++) { @@ -297,9 +318,6 @@ modstack_destartup(struct module_stack* stack, struct module_env* env) fptr_ok(fptr_whitelist_mod_destartup(stack->mod[i]->destartup)); (*stack->mod[i]->destartup)(env, i); } - stack->num = 0; - free(stack->mod); - stack->mod = NULL; } int diff --git a/services/modstack.h b/services/modstack.h index deb8b634b..5674aefdd 100644 --- a/services/modstack.h +++ b/services/modstack.h @@ -61,14 +61,20 @@ struct module_stack { void modstack_init(struct module_stack* stack); /** - * Initialises modules and assignes ids. + * Free the stack of modules + * @param stack: stack that frees up memory. + */ +void modstack_free(struct module_stack* stack); + +/** + * Initialises modules and assignes ids. Calls module_startup(). * @param stack: Expected empty, filled according to module_conf * @param module_conf: string what modules to initialize * @param env: module environment which is inited by the modules. * environment should have a superalloc, cfg, * @return on false a module init failed. */ -int modstack_startup(struct module_stack* stack, const char* module_conf, +int modstack_call_startup(struct module_stack* stack, const char* module_conf, struct module_env* env); /** @@ -103,22 +109,22 @@ const char** module_list_avail(void); * env.need_to_validate is set by the modules. * @return on false a module init failed. */ -int modstack_setup(struct module_stack* stack, const char* module_conf, +int modstack_call_init(struct module_stack* stack, const char* module_conf, struct module_env* env); /** - * Desetup the modules, deinit. + * Deinit the modules. * @param stack: made empty. * @param env: module env for module deinit() calls. */ -void modstack_desetup(struct module_stack* stack, struct module_env* env); +void modstack_call_deinit(struct module_stack* stack, struct module_env* env); /** * Destartup the modules, close, delete. * @param stack: made empty. * @param env: module env for module destartup() calls. */ -void modstack_destartup(struct module_stack* stack, struct module_env* env); +void modstack_call_destartup(struct module_stack* stack, struct module_env* env); /** * Find index of module by name. diff --git a/testcode/unitzonemd.c b/testcode/unitzonemd.c index 6ae29cdc4..81d92c102 100644 --- a/testcode/unitzonemd.c +++ b/testcode/unitzonemd.c @@ -288,9 +288,9 @@ static void zonemd_verify_test(char* zname, char* zfile, char* tastr, if(!env.auth_zones) fatal_exit("out of memory"); memset(&mods, 0, sizeof(mods)); - if(!modstack_startup(&mods, env.cfg->module_conf, &env)) + if(!modstack_call_startup(&mods, env.cfg->module_conf, &env)) fatal_exit("could not modstack_startup"); - if(!modstack_setup(&mods, env.cfg->module_conf, &env)) + if(!modstack_call_init(&mods, env.cfg->module_conf, &env)) fatal_exit("could not modstack_call_init"); env.mesh = mesh_create(&mods, &env); if(!env.mesh) @@ -329,8 +329,9 @@ static void zonemd_verify_test(char* zname, char* zfile, char* tastr, /* desetup test harness */ mesh_delete(env.mesh); - modstack_desetup(&mods, &env); - modstack_destartup(&mods, &env); + modstack_call_deinit(&mods, &env); + modstack_call_destartup(&mods, &env); + modstack_free(&mods); auth_zones_delete(env.auth_zones); anchors_delete(env.anchors); config_delete(env.cfg); diff --git a/util/module.h b/util/module.h index 702372cb8..5bdb622a2 100644 --- a/util/module.h +++ b/util/module.h @@ -713,42 +713,42 @@ struct module_func_block { const char* name; /** - * Initialise the module. Called when restarting or reloading the - * daemon. - * This is the place to apply settings from the config file. + * Set up the module for start. This is called only once at startup. + * Privileged operations like opening device files may be done here. + * The function ptr can be NULL, if it is not used. * @param env: module environment. * @param id: module id number. * return: 0 on error */ - int (*init)(struct module_env* env, int id); + int (*startup)(struct module_env* env, int id); /** - * Deinitialise the module, undo stuff done during init(). - * Called before reloading the daemon. + * Close down the module for stop. This is called only once before + * shutdown to free resources allocated during startup(). + * Closing privileged ports or files must be done here. + * The function ptr can be NULL, if it is not used. * @param env: module environment. * @param id: module id number. */ - void (*deinit)(struct module_env* env, int id); + void (*destartup)(struct module_env* env, int id); /** - * Set up the module for start. This is called only once at startup. - * Privileged operations like opening device files may be done here. - * The function ptr can be NULL, if it is not used. + * Initialise the module. Called when restarting or reloading the + * daemon. + * This is the place to apply settings from the config file. * @param env: module environment. * @param id: module id number. * return: 0 on error */ - int (*startup)(struct module_env* env, int id); + int (*init)(struct module_env* env, int id); /** - * Close down the module for stop. This is called only once before - * shutdown to free resources allocated during startup(). - * Closing privileged ports or files must be done here. - * The function ptr can be NULL, if it is not used. + * Deinitialise the module, undo stuff done during init(). + * Called before reloading the daemon. * @param env: module environment. * @param id: module id number. */ - void (*destartup)(struct module_env* env, int id); + void (*deinit)(struct module_env* env, int id); /** * accept a new query, or work further on existing query. diff --git a/validator/validator.c b/validator/validator.c index 3f62733c7..d02f66d1f 100644 --- a/validator/validator.c +++ b/validator/validator.c @@ -3344,7 +3344,7 @@ val_get_mem(struct module_env* env, int id) */ static struct module_func_block val_block = { "validator", - &val_init, &val_deinit, NULL, NULL, &val_operate, &val_inform_super, + NULL, NULL, &val_init, &val_deinit, &val_operate, &val_inform_super, &val_clear, &val_get_mem }; From d3a22642724579934e6a4d5ac336bfba6d9d5561 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 3 Jul 2024 14:53:42 +0200 Subject: [PATCH 16/66] Changelog entry for #144 and #1098 - Fix #144: Port ipset to BSD pf tables. --- doc/Changelog | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/Changelog b/doc/Changelog index c7fe7982f..d72bf84c8 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -3,6 +3,9 @@ move the exact size of the init value to avoid false positive heap overflow reads from address sanitizers. +3 July 2024: Wouter + - Fix #144: Port ipset to BSD pf tables. + 2 July 2024: Wouter - Fix to remove unused include from the readzone test program. - Fix unused variable warning in do_cache_remove. From 36f9d1a2a9356940959ecb544484ac26f1f19d2d Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 3 Jul 2024 14:59:39 +0200 Subject: [PATCH 17/66] - Add unit test skip files and bison and flex output to gitignore. --- .gitignore | 4 ++++ doc/Changelog | 1 + 2 files changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 985e48869..2d67173eb 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,9 @@ /config.status /dnstap/dnstap_config.h /dnscrypt/dnscrypt_config.h +/util/configlexer.c +/util/configparser.c +/util/configparser.h /clubsyms.def /doc/example.conf /doc/libunbound.3 @@ -55,6 +58,7 @@ /pythonmod/unboundmodule.py /testdata/result.* /testdata/.done-* +/testdata/.skip-* /testdata/.perfstats.txt /doc/html /doc/xml diff --git a/doc/Changelog b/doc/Changelog index d72bf84c8..0cab2de33 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -5,6 +5,7 @@ 3 July 2024: Wouter - Fix #144: Port ipset to BSD pf tables. + - Add unit test skip files and bison and flex output to gitignore. 2 July 2024: Wouter - Fix to remove unused include from the readzone test program. From 94a94fd8c840a7d68fc09c5ae36c316bbf91fd8a Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 3 Jul 2024 15:49:13 +0200 Subject: [PATCH 18/66] - Fix to use modstack_init in zonemd unit test. --- doc/Changelog | 1 + testcode/unitzonemd.c | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/Changelog b/doc/Changelog index 0cab2de33..2fd03c09a 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -6,6 +6,7 @@ 3 July 2024: Wouter - Fix #144: Port ipset to BSD pf tables. - Add unit test skip files and bison and flex output to gitignore. + - Fix to use modstack_init in zonemd unit test. 2 July 2024: Wouter - Fix to remove unused include from the readzone test program. diff --git a/testcode/unitzonemd.c b/testcode/unitzonemd.c index 81d92c102..bf130df5a 100644 --- a/testcode/unitzonemd.c +++ b/testcode/unitzonemd.c @@ -256,7 +256,6 @@ static void zonemd_verify_test(char* zname, char* zfile, char* tastr, struct auth_zone* z; /* setup test harness */ - memset(&mods, 0, sizeof(mods)); memset(&env, 0, sizeof(env)); env.scratch = regional_create(); if(!env.scratch) @@ -287,7 +286,7 @@ static void zonemd_verify_test(char* zname, char* zfile, char* tastr, env.auth_zones = auth_zones_create(); if(!env.auth_zones) fatal_exit("out of memory"); - memset(&mods, 0, sizeof(mods)); + modstack_init(&mods); if(!modstack_call_startup(&mods, env.cfg->module_conf, &env)) fatal_exit("could not modstack_startup"); if(!modstack_call_init(&mods, env.cfg->module_conf, &env)) From 6eb3992c9e5f4e2eff2861a71000820ac2cd9f05 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 3 Jul 2024 15:51:22 +0200 Subject: [PATCH 19/66] - Fix to remove unneeded linebreak in fptr_wlist.c. --- doc/Changelog | 1 + util/fptr_wlist.c | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/Changelog b/doc/Changelog index 2fd03c09a..2bc8a9dd1 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -7,6 +7,7 @@ - Fix #144: Port ipset to BSD pf tables. - Add unit test skip files and bison and flex output to gitignore. - Fix to use modstack_init in zonemd unit test. + - Fix to remove unneeded linebreak in fptr_wlist.c. 2 July 2024: Wouter - Fix to remove unused include from the readzone test program. diff --git a/util/fptr_wlist.c b/util/fptr_wlist.c index b13988835..5e2789865 100644 --- a/util/fptr_wlist.c +++ b/util/fptr_wlist.c @@ -418,7 +418,6 @@ fptr_whitelist_mod_init(int (*fptr)(struct module_env* env, int id)) else if(fptr == &ipset_init) return 1; #endif return 0; - } int From 6b319c97ee2536569d6bc0f0f01f81f31163bc49 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 3 Jul 2024 16:42:52 +0200 Subject: [PATCH 20/66] - Fix compile warnings in fptr_wlist.c. --- doc/Changelog | 1 + util/fptr_wlist.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/doc/Changelog b/doc/Changelog index 2bc8a9dd1..325285337 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -8,6 +8,7 @@ - Add unit test skip files and bison and flex output to gitignore. - Fix to use modstack_init in zonemd unit test. - Fix to remove unneeded linebreak in fptr_wlist.c. + - Fix compile warnings in fptr_wlist.c. 2 July 2024: Wouter - Fix to remove unused include from the readzone test program. diff --git a/util/fptr_wlist.c b/util/fptr_wlist.c index 5e2789865..705dc1bbe 100644 --- a/util/fptr_wlist.c +++ b/util/fptr_wlist.c @@ -453,6 +453,8 @@ fptr_whitelist_mod_startup(int (*fptr)(struct module_env* env, int id)) { #ifdef USE_IPSET if(fptr == &ipset_startup) return 1; +#else + (void)fptr; #endif return 0; } @@ -462,6 +464,8 @@ fptr_whitelist_mod_destartup(void (*fptr)(struct module_env* env, int id)) { #ifdef USE_IPSET if(fptr == &ipset_destartup) return 1; +#else + (void)fptr; #endif return 0; } From ec2f45c6fdc5c28f13b1005857c459bc19ca9af0 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 4 Jul 2024 14:51:18 +0200 Subject: [PATCH 21/66] - Fix to print details about the failure to lookup a DNSKEY record when validation fails due to the missing DNSKEY. Also for key prime and DS lookups. --- doc/Changelog | 5 + testdata/val_failure_dnskey.rpl | 348 ++++++++++++++++++++++++++++++++ validator/validator.c | 68 +++++-- 3 files changed, 406 insertions(+), 15 deletions(-) create mode 100644 testdata/val_failure_dnskey.rpl diff --git a/doc/Changelog b/doc/Changelog index 325285337..2a7cb870d 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,8 @@ +4 July 2024: Wouter + - Fix to print details about the failure to lookup a DNSKEY record + when validation fails due to the missing DNSKEY. Also for key prime + and DS lookups. + 3 July 2024: Yorgos - Fix for repeated use of a DNAME record: first overallocate and then move the exact size of the init value to avoid false positive heap diff --git a/testdata/val_failure_dnskey.rpl b/testdata/val_failure_dnskey.rpl new file mode 100644 index 000000000..3f25f15b2 --- /dev/null +++ b/testdata/val_failure_dnskey.rpl @@ -0,0 +1,348 @@ +; config options +; The island of trust is at example.com +server: + trust-anchor: "example.com. 3600 IN DS 2854 3 1 46e4ffc6e9a4793b488954bd3f0cc6af0dfb201b" + trust-anchor: "example.net. 3600 IN DS 1444 8 2 69887be92d4848c0bc10acc95682a01e7e3b57ab0750a2ee6f72cac7191a64f1" + val-override-date: "20070916134226" + target-fetch-policy: "0 0 0 0 0" + qname-minimisation: "no" + fake-sha1: yes + trust-anchor-signaling: no + minimal-responses: no + log-servfail: yes + val-log-level: 2 + ede: yes + +stub-zone: + name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test validator with failure for chaing of trust lookup. +; The error message that is created, also for EDE is more extensive. + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS K.ROOT-SERVERS.NET. +SECTION ADDITIONAL +K.ROOT-SERVERS.NET. IN A 193.0.14.129 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +com. IN NS +SECTION AUTHORITY +com. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +net. IN NS +SECTION AUTHORITY +net. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 0 100 + ADDRESS 192.5.6.30 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +com. IN NS +SECTION ANSWER +com. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +example.com. IN NS +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +example.net. IN NS +SECTION AUTHORITY +example.net. IN NS ns.example.net. +SECTION ADDITIONAL +ns.example.net. IN A 1.2.3.5 +ENTRY_END +RANGE_END + +; ns.example.com. +RANGE_BEGIN 0 100 + ADDRESS 1.2.3.4 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +example.com. IN NS +SECTION ANSWER +example.com. IN NS ns.example.com. +example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854} +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCMSWxVehgOQLoYclB9PIAbNP229AIUeH0vNNGJhjnZiqgIOKvs1EhzqAo= ;{id = 2854} +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +ns.example.com. IN A +SECTION ANSWER +ns.example.com. IN A 1.2.3.4 +ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCMSWxVehgOQLoYclB9PIAbNP229AIUeH0vNNGJhjnZiqgIOKvs1EhzqAo= ;{id = 2854} +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +ns.example.com. IN AAAA +SECTION AUTHORITY +example.com. IN NS ns.example.com. +example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854} +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCMSWxVehgOQLoYclB9PIAbNP229AIUeH0vNNGJhjnZiqgIOKvs1EhzqAo= ;{id = 2854} +ENTRY_END + +; response to DNSKEY priming query +;ENTRY_BEGIN +;MATCH opcode qtype qname +;ADJUST copy_id +;REPLY QR NOERROR +;SECTION QUESTION +;example.com. IN DNSKEY +;SECTION ANSWER +;example.com. 3600 IN DNSKEY 256 3 3 ALXLUsWqUrY3JYER3T4TBJII s70j+sDS/UT2QRp61SE7S3E EXopNXoFE73JLRmvpi/UrOO/Vz4Se 6wXv/CYCKjGw06U4WRgR YXcpEhJROyNapmdIKSx hOzfLVE1gqA0PweZR8d tY3aNQSRn3sPpwJr6Mi /PqQKAMMrZ9ckJpf1+b QMOOvxgzz2U1GS18b3y ZKcgTMEaJzd/GZYzi/B N2DzQ0MsrSwYXfsNLFO Bbs8PJMW4LYIxeeOe6rUgkWOF 7CC9Dh/dduQ1QrsJhmZAEFfd6ByYV+ ;{id = 2854 (zsk), size = 1688b} +;example.com. 3600 IN RRSIG DNSKEY 3 2 3600 20070926134802 20070829134802 2854 example.com. MCwCFG1yhRNtTEa3Eno2zhVVuy2EJX3wAhQeLyUp6+UXcpC5qGNu9tkrTEgPUg== ;{id = 2854} +;SECTION AUTHORITY +;example.com. IN NS ns.example.com. +;example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854} +;SECTION ADDITIONAL +;ns.example.com. IN A 1.2.3.4 +;ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCMSWxVehgOQLoYclB9PIAbNP229AIUeH0vNNGJhjnZiqgIOKvs1EhzqAo= ;{id = 2854} +;ENTRY_END +; servfail for DNSKEY priming query +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA SERVFAIL +SECTION QUESTION +example.com. IN DNSKEY +ENTRY_END + +; response to query of interest +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION ANSWER +www.example.com. IN A 10.20.30.40 +ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCQMyTjn7WWwpwAR1LlVeLpRgZGuQIUCcJDEkwAuzytTDRlYK7nIMwH1CM= ;{id = 2854} +SECTION AUTHORITY +example.com. IN NS ns.example.com. +example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854} +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +www.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFC99iE9K5y2WNgI0gFvBWaTi9wm6AhUAoUqOpDtG5Zct+Qr9F3mSdnbc6V4= ;{id = 2854} +ENTRY_END +RANGE_END + +; ns.example.com. +RANGE_BEGIN 0 100 + ADDRESS 1.2.3.5 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +example.net. IN NS +SECTION ANSWER +example.net. 3600 IN NS ns.example.net. +example.net. 3600 IN RRSIG NS 8 2 3600 20070926134150 20070829134150 1444 example.net. nHpOqZb00nIGytQ1YmVoXEHURL/75dWhlKSEtRTorjVdPGPZNN7ziCWJW303v7u07TkZ+i6oFVEWG/SDR4ejn5o31UKJy1373PEH/cvPf9/44jw9gAFaHF1eO6ZQGaRQaeEpU06+xUcnc2QXFt6rNu60EsTvMRDN83bD+r7FA7Y= +SECTION ADDITIONAL +ns.example.net. 3600 IN A 1.2.3.5 +ns.example.net. 3600 IN RRSIG A 8 3 3600 20070926134150 20070829134150 1444 example.net. TgQ4nfGtLHuZXlC4JJlVQ6mejf1WJbstTxsh/kgMAc2tryOxF/gvGBHaMtz6oceFZrIgk6g3RYI1Gk5gjSFNADh+EIwI422M8XPAAxRLfFahiO4lr1aCo4c94TYeZNpnDKy81rINTz2hQE1pGWr8Z03ySABqSBnTE1FQt4N/JCo= +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +ns.example.net. IN A +SECTION ANSWER +ns.example.net. 3600 IN A 1.2.3.5 +ns.example.net. 3600 IN RRSIG A 8 3 3600 20070926134150 20070829134150 1444 example.net. TgQ4nfGtLHuZXlC4JJlVQ6mejf1WJbstTxsh/kgMAc2tryOxF/gvGBHaMtz6oceFZrIgk6g3RYI1Gk5gjSFNADh+EIwI422M8XPAAxRLfFahiO4lr1aCo4c94TYeZNpnDKy81rINTz2hQE1pGWr8Z03ySABqSBnTE1FQt4N/JCo= +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +ns.example.net. IN AAAA +SECTION AUTHORITY +example.net. 3600 IN SOA ns.example.net. host.example.net. 1 3600 300 7200 3600 +example.net. 3600 IN RRSIG SOA 8 2 3600 20070926134150 20070829134150 1444 example.net. P5FRQ4A/0n5owaBhZqlYBFD2PNAWJc5oxiDwvwh0hdjxETx8ta3EAvDKtNj5XZ5EKDAhP/tivd+Bq50I0xfRBmrouxgxjgnV3ye8zU+M1fXbuKpsWme9R3S4cs9WYfggTn7X00Af8m0tE62SLH/ZtOOQi2CvOPu7PXtHYT6KW4Q= +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +example.net. IN DNSKEY +SECTION ANSWER +example.net. 3600 IN DNSKEY 257 3 8 AwEAAbd9WqjzE2Pynz21OG5doSf9hFzMr5dhzz2waZ3vTa+0o5r7AjTAqmA1yH/B3+aAMihUm5ucZSfVqo7+kOaRE8yFj9aivOmA1n1+JLevJq/oyvQyjxQN2Qb89LyaNUT5oKZIiL+uyyhNW3KDR3SSbQ/GBwQNDHVcZi+JDR3RC0r7 ;{id = 1444 (ksk), size = 1024b} +example.net. 3600 IN RRSIG DNSKEY 8 2 3600 20070926134150 20070829134150 1444 example.net. hAAlJt/YwAgWBzseK0N42+ysSMaWgntcuftF8a43chLh+fbe3vPWrgwqr/Cic52tu4ZqMox592tqWDxAG7F1eDGfO0SfzS2C9Tc/Wnz5nFjFh75G4Mtt8DTv5vTyGUVX5zAFzV8SNijVC0o1F7MHaVPt3rFtjjg2zW/UOz2m9+U= +ENTRY_END + +; For sub1.example.net. zone; it is co-hosted with example.net, so that +; there can be failures for the DS lookup. But the data lookup succeeds. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www.sub1.example.net. IN A +SECTION ANSWER +www.sub1.example.net. IN A 10.20.30.41 +www.sub1.example.net. 3600 IN RRSIG A 8 4 3600 20070926134150 20070829134150 29332 sub1.example.net. NcFP77Hixawt8hb+STIbbeqdF9tWTuHsbGEB4agKXlwHqS0BnyA+It6+UdE57IF0Kbnc7gSuaslX9At8ctd4HuC/9F/osbo96o23JEfnXPky/r5SsLaeN5KmUmUVjG9oxyAEc6PVlaaQ5a/RhaxmDRaDiku2gB7KjdjPxwxe+Rc54GV2eM3GtcfT+oDakLdSSACqeVjUFIOtYMpG8jAHrBe4uSnjKI7O0fWDFN5OES6sN9iUS9/ceorIoF/gSIqM7xWEuPLxE2c5TtYJyPtMCeGJ9wBP4wrTXfJ58+Lg5SFKgEuKTvAqEv9KEwg/kJb1GQ+ho5XKFO6EII2iyeUK/w== +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR SERVFAIL +SECTION QUESTION +sub1.example.net. IN DS +SECTION ANSWER +; no DS for sub1.example.net id=29332 algo=8 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www.sub2.example.net. IN A +SECTION ANSWER +www.sub2.example.net. IN A 10.20.30.42 +www.sub2.example.net. 3600 IN RRSIG A 8 4 3600 20070926134150 20070829134150 29332 sub2.example.net. FOY6YxNoFyrSkBtWV7HcECmORTMedRWHdGk7Rm04icT8Bw0dWfzVaIpAkBY6FXx8UvqN7McN4IJI5dAVXptfekO+Yvy2PwkjehRUXvQK64XH5UM5pVbX5g8E4pnOrLa/jzPB7srzMpyWVCpt81lPoFpdfXUMm7434ifkTYhpAll7y5NAocFiT3F+XGe06qMIr51WxoFfegIGohMFhkTDUdLWrdV10128W+NzPdwoYtiigtCObKxTtyj3gK+mxqXvX4X4F2YIGQ+mx62ovdUilnLYZm/WC/ZQkdxeOZjeCTxvSpGGG+wtu1QufgIJ+BpAZAOxREOYZkhR29AG0np4EA== +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR SERVFAIL +SECTION QUESTION +sub2.example.net. IN DNSKEY +SECTION ANSWER +; sub2.example.net. IN DNSKEY 257 3 8 AwEAAb4WMOTBLTFvmBra5m6SK4VfViOzmvyUAU0qv861ZQXeEFvwlndqNU9rwRsMxrSWAYs5nHErKDn49usC/HyxxW1477iGFHhfgL4mjNreJm9zft2QFB1VLbRbEPYdDMLCn4co0qnG7/KG8W2i8Pym1L7f+aREwbLo+/716AS2PbaKMhfWLKLiq5wnBcUClQMNzCiwhqxDJp1oePqfkVdeUgXOtgi0dYRIKyQFhJ5VWJ22npoi/Gif0XLCADAlAwRLKc8o/yJkCxskzgpHpw5Cki1lclg0aq4ssOuPRQ+ne6IHYCz9D2mwzulblhLFamKdq7aHzNt4NlyxhpANVFiKLD8= ;{id = 29332 (ksk), size = 2048b} +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +sub2.example.net. IN DS +SECTION ANSWER +sub2.example.net. 3600 IN DS 29332 8 2 d53e615d9d736b0f2a0097f1d5fa51c84320610f94ecbd7197e7de5f44f02d72 +sub2.example.net. 3600 IN RRSIG DS 8 3 3600 20070926134150 20070829134150 1444 example.net. dYLYs1uMxJm5+MB6L1+uStE5S1YtyYR0JF+1pPoTptc/H1hYqMxK7pVQPtIGvq8j8wNyC7jOzALfEXgwRKiSdR1l1GQ5HIxWkhUmkpLcecwJOjemee4nXaifOFa5bdbdYpuDwTiIzx+PvanlaVjEPy0i1IukanDi6jojfyWcgLA= +ENTRY_END +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD DO +SECTION QUESTION +www.example.com. IN A +ENTRY_END + +; The DNSKEY lookup for the key prime is a failure. +STEP 10 CHECK_ANSWER +ENTRY_BEGIN +MATCH all ede=9 +REPLY QR RD RA DO SERVFAIL +SECTION QUESTION +www.example.com. IN A +SECTION ANSWER +ENTRY_END + +STEP 20 QUERY +ENTRY_BEGIN +REPLY RD DO +SECTION QUESTION +www.sub1.example.net. IN A +ENTRY_END + +; The DS lookup is a failure. +STEP 30 CHECK_ANSWER +ENTRY_BEGIN +MATCH all ede=23 +REPLY QR RD RA DO SERVFAIL +SECTION QUESTION +www.sub1.example.net. IN A +SECTION ANSWER +ENTRY_END + +STEP 40 QUERY +ENTRY_BEGIN +REPLY RD DO +SECTION QUESTION +www.sub2.example.net. IN A +ENTRY_END + +; The DNSKEY lookup is a failure. +STEP 50 CHECK_ANSWER +ENTRY_BEGIN +MATCH all ede=9 +REPLY QR RD RA DO SERVFAIL +SECTION QUESTION +www.sub2.example.net. IN A +SECTION ANSWER +ENTRY_END + +SCENARIO_END diff --git a/validator/validator.c b/validator/validator.c index 39ae58eae..59226abcc 100644 --- a/validator/validator.c +++ b/validator/validator.c @@ -72,7 +72,8 @@ /* forward decl for cache response and normal super inform calls of a DS */ static void process_ds_response(struct module_qstate* qstate, struct val_qstate* vq, int id, int rcode, struct dns_msg* msg, - struct query_info* qinfo, struct sock_list* origin, int* suspend); + struct query_info* qinfo, struct sock_list* origin, int* suspend, + struct module_qstate* sub_qstate); /* Updates the suplied EDE (RFC8914) code selectively so we don't lose @@ -2066,7 +2067,7 @@ processFindKey(struct module_qstate* qstate, struct val_qstate* vq, int id) verbose(VERB_ALGO, "Process suspended sub DS response"); msg = vq->sub_ds_msg; process_ds_response(qstate, vq, id, LDNS_RCODE_NOERROR, - msg, &msg->qinfo, NULL, &suspend); + msg, &msg->qinfo, NULL, &suspend, NULL); if(suspend) { /* we'll come back here later to continue */ if(!validate_suspend_setup_timer(qstate, vq, @@ -2082,7 +2083,7 @@ processFindKey(struct module_qstate* qstate, struct val_qstate* vq, int id) vq->key_entry->name)) ) { verbose(VERB_ALGO, "Process cached DS response"); process_ds_response(qstate, vq, id, LDNS_RCODE_NOERROR, - msg, &msg->qinfo, NULL, &suspend); + msg, &msg->qinfo, NULL, &suspend, NULL); if(suspend) { /* we'll come back here later to continue */ if(!validate_suspend_setup_timer(qstate, vq, @@ -2664,6 +2665,8 @@ val_operate(struct module_qstate* qstate, enum module_ev event, int id, * @param ta: trust anchor. * @param qstate: qstate that needs key. * @param id: module id. + * @param sub_qstate: the sub query state, that is the lookup that fetched + * the trust anchor data, it contains error information for the answer. * @return new key entry or NULL on allocation failure. * The key entry will either contain a validated DNSKEY rrset, or * represent a Null key (query failed, but validation did not), or a @@ -2671,7 +2674,8 @@ val_operate(struct module_qstate* qstate, enum module_ev event, int id, */ static struct key_entry_key* primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset, - struct trust_anchor* ta, struct module_qstate* qstate, int id) + struct trust_anchor* ta, struct module_qstate* qstate, int id, + struct module_qstate* sub_qstate) { struct val_env* ve = (struct val_env*)qstate->env->modinfo[id]; struct key_entry_key* kkey = NULL; @@ -2681,11 +2685,18 @@ primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset, int downprot = qstate->env->cfg->harden_algo_downgrade; if(!dnskey_rrset) { + char* err = errinf_to_str_misc(sub_qstate); + char reason[1024]; log_nametypeclass(VERB_OPS, "failed to prime trust anchor -- " "could not fetch DNSKEY rrset", ta->name, LDNS_RR_TYPE_DNSKEY, ta->dclass); reason_bogus = LDNS_EDE_DNSKEY_MISSING; - reason = "no DNSKEY rrset"; + if(!err) { + snprintf(reason, sizeof(reason), "no DNSKEY rrset"); + } else { + snprintf(reason, sizeof(reason), "no DNSKEY rrset " + "[%s]", err); + } if(qstate->env->cfg->harden_dnssec_stripped) { errinf_ede(qstate, reason, reason_bogus); kkey = key_entry_create_bad(qstate->region, ta->name, @@ -2760,6 +2771,9 @@ primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset, * DS response indicated an end to secure space, is_good if the DS * validated. It returns ke=NULL if the DS response indicated that the * request wasn't a delegation point. + * @param sub_qstate: the sub query state, that is the lookup that fetched + * the trust anchor data, it contains error information for the answer. + * Can be NULL. * @return * 0 on success, * 1 on servfail error (malloc failure), @@ -2768,7 +2782,7 @@ primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset, static int ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, int id, int rcode, struct dns_msg* msg, struct query_info* qinfo, - struct key_entry_key** ke) + struct key_entry_key** ke, struct module_qstate* sub_qstate) { struct val_env* ve = (struct val_env*)qstate->env->modinfo[id]; char* reason = NULL; @@ -2783,6 +2797,11 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, verbose(VERB_DETAIL, "DS response was error, thus bogus"); errinf(qstate, rc); reason = "no DS"; + if(sub_qstate) { + errinf(qstate, "["); + errinf(qstate, errinf_to_str_misc(sub_qstate)); + errinf(qstate, "]"); + } reason_bogus = LDNS_EDE_NETWORK_ERROR; errinf_ede(qstate, reason, reason_bogus); goto return_bogus; @@ -3008,11 +3027,15 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, * @param origin: the origin of msg. * @param suspend: returned true if the task takes too long and needs to * suspend to continue the effort later. + * @param sub_qstate: the sub query state, that is the lookup that fetched + * the trust anchor data, it contains error information for the answer. + * Can be NULL. */ static void process_ds_response(struct module_qstate* qstate, struct val_qstate* vq, int id, int rcode, struct dns_msg* msg, struct query_info* qinfo, - struct sock_list* origin, int* suspend) + struct sock_list* origin, int* suspend, + struct module_qstate* sub_qstate) { struct val_env* ve = (struct val_env*)qstate->env->modinfo[id]; struct key_entry_key* dske = NULL; @@ -3020,7 +3043,8 @@ process_ds_response(struct module_qstate* qstate, struct val_qstate* vq, int ret; *suspend = 0; vq->empty_DS_name = NULL; - ret = ds_response_to_ke(qstate, vq, id, rcode, msg, qinfo, &dske); + ret = ds_response_to_ke(qstate, vq, id, rcode, msg, qinfo, &dske, + sub_qstate); if(ret != 0) { switch(ret) { case 1: @@ -3096,11 +3120,13 @@ process_ds_response(struct module_qstate* qstate, struct val_qstate* vq, * @param msg: result message (if rcode is OK). * @param qinfo: from the sub query state, query info. * @param origin: the origin of msg. + * @param sub_qstate: the sub query state, that is the lookup that fetched + * the trust anchor data, it contains error information for the answer. */ static void process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq, int id, int rcode, struct dns_msg* msg, struct query_info* qinfo, - struct sock_list* origin) + struct sock_list* origin, struct module_qstate* sub_qstate) { struct val_env* ve = (struct val_env*)qstate->env->modinfo[id]; struct key_entry_key* old = vq->key_entry; @@ -3113,6 +3139,8 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq, dnskey = reply_find_answer_rrset(qinfo, msg->rep); if(dnskey == NULL) { + char* err; + char reason[1024]; /* bad response */ verbose(VERB_DETAIL, "Missing DNSKEY RRset in response to " "DNSKEY query."); @@ -3124,7 +3152,13 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq, vq->restart_count++; return; } - reason = "No DNSKEY record"; + err = errinf_to_str_misc(sub_qstate); + if(!err) { + snprintf(reason, sizeof(reason), "No DNSKEY record"); + } else { + snprintf(reason, sizeof(reason), "No DNSKEY record " + "[%s]", err); + } reason_bogus = LDNS_EDE_DNSKEY_MISSING; vq->key_entry = key_entry_create_bad(qstate->region, qinfo->qname, qinfo->qname_len, qinfo->qclass, @@ -3198,10 +3232,13 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq, * @param rcode: rcode result value. * @param msg: result message (if rcode is OK). * @param origin: the origin of msg. + * @param sub_qstate: the sub query state, that is the lookup that fetched + * the trust anchor data, it contains error information for the answer. */ static void process_prime_response(struct module_qstate* qstate, struct val_qstate* vq, - int id, int rcode, struct dns_msg* msg, struct sock_list* origin) + int id, int rcode, struct dns_msg* msg, struct sock_list* origin, + struct module_qstate* sub_qstate) { struct val_env* ve = (struct val_env*)qstate->env->modinfo[id]; struct ub_packed_rrset_key* dnskey_rrset = NULL; @@ -3233,7 +3270,8 @@ process_prime_response(struct module_qstate* qstate, struct val_qstate* vq, return; } } - vq->key_entry = primeResponseToKE(dnskey_rrset, ta, qstate, id); + vq->key_entry = primeResponseToKE(dnskey_rrset, ta, qstate, id, + sub_qstate); lock_basic_unlock(&ta->lock); if(vq->key_entry) { if(key_entry_isbad(vq->key_entry) @@ -3284,14 +3322,14 @@ val_inform_super(struct module_qstate* qstate, int id, if(vq->wait_prime_ta) { vq->wait_prime_ta = 0; process_prime_response(super, vq, id, qstate->return_rcode, - qstate->return_msg, qstate->reply_origin); + qstate->return_msg, qstate->reply_origin, qstate); return; } if(qstate->qinfo.qtype == LDNS_RR_TYPE_DS) { int suspend; process_ds_response(super, vq, id, qstate->return_rcode, qstate->return_msg, &qstate->qinfo, - qstate->reply_origin, &suspend); + qstate->reply_origin, &suspend, qstate); /* If NSEC3 was needed during validation, NULL the NSEC3 cache; * it will be re-initiated if needed later on. * Validation (and the cache table) are happening/allocated in @@ -3312,7 +3350,7 @@ val_inform_super(struct module_qstate* qstate, int id, } else if(qstate->qinfo.qtype == LDNS_RR_TYPE_DNSKEY) { process_dnskey_response(super, vq, id, qstate->return_rcode, qstate->return_msg, &qstate->qinfo, - qstate->reply_origin); + qstate->reply_origin, qstate); return; } log_err("internal error in validator: no inform_supers possible"); From ec5f86b4eb228b99603497cb5a6dd039131f1a60 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 5 Jul 2024 08:49:52 +0200 Subject: [PATCH 22/66] - Fix for neater printout for error for missing DS response. --- doc/Changelog | 3 +++ validator/validator.c | 9 ++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/doc/Changelog b/doc/Changelog index 2a7cb870d..9e4b2f104 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,6 @@ +5 July 2024: Wouter + - Fix for neater printout for error for missing DS response. + 4 July 2024: Wouter - Fix to print details about the failure to lookup a DNSKEY record when validation fails due to the missing DNSKEY. Also for key prime diff --git a/validator/validator.c b/validator/validator.c index 59226abcc..4ed12df67 100644 --- a/validator/validator.c +++ b/validator/validator.c @@ -2798,9 +2798,12 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, errinf(qstate, rc); reason = "no DS"; if(sub_qstate) { - errinf(qstate, "["); - errinf(qstate, errinf_to_str_misc(sub_qstate)); - errinf(qstate, "]"); + char* err = errinf_to_str_misc(sub_qstate); + if(err) { + char buf[1024]; + snprintf(buf, sizeof(buf), "[%s]", err); + errinf(qstate, err); + } } reason_bogus = LDNS_EDE_NETWORK_ERROR; errinf_ede(qstate, reason, reason_bogus); From 978b0696d3ad700ec3739b281d31edd071dbee34 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 5 Jul 2024 14:11:26 +0200 Subject: [PATCH 23/66] - Fix neater printout. --- doc/Changelog | 1 + validator/validator.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/Changelog b/doc/Changelog index 9e4b2f104..09d4aa6bc 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,5 +1,6 @@ 5 July 2024: Wouter - Fix for neater printout for error for missing DS response. + - Fix neater printout. 4 July 2024: Wouter - Fix to print details about the failure to lookup a DNSKEY record diff --git a/validator/validator.c b/validator/validator.c index 4ed12df67..f5894e30c 100644 --- a/validator/validator.c +++ b/validator/validator.c @@ -2802,7 +2802,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, if(err) { char buf[1024]; snprintf(buf, sizeof(buf), "[%s]", err); - errinf(qstate, err); + errinf(qstate, buf); } } reason_bogus = LDNS_EDE_NETWORK_ERROR; From b53d90053ec981543cf2a8e5e39783c142f7e644 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 5 Jul 2024 17:18:01 +0200 Subject: [PATCH 24/66] - Fix #1099: Unbound core dump on SIGSEGV. --- doc/Changelog | 1 + services/cache/dns.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/doc/Changelog b/doc/Changelog index 09d4aa6bc..5d195d37d 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,6 +1,7 @@ 5 July 2024: Wouter - Fix for neater printout for error for missing DS response. - Fix neater printout. + - Fix #1099: Unbound core dump on SIGSEGV. 4 July 2024: Wouter - Fix to print details about the failure to lookup a DNSKEY record diff --git a/services/cache/dns.c b/services/cache/dns.c index 632ed79ac..3307f58fe 100644 --- a/services/cache/dns.c +++ b/services/cache/dns.c @@ -115,7 +115,9 @@ store_rrsets(struct module_env* env, struct reply_info* rep, time_t now, } /* if ref was updated make sure the message ttl is updated to * the minimum of the current rrsets. */ + lock_rw_rdlock(&rep->rrsets[i]->entry.lock); ttl = ((struct packed_rrset_data*)rep->rrsets[i]->entry.data)->ttl; + lock_rw_unlock(&rep->rrsets[i]->entry.lock); if(ttl < min_ttl) min_ttl = ttl; } if(min_ttl < rep->ttl) { From c8a228954278d9dcf99b96920d19bc5861b1ae20 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 5 Jul 2024 17:54:46 +0200 Subject: [PATCH 25/66] - Fix for #1099: Fix to check for deleted RRset when the contents is updated and fetched after it is stored, and also check for a changed RRset. --- doc/Changelog | 3 +++ services/cache/dns.c | 15 ++++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/doc/Changelog b/doc/Changelog index 5d195d37d..c184ac22b 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -2,6 +2,9 @@ - Fix for neater printout for error for missing DS response. - Fix neater printout. - Fix #1099: Unbound core dump on SIGSEGV. + - Fix for #1099: Fix to check for deleted RRset when the contents + is updated and fetched after it is stored, and also check for a + changed RRset. 4 July 2024: Wouter - Fix to print details about the failure to lookup a DNSKEY record diff --git a/services/cache/dns.c b/services/cache/dns.c index 3307f58fe..ff434ed4c 100644 --- a/services/cache/dns.c +++ b/services/cache/dns.c @@ -96,7 +96,8 @@ store_rrsets(struct module_env* env, struct reply_info* rep, time_t now, struct ub_packed_rrset_key* ck; lock_rw_rdlock(&rep->ref[i].key->entry.lock); /* if deleted rrset, do not copy it */ - if(rep->ref[i].key->id == 0) + if(rep->ref[i].key->id == 0 || + rep->ref[i].id != rep->ref[i].key->id) ck = NULL; else ck = packed_rrset_copy_region( rep->ref[i].key, region, now); @@ -115,10 +116,14 @@ store_rrsets(struct module_env* env, struct reply_info* rep, time_t now, } /* if ref was updated make sure the message ttl is updated to * the minimum of the current rrsets. */ - lock_rw_rdlock(&rep->rrsets[i]->entry.lock); - ttl = ((struct packed_rrset_data*)rep->rrsets[i]->entry.data)->ttl; - lock_rw_unlock(&rep->rrsets[i]->entry.lock); - if(ttl < min_ttl) min_ttl = ttl; + lock_rw_rdlock(&rep->ref[i].key->entry.lock); + if(rep->ref[i].key->id != 0 && + rep->ref[i].id == rep->ref[i].key->id) { + /* if deleted, skip ttl update. */ + ttl = ((struct packed_rrset_data*)rep->rrsets[i]->entry.data)->ttl; + if(ttl < min_ttl) min_ttl = ttl; + } + lock_rw_unlock(&rep->ref[i].key->entry.lock); } if(min_ttl < rep->ttl) { rep->ttl = min_ttl; From 02f4446833a4b9b167fff9134318b354e73ad9d6 Mon Sep 17 00:00:00 2001 From: Yorgos Thessalonikefs Date: Fri, 5 Jul 2024 19:58:19 +0200 Subject: [PATCH 26/66] - Don't check for message TTL changes if the RRsets remain the same. --- doc/Changelog | 3 +++ services/cache/dns.c | 19 ++++++++++--------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/doc/Changelog b/doc/Changelog index c184ac22b..150817308 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,6 @@ +5 July 2024: Yorgos + - Don't check for message TTL changes if the RRsets remain the same. + 5 July 2024: Wouter - Fix for neater printout for error for missing DS response. - Fix neater printout. diff --git a/services/cache/dns.c b/services/cache/dns.c index ff434ed4c..426c4506e 100644 --- a/services/cache/dns.c +++ b/services/cache/dns.c @@ -113,17 +113,18 @@ store_rrsets(struct module_env* env, struct reply_info* rep, time_t now, /* fallthrough */ case 1: /* ref updated, item inserted */ rep->rrsets[i] = rep->ref[i].key; - } - /* if ref was updated make sure the message ttl is updated to - * the minimum of the current rrsets. */ - lock_rw_rdlock(&rep->ref[i].key->entry.lock); - if(rep->ref[i].key->id != 0 && - rep->ref[i].id == rep->ref[i].key->id) { + /* ref was updated; make sure the message ttl is + * updated to the minimum of the current rrsets. */ + lock_rw_rdlock(&rep->ref[i].key->entry.lock); /* if deleted, skip ttl update. */ - ttl = ((struct packed_rrset_data*)rep->rrsets[i]->entry.data)->ttl; - if(ttl < min_ttl) min_ttl = ttl; + if(rep->ref[i].key->id != 0 && + rep->ref[i].id == rep->ref[i].key->id) { + ttl = ((struct packed_rrset_data*) + rep->rrsets[i]->entry.data)->ttl; + if(ttl < min_ttl) min_ttl = ttl; + } + lock_rw_unlock(&rep->ref[i].key->entry.lock); } - lock_rw_unlock(&rep->ref[i].key->entry.lock); } if(min_ttl < rep->ttl) { rep->ttl = min_ttl; From bed7cc2a909f60a9f4775de70ff40727fc8c55d9 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 8 Jul 2024 15:29:20 +0200 Subject: [PATCH 27/66] - Fix that validation reason failure that uses string print uses separate buffer that is passed, from the scratch validation buffer. --- doc/Changelog | 4 ++++ services/authzone.c | 46 ++++++++++++++++++++++++++-------------- testcode/unitverify.c | 6 ++++-- validator/autotrust.c | 3 ++- validator/val_nsec.c | 12 ++++++----- validator/val_nsec.h | 4 +++- validator/val_nsec3.c | 9 ++++---- validator/val_nsec3.h | 4 +++- validator/val_sigcrypt.c | 21 +++++++++--------- validator/val_sigcrypt.h | 12 +++++++---- validator/val_utils.c | 44 +++++++++++++++++++++----------------- validator/val_utils.h | 26 +++++++++++++++++------ validator/validator.c | 29 +++++++++++++++++-------- 13 files changed, 142 insertions(+), 78 deletions(-) diff --git a/doc/Changelog b/doc/Changelog index 150817308..8fbbf3c05 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,7 @@ +8 July 2024: Wouter + - Fix that validation reason failure that uses string print uses + separate buffer that is passed, from the scratch validation buffer. + 5 July 2024: Yorgos - Don't check for message TTL changes if the RRsets remain the same. diff --git a/services/authzone.c b/services/authzone.c index f01a6d9e0..0f79f42ea 100644 --- a/services/authzone.c +++ b/services/authzone.c @@ -7778,7 +7778,8 @@ static void auth_zone_log(uint8_t* name, enum verbosity_value level, static int zonemd_dnssec_verify_rrset(struct auth_zone* z, struct module_env* env, struct module_stack* mods, struct ub_packed_rrset_key* dnskey, struct auth_data* node, - struct auth_rrset* rrset, char** why_bogus, uint8_t* sigalg) + struct auth_rrset* rrset, char** why_bogus, uint8_t* sigalg, + char* reasonbuf, size_t reasonlen) { struct ub_packed_rrset_key pk; enum sec_status sec; @@ -7808,7 +7809,7 @@ static int zonemd_dnssec_verify_rrset(struct auth_zone* z, "zonemd: verify %s RRset with DNSKEY", typestr); } sec = dnskeyset_verify_rrset(env, ve, &pk, dnskey, sigalg, why_bogus, NULL, - LDNS_SECTION_ANSWER, NULL, &verified); + LDNS_SECTION_ANSWER, NULL, &verified, reasonbuf, reasonlen); if(sec == sec_status_secure) { return 1; } @@ -7851,7 +7852,8 @@ static int nsec3_of_param_has_type(struct auth_rrset* nsec3, int algo, static int zonemd_check_dnssec_absence(struct auth_zone* z, struct module_env* env, struct module_stack* mods, struct ub_packed_rrset_key* dnskey, struct auth_data* apex, - char** reason, char** why_bogus, uint8_t* sigalg) + char** reason, char** why_bogus, uint8_t* sigalg, char* reasonbuf, + size_t reasonlen) { struct auth_rrset* nsec = NULL; if(!apex) { @@ -7863,7 +7865,7 @@ static int zonemd_check_dnssec_absence(struct auth_zone* z, struct ub_packed_rrset_key pk; /* dnssec verify the NSEC */ if(!zonemd_dnssec_verify_rrset(z, env, mods, dnskey, apex, - nsec, why_bogus, sigalg)) { + nsec, why_bogus, sigalg, reasonbuf, reasonlen)) { *reason = "DNSSEC verify failed for NSEC RRset"; return 0; } @@ -7906,7 +7908,7 @@ static int zonemd_check_dnssec_absence(struct auth_zone* z, } /* dnssec verify the NSEC3 */ if(!zonemd_dnssec_verify_rrset(z, env, mods, dnskey, match, - nsec3, why_bogus, sigalg)) { + nsec3, why_bogus, sigalg, reasonbuf, reasonlen)) { *reason = "DNSSEC verify failed for NSEC3 RRset"; return 0; } @@ -7928,7 +7930,7 @@ static int zonemd_check_dnssec_soazonemd(struct auth_zone* z, struct module_env* env, struct module_stack* mods, struct ub_packed_rrset_key* dnskey, struct auth_data* apex, struct auth_rrset* zonemd_rrset, char** reason, char** why_bogus, - uint8_t* sigalg) + uint8_t* sigalg, char* reasonbuf, size_t reasonlen) { struct auth_rrset* soa; if(!apex) { @@ -7941,12 +7943,12 @@ static int zonemd_check_dnssec_soazonemd(struct auth_zone* z, return 0; } if(!zonemd_dnssec_verify_rrset(z, env, mods, dnskey, apex, soa, - why_bogus, sigalg)) { + why_bogus, sigalg, reasonbuf, reasonlen)) { *reason = "DNSSEC verify failed for SOA RRset"; return 0; } if(!zonemd_dnssec_verify_rrset(z, env, mods, dnskey, apex, - zonemd_rrset, why_bogus, sigalg)) { + zonemd_rrset, why_bogus, sigalg, reasonbuf, reasonlen)) { *reason = "DNSSEC verify failed for ZONEMD RRset"; return 0; } @@ -8014,6 +8016,7 @@ auth_zone_verify_zonemd_with_key(struct auth_zone* z, struct module_env* env, struct module_stack* mods, struct ub_packed_rrset_key* dnskey, int is_insecure, char** result, uint8_t* sigalg) { + char reasonbuf[256]; char* reason = NULL, *why_bogus = NULL; struct auth_data* apex = NULL; struct auth_rrset* zonemd_rrset = NULL; @@ -8042,7 +8045,8 @@ auth_zone_verify_zonemd_with_key(struct auth_zone* z, struct module_env* env, } else if(!zonemd_rrset && dnskey && !is_insecure) { /* fetch, DNSSEC verify, and check NSEC/NSEC3 */ if(!zonemd_check_dnssec_absence(z, env, mods, dnskey, apex, - &reason, &why_bogus, sigalg)) { + &reason, &why_bogus, sigalg, reasonbuf, + sizeof(reasonbuf))) { auth_zone_zonemd_fail(z, env, reason, why_bogus, result); return; } @@ -8050,7 +8054,8 @@ auth_zone_verify_zonemd_with_key(struct auth_zone* z, struct module_env* env, } else if(zonemd_rrset && dnskey && !is_insecure) { /* check DNSSEC verify of SOA and ZONEMD */ if(!zonemd_check_dnssec_soazonemd(z, env, mods, dnskey, apex, - zonemd_rrset, &reason, &why_bogus, sigalg)) { + zonemd_rrset, &reason, &why_bogus, sigalg, reasonbuf, + sizeof(reasonbuf))) { auth_zone_zonemd_fail(z, env, reason, why_bogus, result); return; } @@ -8114,7 +8119,8 @@ static struct ub_packed_rrset_key* zonemd_get_dnskey_from_anchor(struct auth_zone* z, struct module_env* env, struct module_stack* mods, struct trust_anchor* anchor, int* is_insecure, char** why_bogus, - struct ub_packed_rrset_key* keystorage) + struct ub_packed_rrset_key* keystorage, char* reasonbuf, + size_t reasonlen) { struct auth_data* apex; struct auth_rrset* dnskey_rrset; @@ -8150,7 +8156,8 @@ zonemd_get_dnskey_from_anchor(struct auth_zone* z, struct module_env* env, auth_zone_log(z->name, VERB_QUERY, "zonemd: verify DNSKEY RRset with trust anchor"); sec = val_verify_DNSKEY_with_TA(env, ve, keystorage, anchor->ds_rrset, - anchor->dnskey_rrset, NULL, why_bogus, NULL, NULL); + anchor->dnskey_rrset, NULL, why_bogus, NULL, NULL, reasonbuf, + reasonlen); regional_free_all(env->scratch); if(sec == sec_status_secure) { /* success */ @@ -8173,7 +8180,8 @@ static struct ub_packed_rrset_key* auth_zone_verify_zonemd_key_with_ds(struct auth_zone* z, struct module_env* env, struct module_stack* mods, struct ub_packed_rrset_key* ds, int* is_insecure, char** why_bogus, - struct ub_packed_rrset_key* keystorage, uint8_t* sigalg) + struct ub_packed_rrset_key* keystorage, uint8_t* sigalg, + char* reasonbuf, size_t reasonlen) { struct auth_data* apex; struct auth_rrset* dnskey_rrset; @@ -8209,7 +8217,7 @@ auth_zone_verify_zonemd_key_with_ds(struct auth_zone* z, keystorage->rk.rrset_class = htons(z->dclass); auth_zone_log(z->name, VERB_QUERY, "zonemd: verify zone DNSKEY with DS"); sec = val_verify_DNSKEY_with_DS(env, ve, keystorage, ds, sigalg, - why_bogus, NULL, NULL); + why_bogus, NULL, NULL, reasonbuf, reasonlen); regional_free_all(env->scratch); if(sec == sec_status_secure) { /* success */ @@ -8235,6 +8243,7 @@ void auth_zonemd_dnskey_lookup_callback(void* arg, int rcode, sldns_buffer* buf, { struct auth_zone* z = (struct auth_zone*)arg; struct module_env* env; + char reasonbuf[256]; char* reason = NULL, *ds_bogus = NULL, *typestr="DNSKEY"; struct ub_packed_rrset_key* dnskey = NULL, *ds = NULL; int is_insecure = 0, downprot; @@ -8346,7 +8355,8 @@ void auth_zonemd_dnskey_lookup_callback(void* arg, int rcode, sldns_buffer* buf, if(!reason && !is_insecure && !dnskey && ds) { dnskey = auth_zone_verify_zonemd_key_with_ds(z, env, &env->mesh->mods, ds, &is_insecure, &ds_bogus, - &keystorage, downprot?sigalg:NULL); + &keystorage, downprot?sigalg:NULL, reasonbuf, + sizeof(reasonbuf)); if(!dnskey && !is_insecure && !reason) reason = "DNSKEY verify with DS failed"; } @@ -8354,6 +8364,7 @@ void auth_zonemd_dnskey_lookup_callback(void* arg, int rcode, sldns_buffer* buf, if(reason) { auth_zone_zonemd_fail(z, env, reason, ds_bogus, NULL); lock_rw_unlock(&z->lock); + regional_free_all(env->scratch); return; } @@ -8438,6 +8449,7 @@ zonemd_lookup_dnskey(struct auth_zone* z, struct module_env* env) void auth_zone_verify_zonemd(struct auth_zone* z, struct module_env* env, struct module_stack* mods, char** result, int offline, int only_online) { + char reasonbuf[256]; char* reason = NULL, *why_bogus = NULL; struct trust_anchor* anchor = NULL; struct ub_packed_rrset_key* dnskey = NULL; @@ -8472,7 +8484,8 @@ void auth_zone_verify_zonemd(struct auth_zone* z, struct module_env* env, } /* equal to trustanchor, no need for online lookups */ dnskey = zonemd_get_dnskey_from_anchor(z, env, mods, anchor, - &is_insecure, &why_bogus, &keystorage); + &is_insecure, &why_bogus, &keystorage, reasonbuf, + sizeof(reasonbuf)); lock_basic_unlock(&anchor->lock); if(!dnskey && !reason && !is_insecure) { reason = "verify DNSKEY RRset with trust anchor failed"; @@ -8498,6 +8511,7 @@ void auth_zone_verify_zonemd(struct auth_zone* z, struct module_env* env, if(reason) { auth_zone_zonemd_fail(z, env, reason, why_bogus, result); + regional_free_all(env->scratch); return; } diff --git a/testcode/unitverify.c b/testcode/unitverify.c index 395b4c257..275435c73 100644 --- a/testcode/unitverify.c +++ b/testcode/unitverify.c @@ -178,6 +178,7 @@ verifytest_rrset(struct module_env* env, struct val_env* ve, struct query_info* qinfo) { enum sec_status sec; + char reasonbuf[256]; char* reason = NULL; uint8_t sigalg[ALGO_NEEDS_MAX+1]; int verified = 0; @@ -188,8 +189,9 @@ verifytest_rrset(struct module_env* env, struct val_env* ve, } setup_sigalg(dnskey, sigalg); /* check all algorithms in the dnskey */ /* ok to give null as qstate here, won't be used for answer section. */ - sec = dnskeyset_verify_rrset(env, ve, rrset, dnskey, sigalg, &reason, NULL, - LDNS_SECTION_ANSWER, NULL, &verified); + sec = dnskeyset_verify_rrset(env, ve, rrset, dnskey, sigalg, &reason, + NULL, LDNS_SECTION_ANSWER, NULL, &verified, reasonbuf, + sizeof(reasonbuf)); if(vsig) { printf("verify outcome is: %s %s\n", sec_status_to_string(sec), reason?reason:""); diff --git a/validator/autotrust.c b/validator/autotrust.c index 3eb13b35c..36cdf3e0a 100644 --- a/validator/autotrust.c +++ b/validator/autotrust.c @@ -1262,12 +1262,13 @@ verify_dnskey(struct module_env* env, struct val_env* ve, struct trust_anchor* tp, struct ub_packed_rrset_key* rrset, struct module_qstate* qstate) { + char reasonbuf[256]; char* reason = NULL; uint8_t sigalg[ALGO_NEEDS_MAX+1]; int downprot = env->cfg->harden_algo_downgrade; enum sec_status sec = val_verify_DNSKEY_with_TA(env, ve, rrset, tp->ds_rrset, tp->dnskey_rrset, downprot?sigalg:NULL, &reason, - NULL, qstate); + NULL, qstate, reasonbuf, sizeof(reasonbuf)); /* sigalg is ignored, it returns algorithms signalled to exist, but * in 5011 there are no other rrsets to check. if downprot is * enabled, then it checks that the DNSKEY is signed with all diff --git a/validator/val_nsec.c b/validator/val_nsec.c index d0cc67ff5..ad0cba1c4 100644 --- a/validator/val_nsec.c +++ b/validator/val_nsec.c @@ -177,7 +177,7 @@ static int nsec_verify_rrset(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* nsec, struct key_entry_key* kkey, char** reason, sldns_ede_code* reason_bogus, - struct module_qstate* qstate) + struct module_qstate* qstate, char* reasonbuf, size_t reasonlen) { struct packed_rrset_data* d = (struct packed_rrset_data*) nsec->entry.data; @@ -189,7 +189,8 @@ nsec_verify_rrset(struct module_env* env, struct val_env* ve, if(d->security == sec_status_secure) return 1; d->security = val_verify_rrset_entry(env, ve, nsec, kkey, reason, - reason_bogus, LDNS_SECTION_AUTHORITY, qstate, &verified); + reason_bogus, LDNS_SECTION_AUTHORITY, qstate, &verified, + reasonbuf, reasonlen); if(d->security == sec_status_secure) { rrset_update_sec_status(env->rrset_cache, nsec, *env->now); return 1; @@ -201,7 +202,8 @@ enum sec_status val_nsec_prove_nodata_dsreply(struct module_env* env, struct val_env* ve, struct query_info* qinfo, struct reply_info* rep, struct key_entry_key* kkey, time_t* proof_ttl, char** reason, - sldns_ede_code* reason_bogus, struct module_qstate* qstate) + sldns_ede_code* reason_bogus, struct module_qstate* qstate, + char* reasonbuf, size_t reasonlen) { struct ub_packed_rrset_key* nsec = reply_find_rrset_section_ns( rep, qinfo->qname, qinfo->qname_len, LDNS_RR_TYPE_NSEC, @@ -219,7 +221,7 @@ val_nsec_prove_nodata_dsreply(struct module_env* env, struct val_env* ve, * 2) this is not a delegation point */ if(nsec) { if(!nsec_verify_rrset(env, ve, nsec, kkey, reason, - reason_bogus, qstate)) { + reason_bogus, qstate, reasonbuf, reasonlen)) { verbose(VERB_ALGO, "NSEC RRset for the " "referral did not verify."); return sec_status_bogus; @@ -250,7 +252,7 @@ val_nsec_prove_nodata_dsreply(struct module_env* env, struct val_env* ve, if(rep->rrsets[i]->rk.type != htons(LDNS_RR_TYPE_NSEC)) continue; if(!nsec_verify_rrset(env, ve, rep->rrsets[i], kkey, reason, - reason_bogus, qstate)) { + reason_bogus, qstate, reasonbuf, reasonlen)) { verbose(VERB_ALGO, "NSEC for empty non-terminal " "did not verify."); *reason = "NSEC for empty non-terminal " diff --git a/validator/val_nsec.h b/validator/val_nsec.h index 81844c908..c1d45314a 100644 --- a/validator/val_nsec.h +++ b/validator/val_nsec.h @@ -68,6 +68,8 @@ struct key_entry_key; * @param reason: string explaining why bogus. * @param reason_bogus: relevant EDE code for validation failure. * @param qstate: qstate with region. + * @param reasonbuf: buffer to use for fail reason string print. + * @param reasonlen: length of reasonbuf. * @return security status. * SECURE: proved absence of DS. * INSECURE: proved that this was not a delegation point. @@ -78,7 +80,7 @@ enum sec_status val_nsec_prove_nodata_dsreply(struct module_env* env, struct val_env* ve, struct query_info* qinfo, struct reply_info* rep, struct key_entry_key* kkey, time_t* proof_ttl, char** reason, sldns_ede_code* reason_bogus, - struct module_qstate* qstate); + struct module_qstate* qstate, char* reasonbuf, size_t reasonlen); /** * nsec typemap check, takes an NSEC-type bitmap as argument, checks for type. diff --git a/validator/val_nsec3.c b/validator/val_nsec3.c index 95d1e4d7e..e790e9982 100644 --- a/validator/val_nsec3.c +++ b/validator/val_nsec3.c @@ -1445,7 +1445,7 @@ static int list_is_secure(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key** list, size_t num, struct key_entry_key* kkey, char** reason, sldns_ede_code *reason_bogus, - struct module_qstate* qstate) + struct module_qstate* qstate, char* reasonbuf, size_t reasonlen) { struct packed_rrset_data* d; size_t i; @@ -1461,7 +1461,7 @@ list_is_secure(struct module_env* env, struct val_env* ve, continue; d->security = val_verify_rrset_entry(env, ve, list[i], kkey, reason, reason_bogus, LDNS_SECTION_AUTHORITY, qstate, - &verified); + &verified, reasonbuf, reasonlen); if(d->security != sec_status_secure) { verbose(VERB_ALGO, "NSEC3 did not verify"); return 0; @@ -1476,7 +1476,7 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key** list, size_t num, struct query_info* qinfo, struct key_entry_key* kkey, char** reason, sldns_ede_code* reason_bogus, struct module_qstate* qstate, - struct nsec3_cache_table* ct) + struct nsec3_cache_table* ct, char* reasonbuf, size_t reasonlen) { struct nsec3_filter flt; struct ce_response ce; @@ -1491,7 +1491,8 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve, *reason = "no valid NSEC3s"; return sec_status_bogus; /* no valid NSEC3s, bogus */ } - if(!list_is_secure(env, ve, list, num, kkey, reason, reason_bogus, qstate)) { + if(!list_is_secure(env, ve, list, num, kkey, reason, reason_bogus, + qstate, reasonbuf, reasonlen)) { *reason = "not all NSEC3 records secure"; return sec_status_bogus; /* not all NSEC3 records secure */ } diff --git a/validator/val_nsec3.h b/validator/val_nsec3.h index 8ca912934..f668a270f 100644 --- a/validator/val_nsec3.h +++ b/validator/val_nsec3.h @@ -210,6 +210,8 @@ nsec3_prove_wildcard(struct module_env* env, struct val_env* ve, * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure. * @param qstate: qstate with region. * @param ct: cached hashes table. + * @param reasonbuf: buffer to use for fail reason string print. + * @param reasonlen: length of reasonbuf. * @return: * sec_status SECURE of the proposition is proven by the NSEC3 RRs, * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored. @@ -222,7 +224,7 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key** list, size_t num, struct query_info* qinfo, struct key_entry_key* kkey, char** reason, sldns_ede_code* reason_bogus, struct module_qstate* qstate, - struct nsec3_cache_table* ct); + struct nsec3_cache_table* ct, char* reasonbuf, size_t reasonlen); /** * Prove NXDOMAIN or NODATA. diff --git a/validator/val_sigcrypt.c b/validator/val_sigcrypt.c index 7c2b9d7e6..3e90eeb84 100644 --- a/validator/val_sigcrypt.c +++ b/validator/val_sigcrypt.c @@ -623,7 +623,8 @@ enum sec_status dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, uint8_t* sigalg, char** reason, sldns_ede_code *reason_bogus, - sldns_pkt_section section, struct module_qstate* qstate, int* verified) + sldns_pkt_section section, struct module_qstate* qstate, int* verified, + char* reasonbuf, size_t reasonlen) { enum sec_status sec; size_t i, num; @@ -680,7 +681,8 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, verbose(VERB_ALGO, "rrset failed to verify: " "no valid signatures for %d algorithms", (int)algo_needs_num_missing(&needs)); - algo_needs_reason(env, alg, reason, "no signatures"); + algo_needs_reason(alg, reason, "no signatures", reasonbuf, + reasonlen); } else { verbose(VERB_ALGO, "rrset failed to verify: " "no valid signatures"); @@ -688,17 +690,16 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, return sec_status_bogus; } -void algo_needs_reason(struct module_env* env, int alg, char** reason, char* s) +void algo_needs_reason(int alg, char** reason, char* s, char* reasonbuf, + size_t reasonlen) { - char buf[256]; sldns_lookup_table *t = sldns_lookup_by_id(sldns_algorithms, alg); if(t&&t->name) - snprintf(buf, sizeof(buf), "%s with algorithm %s", s, t->name); - else snprintf(buf, sizeof(buf), "%s with algorithm ALG%u", s, - (unsigned)alg); - *reason = regional_strdup(env->scratch, buf); - if(!*reason) - *reason = s; + snprintf(reasonbuf, sizeof(reasonlen), "%s with algorithm %s", + s, t->name); + else snprintf(reasonbuf, sizeof(reasonlen), "%s with algorithm " + "ALG%u", s, (unsigned)alg); + *reason = reasonbuf; } enum sec_status diff --git a/validator/val_sigcrypt.h b/validator/val_sigcrypt.h index 1a3d8fcb2..1fac8bde0 100644 --- a/validator/val_sigcrypt.h +++ b/validator/val_sigcrypt.h @@ -134,12 +134,14 @@ int algo_needs_missing(struct algo_needs* n); /** * Format error reason for algorithm missing. - * @param env: module env with scratch for temp storage of string. * @param alg: DNSKEY-algorithm missing. * @param reason: destination. * @param s: string, appended with 'with algorithm ..'. + * @param reasonbuf: buffer to use for fail reason string print. + * @param reasonlen: length of reasonbuf. */ -void algo_needs_reason(struct module_env* env, int alg, char** reason, char* s); +void algo_needs_reason(int alg, char** reason, char* s, char* reasonbuf, + size_t reasonlen); /** * Check if dnskey matches a DS digest @@ -261,6 +263,8 @@ uint16_t dnskey_get_flags(struct ub_packed_rrset_key* k, size_t idx); * @param section: section of packet where this rrset comes from. * @param qstate: qstate with region. * @param verified: if not NULL the number of RRSIG validations is returned. + * @param reasonbuf: buffer to use for fail reason string print. + * @param reasonlen: length of reasonbuf. * @return SECURE if one key in the set verifies one rrsig. * UNCHECKED on allocation errors, unsupported algorithms, malformed data, * and BOGUS on verification failures (no keys match any signatures). @@ -269,8 +273,8 @@ enum sec_status dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, uint8_t* sigalg, char** reason, sldns_ede_code *reason_bogus, - sldns_pkt_section section, struct module_qstate* qstate, int* verified); - + sldns_pkt_section section, struct module_qstate* qstate, int* verified, + char* reasonbuf, size_t reasonlen); /** * verify rrset against one specific dnskey (from rrset) diff --git a/validator/val_utils.c b/validator/val_utils.c index add6d9bba..549264d76 100644 --- a/validator/val_utils.c +++ b/validator/val_utils.c @@ -406,7 +406,7 @@ val_verify_rrset(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* keys, uint8_t* sigalg, char** reason, sldns_ede_code *reason_bogus, sldns_pkt_section section, struct module_qstate* qstate, - int *verified) + int *verified, char* reasonbuf, size_t reasonlen) { enum sec_status sec; struct packed_rrset_data* d = (struct packed_rrset_data*)rrset-> @@ -431,7 +431,7 @@ val_verify_rrset(struct module_env* env, struct val_env* ve, log_nametypeclass(VERB_ALGO, "verify rrset", rrset->rk.dname, ntohs(rrset->rk.type), ntohs(rrset->rk.rrset_class)); sec = dnskeyset_verify_rrset(env, ve, rrset, keys, sigalg, reason, - reason_bogus, section, qstate, verified); + reason_bogus, section, qstate, verified, reasonbuf, reasonlen); verbose(VERB_ALGO, "verify result: %s", sec_status_to_string(sec)); regional_free_all(env->scratch); @@ -466,7 +466,7 @@ val_verify_rrset_entry(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, struct key_entry_key* kkey, char** reason, sldns_ede_code *reason_bogus, sldns_pkt_section section, struct module_qstate* qstate, - int* verified) + int* verified, char* reasonbuf, size_t reasonlen) { /* temporary dnskey rrset-key */ struct ub_packed_rrset_key dnskey; @@ -480,7 +480,7 @@ val_verify_rrset_entry(struct module_env* env, struct val_env* ve, dnskey.entry.key = &dnskey; dnskey.entry.data = kd->rrset_data; sec = val_verify_rrset(env, ve, rrset, &dnskey, kd->algo, reason, - reason_bogus, section, qstate, verified); + reason_bogus, section, qstate, verified, reasonbuf, reasonlen); return sec; } @@ -490,7 +490,7 @@ verify_dnskeys_with_ds_rr(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, struct ub_packed_rrset_key* ds_rrset, size_t ds_idx, char** reason, sldns_ede_code *reason_bogus, struct module_qstate* qstate, - int *nonechecked) + int *nonechecked, char* reasonbuf, size_t reasonlen) { enum sec_status sec = sec_status_bogus; size_t i, num, numchecked = 0, numhashok = 0, numsizesupp = 0; @@ -544,8 +544,8 @@ verify_dnskeys_with_ds_rr(struct module_env* env, struct val_env* ve, return sec_status_insecure; } if(numchecked == 0) { - algo_needs_reason(env, ds_get_key_algo(ds_rrset, ds_idx), - reason, "no keys have a DS"); + algo_needs_reason(ds_get_key_algo(ds_rrset, ds_idx), + reason, "no keys have a DS", reasonbuf, reasonlen); *nonechecked = 1; } else if(numhashok == 0) { *reason = "DS hash mismatches key"; @@ -576,7 +576,8 @@ enum sec_status val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, struct ub_packed_rrset_key* ds_rrset, uint8_t* sigalg, char** reason, - sldns_ede_code *reason_bogus, struct module_qstate* qstate) + sldns_ede_code *reason_bogus, struct module_qstate* qstate, + char* reasonbuf, size_t reasonlen) { /* as long as this is false, we can consider this DS rrset to be * equivalent to no DS rrset. */ @@ -615,7 +616,7 @@ val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve, sec = verify_dnskeys_with_ds_rr(env, ve, dnskey_rrset, ds_rrset, i, reason, reason_bogus, qstate, - &nonechecked); + &nonechecked, reasonbuf, reasonlen); if(sec == sec_status_insecure) { /* DNSKEY too large unsupported or algo refused by * crypto lib. */ @@ -666,8 +667,8 @@ val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve, /* If any were understandable, then it is bad. */ verbose(VERB_QUERY, "Failed to match any usable DS to a DNSKEY."); if(sigalg && (alg=algo_needs_missing(&needs)) != 0) { - algo_needs_reason(env, alg, reason, "missing verification of " - "DNSKEY signature"); + algo_needs_reason(alg, reason, "missing verification of " + "DNSKEY signature", reasonbuf, reasonlen); } return sec_status_bogus; } @@ -676,12 +677,13 @@ struct key_entry_key* val_verify_new_DNSKEYs(struct regional* region, struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, struct ub_packed_rrset_key* ds_rrset, int downprot, char** reason, - sldns_ede_code *reason_bogus, struct module_qstate* qstate) + sldns_ede_code *reason_bogus, struct module_qstate* qstate, + char* reasonbuf, size_t reasonlen) { uint8_t sigalg[ALGO_NEEDS_MAX+1]; enum sec_status sec = val_verify_DNSKEY_with_DS(env, ve, dnskey_rrset, ds_rrset, downprot?sigalg:NULL, reason, - reason_bogus, qstate); + reason_bogus, qstate, reasonbuf, reasonlen); if(sec == sec_status_secure) { return key_entry_create_rrset(region, @@ -706,7 +708,8 @@ val_verify_DNSKEY_with_TA(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, struct ub_packed_rrset_key* ta_ds, struct ub_packed_rrset_key* ta_dnskey, uint8_t* sigalg, char** reason, - sldns_ede_code *reason_bogus, struct module_qstate* qstate) + sldns_ede_code *reason_bogus, struct module_qstate* qstate, + char* reasonbuf, size_t reasonlen) { /* as long as this is false, we can consider this anchor to be * equivalent to no anchor. */ @@ -757,7 +760,8 @@ val_verify_DNSKEY_with_TA(struct module_env* env, struct val_env* ve, continue; sec = verify_dnskeys_with_ds_rr(env, ve, dnskey_rrset, - ta_ds, i, reason, reason_bogus, qstate, &nonechecked); + ta_ds, i, reason, reason_bogus, qstate, &nonechecked, + reasonbuf, reasonlen); if(sec == sec_status_insecure) { has_algo_refusal = 1; continue; @@ -837,8 +841,8 @@ val_verify_DNSKEY_with_TA(struct module_env* env, struct val_env* ve, /* If any were understandable, then it is bad. */ verbose(VERB_QUERY, "Failed to match any usable anchor to a DNSKEY."); if(sigalg && (alg=algo_needs_missing(&needs)) != 0) { - algo_needs_reason(env, alg, reason, "missing verification of " - "DNSKEY signature"); + algo_needs_reason(alg, reason, "missing verification of " + "DNSKEY signature", reasonbuf, reasonlen); } return sec_status_bogus; } @@ -848,12 +852,14 @@ val_verify_new_DNSKEYs_with_ta(struct regional* region, struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, struct ub_packed_rrset_key* ta_ds_rrset, struct ub_packed_rrset_key* ta_dnskey_rrset, int downprot, - char** reason, sldns_ede_code *reason_bogus, struct module_qstate* qstate) + char** reason, sldns_ede_code *reason_bogus, + struct module_qstate* qstate, char* reasonbuf, size_t reasonlen) { uint8_t sigalg[ALGO_NEEDS_MAX+1]; enum sec_status sec = val_verify_DNSKEY_with_TA(env, ve, dnskey_rrset, ta_ds_rrset, ta_dnskey_rrset, - downprot?sigalg:NULL, reason, reason_bogus, qstate); + downprot?sigalg:NULL, reason, reason_bogus, qstate, + reasonbuf, reasonlen); if(sec == sec_status_secure) { return key_entry_create_rrset(region, diff --git a/validator/val_utils.h b/validator/val_utils.h index e8cdcefa6..4fe38c1bb 100644 --- a/validator/val_utils.h +++ b/validator/val_utils.h @@ -125,13 +125,15 @@ void val_find_signer(enum val_classification subtype, * @param section: section of packet where this rrset comes from. * @param qstate: qstate with region. * @param verified: if not NULL, the number of RRSIG validations is returned. + * @param reasonbuf: buffer to use for fail reason string print. + * @param reasonlen: length of reasonbuf. * @return security status of verification. */ enum sec_status val_verify_rrset_entry(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, struct key_entry_key* kkey, char** reason, sldns_ede_code *reason_bogus, sldns_pkt_section section, struct module_qstate* qstate, - int* verified); + int* verified, char* reasonbuf, size_t reasonlen); /** * Verify DNSKEYs with DS rrset. Like val_verify_new_DNSKEYs but @@ -146,6 +148,8 @@ enum sec_status val_verify_rrset_entry(struct module_env* env, * @param reason: reason of failure. Fixed string or alloced in scratch. * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure. * @param qstate: qstate with region. + * @param reasonbuf: buffer to use for fail reason string print. + * @param reasonlen: length of reasonbuf. * @return: sec_status_secure if a DS matches. * sec_status_insecure if end of trust (i.e., unknown algorithms). * sec_status_bogus if it fails. @@ -153,7 +157,8 @@ enum sec_status val_verify_rrset_entry(struct module_env* env, enum sec_status val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, struct ub_packed_rrset_key* ds_rrset, uint8_t* sigalg, char** reason, - sldns_ede_code *reason_bogus, struct module_qstate* qstate); + sldns_ede_code *reason_bogus, struct module_qstate* qstate, + char* reasonbuf, size_t reasonlen); /** * Verify DNSKEYs with DS and DNSKEY rrset. Like val_verify_DNSKEY_with_DS @@ -167,8 +172,10 @@ enum sec_status val_verify_DNSKEY_with_DS(struct module_env* env, * algorithm is enough. The list of signalled algorithms is returned, * must have enough space for ALGO_NEEDS_MAX+1. * @param reason: reason of failure. Fixed string or alloced in scratch. -* @param reason_bogus: EDE (RFC8914) code paired with the reason of failure. + * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure. * @param qstate: qstate with region. + * @param reasonbuf: buffer to use for fail reason string print. + * @param reasonlen: length of reasonbuf. * @return: sec_status_secure if a DS matches. * sec_status_insecure if end of trust (i.e., unknown algorithms). * sec_status_bogus if it fails. @@ -177,7 +184,8 @@ enum sec_status val_verify_DNSKEY_with_TA(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, struct ub_packed_rrset_key* ta_ds, struct ub_packed_rrset_key* ta_dnskey, uint8_t* sigalg, char** reason, - sldns_ede_code *reason_bogus, struct module_qstate* qstate); + sldns_ede_code *reason_bogus, struct module_qstate* qstate, + char* reasonbuf, size_t reasonlen); /** * Verify new DNSKEYs with DS rrset. The DS contains hash values that should @@ -194,6 +202,8 @@ enum sec_status val_verify_DNSKEY_with_TA(struct module_env* env, * @param reason: reason of failure. Fixed string or alloced in scratch. * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure. * @param qstate: qstate with region. + * @param reasonbuf: buffer to use for fail reason string print. + * @param reasonlen: length of reasonbuf. * @return a KeyEntry. This will either contain the now trusted * dnskey_rrset, a "null" key entry indicating that this DS * rrset/DNSKEY pair indicate an secure end to the island of trust @@ -208,7 +218,8 @@ struct key_entry_key* val_verify_new_DNSKEYs(struct regional* region, struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, struct ub_packed_rrset_key* ds_rrset, int downprot, char** reason, - sldns_ede_code *reason_bogus, struct module_qstate* qstate); + sldns_ede_code *reason_bogus, struct module_qstate* qstate, + char* reasonbuf, size_t reasonlen); /** * Verify rrset with trust anchor: DS and DNSKEY rrset. @@ -224,6 +235,8 @@ struct key_entry_key* val_verify_new_DNSKEYs(struct regional* region, * @param reason: reason of failure. Fixed string or alloced in scratch. * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure. * @param qstate: qstate with region. + * @param reasonbuf: buffer to use for fail reason string print. + * @param reasonlen: length of reasonbuf. * @return a KeyEntry. This will either contain the now trusted * dnskey_rrset, a "null" key entry indicating that this DS * rrset/DNSKEY pair indicate an secure end to the island of trust @@ -239,7 +252,8 @@ struct key_entry_key* val_verify_new_DNSKEYs_with_ta(struct regional* region, struct ub_packed_rrset_key* dnskey_rrset, struct ub_packed_rrset_key* ta_ds_rrset, struct ub_packed_rrset_key* ta_dnskey_rrset, int downprot, - char** reason, sldns_ede_code *reason_bogus, struct module_qstate* qstate); + char** reason, sldns_ede_code *reason_bogus, struct module_qstate* qstate, + char* reasonbuf, size_t reasonlen); /** * Determine if DS rrset is usable for validator or not. diff --git a/validator/validator.c b/validator/validator.c index f5894e30c..77718cbfd 100644 --- a/validator/validator.c +++ b/validator/validator.c @@ -647,6 +647,7 @@ validate_msg_signatures(struct module_qstate* qstate, struct val_qstate* vq, struct ub_packed_rrset_key* s; enum sec_status sec; int num_verifies = 0, verified, have_state = 0; + char reasonbuf[256]; char* reason = NULL; sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS; *suspend = 0; @@ -682,7 +683,8 @@ validate_msg_signatures(struct module_qstate* qstate, struct val_qstate* vq, /* Verify the answer rrset */ sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason, - &reason_bogus, LDNS_SECTION_ANSWER, qstate, &verified); + &reason_bogus, LDNS_SECTION_ANSWER, qstate, &verified, + reasonbuf, sizeof(reasonbuf)); /* If the (answer) rrset failed to validate, then this * message is BAD. */ if(sec != sec_status_secure) { @@ -727,7 +729,7 @@ validate_msg_signatures(struct module_qstate* qstate, struct val_qstate* vq, s = chase_reply->rrsets[i]; sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason, &reason_bogus, LDNS_SECTION_AUTHORITY, qstate, - &verified); + &verified, reasonbuf, sizeof(reasonbuf)); /* If anything in the authority section fails to be secure, * we have a bad message. */ if(sec != sec_status_secure) { @@ -773,7 +775,7 @@ validate_msg_signatures(struct module_qstate* qstate, struct val_qstate* vq, if(sname && query_dname_compare(sname, key_entry->name)==0) (void)val_verify_rrset_entry(env, ve, s, key_entry, &reason, NULL, LDNS_SECTION_ADDITIONAL, qstate, - &verified); + &verified, reasonbuf, sizeof(reasonbuf)); /* the additional section can fail to be secure, * it is optional, check signature in case we need * to clean the additional section later. */ @@ -2680,6 +2682,7 @@ primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset, struct val_env* ve = (struct val_env*)qstate->env->modinfo[id]; struct key_entry_key* kkey = NULL; enum sec_status sec = sec_status_unchecked; + char reasonbuf[256]; char* reason = NULL; sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS; int downprot = qstate->env->cfg->harden_algo_downgrade; @@ -2716,7 +2719,7 @@ primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset, /* attempt to verify with trust anchor DS and DNSKEY */ kkey = val_verify_new_DNSKEYs_with_ta(qstate->region, qstate->env, ve, dnskey_rrset, ta->ds_rrset, ta->dnskey_rrset, downprot, - &reason, &reason_bogus, qstate); + &reason, &reason_bogus, qstate, reasonbuf, sizeof(reasonbuf)); if(!kkey) { log_err("out of memory: verifying prime TA"); return NULL; @@ -2785,6 +2788,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, struct key_entry_key** ke, struct module_qstate* sub_qstate) { struct val_env* ve = (struct val_env*)qstate->env->modinfo[id]; + char reasonbuf[256]; char* reason = NULL; sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS; enum val_classification subtype; @@ -2827,7 +2831,9 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, /* Verify only returns BOGUS or SECURE. If the rrset is * bogus, then we are done. */ sec = val_verify_rrset_entry(qstate->env, ve, ds, - vq->key_entry, &reason, &reason_bogus, LDNS_SECTION_ANSWER, qstate, &verified); + vq->key_entry, &reason, &reason_bogus, + LDNS_SECTION_ANSWER, qstate, &verified, reasonbuf, + sizeof(reasonbuf)); if(sec != sec_status_secure) { verbose(VERB_DETAIL, "DS rrset in DS response did " "not verify"); @@ -2877,7 +2883,8 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, /* Try to prove absence of the DS with NSEC */ sec = val_nsec_prove_nodata_dsreply( qstate->env, ve, qinfo, msg->rep, vq->key_entry, - &proof_ttl, &reason, &reason_bogus, qstate); + &proof_ttl, &reason, &reason_bogus, qstate, + reasonbuf, sizeof(reasonbuf)); switch(sec) { case sec_status_secure: verbose(VERB_DETAIL, "NSEC RRset for the " @@ -2914,7 +2921,8 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, sec = nsec3_prove_nods(qstate->env, ve, msg->rep->rrsets + msg->rep->an_numrrsets, msg->rep->ns_numrrsets, qinfo, vq->key_entry, &reason, - &reason_bogus, qstate, &vq->nsec3_cache_table); + &reason_bogus, qstate, &vq->nsec3_cache_table, + reasonbuf, sizeof(reasonbuf)); switch(sec) { case sec_status_insecure: /* case insecure also continues to unsigned @@ -2981,7 +2989,8 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, } sec = val_verify_rrset_entry(qstate->env, ve, cname, vq->key_entry, &reason, &reason_bogus, - LDNS_SECTION_ANSWER, qstate, &verified); + LDNS_SECTION_ANSWER, qstate, &verified, reasonbuf, + sizeof(reasonbuf)); if(sec == sec_status_secure) { verbose(VERB_ALGO, "CNAME validated, " "proof that DS does not exist"); @@ -3135,6 +3144,7 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq, struct key_entry_key* old = vq->key_entry; struct ub_packed_rrset_key* dnskey = NULL; int downprot; + char reasonbuf[256]; char* reason = NULL; sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS; @@ -3185,7 +3195,8 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq, } downprot = qstate->env->cfg->harden_algo_downgrade; vq->key_entry = val_verify_new_DNSKEYs(qstate->region, qstate->env, - ve, dnskey, vq->ds_rrset, downprot, &reason, &reason_bogus, qstate); + ve, dnskey, vq->ds_rrset, downprot, &reason, &reason_bogus, + qstate, reasonbuf, sizeof(reasonbuf)); if(!vq->key_entry) { log_err("out of memory in verify new DNSKEYs"); From 169acfc546cdca5b01364e2cc466775625346278 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 8 Jul 2024 15:38:27 +0200 Subject: [PATCH 28/66] - Fixup algo_needs_reason string buffer length. --- doc/Changelog | 1 + validator/val_sigcrypt.c | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/Changelog b/doc/Changelog index 8fbbf3c05..ad28c27e4 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,6 +1,7 @@ 8 July 2024: Wouter - Fix that validation reason failure that uses string print uses separate buffer that is passed, from the scratch validation buffer. + - Fixup algo_needs_reason string buffer length. 5 July 2024: Yorgos - Don't check for message TTL changes if the RRsets remain the same. diff --git a/validator/val_sigcrypt.c b/validator/val_sigcrypt.c index 3e90eeb84..9251d2b1f 100644 --- a/validator/val_sigcrypt.c +++ b/validator/val_sigcrypt.c @@ -695,10 +695,10 @@ void algo_needs_reason(int alg, char** reason, char* s, char* reasonbuf, { sldns_lookup_table *t = sldns_lookup_by_id(sldns_algorithms, alg); if(t&&t->name) - snprintf(reasonbuf, sizeof(reasonlen), "%s with algorithm %s", - s, t->name); - else snprintf(reasonbuf, sizeof(reasonlen), "%s with algorithm " - "ALG%u", s, (unsigned)alg); + snprintf(reasonbuf, reasonlen, "%s with algorithm %s", s, + t->name); + else snprintf(reasonbuf, reasonlen, "%s with algorithm ALG%u", s, + (unsigned)alg); *reason = reasonbuf; } From be09350eca3ade6a8850237654392cc3b29f4e8b Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 8 Jul 2024 16:50:16 +0200 Subject: [PATCH 29/66] - Fix shadowed error string variable in validator dnskey handling. --- doc/Changelog | 1 + validator/validator.c | 25 +++++++++++-------------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/doc/Changelog b/doc/Changelog index ad28c27e4..e63a8cd40 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -2,6 +2,7 @@ - Fix that validation reason failure that uses string print uses separate buffer that is passed, from the scratch validation buffer. - Fixup algo_needs_reason string buffer length. + - Fix shadowed error string variable in validator dnskey handling. 5 July 2024: Yorgos - Don't check for message TTL changes if the RRsets remain the same. diff --git a/validator/validator.c b/validator/validator.c index 77718cbfd..e6d19a2c9 100644 --- a/validator/validator.c +++ b/validator/validator.c @@ -2689,27 +2689,25 @@ primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset, if(!dnskey_rrset) { char* err = errinf_to_str_misc(sub_qstate); - char reason[1024]; + char rstr[1024]; log_nametypeclass(VERB_OPS, "failed to prime trust anchor -- " "could not fetch DNSKEY rrset", ta->name, LDNS_RR_TYPE_DNSKEY, ta->dclass); reason_bogus = LDNS_EDE_DNSKEY_MISSING; if(!err) { - snprintf(reason, sizeof(reason), "no DNSKEY rrset"); + snprintf(rstr, sizeof(rstr), "no DNSKEY rrset"); } else { - snprintf(reason, sizeof(reason), "no DNSKEY rrset " + snprintf(rstr, sizeof(rstr), "no DNSKEY rrset " "[%s]", err); } if(qstate->env->cfg->harden_dnssec_stripped) { - errinf_ede(qstate, reason, reason_bogus); + errinf_ede(qstate, rstr, reason_bogus); kkey = key_entry_create_bad(qstate->region, ta->name, ta->namelen, ta->dclass, BOGUS_KEY_TTL, - reason_bogus, reason, - *qstate->env->now); + reason_bogus, rstr, *qstate->env->now); } else kkey = key_entry_create_null(qstate->region, ta->name, ta->namelen, ta->dclass, NULL_KEY_TTL, - reason_bogus, reason, - *qstate->env->now); + reason_bogus, rstr, *qstate->env->now); if(!kkey) { log_err("out of memory: allocate fail prime key"); return NULL; @@ -3153,7 +3151,7 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq, if(dnskey == NULL) { char* err; - char reason[1024]; + char rstr[1024]; /* bad response */ verbose(VERB_DETAIL, "Missing DNSKEY RRset in response to " "DNSKEY query."); @@ -3167,21 +3165,20 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq, } err = errinf_to_str_misc(sub_qstate); if(!err) { - snprintf(reason, sizeof(reason), "No DNSKEY record"); + snprintf(rstr, sizeof(rstr), "No DNSKEY record"); } else { - snprintf(reason, sizeof(reason), "No DNSKEY record " + snprintf(rstr, sizeof(rstr), "No DNSKEY record " "[%s]", err); } reason_bogus = LDNS_EDE_DNSKEY_MISSING; vq->key_entry = key_entry_create_bad(qstate->region, qinfo->qname, qinfo->qname_len, qinfo->qclass, - BOGUS_KEY_TTL, reason_bogus, reason, - *qstate->env->now); + BOGUS_KEY_TTL, reason_bogus, rstr, *qstate->env->now); if(!vq->key_entry) { log_err("alloc failure in missing dnskey response"); /* key_entry is NULL for failure in Validate */ } - errinf_ede(qstate, reason, reason_bogus); + errinf_ede(qstate, rstr, reason_bogus); errinf_origin(qstate, origin); errinf_dname(qstate, "for key", qinfo->qname); vq->state = VAL_VALIDATE_STATE; From ea3e3270064ead6cc6de4d39b26196de6f6f7abc Mon Sep 17 00:00:00 2001 From: Yorgos Thessalonikefs Date: Tue, 9 Jul 2024 15:58:30 +0200 Subject: [PATCH 30/66] - Update list of known EDE codes. --- doc/Changelog | 3 +++ sldns/rrdef.h | 5 +++++ sldns/wire2str.c | 5 +++++ 3 files changed, 13 insertions(+) diff --git a/doc/Changelog b/doc/Changelog index e63a8cd40..6bc73c86a 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,6 @@ +9 July 2024: Yorgos + - Update list of known EDE codes. + 8 July 2024: Wouter - Fix that validation reason failure that uses string print uses separate buffer that is passed, from the scratch validation buffer. diff --git a/sldns/rrdef.h b/sldns/rrdef.h index f277fd67a..7cadf7beb 100644 --- a/sldns/rrdef.h +++ b/sldns/rrdef.h @@ -470,6 +470,11 @@ enum sldns_enum_ede_code LDNS_EDE_NO_REACHABLE_AUTHORITY = 22, LDNS_EDE_NETWORK_ERROR = 23, LDNS_EDE_INVALID_DATA = 24, + LDNS_EDE_SIGNATURE_EXPIRED_BEFORE_VALID = 25, + LDNS_EDE_TOO_EARLY = 26, + LDNS_EDE_UNSUPPORTED_NSEC3_ITERATIONS = 27, + LDNS_EDE_BADPROXYPOLICY = 28, + LDNS_EDE_SYNTHESIZED = 29 }; typedef enum sldns_enum_ede_code sldns_ede_code; diff --git a/sldns/wire2str.c b/sldns/wire2str.c index 2b5dc0513..6962d7bab 100644 --- a/sldns/wire2str.c +++ b/sldns/wire2str.c @@ -228,6 +228,11 @@ static sldns_lookup_table sldns_edns_ede_codes_data[] = { { LDNS_EDE_NO_REACHABLE_AUTHORITY, "No Reachable Authority" }, { LDNS_EDE_NETWORK_ERROR, "Network Error" }, { LDNS_EDE_INVALID_DATA, "Invalid Data" }, + { LDNS_EDE_SIGNATURE_EXPIRED_BEFORE_VALID, "Signature Expired Before Valid" }, + { LDNS_EDE_TOO_EARLY, "Non-Replayable Transactions Received in 0-RTT Data" }, + { LDNS_EDE_UNSUPPORTED_NSEC3_ITERATIONS, "Unsupported NSEC3 Iterations Value" }, + { LDNS_EDE_BADPROXYPOLICY, "Unable to Conform to Policy" }, + { LDNS_EDE_SYNTHESIZED, "Synthesized Answer" }, { 0, NULL} }; sldns_lookup_table* sldns_edns_ede_codes = sldns_edns_ede_codes_data; From d43760a8cd7d01f59fd73bf7edbf983903d8a142 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 10 Jul 2024 14:05:43 +0200 Subject: [PATCH 31/66] - For #773: In contrib/unbound.service.in set unbound to start after network-online.target. Also for contrib/unbound_portable.service.in. --- contrib/unbound.service.in | 4 ++-- contrib/unbound_portable.service.in | 4 ++-- doc/Changelog | 4 ++++ 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/contrib/unbound.service.in b/contrib/unbound.service.in index 5a05c5251..8a5d3b2b0 100644 --- a/contrib/unbound.service.in +++ b/contrib/unbound.service.in @@ -42,8 +42,8 @@ [Unit] Description=Validating, recursive, and caching DNS resolver Documentation=man:unbound(8) -After=network.target -Before=network-online.target nss-lookup.target +After=network-online.target +Before=nss-lookup.target [Install] WantedBy=multi-user.target diff --git a/contrib/unbound_portable.service.in b/contrib/unbound_portable.service.in index e763763f0..22cd44638 100644 --- a/contrib/unbound_portable.service.in +++ b/contrib/unbound_portable.service.in @@ -14,8 +14,8 @@ [Unit] Description=Validating, recursive, and caching DNS resolver Documentation=man:unbound(8) -After=network.target -Before=network-online.target nss-lookup.target +After=network-online.target +Before=nss-lookup.target Wants=nss-lookup.target [Install] diff --git a/doc/Changelog b/doc/Changelog index 6bc73c86a..d0b38ef82 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,7 @@ +10 July 2024: Wouter + - For #773: In contrib/unbound.service.in set unbound to start after + network-online.target. Also for contrib/unbound_portable.service.in. + 9 July 2024: Yorgos - Update list of known EDE codes. From 51425b23884a368a2d8471b11fa47dc2d6fa75ed Mon Sep 17 00:00:00 2001 From: Yorgos Thessalonikefs Date: Fri, 12 Jul 2024 15:38:12 +0200 Subject: [PATCH 32/66] - Add RPZ tag tests in acl_interface.tdir. --- doc/Changelog | 3 ++ .../acl_interface.tdir/acl_interface.conf | 46 +++++++++++++++++- testdata/acl_interface.tdir/acl_interface.pre | 15 ++++-- .../acl_interface.test.scenario | 48 +++++++++++++++++++ testdata/acl_interface.tdir/rpz-nx.zone | 3 ++ testdata/acl_interface.tdir/rpz-one.zone | 3 ++ testdata/acl_interface.tdir/rpz-two.zone | 3 ++ 7 files changed, 117 insertions(+), 4 deletions(-) create mode 100644 testdata/acl_interface.tdir/rpz-nx.zone create mode 100644 testdata/acl_interface.tdir/rpz-one.zone create mode 100644 testdata/acl_interface.tdir/rpz-two.zone diff --git a/doc/Changelog b/doc/Changelog index d0b38ef82..14a72306c 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,6 @@ +12 July 2024: Yorgos + - Add RPZ tag tests in acl_interface.tdir. + 10 July 2024: Wouter - For #773: In contrib/unbound.service.in set unbound to start after network-online.target. Also for contrib/unbound_portable.service.in. diff --git a/testdata/acl_interface.tdir/acl_interface.conf b/testdata/acl_interface.tdir/acl_interface.conf index 157a2d7b7..1d9f8c9aa 100644 --- a/testdata/acl_interface.tdir/acl_interface.conf +++ b/testdata/acl_interface.tdir/acl_interface.conf @@ -5,9 +5,10 @@ server: pidfile: "unbound.pid" chroot: "" username: "" + module-config: "respip validator iterator" # respip for the RPZ part do-not-query-localhost: no use-caps-for-id: no - define-tag: "one two refuse" + define-tag: "one two refuse rpz-one rpz-two rpz-nx" # Interface configuration for IPv4 interface: @IPV4_ADDR@@@PORT_ALLOW@ @@ -16,6 +17,9 @@ server: interface: @IPV4_ADDR@@@PORT_TAG_1@ interface: @IPV4_ADDR@@@PORT_TAG_2@ interface: @IPV4_ADDR@@@PORT_TAG_3@ + interface: @IPV4_ADDR@@@PORT_RPZ_1@ + interface: @IPV4_ADDR@@@PORT_RPZ_2@ + interface: @IPV4_ADDR@@@PORT_RPZ_NX@ interface: @IPV4_ADDR@@@PORT_VIEW_INT@ interface: @IPV4_ADDR@@@PORT_VIEW_EXT@ interface: @IPV4_ADDR@@@PORT_VIEW_INTEXT@ @@ -26,6 +30,9 @@ server: interface-action: @IPV4_ADDR@@@PORT_TAG_1@ allow interface-action: @IPV4_ADDR@@@PORT_TAG_2@ allow interface-action: @IPV4_ADDR@@@PORT_TAG_3@ allow + interface-action: @IPV4_ADDR@@@PORT_RPZ_1@ allow + interface-action: @IPV4_ADDR@@@PORT_RPZ_2@ allow + interface-action: @IPV4_ADDR@@@PORT_RPZ_NX@ allow interface-action: @IPV4_ADDR@@@PORT_VIEW_INT@ allow interface-action: @IPV4_ADDR@@@PORT_VIEW_EXT@ allow interface-action: @IPV4_ADDR@@@PORT_VIEW_INTEXT@ allow @@ -33,6 +40,9 @@ server: interface-tag: @IPV4_ADDR@@@PORT_TAG_1@ "one" interface-tag: @IPV4_ADDR@@@PORT_TAG_2@ "two" interface-tag: @IPV4_ADDR@@@PORT_TAG_3@ "refuse" + interface-tag: @IPV4_ADDR@@@PORT_RPZ_1@ "rpz-one" + interface-tag: @IPV4_ADDR@@@PORT_RPZ_2@ "rpz-two" + interface-tag: @IPV4_ADDR@@@PORT_RPZ_NX@ "rpz-nx" interface-tag-action: @IPV4_ADDR@@@PORT_TAG_1@ one redirect interface-tag-data: @IPV4_ADDR@@@PORT_TAG_1@ one "A 1.1.1.1" interface-tag-action: @IPV4_ADDR@@@PORT_TAG_2@ two redirect @@ -50,6 +60,9 @@ server: interface: @IPV6_ADDR@@@PORT_TAG_1@ interface: @IPV6_ADDR@@@PORT_TAG_2@ interface: @IPV6_ADDR@@@PORT_TAG_3@ + interface: @IPV6_ADDR@@@PORT_RPZ_1@ + interface: @IPV6_ADDR@@@PORT_RPZ_2@ + interface: @IPV6_ADDR@@@PORT_RPZ_NX@ interface: @IPV6_ADDR@@@PORT_VIEW_INT@ interface: @IPV6_ADDR@@@PORT_VIEW_EXT@ interface: @IPV6_ADDR@@@PORT_VIEW_INTEXT@ @@ -60,6 +73,9 @@ server: interface-action: @IPV6_ADDR@@@PORT_TAG_1@ allow interface-action: @IPV6_ADDR@@@PORT_TAG_2@ allow interface-action: @IPV6_ADDR@@@PORT_TAG_3@ allow + interface-action: @IPV6_ADDR@@@PORT_RPZ_1@ allow + interface-action: @IPV6_ADDR@@@PORT_RPZ_2@ allow + interface-action: @IPV6_ADDR@@@PORT_RPZ_NX@ allow interface-action: @IPV6_ADDR@@@PORT_VIEW_INT@ allow interface-action: @IPV6_ADDR@@@PORT_VIEW_EXT@ allow interface-action: @IPV6_ADDR@@@PORT_VIEW_INTEXT@ allow @@ -67,6 +83,9 @@ server: interface-tag: @IPV6_ADDR@@@PORT_TAG_1@ "one" interface-tag: @IPV6_ADDR@@@PORT_TAG_2@ "two" interface-tag: @IPV6_ADDR@@@PORT_TAG_3@ "refuse" + interface-tag: @IPV6_ADDR@@@PORT_RPZ_1@ "rpz-one" + interface-tag: @IPV6_ADDR@@@PORT_RPZ_2@ "rpz-two" + interface-tag: @IPV6_ADDR@@@PORT_RPZ_NX@ "rpz-nx" interface-tag-action: @IPV6_ADDR@@@PORT_TAG_1@ one redirect interface-tag-data: @IPV6_ADDR@@@PORT_TAG_1@ one "A 1.1.1.1" interface-tag-action: @IPV6_ADDR@@@PORT_TAG_2@ two redirect @@ -84,6 +103,9 @@ server: interface: @INTERFACE@@@PORT_TAG_1@ interface: @INTERFACE@@@PORT_TAG_2@ interface: @INTERFACE@@@PORT_TAG_3@ + interface: @INTERFACE@@@PORT_RPZ_1@ + interface: @INTERFACE@@@PORT_RPZ_2@ + interface: @INTERFACE@@@PORT_RPZ_NX@ interface: @INTERFACE@@@PORT_VIEW_INT@ interface: @INTERFACE@@@PORT_VIEW_EXT@ interface: @INTERFACE@@@PORT_VIEW_INTEXT@ @@ -94,6 +116,9 @@ server: interface-action: @INTERFACE@@@PORT_TAG_1@ allow interface-action: @INTERFACE@@@PORT_TAG_2@ allow interface-action: @INTERFACE@@@PORT_TAG_3@ allow + interface-action: @INTERFACE@@@PORT_RPZ_1@ allow + interface-action: @INTERFACE@@@PORT_RPZ_2@ allow + interface-action: @INTERFACE@@@PORT_RPZ_NX@ allow interface-action: @INTERFACE@@@PORT_VIEW_INT@ allow interface-action: @INTERFACE@@@PORT_VIEW_EXT@ allow interface-action: @INTERFACE@@@PORT_VIEW_INTEXT@ allow @@ -101,6 +126,9 @@ server: interface-tag: @INTERFACE@@@PORT_TAG_1@ "one" interface-tag: @INTERFACE@@@PORT_TAG_2@ "two" interface-tag: @INTERFACE@@@PORT_TAG_3@ "refuse" + interface-tag: @INTERFACE@@@PORT_RPZ_1@ "rpz-one" + interface-tag: @INTERFACE@@@PORT_RPZ_2@ "rpz-two" + interface-tag: @INTERFACE@@@PORT_RPZ_NX@ "rpz-nx" interface-tag-action: @INTERFACE@@@PORT_TAG_1@ one redirect interface-tag-data: @INTERFACE@@@PORT_TAG_1@ one "A 1.1.1.1" interface-tag-action: @INTERFACE@@@PORT_TAG_2@ two redirect @@ -130,6 +158,22 @@ view: name: "intext" view-first: yes +# RPZ configuration +rpz: + name: "rpz-one" + zonefile: "rpz-one.zone" + tags: "rpz-one" + +rpz: + name: "rpz-two" + zonefile: "rpz-two.zone" + tags: "rpz-two" + +rpz: + name: "rpz-nx" + zonefile: "rpz-nx.zone" + tags: "rpz-nx" + # Stubs configuration forward-zone: name: "." diff --git a/testdata/acl_interface.tdir/acl_interface.pre b/testdata/acl_interface.tdir/acl_interface.pre index ce5358c1b..88ebc4ff9 100644 --- a/testdata/acl_interface.tdir/acl_interface.pre +++ b/testdata/acl_interface.tdir/acl_interface.pre @@ -7,7 +7,7 @@ if test ! -x "`which unshare 2>&1`"; then skip_test "no unshare (from util-linux package) available, skip test" fi -get_random_port 11 +get_random_port 14 PORT_ALLOW=$RND_PORT PORT_DENY=$(($RND_PORT + 1)) @@ -18,8 +18,11 @@ PORT_TAG_3=$(($RND_PORT + 5)) PORT_VIEW_INT=$(($RND_PORT + 6)) PORT_VIEW_EXT=$(($RND_PORT + 7)) PORT_VIEW_INTEXT=$(($RND_PORT + 8)) -FORWARD_PORT=$(($RND_PORT + 9)) -STUB_PORT=$(($RND_PORT + 10)) +PORT_RPZ_1=$(($RND_PORT + 9)) +PORT_RPZ_2=$(($RND_PORT + 10)) +PORT_RPZ_NX=$(($RND_PORT + 11)) +FORWARD_PORT=$(($RND_PORT + 12)) +STUB_PORT=$(($RND_PORT + 13)) IPV4_ADDR=192.168.1.1 IPV6_ADDR=2001:db8::1 @@ -41,6 +44,9 @@ sed \ -e 's/@PORT_VIEW_INT\@/'$PORT_VIEW_INT'/' \ -e 's/@PORT_VIEW_EXT\@/'$PORT_VIEW_EXT'/' \ -e 's/@PORT_VIEW_INTEXT\@/'$PORT_VIEW_INTEXT'/' \ + -e 's/@PORT_RPZ_1\@/'$PORT_RPZ_1'/' \ + -e 's/@PORT_RPZ_2\@/'$PORT_RPZ_2'/' \ + -e 's/@PORT_RPZ_NX\@/'$PORT_RPZ_NX'/' \ -e 's/@FORWARD_PORT\@/'$FORWARD_PORT'/' \ -e 's/@STUB_PORT\@/'$STUB_PORT'/' \ -e 's/@IPV4_ADDR\@/'$IPV4_ADDR'/' \ @@ -63,6 +69,9 @@ echo "PORT_TAG_3=$PORT_TAG_3" >> .tpkg.var.test echo "PORT_VIEW_INT=$PORT_VIEW_INT" >> .tpkg.var.test echo "PORT_VIEW_EXT=$PORT_VIEW_EXT" >> .tpkg.var.test echo "PORT_VIEW_INTEXT=$PORT_VIEW_INTEXT" >> .tpkg.var.test +echo "PORT_RPZ_1=$PORT_RPZ_1" >> .tpkg.var.test +echo "PORT_RPZ_2=$PORT_RPZ_2" >> .tpkg.var.test +echo "PORT_RPZ_NX=$PORT_RPZ_NX" >> .tpkg.var.test echo "FORWARD_PORT=$FORWARD_PORT" >> .tpkg.var.test echo "STUB_PORT=$STUB_PORT" >> .tpkg.var.test echo "IPV4_ADDR=$IPV4_ADDR" >> .tpkg.var.test diff --git a/testdata/acl_interface.tdir/acl_interface.test.scenario b/testdata/acl_interface.tdir/acl_interface.test.scenario index 00b2b059f..4ae0a42f0 100644 --- a/testdata/acl_interface.tdir/acl_interface.test.scenario +++ b/testdata/acl_interface.tdir/acl_interface.test.scenario @@ -78,6 +78,16 @@ expect_refused () { fi } +expect_nx_answer () { + echo "> check answer for NXDOMAIN" + if grep "NXDOMAIN" outfile; then + echo "OK" + else + echo "Not OK" + end 1 + fi +} + expect_external_answer () { echo "> check external answer" if grep "1.2.3.4" outfile; then @@ -118,6 +128,26 @@ expect_tag_two_answer () { fi } +expect_rpz_one_answer () { + echo "> check tag 'one' answer" + if grep "11.11.11.11" outfile; then + echo "OK" + else + echo "Not OK" + end 1 + fi +} + +expect_rpz_two_answer () { + echo "> check tag 'two' answer" + if grep "22.22.22.22" outfile; then + echo "OK" + else + echo "Not OK" + end 1 + fi +} + # do the test for i in 4 6; do @@ -142,6 +172,15 @@ for i in 4 6; do query $i $PORT_TAG_3 "local" expect_refused + query $i $PORT_RPZ_1 "local" + expect_rpz_one_answer + + query $i $PORT_RPZ_2 "local" + expect_rpz_two_answer + + query $i $PORT_RPZ_NX "local" + expect_nx_answer + query $i $PORT_VIEW_INT "www.internal" expect_internal_answer @@ -183,6 +222,15 @@ for addr in $INTERFACE_ADDR_1 $INTERFACE_ADDR_2 $INTERFACE_ADDR_3 $INTERFACE_ADD query_addr $addr $PORT_TAG_3 "local" expect_refused + query_addr $addr $PORT_RPZ_1 "local" + expect_rpz_one_answer + + query_addr $addr $PORT_RPZ_2 "local" + expect_rpz_two_answer + + query_addr $addr $PORT_RPZ_NX "local" + expect_nx_answer + query_addr $addr $PORT_VIEW_INT "www.internal" expect_internal_answer diff --git a/testdata/acl_interface.tdir/rpz-nx.zone b/testdata/acl_interface.tdir/rpz-nx.zone new file mode 100644 index 000000000..a5c828d18 --- /dev/null +++ b/testdata/acl_interface.tdir/rpz-nx.zone @@ -0,0 +1,3 @@ +$ORIGIN rpz-nx. +@ IN SOA no.no no.no 1 2 3 4 5 +local IN CNAME . diff --git a/testdata/acl_interface.tdir/rpz-one.zone b/testdata/acl_interface.tdir/rpz-one.zone new file mode 100644 index 000000000..f5dabab65 --- /dev/null +++ b/testdata/acl_interface.tdir/rpz-one.zone @@ -0,0 +1,3 @@ +$ORIGIN rpz-one. +@ IN SOA no.no no.no 1 2 3 4 5 +local IN A 11.11.11.11 diff --git a/testdata/acl_interface.tdir/rpz-two.zone b/testdata/acl_interface.tdir/rpz-two.zone new file mode 100644 index 000000000..9578dde8f --- /dev/null +++ b/testdata/acl_interface.tdir/rpz-two.zone @@ -0,0 +1,3 @@ +$ORIGIN rpz-two. +@ IN SOA no.no no.no 1 2 3 4 5 +local IN A 22.22.22.22 From 3adb9c8f92ba1e26bbe73da38ab0c471044c3a85 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 12 Jul 2024 16:11:29 +0200 Subject: [PATCH 33/66] - Fix #1103: unbound 1.20.0 segmentation fault with nghttp2. --- doc/Changelog | 3 +++ services/mesh.c | 7 +++++++ testcode/fake_event.c | 4 ++++ util/netevent.c | 7 +++++++ util/netevent.h | 3 +++ 5 files changed, 24 insertions(+) diff --git a/doc/Changelog b/doc/Changelog index 14a72306c..bc71af26d 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,6 +1,9 @@ 12 July 2024: Yorgos - Add RPZ tag tests in acl_interface.tdir. +12 July 2024: Wouter + - Fix #1103: unbound 1.20.0 segmentation fault with nghttp2. + 10 July 2024: Wouter - For #773: In contrib/unbound.service.in set unbound to start after network-online.target. Also for contrib/unbound_portable.service.in. diff --git a/services/mesh.c b/services/mesh.c index 9797c7526..d3811b475 100644 --- a/services/mesh.c +++ b/services/mesh.c @@ -1576,6 +1576,10 @@ void mesh_query_done(struct mesh_state* mstate) tcp_req_info_remove_mesh_state(r->query_reply.c->tcp_req_info, mstate); r_buffer = NULL; } + if(r->query_reply.c->use_h2) { + http2_stream_remove_mesh_state( + r->query_reply.c->h2_stream); + } prev = r; prev_buffer = r_buffer; } @@ -2284,6 +2288,9 @@ mesh_serve_expired_callback(void* arg) r, r_buffer, prev, prev_buffer); if(r->query_reply.c->tcp_req_info) tcp_req_info_remove_mesh_state(r->query_reply.c->tcp_req_info, mstate); + if(r->query_reply.c->use_h2) + http2_stream_remove_mesh_state( + r->query_reply.c->h2_stream); infra_wait_limit_dec(mstate->s.env->infra_cache, &r->query_reply, mstate->s.env->cfg); prev = r; diff --git a/testcode/fake_event.c b/testcode/fake_event.c index 09269289d..f02a98351 100644 --- a/testcode/fake_event.c +++ b/testcode/fake_event.c @@ -1978,4 +1978,8 @@ void http2_stream_add_meshstate(struct http2_stream* ATTR_UNUSED(h2_stream), { } +void http2_stream_remove_mesh_state(struct http2_stream* ATTR_UNUSED(h2_stream)) +{ +} + /*********** End of Dummy routines ***********/ diff --git a/util/netevent.c b/util/netevent.c index 3663144b2..dc5fd63fa 100644 --- a/util/netevent.c +++ b/util/netevent.c @@ -3307,6 +3307,13 @@ void http2_stream_add_meshstate(struct http2_stream* h2_stream, h2_stream->mesh_state = m; } +void http2_stream_remove_mesh_state(struct http2_stream* h2_stream) +{ + if(!h2_stream) + return; + h2_stream->mesh_state = NULL; +} + /** delete http2 session server. After closing connection. */ static void http2_session_server_delete(struct http2_session* h2_session) { diff --git a/util/netevent.h b/util/netevent.h index 1e4a13f9b..6f43ce56c 100644 --- a/util/netevent.h +++ b/util/netevent.h @@ -955,6 +955,9 @@ void http2_session_add_stream(struct http2_session* h2_session, void http2_stream_add_meshstate(struct http2_stream* h2_stream, struct mesh_area* mesh, struct mesh_state* m); +/** Remove mesh state from stream. When the mesh state has been removed. */ +void http2_stream_remove_mesh_state(struct http2_stream* h2_stream); + /** * This routine is published for checks and tests, and is only used internally. * handle libevent callback for timer comm. From 7083d58c6bcbd7fbe6ba39782af0945a43820518 Mon Sep 17 00:00:00 2001 From: Yorgos Thessalonikefs Date: Fri, 12 Jul 2024 16:29:44 +0200 Subject: [PATCH 34/66] - For #1102: clearer text for using interface-* options for the loopback interface. --- doc/Changelog | 2 ++ doc/unbound.conf.5.in | 11 ++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/doc/Changelog b/doc/Changelog index bc71af26d..f304ecb27 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,5 +1,7 @@ 12 July 2024: Yorgos - Add RPZ tag tests in acl_interface.tdir. + - For #1102: clearer text for using interface-* options for the + loopback interface. 12 July 2024: Wouter - Fix #1103: unbound 1.20.0 segmentation fault with nghttp2. diff --git a/doc/unbound.conf.5.in b/doc/unbound.conf.5.in index 34e61d69f..390706af7 100644 --- a/doc/unbound.conf.5.in +++ b/doc/unbound.conf.5.in @@ -788,7 +788,8 @@ transports, regardless of the presence of an DNS Cookie and regardless of the UDP queries without a DNS Cookie receive REFUSED responses with the TC flag set, that may trigger fall back to TCP for those clients. .IP -By default only localhost is \fIallow\fRed, the rest is \fIrefuse\fRd. +By default only localhost (the 127.0.0.0/8 IP netblock, not the loopback +interface) is implicitly \fIallow\fRed, the rest is \fIrefuse\fRd. The default is \fIrefuse\fRd, because that is protocol\-friendly. The DNS protocol is not designed to handle dropped packets due to policy, and dropping may result in (possibly excessive) retried queries. @@ -824,8 +825,12 @@ Similar to \fBaccess\-control:\fR but for interfaces. .IP The action is the same as the ones defined under \fBaccess\-control:\fR. Interfaces are \fIrefuse\fRd by default. -By default only localhost (the IP netblock, not the loopback interface) is -\fIallow\fRed through the default \fBaccess\-control:\fR behavior. +By default only localhost (the 127.0.0.0/8 IP netblock, not the loopback +interface) is implicitly \fIallow\fRed through the default +\fBaccess\-control:\fR behavior. +This also means that any attempt to use the \fBinterface-*:\fR options for the +loopback interface will not work as they will be overridden by the implicit +default "\fBaccess\-control:\fR 127.0.0.0/8 allow" option. .IP Note that the interface needs to be already specified with \fBinterface:\fR and that any \fBaccess-control*:\fR setting overrides all \fBinterface-*:\fR From d52f501d903909096b9d971cbda9e5b65eba6777 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 12 Jul 2024 16:41:46 +0200 Subject: [PATCH 35/66] - For #1103: fix to also drop mesh state reference when a h2 reply is dropped. --- doc/Changelog | 2 ++ services/mesh.c | 3 +++ 2 files changed, 5 insertions(+) diff --git a/doc/Changelog b/doc/Changelog index bc71af26d..88857239b 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -3,6 +3,8 @@ 12 July 2024: Wouter - Fix #1103: unbound 1.20.0 segmentation fault with nghttp2. + - For #1103: fix to also drop mesh state reference when a h2 reply is + dropped. 10 July 2024: Wouter - For #773: In contrib/unbound.service.in set unbound to start after diff --git a/services/mesh.c b/services/mesh.c index d3811b475..fa03c63d8 100644 --- a/services/mesh.c +++ b/services/mesh.c @@ -974,6 +974,9 @@ mesh_state_cleanup(struct mesh_state* mstate) for(; rep; rep=rep->next) { infra_wait_limit_dec(mesh->env->infra_cache, &rep->query_reply, mesh->env->cfg); + if(rep->query_reply.c->use_h2) + http2_stream_remove_mesh_state( + rep->query_reply.c->h2_stream); comm_point_drop_reply(&rep->query_reply); log_assert(mesh->num_reply_addrs > 0); mesh->num_reply_addrs--; From 8947c2c7646c2f8646b3e10efe25552f5e789068 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 15 Jul 2024 14:51:20 +0200 Subject: [PATCH 36/66] - For #1103: fix to also drop mesh state reference when the discard limit is reached, when there is an error making a new recursion state and when the connection is dropped with is_drop. --- doc/Changelog | 5 +++++ services/mesh.c | 12 ++++++++++++ 2 files changed, 17 insertions(+) diff --git a/doc/Changelog b/doc/Changelog index 14e09ab41..30c267cb4 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,8 @@ +15 July 2024: Wouter + - For #1103: fix to also drop mesh state reference when the discard + limit is reached, when there is an error making a new recursion + state and when the connection is dropped with is_drop. + 12 July 2024: Yorgos - Add RPZ tag tests in acl_interface.tdir. - For #1102: clearer text for using interface-* options for the diff --git a/services/mesh.c b/services/mesh.c index fa03c63d8..a78e9dde9 100644 --- a/services/mesh.c +++ b/services/mesh.c @@ -566,6 +566,8 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo, edns->opt_list_inplace_cb_out = NULL; error_encode(r_buffer, LDNS_RCODE_SERVFAIL, qinfo, qid, qflags, edns); + if(rep->c->use_h2) + http2_stream_remove_mesh_state(rep->c->h2_stream); comm_point_send_reply(rep); if(added) mesh_state_delete(&s->s); @@ -1533,6 +1535,9 @@ void mesh_query_done(struct mesh_state* mstate) infra_wait_limit_dec(mstate->s.env->infra_cache, &r->query_reply, mstate->s.env->cfg); mstate->reply_list = NULL; + if(r->query_reply.c->use_h2) + http2_stream_remove_mesh_state( + r->query_reply.c->h2_stream); comm_point_drop_reply(&r->query_reply); mstate->reply_list = reply_list; mstate->s.env->mesh->stats_dropped++; @@ -1565,6 +1570,10 @@ void mesh_query_done(struct mesh_state* mstate) infra_wait_limit_dec(mstate->s.env->infra_cache, &r->query_reply, mstate->s.env->cfg); mstate->reply_list = NULL; + if(r->query_reply.c->use_h2) { + http2_stream_remove_mesh_state( + r->query_reply.c->h2_stream); + } comm_point_drop_reply(&r->query_reply); mstate->reply_list = reply_list; } else { @@ -2258,6 +2267,9 @@ mesh_serve_expired_callback(void* arg) infra_wait_limit_dec(mstate->s.env->infra_cache, &r->query_reply, mstate->s.env->cfg); mstate->reply_list = NULL; + if(r->query_reply.c->use_h2) + http2_stream_remove_mesh_state( + r->query_reply.c->h2_stream); comm_point_drop_reply(&r->query_reply); mstate->reply_list = reply_list; mstate->s.env->mesh->stats_dropped++; From 8fca3e7c5b0acdee9a2b687cddc41e903c0788d5 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 16 Jul 2024 14:23:10 +0200 Subject: [PATCH 37/66] - For #1103: Fix to drop mesh state reference for the http2 stream associated with the reply, not the currently active stream. And it does not remove it twice on a mesh_send_reply call. The reply h2_stream is NULL when not in use, for more initialisation. --- doc/Changelog | 6 ++++++ services/mesh.c | 23 ++++++++--------------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/doc/Changelog b/doc/Changelog index 30c267cb4..e55dc5fc0 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,9 @@ +16 July 2024: Wouter + - For #1103: Fix to drop mesh state reference for the http2 stream + associated with the reply, not the currently active stream. And + it does not remove it twice on a mesh_send_reply call. The reply + h2_stream is NULL when not in use, for more initialisation. + 15 July 2024: Wouter - For #1103: fix to also drop mesh state reference when the discard limit is reached, when there is an error making a new recursion diff --git a/services/mesh.c b/services/mesh.c index a78e9dde9..522118844 100644 --- a/services/mesh.c +++ b/services/mesh.c @@ -977,8 +977,7 @@ mesh_state_cleanup(struct mesh_state* mstate) infra_wait_limit_dec(mesh->env->infra_cache, &rep->query_reply, mesh->env->cfg); if(rep->query_reply.c->use_h2) - http2_stream_remove_mesh_state( - rep->query_reply.c->h2_stream); + http2_stream_remove_mesh_state(rep->h2_stream); comm_point_drop_reply(&rep->query_reply); log_assert(mesh->num_reply_addrs > 0); mesh->num_reply_addrs--; @@ -1536,8 +1535,7 @@ void mesh_query_done(struct mesh_state* mstate) &r->query_reply, mstate->s.env->cfg); mstate->reply_list = NULL; if(r->query_reply.c->use_h2) - http2_stream_remove_mesh_state( - r->query_reply.c->h2_stream); + http2_stream_remove_mesh_state(r->h2_stream); comm_point_drop_reply(&r->query_reply); mstate->reply_list = reply_list; mstate->s.env->mesh->stats_dropped++; @@ -1571,8 +1569,7 @@ void mesh_query_done(struct mesh_state* mstate) &r->query_reply, mstate->s.env->cfg); mstate->reply_list = NULL; if(r->query_reply.c->use_h2) { - http2_stream_remove_mesh_state( - r->query_reply.c->h2_stream); + http2_stream_remove_mesh_state(r->h2_stream); } comm_point_drop_reply(&r->query_reply); mstate->reply_list = reply_list; @@ -1588,10 +1585,8 @@ void mesh_query_done(struct mesh_state* mstate) tcp_req_info_remove_mesh_state(r->query_reply.c->tcp_req_info, mstate); r_buffer = NULL; } - if(r->query_reply.c->use_h2) { - http2_stream_remove_mesh_state( - r->query_reply.c->h2_stream); - } + /* mesh_send_reply removed mesh state from + * http2_stream. */ prev = r; prev_buffer = r_buffer; } @@ -1744,6 +1739,7 @@ int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns, return 0; if(rep->c->use_h2) r->h2_stream = rep->c->h2_stream; + else r->h2_stream = NULL; /* Data related to local alias stored in 'qinfo' (if any) is ephemeral * and can be different for different original queries (even if the @@ -2268,8 +2264,7 @@ mesh_serve_expired_callback(void* arg) &r->query_reply, mstate->s.env->cfg); mstate->reply_list = NULL; if(r->query_reply.c->use_h2) - http2_stream_remove_mesh_state( - r->query_reply.c->h2_stream); + http2_stream_remove_mesh_state(r->h2_stream); comm_point_drop_reply(&r->query_reply); mstate->reply_list = reply_list; mstate->s.env->mesh->stats_dropped++; @@ -2303,9 +2298,7 @@ mesh_serve_expired_callback(void* arg) r, r_buffer, prev, prev_buffer); if(r->query_reply.c->tcp_req_info) tcp_req_info_remove_mesh_state(r->query_reply.c->tcp_req_info, mstate); - if(r->query_reply.c->use_h2) - http2_stream_remove_mesh_state( - r->query_reply.c->h2_stream); + /* mesh_send_reply removed mesh state from http2_stream. */ infra_wait_limit_dec(mstate->s.env->infra_cache, &r->query_reply, mstate->s.env->cfg); prev = r; From c3dd6a2dbd516ecf0b833f0fba1b1c46d65fbf93 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 19 Jul 2024 10:04:40 +0200 Subject: [PATCH 38/66] - Add dnstap-sample-rate that logs only 1/N messages, for high volume server environments. Thanks Dan Luther. --- Makefile.in | 3 ++- dnstap/dnstap.c | 45 +++++++++++++++++++++++++++++++++++++++++++ dnstap/dnstap.h | 8 ++++++++ doc/Changelog | 4 ++++ doc/example.conf.in | 2 ++ doc/unbound.conf.5.in | 7 +++++++ util/config_file.c | 2 ++ util/config_file.h | 2 ++ util/configlexer.lex | 1 + util/configparser.y | 15 ++++++++++++++- 10 files changed, 87 insertions(+), 2 deletions(-) diff --git a/Makefile.in b/Makefile.in index f30ca81a8..e265a9313 100644 --- a/Makefile.in +++ b/Makefile.in @@ -439,7 +439,8 @@ unbound-control-setup: smallapp/unbound-control-setup.sh dnstap.lo dnstap.o: $(srcdir)/dnstap/dnstap.c config.h dnstap/dnstap_config.h \ dnstap/dnstap.pb-c.c dnstap/dnstap.pb-c.h $(srcdir)/dnstap/dnstap.h \ $(srcdir)/util/config_file.h $(srcdir)/util/log.h \ - $(srcdir)/util/netevent.h $(srcdir)/util/net_help.h + $(srcdir)/util/netevent.h $(srcdir)/util/net_help.h \ + $(srcdir)/util/locks.h dnstap/dnstap.pb-c.c dnstap/dnstap.pb-c.h: $(srcdir)/dnstap/dnstap.proto @-if test ! -d dnstap; then $(INSTALL) -d dnstap; fi diff --git a/dnstap/dnstap.c b/dnstap/dnstap.c index 5cdda0c2d..cff308f93 100644 --- a/dnstap/dnstap.c +++ b/dnstap/dnstap.c @@ -86,6 +86,31 @@ dt_pack(const Dnstap__Dnstap *d, void **buf, size_t *sz) return 1; } +/** See if the message is sent due to dnstap sample rate */ +static int +dt_sample_rate_limited(struct dt_env* env) +{ + lock_basic_lock(&env->sample_lock); + /* Sampling is every [n] packets. Where n==1, every packet is sent */ + if(env->sample_rate > 1) { + int submit = 0; + /* if sampling is engaged... */ + if (env->sample_rate_count > env->sample_rate) { + /* once the count passes the limit */ + /* submit the message */ + submit = 1; + /* and reset the count */ + env->sample_rate_count = 0; + } + /* increment count regardless */ + env->sample_rate_count++; + lock_basic_unlock(&env->sample_lock); + return !submit; + } + lock_basic_unlock(&env->sample_lock); + return 0; +} + static void dt_send(const struct dt_env *env, void *buf, size_t len_buf) { @@ -146,6 +171,7 @@ dt_create(struct config_file* cfg) env = (struct dt_env *) calloc(1, sizeof(struct dt_env)); if (!env) return NULL; + lock_basic_init(&env->sample_lock); env->dtio = dt_io_thread_create(); if(!env->dtio) { @@ -241,6 +267,12 @@ dt_apply_cfg(struct dt_env *env, struct config_file *cfg) { verbose(VERB_OPS, "dnstap Message/FORWARDER_RESPONSE enabled"); } + lock_basic_lock(&env->sample_lock); + if((env->sample_rate = (unsigned int)cfg->dnstap_sample_rate)) + { + verbose(VERB_OPS, "dnstap SAMPLE_RATE enabled and set to \"%d\"", (int)env->sample_rate); + } + lock_basic_unlock(&env->sample_lock); } int @@ -273,6 +305,7 @@ dt_delete(struct dt_env *env) if (!env) return; dt_io_thread_delete(env->dtio); + lock_basic_destroy(&env->sample_lock); free(env->identity); free(env->version); free(env); @@ -409,6 +442,9 @@ dt_msg_send_client_query(struct dt_env *env, struct dt_msg dm; struct timeval qtime; + if(dt_sample_rate_limited(env)) + return; + if(tstamp) memcpy(&qtime, tstamp, sizeof(qtime)); else gettimeofday(&qtime, NULL); @@ -447,6 +483,9 @@ dt_msg_send_client_response(struct dt_env *env, struct dt_msg dm; struct timeval rtime; + if(dt_sample_rate_limited(env)) + return; + gettimeofday(&rtime, NULL); /* type */ @@ -484,6 +523,9 @@ dt_msg_send_outside_query(struct dt_env *env, struct timeval qtime; uint16_t qflags; + if(dt_sample_rate_limited(env)) + return; + gettimeofday(&qtime, NULL); qflags = sldns_buffer_read_u16_at(qmsg, 2); @@ -537,6 +579,9 @@ dt_msg_send_outside_response(struct dt_env *env, struct dt_msg dm; uint16_t qflags; + if(dt_sample_rate_limited(env)) + return; + (void)qbuf_len; log_assert(qbuf_len >= sizeof(qflags)); memcpy(&qflags, qbuf, sizeof(qflags)); qflags = ntohs(qflags); diff --git a/dnstap/dnstap.h b/dnstap/dnstap.h index 77914c20c..21c033697 100644 --- a/dnstap/dnstap.h +++ b/dnstap/dnstap.h @@ -39,6 +39,7 @@ #ifdef USE_DNSTAP +#include "util/locks.h" struct config_file; struct sldns_buffer; struct dt_msg_queue; @@ -75,6 +76,13 @@ struct dt_env { unsigned log_forwarder_query_messages : 1; /** whether to log Message/FORWARDER_RESPONSE */ unsigned log_forwarder_response_messages : 1; + + /** lock on sample count */ + lock_basic_type sample_lock; + /** rate limit value from config, samples 1/N messages */ + unsigned int sample_rate; + /** rate limit counter */ + unsigned int sample_rate_count; }; /** diff --git a/doc/Changelog b/doc/Changelog index e55dc5fc0..916d8eeaa 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,7 @@ +19 July 2024: Wouter + - Add dnstap-sample-rate that logs only 1/N messages, for high volume + server environments. Thanks Dan Luther. + 16 July 2024: Wouter - For #1103: Fix to drop mesh state reference for the http2 stream associated with the reply, not the currently active stream. And diff --git a/doc/example.conf.in b/doc/example.conf.in index e0aec8ec7..329bed150 100644 --- a/doc/example.conf.in +++ b/doc/example.conf.in @@ -1329,6 +1329,8 @@ remote-control: # dnstap-identity: "" # # if "" it uses the package version. # dnstap-version: "" +# # log only 1/N messages, if 0 it is disabled. default 0. +# dnstap-sample-rate: 0 # dnstap-log-resolver-query-messages: no # dnstap-log-resolver-response-messages: no # dnstap-log-client-query-messages: no diff --git a/doc/unbound.conf.5.in b/doc/unbound.conf.5.in index 390706af7..ab146d5e4 100644 --- a/doc/unbound.conf.5.in +++ b/doc/unbound.conf.5.in @@ -2852,6 +2852,13 @@ Default is "". The version to send with messages, if "" the package version is used. Default is "". .TP +.B dnstap-sample-rate: \fI +The sample rate for log of messages, it logs only 1/N messages. With 0 it +is disabled. Default is 0. This is useful in a high volume environment, +where log functionality would otherwise not be reliable. For example 10 +would spend only 1/10th time on logging, and 100 would only spend a +hundredth of the time on logging. +.TP .B dnstap-log-resolver-query-messages: \fI Enable to log resolver query messages. Default is no. These are messages from Unbound to upstream servers. diff --git a/util/config_file.c b/util/config_file.c index 2ac6c4680..6c42a80d0 100644 --- a/util/config_file.c +++ b/util/config_file.c @@ -770,6 +770,7 @@ int config_set_option(struct config_file* cfg, const char* opt, else S_YNO("dnstap-send-version:", dnstap_send_version) else S_STR("dnstap-identity:", dnstap_identity) else S_STR("dnstap-version:", dnstap_version) + else S_NUMBER_OR_ZERO("dnstap-sample-rate:", dnstap_sample_rate) else S_YNO("dnstap-log-resolver-query-messages:", dnstap_log_resolver_query_messages) else S_YNO("dnstap-log-resolver-response-messages:", @@ -1249,6 +1250,7 @@ config_get_option(struct config_file* cfg, const char* opt, else O_YNO(opt, "dnstap-send-version", dnstap_send_version) else O_STR(opt, "dnstap-identity", dnstap_identity) else O_STR(opt, "dnstap-version", dnstap_version) + else O_UNS(opt, "dnstap-sample-rate", dnstap_sample_rate) else O_YNO(opt, "dnstap-log-resolver-query-messages", dnstap_log_resolver_query_messages) else O_YNO(opt, "dnstap-log-resolver-response-messages", diff --git a/util/config_file.h b/util/config_file.h index d3a2e268c..cca714127 100644 --- a/util/config_file.h +++ b/util/config_file.h @@ -592,6 +592,8 @@ struct config_file { char* dnstap_identity; /** dnstap "version", package version is used if "". */ char* dnstap_version; + /** dnstap sample rate */ + int dnstap_sample_rate; /** true to log dnstap RESOLVER_QUERY message events */ int dnstap_log_resolver_query_messages; diff --git a/util/configlexer.lex b/util/configlexer.lex index 7ae1b8c38..31a37d50d 100644 --- a/util/configlexer.lex +++ b/util/configlexer.lex @@ -513,6 +513,7 @@ dnstap-log-forwarder-query-messages{COLON} { YDVAR(1, VAR_DNSTAP_LOG_FORWARDER_QUERY_MESSAGES) } dnstap-log-forwarder-response-messages{COLON} { YDVAR(1, VAR_DNSTAP_LOG_FORWARDER_RESPONSE_MESSAGES) } +dnstap-sample-rate { YDVAR(1, VAR_DNSTAP_SAMPLE_RATE) } disable-dnssec-lame-check{COLON} { YDVAR(1, VAR_DISABLE_DNSSEC_LAME_CHECK) } ip-ratelimit{COLON} { YDVAR(1, VAR_IP_RATELIMIT) } ip-ratelimit-cookie{COLON} { YDVAR(1, VAR_IP_RATELIMIT_COOKIE) } diff --git a/util/configparser.y b/util/configparser.y index 5f42126f7..cf026bdad 100644 --- a/util/configparser.y +++ b/util/configparser.y @@ -137,6 +137,7 @@ extern struct config_parser_state* cfg_parser; %token VAR_DNSTAP_LOG_CLIENT_RESPONSE_MESSAGES %token VAR_DNSTAP_LOG_FORWARDER_QUERY_MESSAGES %token VAR_DNSTAP_LOG_FORWARDER_RESPONSE_MESSAGES +%token VAR_DNSTAP_SAMPLE_RATE %token VAR_RESPONSE_IP_TAG VAR_RESPONSE_IP VAR_RESPONSE_IP_DATA %token VAR_HARDEN_ALGO_DOWNGRADE VAR_IP_TRANSPARENT %token VAR_IP_DSCP @@ -3453,7 +3454,8 @@ content_dt: dt_dnstap_enable | dt_dnstap_socket_path | dt_dnstap_bidirectional | dt_dnstap_log_client_query_messages | dt_dnstap_log_client_response_messages | dt_dnstap_log_forwarder_query_messages | - dt_dnstap_log_forwarder_response_messages + dt_dnstap_log_forwarder_response_messages | + dt_dnstap_sample_rate ; dt_dnstap_enable: VAR_DNSTAP_ENABLE STRING_ARG { @@ -3617,6 +3619,17 @@ dt_dnstap_log_forwarder_response_messages: VAR_DNSTAP_LOG_FORWARDER_RESPONSE_MES free($2); } ; +dt_dnstap_sample_rate: VAR_DNSTAP_SAMPLE_RATE STRING_ARG + { + OUTYY(("P(dt_dnstap_sample_rate:%s)\n", $2)); + if(atoi($2) == 0 && strcmp($2, "0") != 0) + yyerror("number expected"); + else if(atoi($2) < 0) + yyerror("dnstap sample rate too small"); + else cfg_parser->cfg->dnstap_sample_rate = atoi($2); + free($2); + } + ; pythonstart: VAR_PYTHON { OUTYY(("\nP(python:)\n")); From 3af4e44646444ad55ae86cf1e09862a71e9e0e66 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 19 Jul 2024 16:16:02 +0200 Subject: [PATCH 39/66] - Fix dnstap wakeup, a running wakeup timer is left to expire and not increased, a timer is started when the dtio thread is sleeping, the timer set disabled when the dtio thread goes to sleep, and after sleep the thread checks to see if there are messages to log immediately. --- dnstap/dtstream.c | 43 +++++++++++++++++++++++++++++++++++-------- doc/Changelog | 5 +++++ testcode/fake_event.c | 6 ++++++ 3 files changed, 46 insertions(+), 8 deletions(-) diff --git a/dnstap/dtstream.c b/dnstap/dtstream.c index e381def19..de635f195 100644 --- a/dnstap/dtstream.c +++ b/dnstap/dtstream.c @@ -176,10 +176,7 @@ void mq_wakeup_cb(void* arg) { struct dt_msg_queue* mq = (struct dt_msg_queue*)arg; - /* even if the dtio is already active, because perhaps much - * traffic suddenly, we leave the timer running to save on - * managing it, the once a second timer is less work then - * starting and stopping the timer frequently */ + lock_basic_lock(&mq->dtio->wakeup_timer_lock); mq->dtio->wakeup_timer_enabled = 0; lock_basic_unlock(&mq->dtio->wakeup_timer_lock); @@ -210,6 +207,8 @@ dt_msg_queue_start_timer(struct dt_msg_queue* mq, int wakeupnow) lock_basic_lock(&mq->dtio->wakeup_timer_lock); if(mq->dtio->wakeup_timer_enabled) { if(wakeupnow) { + tv.tv_sec = 0; + tv.tv_usec = 0; comm_timer_set(mq->wakeup_timer, &tv); } lock_basic_unlock(&mq->dtio->wakeup_timer_lock); @@ -221,8 +220,14 @@ dt_msg_queue_start_timer(struct dt_msg_queue* mq, int wakeupnow) if(!wakeupnow) { tv.tv_sec = 1; tv.tv_usec = 0; + /* If it is already set, keep it running. */ + if(!comm_timer_is_set(mq->wakeup_timer)) + comm_timer_set(mq->wakeup_timer, &tv); + } else { + tv.tv_sec = 0; + tv.tv_usec = 0; + comm_timer_set(mq->wakeup_timer, &tv); } - comm_timer_set(mq->wakeup_timer, &tv); lock_basic_unlock(&mq->dtio->wakeup_timer_lock); } @@ -260,8 +265,9 @@ dt_msg_queue_submit(struct dt_msg_queue* mq, void* buf, size_t len) /* acquire lock */ lock_basic_lock(&mq->lock); - /* if list was empty, start timer for (eventual) wakeup */ - if(mq->first == NULL) + /* if list was empty, start timer for (eventual) wakeup, + * or if dtio is not writing now an eventual wakeup is needed. */ + if(mq->first == NULL || !mq->dtio->event_added_is_write) wakeupstarttimer = 1; /* if list contains more than wakeupnum elements, wakeup now, * or if list is (going to be) almost full */ @@ -1259,6 +1265,13 @@ static void dtio_sleep(struct dt_io_thread* dtio) /* unregister the event polling for write, because there is * nothing to be written */ (void)dtio_add_output_event_read(dtio); + + /* Set wakeuptimer enabled off; so that the next worker thread that + * wants to log starts a timer if needed, since the writer thread + * has gone to sleep. */ + lock_basic_lock(&dtio->wakeup_timer_lock); + dtio->wakeup_timer_enabled = 0; + lock_basic_unlock(&dtio->wakeup_timer_lock); } #ifdef HAVE_SSL @@ -1521,8 +1534,22 @@ void dtio_output_cb(int ATTR_UNUSED(fd), short bits, void* arg) /* no messages on the first iteration, * the queues are all empty */ dtio_sleep(dtio); + /* After putting to sleep, see if + * a message is in a message queue, + * if so, resume service. Stops a + * race condition where a thread could + * have one message but the dtio + * also just went to sleep. With the + * message queued between the + * dtio_find_msg and dtio_sleep + * calls. */ + if(dtio_find_msg(dtio)) { + if(!dtio_add_output_event_write(dtio)) + return; + } } - return; /* nothing to do */ + if(!dtio->cur_msg) + return; /* nothing to do */ } } diff --git a/doc/Changelog b/doc/Changelog index 916d8eeaa..815773d1a 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,6 +1,11 @@ 19 July 2024: Wouter - Add dnstap-sample-rate that logs only 1/N messages, for high volume server environments. Thanks Dan Luther. + - Fix dnstap wakeup, a running wakeup timer is left to expire and not + increased, a timer is started when the dtio thread is sleeping, + the timer set disabled when the dtio thread goes to sleep, and + after sleep the thread checks to see if there are messages to log + immediately. 16 July 2024: Wouter - For #1103: Fix to drop mesh state reference for the http2 stream diff --git a/testcode/fake_event.c b/testcode/fake_event.c index f02a98351..a517fa5f3 100644 --- a/testcode/fake_event.c +++ b/testcode/fake_event.c @@ -1655,6 +1655,12 @@ void comm_timer_set(struct comm_timer* timer, struct timeval* tv) timeval_add(&t->tv, &t->runtime->now_tv); } +int comm_timer_is_set(struct comm_timer* timer) +{ + struct fake_timer* t = (struct fake_timer*)timer; + return t->enabled; +} + void comm_timer_delete(struct comm_timer* timer) { struct fake_timer* t = (struct fake_timer*)timer; From f9bd35dcfa256a9dced78518430220f493a9524f Mon Sep 17 00:00:00 2001 From: Richard Barnes Date: Mon, 22 Jul 2024 16:37:38 -0700 Subject: [PATCH 40/66] Make fallthrough explicit for libworker.c The code currently doesn't compile with LLVM's `-Wimplicit-fallthrough` flag, but the attribute works for both GCC (>=7) and LLVM. --- libunbound/libworker.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libunbound/libworker.c b/libunbound/libworker.c index 5c75f61d8..191aa0b27 100644 --- a/libunbound/libworker.c +++ b/libunbound/libworker.c @@ -292,7 +292,7 @@ libworker_do_cmd(struct libworker* w, uint8_t* msg, uint32_t len) log_err("unknown command for bg worker %d", (int)context_serial_getcmd(msg, len)); /* and fall through to quit */ - /* fallthrough */ + __attribute__((fallthrough)); case UB_LIBCMD_QUIT: free(msg); comm_base_exit(w->base); From 3512eaec4851d472f509c5a7bd67c7d360a7e0b7 Mon Sep 17 00:00:00 2001 From: Yorgos Thessalonikefs Date: Tue, 23 Jul 2024 09:07:06 +0200 Subject: [PATCH 41/66] - Fix #1106: ratelimit-below-domain logs the wrong FROM address. --- doc/Changelog | 3 +++ services/outside_network.c | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/doc/Changelog b/doc/Changelog index 815773d1a..6bac83a7c 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,6 @@ +23 July 2024: Yorgos + - Fix #1106: ratelimit-below-domain logs the wrong FROM address. + 19 July 2024: Wouter - Add dnstap-sample-rate that logs only 1/N messages, for high volume server environments. Thanks Dan Luther. diff --git a/services/outside_network.c b/services/outside_network.c index eab94535c..58f1e6d58 100644 --- a/services/outside_network.c +++ b/services/outside_network.c @@ -3466,7 +3466,10 @@ outnet_serviced_query(struct outside_network* outnet, timenow = *env->now; if(!infra_ratelimit_inc(env->infra_cache, zone, zonelen, timenow, env->cfg->ratelimit_backoff, - &qstate->qinfo, qstate->reply)) { + &qstate->qinfo, + qstate->mesh_info->reply_list + ?&qstate->mesh_info->reply_list->query_reply + :NULL)) { /* Can we pass through with slip factor? */ if(env->cfg->ratelimit_factor == 0 || ub_random_max(env->rnd, From 5bea29b01cdd79262dd19f27e03527c9e625ccb0 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 23 Jul 2024 09:47:42 +0200 Subject: [PATCH 42/66] - For #1110: Test for fallthrough attribute in configure and add fallthrough attribute annotations. --- config.h.in | 6 ++ configure | 69 ++++++++++++ configure.ac | 41 ++++++++ dns64/dns64.c | 1 + doc/Changelog | 5 + libunbound/libworker.c | 3 +- services/cache/dns.c | 1 + services/rpz.c | 24 +++-- sldns/parseutil.c | 9 ++ sldns/wire2str.c | 1 + util/data/msgparse.c | 1 + util/netevent.c | 1 + util/proxy_protocol.c | 1 + util/siphash.c | 6 ++ util/storage/lookup3.c | 227 +++++++++++++++++++++++++++++++--------- validator/val_secalgo.c | 3 + 16 files changed, 345 insertions(+), 54 deletions(-) diff --git a/config.h.in b/config.h.in index 88347fe4d..4a7143c52 100644 --- a/config.h.in +++ b/config.h.in @@ -1,5 +1,8 @@ /* config.h.in. Generated from configure.ac by autoheader. */ +/* apply the fallthrough attribute. */ +#undef ATTR_FALLTHROUGH + /* apply the noreturn attribute to a function that exits the program */ #undef ATTR_NORETURN @@ -57,6 +60,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_ARPA_INET_H +/* Whether the C compiler accepts the "fallthrough" attribute */ +#undef HAVE_ATTR_FALLTHROUGH + /* Whether the C compiler accepts the "format" attribute */ #undef HAVE_ATTR_FORMAT diff --git a/configure b/configure index 9dc603045..9998b36e4 100755 --- a/configure +++ b/configure @@ -7026,6 +7026,75 @@ printf "%s\n" "#define ATTR_NORETURN __attribute__((__noreturn__))" >>confdefs.h fi + + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C compiler (${CC-cc}) accepts the \"fallthrough\" attribute" >&5 +printf %s "checking whether the C compiler (${CC-cc}) accepts the \"fallthrough\" attribute... " >&6; } +BAKCFLAGS="$CFLAGS" +CFLAGS="$CFLAGS -Werror" +if test ${ac_cv_c_fallthrough_attribute+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_cv_c_fallthrough_attribute=no +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + #include +void f(int x) { + int y = 0; + switch(x) { + case 1: + y = 1; + __attribute__((fallthrough)); + /* fallthrough */ + case 2: + y++; + break; + case 3: + y = 3; + break; + } + printf("%d", y); +} + +int +main (void) +{ + + f(1); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_c_fallthrough_attribute="yes" +else $as_nop + ac_cv_c_fallthrough_attribute="no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + +fi + +CFLAGS="$BAKCFLAGS" + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_fallthrough_attribute" >&5 +printf "%s\n" "$ac_cv_c_fallthrough_attribute" >&6; } +if test $ac_cv_c_fallthrough_attribute = yes; then + +printf "%s\n" "#define HAVE_ATTR_FALLTHROUGH 1" >>confdefs.h + + +printf "%s\n" "#define ATTR_FALLTHROUGH __attribute__((fallthrough));" >>confdefs.h + +else + +printf "%s\n" "#define ATTR_FALLTHROUGH /**/" >>confdefs.h + +fi + + if test "$srcdir" != "."; then CPPFLAGS="$CPPFLAGS -I$srcdir" fi diff --git a/configure.ac b/configure.ac index 5597abb88..6adbe0e59 100644 --- a/configure.ac +++ b/configure.ac @@ -365,6 +365,47 @@ fi CHECK_NORETURN_ATTRIBUTE +AC_DEFUN([CHECK_FALLTHROUGH_ATTRIBUTE], +[AC_REQUIRE([AC_PROG_CC]) +AC_MSG_CHECKING(whether the C compiler (${CC-cc}) accepts the "fallthrough" attribute) +BAKCFLAGS="$CFLAGS" +CFLAGS="$CFLAGS -Werror" +AC_CACHE_VAL(ac_cv_c_fallthrough_attribute, +[ac_cv_c_fallthrough_attribute=no +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include +void f(int x) { + int y = 0; + switch(x) { + case 1: + y = 1; + __attribute__((fallthrough)); + /* fallthrough */ + case 2: + y++; + break; + case 3: + y = 3; + break; + } + printf("%d", y); +} +]], [[ + f(1); +]])],[ac_cv_c_fallthrough_attribute="yes"],[ac_cv_c_fallthrough_attribute="no"]) +]) +CFLAGS="$BAKCFLAGS" + +AC_MSG_RESULT($ac_cv_c_fallthrough_attribute) +if test $ac_cv_c_fallthrough_attribute = yes; then + AC_DEFINE(HAVE_ATTR_FALLTHROUGH, 1, [Whether the C compiler accepts the "fallthrough" attribute]) + AC_DEFINE(ATTR_FALLTHROUGH, [__attribute__((fallthrough));], [apply the fallthrough attribute.]) +else + AC_DEFINE(ATTR_FALLTHROUGH,[], [apply the fallthrough attribute.]) +fi +])dnl End of CHECK_FALLTHROUGH_ATTRIBUTE + +CHECK_FALLTHROUGH_ATTRIBUTE + if test "$srcdir" != "."; then CPPFLAGS="$CPPFLAGS -I$srcdir" fi diff --git a/dns64/dns64.c b/dns64/dns64.c index 3a43698a8..cfb6ce63e 100644 --- a/dns64/dns64.c +++ b/dns64/dns64.c @@ -701,6 +701,7 @@ dns64_operate(struct module_qstate* qstate, enum module_ev event, int id, iq->state = DNS64_NEW_QUERY; iq->started_no_cache_store = qstate->no_cache_store; qstate->no_cache_store = 1; + ATTR_FALLTHROUGH /* fallthrough */ case module_event_pass: qstate->ext_state[id] = handle_event_pass(qstate, id); diff --git a/doc/Changelog b/doc/Changelog index 6bac83a7c..221e8d02d 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,6 +1,11 @@ 23 July 2024: Yorgos - Fix #1106: ratelimit-below-domain logs the wrong FROM address. +23 July 2024: Wouter + - Merge #1110: Make fallthrough explicit for libworker.c. + - For #1110: Test for fallthrough attribute in configure and add + fallthrough attribute annotations. + 19 July 2024: Wouter - Add dnstap-sample-rate that logs only 1/N messages, for high volume server environments. Thanks Dan Luther. diff --git a/libunbound/libworker.c b/libunbound/libworker.c index 191aa0b27..94b644a49 100644 --- a/libunbound/libworker.c +++ b/libunbound/libworker.c @@ -292,7 +292,8 @@ libworker_do_cmd(struct libworker* w, uint8_t* msg, uint32_t len) log_err("unknown command for bg worker %d", (int)context_serial_getcmd(msg, len)); /* and fall through to quit */ - __attribute__((fallthrough)); + ATTR_FALLTHROUGH + /* fallthrough */ case UB_LIBCMD_QUIT: free(msg); comm_base_exit(w->base); diff --git a/services/cache/dns.c b/services/cache/dns.c index 426c4506e..60e79a2e7 100644 --- a/services/cache/dns.c +++ b/services/cache/dns.c @@ -110,6 +110,7 @@ store_rrsets(struct module_env* env, struct reply_info* rep, time_t now, /* no break: also copy key item */ /* the line below is matched by gcc regex and silences * the fallthrough warning */ + ATTR_FALLTHROUGH /* fallthrough */ case 1: /* ref updated, item inserted */ rep->rrsets[i] = rep->ref[i].key; diff --git a/services/rpz.c b/services/rpz.c index 1223f6771..d8999a8a5 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -242,10 +242,14 @@ rpz_action_to_localzone_type(enum rpz_action a) case RPZ_NODATA_ACTION: return local_zone_always_nodata; case RPZ_DROP_ACTION: return local_zone_always_deny; case RPZ_PASSTHRU_ACTION: return local_zone_always_transparent; - case RPZ_LOCAL_DATA_ACTION: /* fallthrough */ + case RPZ_LOCAL_DATA_ACTION: + ATTR_FALLTHROUGH + /* fallthrough */ case RPZ_CNAME_OVERRIDE_ACTION: return local_zone_redirect; case RPZ_TCP_ONLY_ACTION: return local_zone_truncate; - case RPZ_INVALID_ACTION: /* fallthrough */ + case RPZ_INVALID_ACTION: + ATTR_FALLTHROUGH + /* fallthrough */ default: return local_zone_invalid; } } @@ -258,10 +262,14 @@ rpz_action_to_respip_action(enum rpz_action a) case RPZ_NODATA_ACTION: return respip_always_nodata; case RPZ_DROP_ACTION: return respip_always_deny; case RPZ_PASSTHRU_ACTION: return respip_always_transparent; - case RPZ_LOCAL_DATA_ACTION: /* fallthrough */ + case RPZ_LOCAL_DATA_ACTION: + ATTR_FALLTHROUGH + /* fallthrough */ case RPZ_CNAME_OVERRIDE_ACTION: return respip_redirect; case RPZ_TCP_ONLY_ACTION: return respip_truncate; - case RPZ_INVALID_ACTION: /* fallthrough */ + case RPZ_INVALID_ACTION: + ATTR_FALLTHROUGH + /* fallthrough */ default: return respip_invalid; } } @@ -276,7 +284,9 @@ localzone_type_to_rpz_action(enum localzone_type lzt) case local_zone_always_transparent: return RPZ_PASSTHRU_ACTION; case local_zone_redirect: return RPZ_LOCAL_DATA_ACTION; case local_zone_truncate: return RPZ_TCP_ONLY_ACTION; - case local_zone_invalid: /* fallthrough */ + case local_zone_invalid: + ATTR_FALLTHROUGH + /* fallthrough */ default: return RPZ_INVALID_ACTION; } } @@ -291,7 +301,9 @@ respip_action_to_rpz_action(enum respip_action a) case respip_always_transparent: return RPZ_PASSTHRU_ACTION; case respip_redirect: return RPZ_LOCAL_DATA_ACTION; case respip_truncate: return RPZ_TCP_ONLY_ACTION; - case respip_invalid: /* fallthrough */ + case respip_invalid: + ATTR_FALLTHROUGH + /* fallthrough */ default: return RPZ_INVALID_ACTION; } } diff --git a/sldns/parseutil.c b/sldns/parseutil.c index dd1f33484..f696c6af1 100644 --- a/sldns/parseutil.c +++ b/sldns/parseutil.c @@ -436,11 +436,13 @@ sldns_b32_ntop_base(const uint8_t* src, size_t src_sz, char* dst, size_t dst_sz, /* ........ ........ ....4444 4....... ........ */ c = src[3] >> 7 ; + ATTR_FALLTHROUGH /* fallthrough */ case 3: dst[4] = b32[(src[2] & 0x0f) << 1 | c]; /* ........ .......3 3333.... ........ ........ */ c = src[2] >> 4 ; + ATTR_FALLTHROUGH /* fallthrough */ case 2: dst[3] = b32[(src[1] & 0x01) << 4 | c]; @@ -449,6 +451,7 @@ sldns_b32_ntop_base(const uint8_t* src, size_t src_sz, char* dst, size_t dst_sz, /* .....111 11...... ........ ........ ........ */ c = src[1] >> 6 ; + ATTR_FALLTHROUGH /* fallthrough */ case 1: dst[1] = b32[(src[0] & 0x07) << 2 | c]; @@ -460,11 +463,14 @@ sldns_b32_ntop_base(const uint8_t* src, size_t src_sz, char* dst, size_t dst_sz, switch (src_sz) { case 1: dst[2] = '='; dst[3] = '='; + ATTR_FALLTHROUGH /* fallthrough */ case 2: dst[4] = '='; + ATTR_FALLTHROUGH /* fallthrough */ case 3: dst[5] = '='; dst[6] = '='; + ATTR_FALLTHROUGH /* fallthrough */ case 4: dst[7] = '='; } @@ -577,17 +583,20 @@ sldns_b32_pton_base(const char* src, size_t src_sz, uint8_t* dst, size_t dst_sz, /* ........ ........ ........ .55555.. ........ */ /* ........ ........ ....4444 4....... ........ */ dst[3] = buf[4] << 7 | buf[5] << 2 | buf[6] >> 3; + ATTR_FALLTHROUGH /* fallthrough */ case 5: /* ........ ........ ....4444 4....... ........ */ /* ........ .......3 3333.... ........ ........ */ dst[2] = buf[3] << 4 | buf[4] >> 1; + ATTR_FALLTHROUGH /* fallthrough */ case 4: /* ........ .......3 3333.... ........ ........ */ /* ........ ..22222. ........ ........ ........ */ /* .....111 11...... ........ ........ ........ */ dst[1] = buf[1] << 6 | buf[2] << 1 | buf[3] >> 4; + ATTR_FALLTHROUGH /* fallthrough */ case 2: /* .....111 11...... ........ ........ ........ */ diff --git a/sldns/wire2str.c b/sldns/wire2str.c index 6962d7bab..ff8399947 100644 --- a/sldns/wire2str.c +++ b/sldns/wire2str.c @@ -1241,6 +1241,7 @@ int sldns_wire2str_svcparam_scan(uint8_t** d, size_t* dlen, char** s, size_t* sl r = sldns_wire2str_svcparam_ech2str(s, slen, data_len, *d); break; case SVCB_KEY_DOHPATH: + ATTR_FALLTHROUGH /* fallthrough */ default: r = sldns_str_print(s, slen, "=\""); diff --git a/util/data/msgparse.c b/util/data/msgparse.c index d06b7bb25..ab1e0b557 100644 --- a/util/data/msgparse.c +++ b/util/data/msgparse.c @@ -1091,6 +1091,7 @@ parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len, break; case COOKIE_STATUS_CLIENT_ONLY: edns->cookie_client = 1; + ATTR_FALLTHROUGH /* fallthrough */ case COOKIE_STATUS_FUTURE: case COOKIE_STATUS_EXPIRED: diff --git a/util/netevent.c b/util/netevent.c index dc5fd63fa..9d5131da9 100644 --- a/util/netevent.c +++ b/util/netevent.c @@ -329,6 +329,7 @@ udp_send_errno_needs_log(struct sockaddr* addr, socklen_t addrlen) case EACCES: if(verbosity < VERB_ALGO) return 0; + break; default: break; } diff --git a/util/proxy_protocol.c b/util/proxy_protocol.c index a18804974..235538b62 100644 --- a/util/proxy_protocol.c +++ b/util/proxy_protocol.c @@ -153,6 +153,7 @@ pp2_write_to_buf(uint8_t* buf, size_t buflen, break; #endif /* INET6 */ case AF_UNIX: + ATTR_FALLTHROUGH /* fallthrough */ default: return 0; diff --git a/util/siphash.c b/util/siphash.c index 32797dff6..a13657ccf 100644 --- a/util/siphash.c +++ b/util/siphash.c @@ -128,26 +128,32 @@ int siphash(const uint8_t *in, const size_t inlen, const uint8_t *k, case 7: b |= ((uint64_t)in[6]) << 48; /** EDIT annotate case statement fallthrough for gcc */ + ATTR_FALLTHROUGH /* fallthrough */ case 6: b |= ((uint64_t)in[5]) << 40; /** EDIT annotate case statement fallthrough for gcc */ + ATTR_FALLTHROUGH /* fallthrough */ case 5: b |= ((uint64_t)in[4]) << 32; /** EDIT annotate case statement fallthrough for gcc */ + ATTR_FALLTHROUGH /* fallthrough */ case 4: b |= ((uint64_t)in[3]) << 24; /** EDIT annotate case statement fallthrough for gcc */ + ATTR_FALLTHROUGH /* fallthrough */ case 3: b |= ((uint64_t)in[2]) << 16; /** EDIT annotate case statement fallthrough for gcc */ + ATTR_FALLTHROUGH /* fallthrough */ case 2: b |= ((uint64_t)in[1]) << 8; /** EDIT annotate case statement fallthrough for gcc */ + ATTR_FALLTHROUGH /* fallthrough */ case 1: b |= ((uint64_t)in[0]); diff --git a/util/storage/lookup3.c b/util/storage/lookup3.c index f2a48f413..ae7c166ec 100644 --- a/util/storage/lookup3.c +++ b/util/storage/lookup3.c @@ -254,11 +254,15 @@ uint32_t initval) /* the previous hash, or an arbitrary value */ switch(length) /* all the case statements fall through */ { case 3 : c+=k[2]; - /* fallthrough */ + ATTR_FALLTHROUGH + /* fallthrough */ case 2 : b+=k[1]; - /* fallthrough */ + ATTR_FALLTHROUGH + /* fallthrough */ case 1 : a+=k[0]; final(a,b,c); + ATTR_FALLTHROUGH + /* fallthrough */ case 0: /* case 0: nothing left to add */ break; } @@ -304,9 +308,15 @@ uint32_t *pb) /* IN: more seed OUT: secondary hash value */ switch(length) /* all the case statements fall through */ { case 3 : c+=k[2]; + ATTR_FALLTHROUGH + /* fallthrough */ case 2 : b+=k[1]; + ATTR_FALLTHROUGH + /* fallthrough */ case 1 : a+=k[0]; final(a,b,c); + ATTR_FALLTHROUGH + /* fallthrough */ case 0: /* case 0: nothing left to add */ break; } @@ -404,16 +414,32 @@ uint32_t hashlittle( const void *key, size_t length, uint32_t initval) switch(length) { case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; - case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ - case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ - case 9 : c+=k8[8]; /* fall through */ + case 11: c+=((uint32_t)k8[10])<<16; + ATTR_FALLTHROUGH + /* fallthrough */ + case 10: c+=((uint32_t)k8[9])<<8; + ATTR_FALLTHROUGH + /* fallthrough */ + case 9 : c+=k8[8]; + ATTR_FALLTHROUGH + /* fallthrough */ case 8 : b+=k[1]; a+=k[0]; break; - case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ - case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ - case 5 : b+=k8[4]; /* fall through */ + case 7 : b+=((uint32_t)k8[6])<<16; + ATTR_FALLTHROUGH + /* fallthrough */ + case 6 : b+=((uint32_t)k8[5])<<8; + ATTR_FALLTHROUGH + /* fallthrough */ + case 5 : b+=k8[4]; + ATTR_FALLTHROUGH + /* fallthrough */ case 4 : a+=k[0]; break; - case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ - case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ + case 3 : a+=((uint32_t)k8[2])<<16; + ATTR_FALLTHROUGH + /* fallthrough */ + case 2 : a+=((uint32_t)k8[1])<<8; + ATTR_FALLTHROUGH + /* fallthrough */ case 1 : a+=k8[0]; break; case 0 : return c; } @@ -443,23 +469,33 @@ uint32_t hashlittle( const void *key, size_t length, uint32_t initval) b+=k[2]+(((uint32_t)k[3])<<16); a+=k[0]+(((uint32_t)k[1])<<16); break; - case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 11: c+=((uint32_t)k8[10])<<16; + ATTR_FALLTHROUGH + /* fallthrough */ case 10: c+=k[4]; b+=k[2]+(((uint32_t)k[3])<<16); a+=k[0]+(((uint32_t)k[1])<<16); break; - case 9 : c+=k8[8]; /* fall through */ + case 9 : c+=k8[8]; + ATTR_FALLTHROUGH + /* fallthrough */ case 8 : b+=k[2]+(((uint32_t)k[3])<<16); a+=k[0]+(((uint32_t)k[1])<<16); break; - case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 7 : b+=((uint32_t)k8[6])<<16; + ATTR_FALLTHROUGH + /* fallthrough */ case 6 : b+=k[2]; a+=k[0]+(((uint32_t)k[1])<<16); break; - case 5 : b+=k8[4]; /* fall through */ + case 5 : b+=k8[4]; + ATTR_FALLTHROUGH + /* fallthrough */ case 4 : a+=k[0]+(((uint32_t)k[1])<<16); break; - case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 3 : a+=((uint32_t)k8[2])<<16; + ATTR_FALLTHROUGH + /* fallthrough */ case 2 : a+=k[0]; break; case 1 : a+=k8[0]; @@ -494,27 +530,38 @@ uint32_t hashlittle( const void *key, size_t length, uint32_t initval) switch(length) /* all the case statements fall through */ { case 12: c+=((uint32_t)k[11])<<24; - /* fallthrough */ + ATTR_FALLTHROUGH + /* fallthrough */ case 11: c+=((uint32_t)k[10])<<16; - /* fallthrough */ + ATTR_FALLTHROUGH + /* fallthrough */ case 10: c+=((uint32_t)k[9])<<8; - /* fallthrough */ + ATTR_FALLTHROUGH + /* fallthrough */ case 9 : c+=k[8]; - /* fallthrough */ + ATTR_FALLTHROUGH + /* fallthrough */ case 8 : b+=((uint32_t)k[7])<<24; - /* fallthrough */ + ATTR_FALLTHROUGH + /* fallthrough */ case 7 : b+=((uint32_t)k[6])<<16; - /* fallthrough */ + ATTR_FALLTHROUGH + /* fallthrough */ case 6 : b+=((uint32_t)k[5])<<8; - /* fallthrough */ + ATTR_FALLTHROUGH + /* fallthrough */ case 5 : b+=k[4]; - /* fallthrough */ + ATTR_FALLTHROUGH + /* fallthrough */ case 4 : a+=((uint32_t)k[3])<<24; - /* fallthrough */ + ATTR_FALLTHROUGH + /* fallthrough */ case 3 : a+=((uint32_t)k[2])<<16; - /* fallthrough */ + ATTR_FALLTHROUGH + /* fallthrough */ case 2 : a+=((uint32_t)k[1])<<8; - /* fallthrough */ + ATTR_FALLTHROUGH + /* fallthrough */ case 1 : a+=k[0]; break; case 0 : return c; @@ -603,16 +650,32 @@ void hashlittle2( switch(length) { case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; - case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ - case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ - case 9 : c+=k8[8]; /* fall through */ + case 11: c+=((uint32_t)k8[10])<<16; + ATTR_FALLTHROUGH + /* fallthrough */ + case 10: c+=((uint32_t)k8[9])<<8; + ATTR_FALLTHROUGH + /* fallthrough */ + case 9 : c+=k8[8]; + ATTR_FALLTHROUGH + /* fallthrough */ case 8 : b+=k[1]; a+=k[0]; break; - case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ - case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ - case 5 : b+=k8[4]; /* fall through */ + case 7 : b+=((uint32_t)k8[6])<<16; + ATTR_FALLTHROUGH + /* fallthrough */ + case 6 : b+=((uint32_t)k8[5])<<8; + ATTR_FALLTHROUGH + /* fallthrough */ + case 5 : b+=k8[4]; + ATTR_FALLTHROUGH + /* fallthrough */ case 4 : a+=k[0]; break; - case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ - case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ + case 3 : a+=((uint32_t)k8[2])<<16; + ATTR_FALLTHROUGH + /* fallthrough */ + case 2 : a+=((uint32_t)k8[1])<<8; + ATTR_FALLTHROUGH + /* fallthrough */ case 1 : a+=k8[0]; break; case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ } @@ -642,23 +705,33 @@ void hashlittle2( b+=k[2]+(((uint32_t)k[3])<<16); a+=k[0]+(((uint32_t)k[1])<<16); break; - case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 11: c+=((uint32_t)k8[10])<<16; + ATTR_FALLTHROUGH + /* fallthrough */ case 10: c+=k[4]; b+=k[2]+(((uint32_t)k[3])<<16); a+=k[0]+(((uint32_t)k[1])<<16); break; - case 9 : c+=k8[8]; /* fall through */ + case 9 : c+=k8[8]; + ATTR_FALLTHROUGH + /* fallthrough */ case 8 : b+=k[2]+(((uint32_t)k[3])<<16); a+=k[0]+(((uint32_t)k[1])<<16); break; - case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 7 : b+=((uint32_t)k8[6])<<16; + ATTR_FALLTHROUGH + /* fallthrough */ case 6 : b+=k[2]; a+=k[0]+(((uint32_t)k[1])<<16); break; - case 5 : b+=k8[4]; /* fall through */ + case 5 : b+=k8[4]; + ATTR_FALLTHROUGH + /* fallthrough */ case 4 : a+=k[0]+(((uint32_t)k[1])<<16); break; - case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 3 : a+=((uint32_t)k8[2])<<16; + ATTR_FALLTHROUGH + /* fallthrough */ case 2 : a+=k[0]; break; case 1 : a+=k8[0]; @@ -693,16 +766,38 @@ void hashlittle2( switch(length) /* all the case statements fall through */ { case 12: c+=((uint32_t)k[11])<<24; + ATTR_FALLTHROUGH + /* fallthrough */ case 11: c+=((uint32_t)k[10])<<16; + ATTR_FALLTHROUGH + /* fallthrough */ case 10: c+=((uint32_t)k[9])<<8; + ATTR_FALLTHROUGH + /* fallthrough */ case 9 : c+=k[8]; + ATTR_FALLTHROUGH + /* fallthrough */ case 8 : b+=((uint32_t)k[7])<<24; + ATTR_FALLTHROUGH + /* fallthrough */ case 7 : b+=((uint32_t)k[6])<<16; + ATTR_FALLTHROUGH + /* fallthrough */ case 6 : b+=((uint32_t)k[5])<<8; + ATTR_FALLTHROUGH + /* fallthrough */ case 5 : b+=k[4]; + ATTR_FALLTHROUGH + /* fallthrough */ case 4 : a+=((uint32_t)k[3])<<24; + ATTR_FALLTHROUGH + /* fallthrough */ case 3 : a+=((uint32_t)k[2])<<16; + ATTR_FALLTHROUGH + /* fallthrough */ case 2 : a+=((uint32_t)k[1])<<8; + ATTR_FALLTHROUGH + /* fallthrough */ case 1 : a+=k[0]; break; case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ @@ -784,16 +879,32 @@ uint32_t hashbig( const void *key, size_t length, uint32_t initval) switch(length) /* all the case statements fall through */ { case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; - case 11: c+=((uint32_t)k8[10])<<8; /* fall through */ - case 10: c+=((uint32_t)k8[9])<<16; /* fall through */ - case 9 : c+=((uint32_t)k8[8])<<24; /* fall through */ + case 11: c+=((uint32_t)k8[10])<<8; + ATTR_FALLTHROUGH + /* fallthrough */ + case 10: c+=((uint32_t)k8[9])<<16; + ATTR_FALLTHROUGH + /* fallthrough */ + case 9 : c+=((uint32_t)k8[8])<<24; + ATTR_FALLTHROUGH + /* fallthrough */ case 8 : b+=k[1]; a+=k[0]; break; - case 7 : b+=((uint32_t)k8[6])<<8; /* fall through */ - case 6 : b+=((uint32_t)k8[5])<<16; /* fall through */ - case 5 : b+=((uint32_t)k8[4])<<24; /* fall through */ + case 7 : b+=((uint32_t)k8[6])<<8; + ATTR_FALLTHROUGH + /* fallthrough */ + case 6 : b+=((uint32_t)k8[5])<<16; + ATTR_FALLTHROUGH + /* fallthrough */ + case 5 : b+=((uint32_t)k8[4])<<24; + ATTR_FALLTHROUGH + /* fallthrough */ case 4 : a+=k[0]; break; - case 3 : a+=((uint32_t)k8[2])<<8; /* fall through */ - case 2 : a+=((uint32_t)k8[1])<<16; /* fall through */ + case 3 : a+=((uint32_t)k8[2])<<8; + ATTR_FALLTHROUGH + /* fallthrough */ + case 2 : a+=((uint32_t)k8[1])<<16; + ATTR_FALLTHROUGH + /* fallthrough */ case 1 : a+=((uint32_t)k8[0])<<24; break; case 0 : return c; } @@ -827,16 +938,38 @@ uint32_t hashbig( const void *key, size_t length, uint32_t initval) switch(length) /* all the case statements fall through */ { case 12: c+=k[11]; + ATTR_FALLTHROUGH + /* fallthrough */ case 11: c+=((uint32_t)k[10])<<8; + ATTR_FALLTHROUGH + /* fallthrough */ case 10: c+=((uint32_t)k[9])<<16; + ATTR_FALLTHROUGH + /* fallthrough */ case 9 : c+=((uint32_t)k[8])<<24; + ATTR_FALLTHROUGH + /* fallthrough */ case 8 : b+=k[7]; + ATTR_FALLTHROUGH + /* fallthrough */ case 7 : b+=((uint32_t)k[6])<<8; + ATTR_FALLTHROUGH + /* fallthrough */ case 6 : b+=((uint32_t)k[5])<<16; + ATTR_FALLTHROUGH + /* fallthrough */ case 5 : b+=((uint32_t)k[4])<<24; + ATTR_FALLTHROUGH + /* fallthrough */ case 4 : a+=k[3]; + ATTR_FALLTHROUGH + /* fallthrough */ case 3 : a+=((uint32_t)k[2])<<8; + ATTR_FALLTHROUGH + /* fallthrough */ case 2 : a+=((uint32_t)k[1])<<16; + ATTR_FALLTHROUGH + /* fallthrough */ case 1 : a+=((uint32_t)k[0])<<24; break; case 0 : return c; diff --git a/validator/val_secalgo.c b/validator/val_secalgo.c index 40ebb1728..be8347b1b 100644 --- a/validator/val_secalgo.c +++ b/validator/val_secalgo.c @@ -2060,11 +2060,13 @@ verify_canonrrset(sldns_buffer* buf, int algo, unsigned char* sigblock, digest_size = (digest_size ? digest_size : SHA1_DIGEST_SIZE); #endif /* double fallthrough annotation to please gcc parser */ + ATTR_FALLTHROUGH /* fallthrough */ #ifdef USE_SHA2 /* fallthrough */ case LDNS_RSASHA256: digest_size = (digest_size ? digest_size : SHA256_DIGEST_SIZE); + ATTR_FALLTHROUGH /* fallthrough */ case LDNS_RSASHA512: digest_size = (digest_size ? digest_size : SHA512_DIGEST_SIZE); @@ -2080,6 +2082,7 @@ verify_canonrrset(sldns_buffer* buf, int algo, unsigned char* sigblock, #ifdef USE_ECDSA case LDNS_ECDSAP256SHA256: digest_size = (digest_size ? digest_size : SHA256_DIGEST_SIZE); + ATTR_FALLTHROUGH /* fallthrough */ case LDNS_ECDSAP384SHA384: digest_size = (digest_size ? digest_size : SHA384_DIGEST_SIZE); From 8de5ae3552620b9273d53ae6d2d02b45217bcad0 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 23 Jul 2024 09:55:31 +0200 Subject: [PATCH 43/66] - Fix compile when the compiler does not support the noreturn attribute. --- configure | 4 ++++ configure.ac | 2 ++ doc/Changelog | 2 ++ 3 files changed, 8 insertions(+) diff --git a/configure b/configure index 9998b36e4..6a4066771 100755 --- a/configure +++ b/configure @@ -7023,6 +7023,10 @@ printf "%s\n" "#define HAVE_ATTR_NORETURN 1" >>confdefs.h printf "%s\n" "#define ATTR_NORETURN __attribute__((__noreturn__))" >>confdefs.h +else + +printf "%s\n" "#define ATTR_NORETURN /**/" >>confdefs.h + fi diff --git a/configure.ac b/configure.ac index 6adbe0e59..977b91a24 100644 --- a/configure.ac +++ b/configure.ac @@ -360,6 +360,8 @@ AC_MSG_RESULT($ac_cv_c_noreturn_attribute) if test $ac_cv_c_noreturn_attribute = yes; then AC_DEFINE(HAVE_ATTR_NORETURN, 1, [Whether the C compiler accepts the "noreturn" attribute]) AC_DEFINE(ATTR_NORETURN, [__attribute__((__noreturn__))], [apply the noreturn attribute to a function that exits the program]) +else + AC_DEFINE(ATTR_NORETURN,[], [apply the noreturn attribute to a function that exits the program]) fi ])dnl End of CHECK_NORETURN_ATTRIBUTE diff --git a/doc/Changelog b/doc/Changelog index 221e8d02d..e9b2e7a40 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -5,6 +5,8 @@ - Merge #1110: Make fallthrough explicit for libworker.c. - For #1110: Test for fallthrough attribute in configure and add fallthrough attribute annotations. + - Fix compile when the compiler does not support the noreturn + attribute. 19 July 2024: Wouter - Add dnstap-sample-rate that logs only 1/N messages, for high volume From 30da725e671911fc04d130c81cfdf3043befe8ed Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 23 Jul 2024 10:02:39 +0200 Subject: [PATCH 44/66] - Fix to have empty definition when not supported for weak attribute. --- configure | 4 ++++ configure.ac | 2 ++ doc/Changelog | 1 + 3 files changed, 7 insertions(+) diff --git a/configure b/configure index 6a4066771..daa37e504 100755 --- a/configure +++ b/configure @@ -6976,6 +6976,10 @@ printf "%s\n" "#define HAVE_ATTR_WEAK 1" >>confdefs.h printf "%s\n" "#define ATTR_WEAK __attribute__((weak))" >>confdefs.h +else + +printf "%s\n" "#define ATTR_WEAK /**/" >>confdefs.h + fi diff --git a/configure.ac b/configure.ac index 977b91a24..4d17fb8c1 100644 --- a/configure.ac +++ b/configure.ac @@ -339,6 +339,8 @@ AC_MSG_RESULT($ac_cv_c_weak_attribute) if test $ac_cv_c_weak_attribute = yes; then AC_DEFINE(HAVE_ATTR_WEAK, 1, [Whether the C compiler accepts the "weak" attribute]) AC_DEFINE(ATTR_WEAK, [__attribute__((weak))], [apply the weak attribute to a symbol]) +else + AC_DEFINE(ATTR_WEAK,[], [apply the weak attribute to a symbol]) fi ])dnl End of CHECK_WEAK_ATTRIBUTE diff --git a/doc/Changelog b/doc/Changelog index e9b2e7a40..45099106d 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -7,6 +7,7 @@ fallthrough attribute annotations. - Fix compile when the compiler does not support the noreturn attribute. + - Fix to have empty definition when not supported for weak attribute. 19 July 2024: Wouter - Add dnstap-sample-rate that logs only 1/N messages, for high volume From c4541e634b3e5e2b115604d0c525946bd8ee0412 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 23 Jul 2024 10:42:36 +0200 Subject: [PATCH 45/66] - Fix uninitialized variable warning in create_tcp_accept_sock. --- doc/Changelog | 1 + services/listen_dnsport.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/Changelog b/doc/Changelog index 45099106d..5315a8e4b 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -8,6 +8,7 @@ - Fix compile when the compiler does not support the noreturn attribute. - Fix to have empty definition when not supported for weak attribute. + - Fix uninitialized variable warning in create_tcp_accept_sock. 19 July 2024: Wouter - Add dnstap-sample-rate that logs only 1/N messages, for high volume diff --git a/services/listen_dnsport.c b/services/listen_dnsport.c index 7eb59a161..6c0691f2a 100644 --- a/services/listen_dnsport.c +++ b/services/listen_dnsport.c @@ -675,7 +675,7 @@ create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto, int* reuseport, int transparent, int mss, int nodelay, int freebind, int use_systemd, int dscp) { - int s; + int s = -1; char* err; #if defined(SO_REUSEADDR) || defined(SO_REUSEPORT) || defined(IPV6_V6ONLY) || defined(IP_TRANSPARENT) || defined(IP_BINDANY) || defined(IP_FREEBIND) || defined(SO_BINDANY) int on = 1; From 671e11552c32f472a26b5bf2cb9b1f85dd42e5db Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 23 Jul 2024 14:56:21 +0200 Subject: [PATCH 46/66] - Fix link of dnstap without openssl. --- dnstap/dtstream.c | 2 ++ doc/Changelog | 1 + 2 files changed, 3 insertions(+) diff --git a/dnstap/dtstream.c b/dnstap/dtstream.c index de635f195..2d5ab20f0 100644 --- a/dnstap/dtstream.c +++ b/dnstap/dtstream.c @@ -1510,8 +1510,10 @@ void dtio_output_cb(int ATTR_UNUSED(fd), short bits, void* arg) #endif if((bits&UB_EV_READ || dtio->ssl_brief_write)) { +#ifdef HAVE_SSL if(dtio->ssl_brief_write) (void)dtio_disable_brief_write(dtio); +#endif if(dtio->ready_frame_sent && !dtio->accept_frame_received) { if(dtio_read_accept_frame(dtio) <= 0) return; diff --git a/doc/Changelog b/doc/Changelog index 5315a8e4b..bbbbcb5b5 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -9,6 +9,7 @@ attribute. - Fix to have empty definition when not supported for weak attribute. - Fix uninitialized variable warning in create_tcp_accept_sock. + - Fix link of dnstap without openssl. 19 July 2024: Wouter - Add dnstap-sample-rate that logs only 1/N messages, for high volume From 83e6977f063de41b3b01e19ae0588ee9ead8a419 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 23 Jul 2024 15:06:54 +0200 Subject: [PATCH 47/66] - Fix link of unbound-dnstap-socket without openssl. --- dnstap/unbound-dnstap-socket.c | 6 +++++- doc/Changelog | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/dnstap/unbound-dnstap-socket.c b/dnstap/unbound-dnstap-socket.c index 7772e763d..a437b6296 100644 --- a/dnstap/unbound-dnstap-socket.c +++ b/dnstap/unbound-dnstap-socket.c @@ -822,7 +822,6 @@ static int reply_with_accept(struct tap_data* data) { #ifdef USE_DNSTAP /* len includes the escape and framelength */ - int r; size_t len = 0; void* acceptframe = fstrm_create_control_frame_accept( DNSTAP_CONTENT_TYPE, &len); @@ -833,6 +832,8 @@ static int reply_with_accept(struct tap_data* data) fd_set_block(data->fd); if(data->ssl) { +#ifdef HAVE_SSL + int r; if((r=SSL_write(data->ssl, acceptframe, len)) <= 0) { int r2; if((r2=SSL_get_error(data->ssl, r)) == SSL_ERROR_ZERO_RETURN) @@ -843,6 +844,7 @@ static int reply_with_accept(struct tap_data* data) free(acceptframe); return 0; } +#endif } else { if(send(data->fd, acceptframe, len, 0) == -1) { log_err("send failed: %s", sock_strerror(errno)); @@ -878,6 +880,7 @@ static int reply_with_finish(struct tap_data* data) fd_set_block(data->fd); if(data->ssl) { +#ifdef HAVE_SSL int r; if((r=SSL_write(data->ssl, finishframe, len)) <= 0) { int r2; @@ -889,6 +892,7 @@ static int reply_with_finish(struct tap_data* data) free(finishframe); return 0; } +#endif } else { if(send(data->fd, finishframe, len, 0) == -1) { log_err("send failed: %s", sock_strerror(errno)); diff --git a/doc/Changelog b/doc/Changelog index bbbbcb5b5..16f9a2769 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -10,6 +10,7 @@ - Fix to have empty definition when not supported for weak attribute. - Fix uninitialized variable warning in create_tcp_accept_sock. - Fix link of dnstap without openssl. + - Fix link of unbound-dnstap-socket without openssl. 19 July 2024: Wouter - Add dnstap-sample-rate that logs only 1/N messages, for high volume From 7d4d21764a37042edb3ae0d8dad623920cbd6c94 Mon Sep 17 00:00:00 2001 From: Yorgos Thessalonikefs Date: Tue, 23 Jul 2024 20:22:25 +0200 Subject: [PATCH 48/66] - Cleanup ede.tdir test. --- doc/Changelog | 1 + testdata/ede.tdir/ede-auth.conf | 1 - testdata/ede.tdir/ede.conf | 16 ++++++++-------- testdata/ede.tdir/ede.test | 3 --- 4 files changed, 9 insertions(+), 12 deletions(-) diff --git a/doc/Changelog b/doc/Changelog index 16f9a2769..d8676d62a 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,5 +1,6 @@ 23 July 2024: Yorgos - Fix #1106: ratelimit-below-domain logs the wrong FROM address. + - Cleanup ede.tdir test. 23 July 2024: Wouter - Merge #1110: Make fallthrough explicit for libworker.c. diff --git a/testdata/ede.tdir/ede-auth.conf b/testdata/ede.tdir/ede-auth.conf index d78da0382..81a9f6bfa 100644 --- a/testdata/ede.tdir/ede-auth.conf +++ b/testdata/ede.tdir/ede-auth.conf @@ -24,4 +24,3 @@ auth-zone: auth-zone: name: "rrsig-failures.test" zonefile: "bogus/rrsig-failures.test.signed" - diff --git a/testdata/ede.tdir/ede.conf b/testdata/ede.tdir/ede.conf index 639899d13..1a9cc7e30 100644 --- a/testdata/ede.tdir/ede.conf +++ b/testdata/ede.tdir/ede.conf @@ -33,18 +33,18 @@ server: local-zone: test nodefault do-not-query-localhost: no -forward-zone: +stub-zone: name: "dnssec-failures.test" - forward-addr: 127.0.0.1@@PORT2@ + stub-addr: 127.0.0.1@@PORT2@ -forward-zone: +stub-zone: name: "dnskey-failures.test" - forward-addr: 127.0.0.1@@PORT2@ + stub-addr: 127.0.0.1@@PORT2@ -forward-zone: +stub-zone: name: "nsec-failures.test" - forward-addr: 127.0.0.1@@PORT2@ + stub-addr: 127.0.0.1@@PORT2@ -forward-zone: +stub-zone: name: "rrsig-failures.test" - forward-addr: 127.0.0.1@@PORT2@ + stub-addr: 127.0.0.1@@PORT2@ diff --git a/testdata/ede.tdir/ede.test b/testdata/ede.tdir/ede.test index e45085ebf..d166b2e9a 100644 --- a/testdata/ede.tdir/ede.test +++ b/testdata/ede.tdir/ede.test @@ -5,9 +5,6 @@ [ -f .tpkg.var.test ] && source .tpkg.var.test -# DNSSEC failure: Signature Expired or DNSKEY Missing (depending on the servfail configuration) -dig @127.0.0.1 -p $UNBOUND_PORT servfail.nl > servfail.txt - # DNSSEC failure: key not incepted dig @127.0.0.1 -p $UNBOUND_PORT notyetincepted.dnssec-failures.test. TXT +dnssec > sig_notyetincepted.txt From c717debace45b941e0f97c2c401a891008beaec4 Mon Sep 17 00:00:00 2001 From: Yorgos Thessalonikefs Date: Wed, 24 Jul 2024 01:54:02 +0200 Subject: [PATCH 49/66] - For #935 and #1104, clarify RPZ order and semantics. --- doc/Changelog | 1 + doc/example.conf.in | 3 ++- doc/unbound.conf.5.in | 23 +++++++++++++---------- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/doc/Changelog b/doc/Changelog index d8676d62a..15919c46c 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,6 +1,7 @@ 23 July 2024: Yorgos - Fix #1106: ratelimit-below-domain logs the wrong FROM address. - Cleanup ede.tdir test. + - For #935 and #1104, clarify RPZ order and semantics. 23 July 2024: Wouter - Merge #1110: Make fallthrough explicit for libworker.c. diff --git a/doc/example.conf.in b/doc/example.conf.in index 329bed150..19aa59952 100644 --- a/doc/example.conf.in +++ b/doc/example.conf.in @@ -1339,7 +1339,8 @@ remote-control: # dnstap-log-forwarder-response-messages: no # Response Policy Zones -# RPZ policies. Applied in order of configuration. QNAME, Response IP +# RPZ policies. Applied in order of configuration. Any match from an earlier +# RPZ zone will terminate the RPZ lookup. QNAME, Response IP # Address, nsdname, nsip and clientip triggers are supported. Supported # actions are: NXDOMAIN, NODATA, PASSTHRU, DROP, Local Data, tcp-only # and drop. Policies can be loaded from a file, or using zone diff --git a/doc/unbound.conf.5.in b/doc/unbound.conf.5.in index ab146d5e4..90e109ad7 100644 --- a/doc/unbound.conf.5.in +++ b/doc/unbound.conf.5.in @@ -2883,9 +2883,11 @@ Enable to log forwarder response messages. Default is no. .SS Response Policy Zone Options .LP Response Policy Zones are configured with \fBrpz:\fR, and each one must have a -\fBname:\fR. There can be multiple ones, by listing multiple rpz clauses, each -with a different name. RPZ clauses are applied in order of configuration. The -\fBrespip\fR module needs to be added to the \fBmodule-config\fR, e.g.: +\fBname:\fR. There can be multiple ones, by listing multiple RPZ clauses, each +with a different name. RPZ clauses are applied in order of configuration and +any match from an earlier RPZ zone will terminate the RPZ lookup. Note that a +PASSTHRU action is still considered a match. +The \fBrespip\fR module needs to be added to the \fBmodule-config\fR, e.g.: \fBmodule-config: "respip validator iterator"\fR. .P QNAME, Response IP Address, nsdname, nsip and clientip triggers are supported. @@ -2893,12 +2895,13 @@ Supported actions are: NXDOMAIN, NODATA, PASSTHRU, DROP, Local Data, tcp\-only and drop. RPZ QNAME triggers are applied after \fBlocal\-zones\fR and before \fBauth\-zones\fR. .P -The rpz zone is formatted with a SOA start record as usual. The items in -the zone are entries, that specify what to act on (the trigger) and what to -do (the action). The trigger to act on is recorded in the name, the action -to do is recorded as the resource record. The names all end in the zone -name, so you could type the trigger names without a trailing dot in the -zonefile. +The RPZ zone is a regular DNS zone formatted with a SOA start record as usual. +The items in the zone are entries, that specify what to act on (the trigger) +and what to do (the action). +The trigger to act on is recorded in the name, the action to do is recorded as +the resource record. +The names all end in the zone name, so you could type the trigger names without +a trailing dot in the zonefile. .P An example RPZ record, that answers example.com with NXDOMAIN .nf @@ -2998,7 +3001,7 @@ externally blocked. Default is no. If enabled the zone is authoritatively answered for and queries for the RPZ zone information are answered to downstream clients. This is useful for monitoring scripts, that can then access the SOA information to check if -the rpz information is up to date. Default is no. +the RPZ information is up to date. Default is no. .TP .B tags: \fI Limit the policies from this RPZ clause to clients with a matching tag. Tags From f094f4ea3c943c5b5b2b6fa8bee0e7a8f3cfdc51 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 25 Jul 2024 11:42:22 +0200 Subject: [PATCH 50/66] - Add root key 38696 from 2024 for DNSSEC validation. It is added to the default root keys in unbound-anchor. The content can be inspected with `unbound-anchor -l`. --- doc/Changelog | 5 +++++ smallapp/unbound-anchor.c | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/doc/Changelog b/doc/Changelog index 15919c46c..aba7407d7 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,8 @@ +25 July 2024: Wouter + - Add root key 38696 from 2024 for DNSSEC validation. It is added + to the default root keys in unbound-anchor. The content can be + inspected with `unbound-anchor -l`. + 23 July 2024: Yorgos - Fix #1106: ratelimit-below-domain logs the wrong FROM address. - Cleanup ede.tdir test. diff --git a/smallapp/unbound-anchor.c b/smallapp/unbound-anchor.c index aa39dcf0d..bd4a121f7 100644 --- a/smallapp/unbound-anchor.c +++ b/smallapp/unbound-anchor.c @@ -183,7 +183,9 @@ static const char DS_TRUST_ANCHOR[] = /* The anchors must start on a new line with ". IN DS and end with \n"[;] * because the makedist script greps on the source here */ /* anchor 20326 is from 2017 */ -". IN DS 20326 8 2 E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D\n"; +". IN DS 20326 8 2 E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D\n" + /* anchor 38696 is from 2024 */ +". IN DS 38696 8 2 683D2D0ACB8C9B712A1948B27F741219298D0A450D612C483AF444A4C0FB2B16\n"; /** verbosity for this application */ static int verb = 0; From 6af28bed08f6b28edc2671973c665a198348d912 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 30 Jul 2024 13:47:53 +0200 Subject: [PATCH 51/66] - Fix to document parameters of auth_zone_verify_zonemd_with_key. --- doc/Changelog | 3 +++ services/authzone.c | 2 ++ 2 files changed, 5 insertions(+) diff --git a/doc/Changelog b/doc/Changelog index aba7407d7..f6f943045 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,6 @@ +30 July 2024: Wouter + - Fix to document parameters of auth_zone_verify_zonemd_with_key. + 25 July 2024: Wouter - Add root key 38696 from 2024 for DNSSEC validation. It is added to the default root keys in unbound-anchor. The content can be diff --git a/services/authzone.c b/services/authzone.c index 0f79f42ea..580a681f5 100644 --- a/services/authzone.c +++ b/services/authzone.c @@ -8112,6 +8112,8 @@ auth_zone_verify_zonemd_with_key(struct auth_zone* z, struct module_env* env, * @param why_bogus: if the routine fails, returns the failure reason. * @param keystorage: where to store the ub_packed_rrset_key that is created * on success. A pointer to it is returned on success. + * @param reasonbuf: buffer to use for fail reason string print. + * @param reasonlen: length of reasonbuf. * @return the dnskey RRset, reference to zone data and keystorage, or * NULL on failure. */ From 03b511b1a20e4d73e12d401b00fe235f1bea413a Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 31 Jul 2024 11:42:44 +0200 Subject: [PATCH 52/66] - Fix for #1114: Fix that cache fill for forward-host names is performed, so that with nonzero target-fetch-policy it fetches forwarder addresses and uses them from cache. Also updated that delegation point cache fill routines use CDflag for AAAA message lookups, so that its negative lookup stops a recursion since the cache uses the bit for disambiguation for dns64 but the recursion uses CDflag for the AAAA target lookups, so the check correctly stops a useless recursion by its cache lookup. --- doc/Changelog | 10 +++ iterator/iterator.c | 5 ++ services/cache/dns.c | 14 ++++ testdata/fwd_name_lookup.rpl | 152 +++++++++++++++++++++++++++++++++++ 4 files changed, 181 insertions(+) create mode 100644 testdata/fwd_name_lookup.rpl diff --git a/doc/Changelog b/doc/Changelog index f6f943045..ec8024cc1 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,13 @@ +31 July 2024: Wouter + - Fix for #1114: Fix that cache fill for forward-host names is + performed, so that with nonzero target-fetch-policy it fetches + forwarder addresses and uses them from cache. Also updated that + delegation point cache fill routines use CDflag for AAAA message + lookups, so that its negative lookup stops a recursion since the + cache uses the bit for disambiguation for dns64 but the recursion + uses CDflag for the AAAA target lookups, so the check correctly + stops a useless recursion by its cache lookup. + 30 July 2024: Wouter - Fix to document parameters of auth_zone_verify_zonemd_with_key. diff --git a/iterator/iterator.c b/iterator/iterator.c index cddb02717..b348e9867 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -1560,6 +1560,11 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, errinf(qstate, "malloc failure for forward zone"); return error_response(qstate, id, LDNS_RCODE_SERVFAIL); } + if(!cache_fill_missing(qstate->env, iq->qchase.qclass, + qstate->region, iq->dp)) { + errinf(qstate, "malloc failure, copy extra info into delegation point"); + return error_response(qstate, id, LDNS_RCODE_SERVFAIL); + } if((qstate->query_flags&BIT_RD)==0) { /* If the server accepts RD=0 queries and forwards * with RD=1, then if the server is listed as an NS diff --git a/services/cache/dns.c b/services/cache/dns.c index 60e79a2e7..5e74c3169 100644 --- a/services/cache/dns.c +++ b/services/cache/dns.c @@ -346,6 +346,13 @@ find_add_addrs(struct module_env* env, uint16_t qclass, * not use dns64 translation */ neg = msg_cache_lookup(env, ns->name, ns->namelen, LDNS_RR_TYPE_AAAA, qclass, 0, now, 0); + /* Because recursion for lookup uses BIT_CD, check + * for that so it stops the recursion lookup, if a + * negative answer is cached. Because the cache uses + * the CD flag for type AAAA. */ + if(!neg) + neg = msg_cache_lookup(env, ns->name, ns->namelen, + LDNS_RR_TYPE_AAAA, qclass, BIT_CD, now, 0); if(neg) { delegpt_add_neg_msg(dp, neg); lock_rw_unlock(&neg->entry.lock); @@ -405,6 +412,13 @@ cache_fill_missing(struct module_env* env, uint16_t qclass, * not use dns64 translation */ neg = msg_cache_lookup(env, ns->name, ns->namelen, LDNS_RR_TYPE_AAAA, qclass, 0, now, 0); + /* Because recursion for lookup uses BIT_CD, check + * for that so it stops the recursion lookup, if a + * negative answer is cached. Because the cache uses + * the CD flag for type AAAA. */ + if(!neg) + neg = msg_cache_lookup(env, ns->name, ns->namelen, + LDNS_RR_TYPE_AAAA, qclass, BIT_CD, now, 0); if(neg) { delegpt_add_neg_msg(dp, neg); lock_rw_unlock(&neg->entry.lock); diff --git a/testdata/fwd_name_lookup.rpl b/testdata/fwd_name_lookup.rpl new file mode 100644 index 000000000..dbcfffba5 --- /dev/null +++ b/testdata/fwd_name_lookup.rpl @@ -0,0 +1,152 @@ +; config options +server: + # must have target-fetch-policy to fetch forward-host name. + target-fetch-policy: "3 2 1 0 0" + qname-minimisation: no + minimal-responses: no + +forward-zone: + name: "." + forward-addr: 1.2.3.4 + forward-host: ns.example.com +CONFIG_END + +SCENARIO_BEGIN Test forward with forward-host lookup for more addresses + +; Forward server +RANGE_BEGIN 0 15 + ADDRESS 1.2.3.4 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +ns.example.com. IN A +SECTION ANSWER +ns.example.com. IN A 1.2.3.4 +ns.example.com. IN A 1.2.3.5 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +ns.example.com. IN AAAA +SECTION ANSWER +SECTION AUTHORITY +example.com. IN SOA ns.example.com. host.example.com. 3 3600 300 86400 3600 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION ANSWER +www.example.com. IN A 1.2.3.6 +ENTRY_END +RANGE_END + +; The forward server gives no answers. +RANGE_BEGIN 20 55 + ADDRESS 1.2.3.4 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR SERVFAIL +SECTION QUESTION +www2.example.com. IN A +SECTION ANSWER +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR SERVFAIL +SECTION QUESTION +www3.example.com. IN A +SECTION ANSWER +ENTRY_END +RANGE_END + +; The other forward server. +RANGE_BEGIN 20 55 + ADDRESS 1.2.3.5 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +www2.example.com. IN A +SECTION ANSWER +www2.example.com. IN A 1.2.3.7 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +www3.example.com. IN A +SECTION ANSWER +www3.example.com. IN A 1.2.3.8 +ENTRY_END +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +www.example.com. IN A +ENTRY_END + +; recursion happens here. +STEP 10 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION ANSWER +www.example.com. IN A 1.2.3.6 +ENTRY_END + +; The address 1.2.3.4 is not responding so it has to fail over to the +; address from the name lookup. +STEP 20 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +www2.example.com. IN A +ENTRY_END + +STEP 30 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +www2.example.com. IN A +SECTION ANSWER +www2.example.com. IN A 1.2.3.7 +ENTRY_END + +STEP 40 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +www3.example.com. IN A +ENTRY_END + +STEP 50 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +www3.example.com. IN A +SECTION ANSWER +www3.example.com. IN A 1.2.3.8 +ENTRY_END + +SCENARIO_END From 9a6b6765ccee9335bb61f2f56b3df2f978deb495 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 1 Aug 2024 16:12:04 +0200 Subject: [PATCH 53/66] - Fix dnstap test program, cleans up to have clean memory on exit, for tap_data_free, does not delete NULL items. Also it does not try to free the tail, specifically in the free of the list since that picked up the next item in the list for its loop causing invalid free. Added internal unit test to unbound-dnstap-socket for that. --- dnstap/unbound-dnstap-socket.c | 79 +++++++++++++++++++++++++------- doc/Changelog | 7 +++ testdata/dnstap.tdir/dnstap.post | 2 + testdata/dnstap.tdir/dnstap.test | 2 - 4 files changed, 71 insertions(+), 19 deletions(-) diff --git a/dnstap/unbound-dnstap-socket.c b/dnstap/unbound-dnstap-socket.c index a437b6296..f203aa7d7 100644 --- a/dnstap/unbound-dnstap-socket.c +++ b/dnstap/unbound-dnstap-socket.c @@ -200,18 +200,25 @@ static void tap_data_list_try_to_free_tail(struct tap_data_list* list) } /** delete the tap structure */ -static void tap_data_free(struct tap_data* data) +static void tap_data_free(struct tap_data* data, int free_tail) { - ub_event_del(data->ev); - ub_event_free(data->ev); + if(!data) + return; + if(data->ev) { + ub_event_del(data->ev); + ub_event_free(data->ev); + } #ifdef HAVE_SSL SSL_free(data->ssl); #endif - close(data->fd); + sock_close(data->fd); free(data->id); free(data->frame); - data->data_list->d = NULL; - tap_data_list_try_to_free_tail(data->data_list); + if(data->data_list) { + data->data_list->d = NULL; + if(free_tail) + tap_data_list_try_to_free_tail(data->data_list); + } free(data); } @@ -237,7 +244,7 @@ static void tap_data_list_delete(struct tap_data_list* list) while(e) { next = e->next; if(e->d) { - tap_data_free(e->d); + tap_data_free(e->d, 0); e->d = NULL; } free(e); @@ -260,7 +267,7 @@ static void tap_socket_close(struct tap_socket* s) { if(!s) return; if(s->fd == -1) return; - close(s->fd); + sock_close(s->fd); s->fd = -1; } @@ -992,7 +999,7 @@ static int tap_handshake(struct tap_data* data) return 0; } else if(r == 0) { /* closed */ - tap_data_free(data); + tap_data_free(data, 1); return 0; } else if(want == SSL_ERROR_SYSCALL) { /* SYSCALL and errno==0 means closed uncleanly */ @@ -1010,7 +1017,7 @@ static int tap_handshake(struct tap_data* data) if(!silent) log_err("SSL_handshake syscall: %s", strerror(errno)); - tap_data_free(data); + tap_data_free(data, 1); return 0; } else { unsigned long err = ERR_get_error(); @@ -1020,7 +1027,7 @@ static int tap_handshake(struct tap_data* data) verbose(VERB_OPS, "ssl handshake failed " "from %s", data->id); } - tap_data_free(data); + tap_data_free(data, 1); return 0; } } @@ -1028,7 +1035,7 @@ static int tap_handshake(struct tap_data* data) data->ssl_handshake_done = 1; if(!tap_check_peer(data)) { /* closed */ - tap_data_free(data); + tap_data_free(data, 1); return 0; } return 1; @@ -1054,7 +1061,7 @@ void dtio_tap_callback(int ATTR_UNUSED(fd), short ATTR_UNUSED(bits), void* arg) if(verbosity>=4) log_info("s recv %d", (int)ret); if(ret == 0) { /* closed or error */ - tap_data_free(data); + tap_data_free(data, 1); return; } else if(ret == -1) { /* continue later */ @@ -1076,7 +1083,7 @@ void dtio_tap_callback(int ATTR_UNUSED(fd), short ATTR_UNUSED(bits), void* arg) data->frame = calloc(1, data->len); if(!data->frame) { log_err("out of memory"); - tap_data_free(data); + tap_data_free(data, 1); return; } } @@ -1089,7 +1096,7 @@ void dtio_tap_callback(int ATTR_UNUSED(fd), short ATTR_UNUSED(bits), void* arg) if(verbosity>=4) log_info("f recv %d", (int)r); if(r == 0) { /* closed or error */ - tap_data_free(data); + tap_data_free(data, 1); return; } else if(r == -1) { /* continue later */ @@ -1114,13 +1121,13 @@ void dtio_tap_callback(int ATTR_UNUSED(fd), short ATTR_UNUSED(bits), void* arg) data->is_bidirectional = 1; if(verbosity) log_info("bidirectional stream"); if(!reply_with_accept(data)) { - tap_data_free(data); + tap_data_free(data, 1); return; } } else if(data->len >= 4 && sldns_read_uint32(data->frame) == FSTRM_CONTROL_FRAME_STOP && data->is_bidirectional) { if(!reply_with_finish(data)) { - tap_data_free(data); + tap_data_free(data, 1); return; } } @@ -1400,6 +1407,41 @@ static int internal_unittest() /* clean up */ tap_data_list_delete(socket->data_list); free(socket); + + /* Start again. Add two elements */ + socket = calloc(1, sizeof(*socket)); + log_assert(socket); + for(i=0; i<2; i++) { + datas[i] = calloc(1, sizeof(struct tap_data)); + log_assert(datas[i]); + log_assert(tap_data_list_insert(&socket->data_list, datas[i])); + } + /* sanity base check */ + list = socket->data_list; + for(i=0; list; i++) list = list->next; + log_assert(i==2); + + /* Free the last data, tail cannot be erased */ + list = socket->data_list; + while(list->next) list = list->next; + free(list->d); + list->d = NULL; + tap_data_list_try_to_free_tail(list); + list = socket->data_list; + for(i=0; list; i++) list = list->next; + log_assert(i==2); + + /* clean up */ + tap_data_list_delete(socket->data_list); + free(socket); + + if(log_get_lock()) { + lock_basic_destroy((lock_basic_type*)log_get_lock()); + } + checklock_stop(); +#ifdef USE_WINSOCK + WSACleanup(); +#endif return 0; } @@ -1531,6 +1573,9 @@ int main(int argc, char** argv) config_delstrlist(tcp_list.first); config_delstrlist(tls_list.first); + if(log_get_lock()) { + lock_basic_destroy((lock_basic_type*)log_get_lock()); + } checklock_stop(); #ifdef USE_WINSOCK WSACleanup(); diff --git a/doc/Changelog b/doc/Changelog index ec8024cc1..7e1b81eb4 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,10 @@ +1 August 2024: Wouter + - Fix dnstap test program, cleans up to have clean memory on exit, + for tap_data_free, does not delete NULL items. Also it does not try + to free the tail, specifically in the free of the list since that + picked up the next item in the list for its loop causing invalid + free. Added internal unit test to unbound-dnstap-socket for that. + 31 July 2024: Wouter - Fix for #1114: Fix that cache fill for forward-host names is performed, so that with nonzero target-fetch-policy it fetches diff --git a/testdata/dnstap.tdir/dnstap.post b/testdata/dnstap.tdir/dnstap.post index 6d5e9d50d..8fefc7e84 100644 --- a/testdata/dnstap.tdir/dnstap.post +++ b/testdata/dnstap.tdir/dnstap.post @@ -12,4 +12,6 @@ kill_pid $FWD_PID kill $UNBOUND_PID kill $UNBOUND_PID >/dev/null 2>&1 cat unbound.log +cat tap.log +cat tap.errlog exit 0 diff --git a/testdata/dnstap.tdir/dnstap.test b/testdata/dnstap.tdir/dnstap.test index 3ec9c77bd..ebb180251 100644 --- a/testdata/dnstap.tdir/dnstap.test +++ b/testdata/dnstap.tdir/dnstap.test @@ -122,8 +122,6 @@ if test $num_responses -gt 2; then fi echo "> cat logfiles" -cat tap.log -cat tap.errlog cat fwd.log echo "> OK" exit 0 From 92be76fb89a27359ef6992e3a07d8bd1e27b1df8 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 1 Aug 2024 17:15:07 +0200 Subject: [PATCH 54/66] - Fix that the worker mem report with alloc stats does not attempt to print memory use of forwards and hints if they have been deleted already. --- daemon/worker.c | 8 +++++--- doc/Changelog | 3 +++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/daemon/worker.c b/daemon/worker.c index b35fe65a3..dd14a5a3c 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -160,9 +160,11 @@ worker_mem_report(struct worker* ATTR_UNUSED(worker), + sizeof(worker->rndstate) + regional_get_mem(worker->scratchpad) + sizeof(*worker->env.scratch_buffer) - + sldns_buffer_capacity(worker->env.scratch_buffer) - + forwards_get_mem(worker->env.fwds) - + hints_get_mem(worker->env.hints); + + sldns_buffer_capacity(worker->env.scratch_buffer); + if(worker->daemon->env->fwds) + me += forwards_get_mem(worker->env.fwds); + if(worker->daemon->env->hints) + me += hints_get_mem(worker->env.hints); if(worker->thread_num == 0) me += acl_list_get_mem(worker->daemon->acl); if(cur_serv) { diff --git a/doc/Changelog b/doc/Changelog index 7e1b81eb4..f94ed7fec 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -4,6 +4,9 @@ to free the tail, specifically in the free of the list since that picked up the next item in the list for its loop causing invalid free. Added internal unit test to unbound-dnstap-socket for that. + - Fix that the worker mem report with alloc stats does not attempt + to print memory use of forwards and hints if they have been + deleted already. 31 July 2024: Wouter - Fix for #1114: Fix that cache fill for forward-host names is From befa7d8cd85b9a961f97fdd8c3c430fe0c5a6968 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 2 Aug 2024 08:54:54 +0200 Subject: [PATCH 55/66] - Fix that alloc stats has strdup checks, it stops debuggers from complaining about mismatch at free time. --- config.h.in | 3 +++ configure.ac | 3 +++ doc/Changelog | 4 ++++ util/alloc.c | 22 ++++++++++++++++++++++ 4 files changed, 32 insertions(+) diff --git a/config.h.in b/config.h.in index 4a7143c52..099206025 100644 --- a/config.h.in +++ b/config.h.in @@ -1496,6 +1496,7 @@ struct sockaddr_storage; # define calloc(n,s) unbound_stat_calloc_log(n, s, __FILE__, __LINE__, __func__) # define free(p) unbound_stat_free_log(p, __FILE__, __LINE__, __func__) # define realloc(p,s) unbound_stat_realloc_log(p, s, __FILE__, __LINE__, __func__) +# define strdup(s) unbound_stat_strdup_log(s, __FILE__, __LINE__, __func__) void *unbound_stat_malloc(size_t size); void *unbound_stat_calloc(size_t nmemb, size_t size); void unbound_stat_free(void *ptr); @@ -1508,6 +1509,8 @@ void unbound_stat_free_log(void *ptr, const char* file, int line, const char* func); void *unbound_stat_realloc_log(void *ptr, size_t size, const char* file, int line, const char* func); +char *unbound_stat_strdup_log(const char *s, const char* file, int line, + const char* func); #elif defined(UNBOUND_ALLOC_LITE) # include "util/alloc.h" #endif /* UNBOUND_ALLOC_LITE and UNBOUND_ALLOC_STATS */ diff --git a/configure.ac b/configure.ac index 4d17fb8c1..feeaf34c2 100644 --- a/configure.ac +++ b/configure.ac @@ -2329,6 +2329,7 @@ struct sockaddr_storage; # define calloc(n,s) unbound_stat_calloc_log(n, s, __FILE__, __LINE__, __func__) # define free(p) unbound_stat_free_log(p, __FILE__, __LINE__, __func__) # define realloc(p,s) unbound_stat_realloc_log(p, s, __FILE__, __LINE__, __func__) +# define strdup(s) unbound_stat_strdup_log(s, __FILE__, __LINE__, __func__) void *unbound_stat_malloc(size_t size); void *unbound_stat_calloc(size_t nmemb, size_t size); void unbound_stat_free(void *ptr); @@ -2341,6 +2342,8 @@ void unbound_stat_free_log(void *ptr, const char* file, int line, const char* func); void *unbound_stat_realloc_log(void *ptr, size_t size, const char* file, int line, const char* func); +char *unbound_stat_strdup_log(const char *s, const char* file, int line, + const char* func); #elif defined(UNBOUND_ALLOC_LITE) # include "util/alloc.h" #endif /* UNBOUND_ALLOC_LITE and UNBOUND_ALLOC_STATS */ diff --git a/doc/Changelog b/doc/Changelog index f94ed7fec..a3c4bda09 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,7 @@ +2 August 2024: Wouter + - Fix that alloc stats has strdup checks, it stops debuggers from + complaining about mismatch at free time. + 1 August 2024: Wouter - Fix dnstap test program, cleans up to have clean memory on exit, for tap_data_free, does not delete NULL items. Also it does not try diff --git a/util/alloc.c b/util/alloc.c index 7e9618931..d00976b7f 100644 --- a/util/alloc.c +++ b/util/alloc.c @@ -466,6 +466,19 @@ void *unbound_stat_realloc(void *ptr, size_t size) memcpy(res+8, &mem_special, sizeof(mem_special)); return res+16; } +/** strdup with stats */ +char *unbound_stat_strdup(const char* s) +{ + size_t len; + char* res; + if(!s) return NULL; + len = strlen(s); + res = unbound_stat_malloc(len+1); + if(!res) return NULL; + memmove(res, s, len); + res[len]=0; + return res; +} /** log to file where alloc was done */ void *unbound_stat_malloc_log(size_t size, const char* file, int line, @@ -507,6 +520,15 @@ void *unbound_stat_realloc_log(void *ptr, size_t size, const char* file, return unbound_stat_realloc(ptr, size); } +/** log to file where strdup was done */ +char *unbound_stat_strdup_log(const char *s, const char* file, int line, + const char* func) +{ + log_info("%s:%d %s strdup size %u", file, line, func, + (s?(unsigned)strlen(s)+1:0)); + return unbound_stat_strdup(s); +} + #endif /* UNBOUND_ALLOC_STATS */ #ifdef UNBOUND_ALLOC_LITE #undef malloc From 6106528a500276a86b264ba3be700eb133735a5e Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 2 Aug 2024 08:58:22 +0200 Subject: [PATCH 56/66] - Fix testbound for alloc stats strdup in util/alloc.c. --- testcode/testbound.c | 17 ----------------- util/alloc.c | 3 +-- 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/testcode/testbound.c b/testcode/testbound.c index f023860e0..123fe0d4e 100644 --- a/testcode/testbound.c +++ b/testcode/testbound.c @@ -72,23 +72,6 @@ int daemon_main(int argc, char* argv[]); /** config files (removed at exit) */ static struct config_strlist* cfgfiles = NULL; -#ifdef UNBOUND_ALLOC_STATS -# define strdup(s) unbound_stat_strdup_log(s, __FILE__, __LINE__, __func__) -char* unbound_stat_strdup_log(char* s, const char* file, int line, - const char* func); -char* unbound_stat_strdup_log(char* s, const char* file, int line, - const char* func) { - char* result; - size_t len; - if(!s) return NULL; - len = strlen(s); - log_info("%s:%d %s strdup(%u)", file, line, func, (unsigned)len+1); - result = unbound_stat_malloc(len+1); - memmove(result, s, len+1); - return result; -} -#endif /* UNBOUND_ALLOC_STATS */ - /** give commandline usage for testbound. */ static void testbound_usage(void) diff --git a/util/alloc.c b/util/alloc.c index d00976b7f..a6c911803 100644 --- a/util/alloc.c +++ b/util/alloc.c @@ -475,8 +475,7 @@ char *unbound_stat_strdup(const char* s) len = strlen(s); res = unbound_stat_malloc(len+1); if(!res) return NULL; - memmove(res, s, len); - res[len]=0; + memmove(res, s, len+1); return res; } From 50cf55bdac1b02f0704692359abc328c8bd4fac7 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 2 Aug 2024 08:59:47 +0200 Subject: [PATCH 57/66] Update changelog. - Fix testbound for alloc stats strdup in util/alloc.c. --- doc/Changelog | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/Changelog b/doc/Changelog index a3c4bda09..157d31a6d 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,6 +1,7 @@ 2 August 2024: Wouter - Fix that alloc stats has strdup checks, it stops debuggers from complaining about mismatch at free time. + - Fix testbound for alloc stats strdup in util/alloc.c. 1 August 2024: Wouter - Fix dnstap test program, cleans up to have clean memory on exit, From ad21dbd1c21f870eb9b0c9dd0e43f7614c6113d8 Mon Sep 17 00:00:00 2001 From: Wouter Wijngaards Date: Fri, 2 Aug 2024 13:32:08 +0200 Subject: [PATCH 58/66] Cookie secret file (#1090) * - cookie-secret-file, define struct. * - cookie-secret-file, add config option, create, read and delete struct. * - cookie-secret-file, check cookie secrets for cookie validation. * - cookie-secret-file, unbound-control add_cookie_secret, drop_cookie_secret, activate_cookie_secret and print_cookie_secrets. * - cookie-secret-file, test and fix locks, renew writes a fresh cookie, staging cookies get a fresh cookie and spelling in error message. * - cookie-secret-file, remove unused variable from cookie file unit test. * Remove unshare and faketime dependencies for cookie_file test; documentation nits. --------- Co-authored-by: Yorgos Thessalonikefs --- Makefile.in | 2 +- daemon/daemon.c | 9 + daemon/daemon.h | 3 + daemon/remote.c | 214 ++++++++++++++++++ daemon/worker.c | 3 +- doc/example.conf.in | 5 + doc/unbound-control.8.in | 35 +++ doc/unbound.conf.5.in | 14 ++ smallapp/unbound-control.c | 4 + testcode/unitmain.c | 2 +- testdata/cookie_file.tdir/cookie_file.conf | 19 ++ testdata/cookie_file.tdir/cookie_file.dsc | 16 ++ testdata/cookie_file.tdir/cookie_file.post | 10 + testdata/cookie_file.tdir/cookie_file.pre | 24 ++ testdata/cookie_file.tdir/cookie_file.test | 248 +++++++++++++++++++++ util/config_file.c | 6 + util/config_file.h | 2 + util/configlexer.lex | 1 + util/configparser.y | 86 +++---- util/data/msgparse.c | 49 +++- util/data/msgparse.h | 5 +- util/edns.c | 186 ++++++++++++++++ util/edns.h | 85 +++++++ util/net_help.c | 40 ++++ util/net_help.h | 9 + 25 files changed, 1024 insertions(+), 53 deletions(-) create mode 100644 testdata/cookie_file.tdir/cookie_file.conf create mode 100644 testdata/cookie_file.tdir/cookie_file.dsc create mode 100644 testdata/cookie_file.tdir/cookie_file.post create mode 100644 testdata/cookie_file.tdir/cookie_file.pre create mode 100644 testdata/cookie_file.tdir/cookie_file.test diff --git a/Makefile.in b/Makefile.in index e265a9313..672435e01 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1298,7 +1298,7 @@ remote.lo remote.o: $(srcdir)/daemon/remote.c config.h $(srcdir)/daemon/remote.h $(srcdir)/validator/val_anchor.h $(srcdir)/iterator/iterator.h $(srcdir)/services/outbound_list.h \ $(srcdir)/iterator/iter_fwd.h $(srcdir)/iterator/iter_hints.h $(srcdir)/iterator/iter_delegpt.h \ $(srcdir)/services/outside_network.h $(srcdir)/sldns/str2wire.h $(srcdir)/sldns/parseutil.h \ - $(srcdir)/sldns/wire2str.h + $(srcdir)/sldns/wire2str.h $(srcdir)/util/edns.h stats.lo stats.o: $(srcdir)/daemon/stats.c config.h $(srcdir)/daemon/stats.h $(srcdir)/util/timehist.h \ $(srcdir)/libunbound/unbound.h $(srcdir)/daemon/worker.h $(srcdir)/libunbound/worker.h $(srcdir)/sldns/sbuffer.h \ $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h \ diff --git a/daemon/daemon.c b/daemon/daemon.c index d81bec844..72b4a43be 100644 --- a/daemon/daemon.c +++ b/daemon/daemon.c @@ -735,6 +735,14 @@ daemon_fork(struct daemon* daemon) "dnscrypt support"); #endif } + if(daemon->cfg->cookie_secret_file && + daemon->cfg->cookie_secret_file[0]) { + if(!(daemon->cookie_secrets = cookie_secrets_create())) + fatal_exit("Could not create cookie_secrets: out of memory"); + if(!cookie_secrets_apply_cfg(daemon->cookie_secrets, + daemon->cfg->cookie_secret_file)) + fatal_exit("Could not setup cookie_secrets"); + } /* create global local_zones */ if(!(daemon->local_zones = local_zones_create())) fatal_exit("Could not create local zones: out of memory"); @@ -929,6 +937,7 @@ daemon_delete(struct daemon* daemon) acl_list_delete(daemon->acl); acl_list_delete(daemon->acl_interface); tcl_list_delete(daemon->tcl); + cookie_secrets_delete(daemon->cookie_secrets); listen_desetup_locks(); free(daemon->chroot); free(daemon->pidfile); diff --git a/daemon/daemon.h b/daemon/daemon.h index a6b6391cc..5c3a114cc 100644 --- a/daemon/daemon.h +++ b/daemon/daemon.h @@ -58,6 +58,7 @@ struct ub_randstate; struct daemon_remote; struct respip_set; struct shm_main_info; +struct cookie_secrets; #include "dnstap/dnstap_config.h" #ifdef USE_DNSTAP @@ -148,6 +149,8 @@ struct daemon { #endif /** reuse existing cache on reload if other conditions allow it. */ int reuse_cache; + /** the EDNS cookie secrets from the cookie-secret-file */ + struct cookie_secrets* cookie_secrets; }; /** diff --git a/daemon/remote.c b/daemon/remote.c index a5db4330c..855b1f963 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -88,6 +88,7 @@ #include "sldns/wire2str.h" #include "sldns/sbuffer.h" #include "util/timeval_func.h" +#include "util/edns.h" #ifdef USE_CACHEDB #include "cachedb/cachedb.h" #endif @@ -3195,6 +3196,210 @@ do_rpz_disable(RES* ssl, struct worker* worker, char* arg) do_rpz_enable_disable(ssl, worker, arg, 0); } +/** Write the cookie secrets to file, returns `0` on failure. + * Caller has to hold the lock. */ +static int +cookie_secret_file_dump(RES* ssl, struct worker* worker) { + char const* secret_file = worker->env.cfg->cookie_secret_file; + struct cookie_secrets* cookie_secrets = worker->daemon->cookie_secrets; + char secret_hex[UNBOUND_COOKIE_SECRET_SIZE * 2 + 1]; + FILE* f; + size_t i; + if(secret_file == NULL || secret_file[0]==0) { + (void)ssl_printf(ssl, "error: no cookie secret file configured\n"); + return 0; + } + log_assert( secret_file != NULL ); + + /* open write only and truncate */ + if((f = fopen(secret_file, "w")) == NULL ) { + (void)ssl_printf(ssl, "unable to open cookie secret file %s: %s", + secret_file, strerror(errno)); + return 0; + } + if(cookie_secrets == NULL) { + /* nothing to write */ + fclose(f); + return 1; + } + + for(i = 0; i < cookie_secrets->cookie_count; i++) { + struct cookie_secret const* cs = &cookie_secrets-> + cookie_secrets[i]; + ssize_t const len = hex_ntop(cs->cookie_secret, + UNBOUND_COOKIE_SECRET_SIZE, secret_hex, + sizeof(secret_hex)); + (void)len; /* silence unused variable warning with -DNDEBUG */ + log_assert( len == UNBOUND_COOKIE_SECRET_SIZE * 2 ); + secret_hex[UNBOUND_COOKIE_SECRET_SIZE * 2] = '\0'; + fprintf(f, "%s\n", secret_hex); + } + explicit_bzero(secret_hex, sizeof(secret_hex)); + fclose(f); + return 1; +} + +/** Activate cookie secret */ +static void +do_activate_cookie_secret(RES* ssl, struct worker* worker) { + char const* secret_file = worker->env.cfg->cookie_secret_file; + struct cookie_secrets* cookie_secrets = worker->daemon->cookie_secrets; + + if(secret_file == NULL || secret_file[0] == 0) { + (void)ssl_printf(ssl, "error: no cookie secret file configured\n"); + return; + } + if(cookie_secrets == NULL) { + (void)ssl_printf(ssl, "error: there are no cookie_secrets."); + return; + } + lock_basic_lock(&cookie_secrets->lock); + + if(cookie_secrets->cookie_count <= 1 ) { + lock_basic_unlock(&cookie_secrets->lock); + (void)ssl_printf(ssl, "error: no staging cookie secret to activate\n"); + return; + } + /* Only the worker 0 writes to file, the others update state. */ + if(worker->thread_num == 0 && !cookie_secret_file_dump(ssl, worker)) { + lock_basic_unlock(&cookie_secrets->lock); + (void)ssl_printf(ssl, "error: writing to cookie secret file: \"%s\"\n", + secret_file); + return; + } + activate_cookie_secret(cookie_secrets); + if(worker->thread_num == 0) + (void)cookie_secret_file_dump(ssl, worker); + lock_basic_unlock(&cookie_secrets->lock); + send_ok(ssl); +} + +/** Drop cookie secret */ +static void +do_drop_cookie_secret(RES* ssl, struct worker* worker) { + char const* secret_file = worker->env.cfg->cookie_secret_file; + struct cookie_secrets* cookie_secrets = worker->daemon->cookie_secrets; + + if(secret_file == NULL || secret_file[0] == 0) { + (void)ssl_printf(ssl, "error: no cookie secret file configured\n"); + return; + } + if(cookie_secrets == NULL) { + (void)ssl_printf(ssl, "error: there are no cookie_secrets."); + return; + } + lock_basic_lock(&cookie_secrets->lock); + + if(cookie_secrets->cookie_count <= 1 ) { + lock_basic_unlock(&cookie_secrets->lock); + (void)ssl_printf(ssl, "error: can not drop the currently active cookie secret\n"); + return; + } + /* Only the worker 0 writes to file, the others update state. */ + if(worker->thread_num == 0 && !cookie_secret_file_dump(ssl, worker)) { + lock_basic_unlock(&cookie_secrets->lock); + (void)ssl_printf(ssl, "error: writing to cookie secret file: \"%s\"\n", + secret_file); + return; + } + drop_cookie_secret(cookie_secrets); + if(worker->thread_num == 0) + (void)cookie_secret_file_dump(ssl, worker); + lock_basic_unlock(&cookie_secrets->lock); + send_ok(ssl); +} + +/** Add cookie secret */ +static void +do_add_cookie_secret(RES* ssl, struct worker* worker, char* arg) { + uint8_t secret[UNBOUND_COOKIE_SECRET_SIZE]; + char const* secret_file = worker->env.cfg->cookie_secret_file; + struct cookie_secrets* cookie_secrets = worker->daemon->cookie_secrets; + + if(secret_file == NULL || secret_file[0] == 0) { + (void)ssl_printf(ssl, "error: no cookie secret file configured\n"); + return; + } + if(cookie_secrets == NULL) { + worker->daemon->cookie_secrets = cookie_secrets_create(); + if(!worker->daemon->cookie_secrets) { + (void)ssl_printf(ssl, "error: out of memory"); + return; + } + cookie_secrets = worker->daemon->cookie_secrets; + } + lock_basic_lock(&cookie_secrets->lock); + + if(*arg == '\0') { + lock_basic_unlock(&cookie_secrets->lock); + (void)ssl_printf(ssl, "error: missing argument (cookie_secret)\n"); + return; + } + if(strlen(arg) != 32) { + lock_basic_unlock(&cookie_secrets->lock); + explicit_bzero(arg, strlen(arg)); + (void)ssl_printf(ssl, "invalid cookie secret: invalid argument length\n"); + (void)ssl_printf(ssl, "please provide a 128bit hex encoded secret\n"); + return; + } + if(hex_pton(arg, secret, UNBOUND_COOKIE_SECRET_SIZE) != + UNBOUND_COOKIE_SECRET_SIZE ) { + lock_basic_unlock(&cookie_secrets->lock); + explicit_bzero(secret, UNBOUND_COOKIE_SECRET_SIZE); + explicit_bzero(arg, strlen(arg)); + (void)ssl_printf(ssl, "invalid cookie secret: parse error\n"); + (void)ssl_printf(ssl, "please provide a 128bit hex encoded secret\n"); + return; + } + /* Only the worker 0 writes to file, the others update state. */ + if(worker->thread_num == 0 && !cookie_secret_file_dump(ssl, worker)) { + lock_basic_unlock(&cookie_secrets->lock); + explicit_bzero(secret, UNBOUND_COOKIE_SECRET_SIZE); + explicit_bzero(arg, strlen(arg)); + (void)ssl_printf(ssl, "error: writing to cookie secret file: \"%s\"\n", + secret_file); + return; + } + add_cookie_secret(cookie_secrets, secret, UNBOUND_COOKIE_SECRET_SIZE); + explicit_bzero(secret, UNBOUND_COOKIE_SECRET_SIZE); + if(worker->thread_num == 0) + (void)cookie_secret_file_dump(ssl, worker); + lock_basic_unlock(&cookie_secrets->lock); + explicit_bzero(arg, strlen(arg)); + send_ok(ssl); +} + +/** Print cookie secrets */ +static void +do_print_cookie_secrets(RES* ssl, struct worker* worker) { + struct cookie_secrets* cookie_secrets = worker->daemon->cookie_secrets; + char secret_hex[UNBOUND_COOKIE_SECRET_SIZE * 2 + 1]; + int i; + + if(!cookie_secrets) + return; /* Output is empty. */ + lock_basic_lock(&cookie_secrets->lock); + for(i = 0; (size_t)i < cookie_secrets->cookie_count; i++) { + struct cookie_secret const* cs = &cookie_secrets-> + cookie_secrets[i]; + ssize_t const len = hex_ntop(cs->cookie_secret, + UNBOUND_COOKIE_SECRET_SIZE, secret_hex, + sizeof(secret_hex)); + (void)len; /* silence unused variable warning with -DNDEBUG */ + log_assert( len == UNBOUND_COOKIE_SECRET_SIZE * 2 ); + secret_hex[UNBOUND_COOKIE_SECRET_SIZE * 2] = '\0'; + if (i == 0) + (void)ssl_printf(ssl, "active : %s\n", secret_hex); + else if (cookie_secrets->cookie_count == 2) + (void)ssl_printf(ssl, "staging: %s\n", secret_hex); + else + (void)ssl_printf(ssl, "staging[%d]: %s\n", i, + secret_hex); + } + lock_basic_unlock(&cookie_secrets->lock); + explicit_bzero(secret_hex, sizeof(secret_hex)); +} + /** check for name with end-of-string, space or tab after it */ static int cmdcmp(char* p, const char* cmd, size_t len) @@ -3327,6 +3532,9 @@ execute_cmd(struct daemon_remote* rc, RES* ssl, char* cmd, } else if(cmdcmp(p, "view_local_datas", 16)) { do_view_datas_add(rc, ssl, worker, skipwhite(p+16)); return; + } else if(cmdcmp(p, "print_cookie_secrets", 20)) { + do_print_cookie_secrets(ssl, worker); + return; } #ifdef THREADS_DISABLED @@ -3391,6 +3599,12 @@ execute_cmd(struct daemon_remote* rc, RES* ssl, char* cmd, do_rpz_enable(ssl, worker, skipwhite(p+10)); } else if(cmdcmp(p, "rpz_disable", 11)) { do_rpz_disable(ssl, worker, skipwhite(p+11)); + } else if(cmdcmp(p, "add_cookie_secret", 17)) { + do_add_cookie_secret(ssl, worker, skipwhite(p+17)); + } else if(cmdcmp(p, "drop_cookie_secret", 18)) { + do_drop_cookie_secret(ssl, worker); + } else if(cmdcmp(p, "activate_cookie_secret", 22)) { + do_activate_cookie_secret(ssl, worker); } else { (void)ssl_printf(ssl, "error unknown command '%s'\n", p); } diff --git a/daemon/worker.c b/daemon/worker.c index dd14a5a3c..84e18f2d0 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -1573,7 +1573,8 @@ worker_handle_request(struct comm_point* c, void* arg, int error, if((ret=parse_edns_from_query_pkt( c->buffer, &edns, worker->env.cfg, c, repinfo, (worker->env.now ? *worker->env.now : time(NULL)), - worker->scratchpad)) != 0) { + worker->scratchpad, + worker->daemon->cookie_secrets)) != 0) { struct edns_data reply_edns; verbose(VERB_ALGO, "worker parse edns: formerror."); log_addr(VERB_CLIENT, "from", &repinfo->client_addr, diff --git a/doc/example.conf.in b/doc/example.conf.in index 19aa59952..9f3fc0dce 100644 --- a/doc/example.conf.in +++ b/doc/example.conf.in @@ -1044,6 +1044,11 @@ server: # example value "000102030405060708090a0b0c0d0e0f". # cookie-secret: <128 bit random hex string> + # File with cookie secrets, the 'cookie-secret:' option is ignored + # and the file can be managed to have staging and active secrets + # with remote control commands. Disabled with "". Default is "". + # cookie-secret-file: "/usr/local/etc/unbound_cookiesecrets.txt" + # Enable to attach Extended DNS Error codes (RFC8914) to responses. # ede: no diff --git a/doc/unbound-control.8.in b/doc/unbound-control.8.in index 8d98d05c8..17073f938 100644 --- a/doc/unbound-control.8.in +++ b/doc/unbound-control.8.in @@ -350,6 +350,41 @@ Remove a list of \fIlocal_data\fR for given view from stdin. Like local_datas_re .TP .B view_local_datas \fIview\fR Add a list of \fIlocal_data\fR for given view from stdin. Like local_datas. +.TP +.B add_cookie_secret +Add or replace a cookie secret persistently. needs to be an 128 bit +hex string. +.IP +Cookie secrets can be either \fIactive\fR or \fIstaging\fR. \fIActive\fR cookie +secrets are used to create DNS Cookies, but verification of a DNS Cookie +succeeds with any of the \fIactive\fR or \fIstaging\fR cookie secrets. The +state of the current cookie secrets can be printed with the +\fBprint_cookie_secrets\fR command. +.IP +When there are no cookie secrets configured yet, the is added as +\fIactive\fR. If there is already an \fIactive\fR cookie secret, the +is added as \fIstaging\fR or replacing an existing \fIstaging\fR secret. +.IP +To "roll" a cookie secret used in an anycast set. The new secret has to be +added as staging secret to \fBall\fR nodes in the anycast set. When \fBall\fR +nodes can verify DNS Cookies with the new secret, the new secret can be +activated with the \fBactivate_cookie_secret\fR command. After \fBall\fR nodes +have the new secret \fIactive\fR for at least one hour, the previous secret can +be dropped with the \fBdrop_cookie_secret\fR command. +.IP +Persistence is accomplished by writing to a file which if configured with the +\fBcookie\-secret\-file\fR option in the server section of the config file. +This is disabled by default, "". +.TP +.B drop_cookie_secret +Drop the \fIstaging\fR cookie secret. +.TP +.B activate_cookie_secret +Make the current \fIstaging\fR cookie secret \fIactive\fR, and the current +\fIactive\fR cookie secret \fIstaging\fR. +.TP +.B print_cookie_secrets +Show the current configured cookie secrets with their status. .SH "EXIT CODE" The unbound\-control program exits with status code 1 on error, 0 on success. .SH "SET UP" diff --git a/doc/unbound.conf.5.in b/doc/unbound.conf.5.in index 90e109ad7..d6d9c905c 100644 --- a/doc/unbound.conf.5.in +++ b/doc/unbound.conf.5.in @@ -1983,6 +1983,20 @@ Useful to explicitly set for servers in an anycast deployment that need to share the secret in order to verify each other's Server Cookies. An example hex string would be "000102030405060708090a0b0c0d0e0f". Default is a 128 bits random secret generated at startup time. +This option is ignored if a \fBcookie\-secret\-file\fR is +present. In that case the secrets from that file are used in DNS Cookie +calculations. +.TP 5 +.B cookie\-secret\-file: \fI +File from which the secrets are read used in DNS Cookie calculations. When this +file exists, the secrets in this file are used and the secret specified by the +\fBcookie-secret\fR option is ignored. +Enable it by setting a filename, like "/usr/local/etc/unbound_cookiesecrets.txt". +The content of this file must be manipulated with the \fBadd_cookie_secret\fR, +\fBdrop_cookie_secret\fR and \fBactivate_cookie_secret\fR commands to the +\fIunbound\-control\fR(8) tool. Please see that manpage on how to perform a +safe cookie secret rollover. +Default is "" (disabled). .TP 5 .B edns\-client\-string: \fI Include an EDNS0 option containing configured ascii string in queries with diff --git a/smallapp/unbound-control.c b/smallapp/unbound-control.c index 50a465bd5..21e7eb82d 100644 --- a/smallapp/unbound-control.c +++ b/smallapp/unbound-control.c @@ -186,6 +186,10 @@ usage(void) printf(" rpz_enable zone Enable the RPZ zone if it had previously\n"); printf(" been disabled\n"); printf(" rpz_disable zone Disable the RPZ zone\n"); + printf(" add_cookie_secret add (or replace) a new cookie secret \n"); + printf(" drop_cookie_secret drop a staging cookie secret\n"); + printf(" activate_cookie_secret make a staging cookie secret active\n"); + printf(" print_cookie_secrets show all cookie secrets with their status\n"); printf("Version %s\n", PACKAGE_VERSION); printf("BSD licensed, see LICENSE in source package for details.\n"); printf("Report bugs to %s\n", PACKAGE_BUGREPORT); diff --git a/testcode/unitmain.c b/testcode/unitmain.c index 1ddc56750..084c12b93 100644 --- a/testcode/unitmain.c +++ b/testcode/unitmain.c @@ -1117,7 +1117,7 @@ static void edns_ede_encode_encodedecode(struct query_info* qinfo, sldns_buffer_skip(pkt, 2 + 2); /* decode */ unit_assert(parse_edns_from_query_pkt(pkt, edns, NULL, NULL, NULL, 0, - region) == 0); + region, NULL) == 0); } static void edns_ede_encode_check(struct edns_data* edns, int* found_ede, diff --git a/testdata/cookie_file.tdir/cookie_file.conf b/testdata/cookie_file.tdir/cookie_file.conf new file mode 100644 index 000000000..25dd93f52 --- /dev/null +++ b/testdata/cookie_file.tdir/cookie_file.conf @@ -0,0 +1,19 @@ +server: + verbosity: 7 + use-syslog: no + directory: "" + pidfile: "unbound.pid" + chroot: "" + username: "" + do-not-query-localhost: no + use-caps-for-id: no + port: @SERVER_PORT@ + interface: 127.0.0.1 + cookie-secret-file: "cookie_secrets.txt" + answer-cookie: yes + access-control: 127.0.0.0/8 allow_cookie # BADCOOKIE for incomplete/invalid cookies + +remote-control: + control-enable: yes + control-port: @CONTROL_PORT@ + control-use-cert: no diff --git a/testdata/cookie_file.tdir/cookie_file.dsc b/testdata/cookie_file.tdir/cookie_file.dsc new file mode 100644 index 000000000..4f321bd2e --- /dev/null +++ b/testdata/cookie_file.tdir/cookie_file.dsc @@ -0,0 +1,16 @@ +BaseName: cookie_file +Version: 1.0 +Description: Check the cookie rollover +CreationDate: Fri 14 Jun 11:00:00 CEST 2024 +Maintainer: +Category: +Component: +CmdDepends: +Depends: +Help: +Pre: cookie_file.pre +Post: cookie_file.post +Test: cookie_file.test +AuxFiles: +Passed: +Failure: diff --git a/testdata/cookie_file.tdir/cookie_file.post b/testdata/cookie_file.tdir/cookie_file.post new file mode 100644 index 000000000..b64af9cbd --- /dev/null +++ b/testdata/cookie_file.tdir/cookie_file.post @@ -0,0 +1,10 @@ +# #-- cookie_file.post --# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# source the test var file when it's there +[ -f .tpkg.var.test ] && source .tpkg.var.test +# +# do your teardown here +. ../common.sh +kill_from_pidfile "unbound.pid" +cat unbound.log diff --git a/testdata/cookie_file.tdir/cookie_file.pre b/testdata/cookie_file.tdir/cookie_file.pre new file mode 100644 index 000000000..61da5425a --- /dev/null +++ b/testdata/cookie_file.tdir/cookie_file.pre @@ -0,0 +1,24 @@ +# #-- cookie_file.pre--# +PRE="../.." +. ../common.sh + +get_random_port 2 +SERVER_PORT=$RND_PORT +CONTROL_PORT=$(($RND_PORT + 1)) +echo "SERVER_PORT=$SERVER_PORT" >> .tpkg.var.test +echo "CONTROL_PORT=$CONTROL_PORT" >> .tpkg.var.test + +# make config file +sed \ + -e 's/@SERVER_PORT\@/'$SERVER_PORT'/' \ + -e 's/@CONTROL_PORT\@/'$CONTROL_PORT'/' \ + < cookie_file.conf > ub.conf + +# empty cookie file +touch cookie_secrets.txt + +# start unbound in the background +$PRE/unbound -d -c ub.conf > unbound.log 2>&1 & + +cat .tpkg.var.test +wait_unbound_up unbound.log diff --git a/testdata/cookie_file.tdir/cookie_file.test b/testdata/cookie_file.tdir/cookie_file.test new file mode 100644 index 000000000..7da4fa657 --- /dev/null +++ b/testdata/cookie_file.tdir/cookie_file.test @@ -0,0 +1,248 @@ +# #-- cookie_file.test --# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# use .tpkg.var.test for in test variable passing +[ -f .tpkg.var.test ] && source .tpkg.var.test +PRE="../.." +. ../common.sh + +first_secret=dd3bdf9344b678b185a6f5cb60fca715 +second_secret=445536bcd2513298075a5d379663c962 + + +teststep "Add first secret" +echo ">> add_cookie_secret $first_secret" +$PRE/unbound-control -c ub.conf add_cookie_secret $first_secret +# check secret is persisted +outfile=cookie_secrets.1 +$PRE/unbound-control -c ub.conf print_cookie_secrets > $outfile +if ! grep -q "$first_secret" $outfile +then + sleep 1 + $PRE/unbound-control -c ub.conf print_cookie_secrets > $outfile +fi +if ! grep -q "$first_secret" $outfile +then + sleep 1 + $PRE/unbound-control -c ub.conf print_cookie_secrets > $outfile +fi +if ! grep -q "$first_secret" $outfile +then + sleep 1 + $PRE/unbound-control -c ub.conf print_cookie_secrets > $outfile +fi +if ! grep -q "^active.*$first_secret" $outfile +then + cat $outfile + echo "First secret was not provisioned" + exit 1 +fi +echo ">> print_cookie_secrets" +cat $outfile + + +teststep "Get a valid cookie for this secret" +outfile=dig.output.1 +dig version.server ch txt @127.0.0.1 -p $SERVER_PORT +cookie=3132333435363738 > $outfile +if ! grep -q "BADCOOKIE" $outfile +then + cat $outfile + echo "Did not get a BADCOOKIE response for a client-only cookie" + exit 1 +fi +if ! grep -q "COOKIE: 3132333435363738" $outfile +then + cat $outfile + echo "Did not get a cookie in the response" + exit 1 +fi +first_cookie=$(grep "; COOKIE:" $outfile | cut -d ' ' -f 3) +cat $outfile +echo "first cookie: $first_cookie" + + +teststep "Verify the first cookie can be reused" +outfile=dig.output.2 +dig version.server ch txt @127.0.0.1 -p $SERVER_PORT +cookie=$first_cookie > $outfile +if grep -q "BADCOOKIE" $outfile +then + cat $outfile + echo "Got BADCOOKIE response for a valid cookie" + exit 1 +fi +if ! grep -q "COOKIE: $first_cookie" $outfile +then + cat $outfile + echo "Did not get the same first cookie in the response" + exit 1 +fi + + +teststep "Add second secret" +outfile=cookie_secrets.2 +echo ">> add_cookie_secret $second_secret" +$PRE/unbound-control -c ub.conf add_cookie_secret $second_secret +$PRE/unbound-control -c ub.conf print_cookie_secrets > $outfile +if ! grep -q "$second_secret" $outfile +then + sleep 1 + $PRE/unbound-control -c ub.conf print_cookie_secrets > $outfile +fi +if ! grep -q "$second_secret" $outfile +then + sleep 1 + $PRE/unbound-control -c ub.conf print_cookie_secrets > $outfile +fi +if ! grep -q "$second_secret" $outfile +then + sleep 1 + $PRE/unbound-control -c ub.conf print_cookie_secrets > $outfile +fi +if ! grep -q "^staging.*$second_secret" $outfile \ + || ! grep -q "^active.*$first_secret" $outfile +then + cat $outfile + echo "Secrets were not provisioned" + exit 1 +fi +echo ">> print_cookie_secrets" +cat $outfile +echo ">> cookie_secrets.txt" +cat cookie_secrets.txt + + +teststep "Verify the first cookie can be reused" +outfile=dig.output.3 +dig version.server ch txt @127.0.0.1 -p $SERVER_PORT +cookie=$first_cookie > $outfile +if grep -q "BADCOOKIE" $outfile +then + cat $outfile + echo "Got BADCOOKIE response for a valid cookie" + exit 1 +fi +if ! grep -q "COOKIE: $first_cookie" $outfile +then + cat $outfile + echo "Did not get the same first cookie in the response" + exit 1 +fi + + +teststep "Secret rollover" +outfile=cookie_secrets.3 +$PRE/unbound-control -c ub.conf activate_cookie_secret +$PRE/unbound-control -c ub.conf print_cookie_secrets > $outfile +if ! grep -q "^active.*$second_secret" $outfile +then + sleep 1 + $PRE/unbound-control -c ub.conf print_cookie_secrets > $outfile +fi +if ! grep -q "^active.*$second_secret" $outfile +then + sleep 1 + $PRE/unbound-control -c ub.conf print_cookie_secrets > $outfile +fi +if ! grep -q "^active.*$second_secret" $outfile +then + sleep 1 + $PRE/unbound-control -c ub.conf print_cookie_secrets > $outfile +fi +if ! grep -q "^active.*$second_secret" $outfile \ + || ! grep -q "^staging.*$first_secret" $outfile +then + cat $outfile + echo "Second secret was not activated" + exit 1 +fi +echo ">> activate cookie secret, printout" +cat $outfile +echo ">> cookie_secrets.txt" +cat cookie_secrets.txt + + +teststep "Verify the first cookie can be reused but a new cookie is returned from the second secret" +outfile=dig.output.4 +dig version.server ch txt @127.0.0.1 -p $SERVER_PORT +cookie=$first_cookie > $outfile +if grep -q "BADCOOKIE" $outfile +then + cat $outfile + echo "Got BADCOOKIE response for a valid cookie" + exit 1 +fi +if ! grep -q "COOKIE: 3132333435363738" $outfile +then + cat $outfile + echo "Did not get a cookie in the response" + exit 1 +fi +if grep -q "COOKIE: $first_cookie" $outfile +then + cat $outfile + echo "Got the same first cookie in the response while the second secret is active" + exit 1 +fi +second_cookie=$(grep "; COOKIE:" $outfile | cut -d ' ' -f 3) +cat $outfile +echo "second cookie: $second_cookie" + + +teststep "Drop cookie secret" +outfile=cookie_secrets.4 +$PRE/unbound-control -c ub.conf drop_cookie_secret +$PRE/unbound-control -c ub.conf print_cookie_secrets > $outfile +if grep -q "^staging.*$first_secret" $outfile +then + sleep 1 + $PRE/unbound-control -c ub.conf print_cookie_secrets > $outfile +fi +if grep -q "^staging.*$first_secret" $outfile +then + sleep 1 + $PRE/unbound-control -c ub.conf print_cookie_secrets > $outfile +fi +if grep -q "^staging.*$first_secret" $outfile +then + sleep 1 + $PRE/unbound-control -c ub.conf print_cookie_secrets > $outfile +fi +if grep -q "^staging.*$first_secret" $outfile +then + cat $outfile + echo "First secret was not dropped" + exit 1 +fi +echo ">> drop cookie secret, printout" +cat $outfile +echo ">> cookie_secrets.txt" +cat cookie_secrets.txt + + +teststep "Verify the first cookie can not be reused and the second cookie is returned instead" +outfile=dig.output.4 +dig version.server ch txt @127.0.0.1 -p $SERVER_PORT +cookie=$first_cookie > $outfile +if ! grep -q "BADCOOKIE" $outfile +then + cat $outfile + echo "Did not get BADCOOKIE response for an invalid cookie" + exit 1 +fi +if ! grep -q "COOKIE: 3132333435363738" $outfile +then + cat $outfile + echo "Did not get a cookie in the response" + exit 1 +fi +if grep -q "COOKIE: $first_cookie" $outfile +then + cat $outfile + echo "Got the same first cookie in the response while the second secret is active" + exit 1 +fi +if ! grep -q "COOKIE: $second_cookie" $outfile +then + cat $outfile + echo "Did not get the same second cookie in the response" + exit 1 +fi + +exit 0 diff --git a/util/config_file.c b/util/config_file.c index 6c42a80d0..9a93befd3 100644 --- a/util/config_file.c +++ b/util/config_file.c @@ -387,6 +387,7 @@ config_create(void) memset(cfg->cookie_secret, 0, sizeof(cfg->cookie_secret)); cfg->cookie_secret_len = 16; init_cookie_secret(cfg->cookie_secret, cfg->cookie_secret_len); + cfg->cookie_secret_file = NULL; #ifdef USE_CACHEDB if(!(cfg->cachedb_backend = strdup("testframe"))) goto error_exit; if(!(cfg->cachedb_secret = strdup("default"))) goto error_exit; @@ -839,6 +840,8 @@ int config_set_option(struct config_file* cfg, const char* opt, { IS_NUMBER_OR_ZERO; cfg->ipsecmod_max_ttl = atoi(val); } else S_YNO("ipsecmod-strict:", ipsecmod_strict) #endif + else S_YNO("answer-cookie:", do_answer_cookie) + else S_STR("cookie-secret-file:", cookie_secret_file) #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) @@ -1336,6 +1339,8 @@ config_get_option(struct config_file* cfg, const char* opt, else O_LST(opt, "ipsecmod-whitelist", ipsecmod_whitelist) else O_YNO(opt, "ipsecmod-strict", ipsecmod_strict) #endif + else O_YNO(opt, "answer-cookie", do_answer_cookie) + else O_STR(opt, "cookie-secret-file", cookie_secret_file) #ifdef USE_CACHEDB else O_STR(opt, "backend", cachedb_backend) else O_STR(opt, "secret-seed", cachedb_secret) @@ -1721,6 +1726,7 @@ config_delete(struct config_file* cfg) free(cfg->ipsecmod_hook); config_delstrlist(cfg->ipsecmod_whitelist); #endif + free(cfg->cookie_secret_file); #ifdef USE_CACHEDB free(cfg->cachedb_backend); free(cfg->cachedb_secret); diff --git a/util/config_file.h b/util/config_file.h index cca714127..23aacc67a 100644 --- a/util/config_file.h +++ b/util/config_file.h @@ -750,6 +750,8 @@ struct config_file { uint8_t cookie_secret[40]; /** cookie secret length */ size_t cookie_secret_len; + /** path to cookie secret store */ + char* cookie_secret_file; /* ipset module */ #ifdef USE_IPSET diff --git a/util/configlexer.lex b/util/configlexer.lex index 31a37d50d..cd5062092 100644 --- a/util/configlexer.lex +++ b/util/configlexer.lex @@ -582,6 +582,7 @@ udp-upstream-without-downstream{COLON} { YDVAR(1, VAR_UDP_UPSTREAM_WITHOUT_DOWNS tcp-connection-limit{COLON} { YDVAR(2, VAR_TCP_CONNECTION_LIMIT) } answer-cookie{COLON} { YDVAR(1, VAR_ANSWER_COOKIE ) } cookie-secret{COLON} { YDVAR(1, VAR_COOKIE_SECRET) } +cookie-secret-file{COLON} { YDVAR(1, VAR_COOKIE_SECRET_FILE) } edns-client-string{COLON} { YDVAR(2, VAR_EDNS_CLIENT_STRING) } edns-client-string-opcode{COLON} { YDVAR(1, VAR_EDNS_CLIENT_STRING_OPCODE) } nsid{COLON} { YDVAR(1, VAR_NSID ) } diff --git a/util/configparser.y b/util/configparser.y index cf026bdad..b650b8109 100644 --- a/util/configparser.y +++ b/util/configparser.y @@ -205,6 +205,7 @@ extern struct config_parser_state* cfg_parser; %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 VAR_CACHEDB_CHECK_WHEN_SERVE_EXPIRED +%token VAR_COOKIE_SECRET_FILE %% toplevelvars: /* empty */ | toplevelvars toplevelvar ; @@ -342,7 +343,7 @@ content_server: server_num_threads | server_verbosity | server_port | server_interface_automatic_ports | server_ede | server_proxy_protocol_port | server_statistics_inhibit_zero | server_harden_unknown_additional | server_disable_edns_do | - server_log_destaddr + server_log_destaddr | server_cookie_secret_file ; stubstart: VAR_STUB_ZONE { @@ -3998,45 +3999,52 @@ server_cookie_secret: VAR_COOKIE_SECRET STRING_ARG free($2); } ; - ipsetstart: VAR_IPSET - { - OUTYY(("\nP(ipset:)\n")); - cfg_parser->started_toplevel = 1; - } - ; - contents_ipset: contents_ipset content_ipset - | ; - content_ipset: ipset_name_v4 | ipset_name_v6 - ; - ipset_name_v4: VAR_IPSET_NAME_V4 STRING_ARG - { - #ifdef USE_IPSET - OUTYY(("P(name-v4:%s)\n", $2)); - if(cfg_parser->cfg->ipset_name_v4) - yyerror("ipset name v4 override, there must be one " - "name for ip v4"); - free(cfg_parser->cfg->ipset_name_v4); - cfg_parser->cfg->ipset_name_v4 = $2; - #else - OUTYY(("P(Compiled without ipset, ignoring)\n")); - free($2); - #endif - } +server_cookie_secret_file: VAR_COOKIE_SECRET_FILE STRING_ARG + { + OUTYY(("P(cookie_secret_file:%s)\n", $2)); + free(cfg_parser->cfg->cookie_secret_file); + cfg_parser->cfg->cookie_secret_file = $2; + } ; - ipset_name_v6: VAR_IPSET_NAME_V6 STRING_ARG - { - #ifdef USE_IPSET - OUTYY(("P(name-v6:%s)\n", $2)); - if(cfg_parser->cfg->ipset_name_v6) - yyerror("ipset name v6 override, there must be one " - "name for ip v6"); - free(cfg_parser->cfg->ipset_name_v6); - cfg_parser->cfg->ipset_name_v6 = $2; - #else - OUTYY(("P(Compiled without ipset, ignoring)\n")); - free($2); - #endif - } +ipsetstart: VAR_IPSET + { + OUTYY(("\nP(ipset:)\n")); + cfg_parser->started_toplevel = 1; + } + ; +contents_ipset: contents_ipset content_ipset + | ; +content_ipset: ipset_name_v4 | ipset_name_v6 + ; +ipset_name_v4: VAR_IPSET_NAME_V4 STRING_ARG + { + #ifdef USE_IPSET + OUTYY(("P(name-v4:%s)\n", $2)); + if(cfg_parser->cfg->ipset_name_v4) + yyerror("ipset name v4 override, there must be one " + "name for ip v4"); + free(cfg_parser->cfg->ipset_name_v4); + cfg_parser->cfg->ipset_name_v4 = $2; + #else + OUTYY(("P(Compiled without ipset, ignoring)\n")); + free($2); + #endif + } + ; +ipset_name_v6: VAR_IPSET_NAME_V6 STRING_ARG + { + #ifdef USE_IPSET + OUTYY(("P(name-v6:%s)\n", $2)); + if(cfg_parser->cfg->ipset_name_v6) + yyerror("ipset name v6 override, there must be one " + "name for ip v6"); + free(cfg_parser->cfg->ipset_name_v6); + cfg_parser->cfg->ipset_name_v6 = $2; + #else + OUTYY(("P(Compiled without ipset, ignoring)\n")); + free($2); + #endif + } ; %% diff --git a/util/data/msgparse.c b/util/data/msgparse.c index ab1e0b557..6963d8501 100644 --- a/util/data/msgparse.c +++ b/util/data/msgparse.c @@ -947,7 +947,8 @@ parse_packet(sldns_buffer* pkt, struct msg_parse* msg, struct regional* region) static int parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len, struct edns_data* edns, struct config_file* cfg, struct comm_point* c, - struct comm_reply* repinfo, uint32_t now, struct regional* region) + struct comm_reply* repinfo, uint32_t now, struct regional* region, + struct cookie_secrets* cookie_secrets) { /* To respond with a Keepalive option, the client connection must have * received one message with a TCP Keepalive EDNS option, and that @@ -1070,13 +1071,24 @@ parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len, &((struct sockaddr_in6*)&repinfo->remote_addr)->sin6_addr, 16); } - cookie_val_status = edns_cookie_server_validate( - rdata_ptr, opt_len, cfg->cookie_secret, - cfg->cookie_secret_len, cookie_is_v4, - server_cookie, now); + if(cfg->cookie_secret_file && + cfg->cookie_secret_file[0]) { + /* Loop over the active and staging cookies. */ + cookie_val_status = + cookie_secrets_server_validate( + rdata_ptr, opt_len, cookie_secrets, + cookie_is_v4, server_cookie, now); + } else { + /* Use the cookie option value to validate. */ + cookie_val_status = edns_cookie_server_validate( + rdata_ptr, opt_len, cfg->cookie_secret, + cfg->cookie_secret_len, cookie_is_v4, + server_cookie, now); + } + if(cookie_val_status == COOKIE_STATUS_VALID_RENEW) + edns->cookie_valid = 1; switch(cookie_val_status) { case COOKIE_STATUS_VALID: - case COOKIE_STATUS_VALID_RENEW: edns->cookie_valid = 1; /* Reuse cookie */ if(!edns_opt_list_append( @@ -1093,12 +1105,28 @@ parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len, edns->cookie_client = 1; ATTR_FALLTHROUGH /* fallthrough */ + case COOKIE_STATUS_VALID_RENEW: case COOKIE_STATUS_FUTURE: case COOKIE_STATUS_EXPIRED: case COOKIE_STATUS_INVALID: default: - edns_cookie_server_write(server_cookie, - cfg->cookie_secret, cookie_is_v4, now); + if(cfg->cookie_secret_file && + cfg->cookie_secret_file[0]) { + if(!cookie_secrets) + break; + lock_basic_lock(&cookie_secrets->lock); + if(cookie_secrets->cookie_count < 1) { + lock_basic_unlock(&cookie_secrets->lock); + break; + } + edns_cookie_server_write(server_cookie, + cookie_secrets->cookie_secrets[0].cookie_secret, + cookie_is_v4, now); + lock_basic_unlock(&cookie_secrets->lock); + } else { + edns_cookie_server_write(server_cookie, + cfg->cookie_secret, cookie_is_v4, now); + } if(!edns_opt_list_append(&edns->opt_list_out, LDNS_EDNS_COOKIE, 24, server_cookie, region)) { @@ -1240,7 +1268,8 @@ skip_pkt_rrs(sldns_buffer* pkt, int num) int parse_edns_from_query_pkt(sldns_buffer* pkt, struct edns_data* edns, struct config_file* cfg, struct comm_point* c, - struct comm_reply* repinfo, time_t now, struct regional* region) + struct comm_reply* repinfo, time_t now, struct regional* region, + struct cookie_secrets* cookie_secrets) { size_t rdata_len; uint8_t* rdata_ptr; @@ -1286,7 +1315,7 @@ parse_edns_from_query_pkt(sldns_buffer* pkt, struct edns_data* edns, rdata_ptr = sldns_buffer_current(pkt); /* ignore rrsigs */ return parse_edns_options_from_query(rdata_ptr, rdata_len, edns, cfg, - c, repinfo, now, region); + c, repinfo, now, region, cookie_secrets); } void diff --git a/util/data/msgparse.h b/util/data/msgparse.h index 656e0d285..aebd48efa 100644 --- a/util/data/msgparse.h +++ b/util/data/msgparse.h @@ -73,6 +73,7 @@ struct edns_option; struct config_file; struct comm_point; struct comm_reply; +struct cookie_secrets; /** number of buckets in parse rrset hash table. Must be power of 2. */ #define PARSE_TABLE_SIZE 32 @@ -322,12 +323,14 @@ int skip_pkt_rrs(struct sldns_buffer* pkt, int num); * @param repinfo: commreply to determine the client address * @param now: current time * @param region: region to alloc results in (edns option contents) + * @param cookie_secrets: the cookie secrets for EDNS COOKIE validation. * @return: 0 on success, or an RCODE on error. * RCODE formerr if OPT is badly formatted and so on. */ int parse_edns_from_query_pkt(struct sldns_buffer* pkt, struct edns_data* edns, struct config_file* cfg, struct comm_point* c, - struct comm_reply* repinfo, time_t now, struct regional* region); + struct comm_reply* repinfo, time_t now, struct regional* region, + struct cookie_secrets* cookie_secrets); /** * Calculate hash value for rrset in packet. diff --git a/util/edns.c b/util/edns.c index 2b4047f0b..ee95a6912 100644 --- a/util/edns.c +++ b/util/edns.c @@ -187,3 +187,189 @@ edns_cookie_server_validate(const uint8_t* cookie, size_t cookie_len, return COOKIE_STATUS_VALID_RENEW; return COOKIE_STATUS_VALID; } + +struct cookie_secrets* +cookie_secrets_create(void) +{ + struct cookie_secrets* cookie_secrets = calloc(1, + sizeof(*cookie_secrets)); + if(!cookie_secrets) + return NULL; + lock_basic_init(&cookie_secrets->lock); + lock_protect(&cookie_secrets->lock, &cookie_secrets->cookie_count, + sizeof(cookie_secrets->cookie_count)); + lock_protect(&cookie_secrets->lock, cookie_secrets->cookie_secrets, + sizeof(cookie_secret_type)*UNBOUND_COOKIE_HISTORY_SIZE); + return cookie_secrets; +} + +void +cookie_secrets_delete(struct cookie_secrets* cookie_secrets) +{ + if(!cookie_secrets) + return; + lock_basic_destroy(&cookie_secrets->lock); + explicit_bzero(cookie_secrets->cookie_secrets, + sizeof(cookie_secret_type)*UNBOUND_COOKIE_HISTORY_SIZE); + free(cookie_secrets); +} + +/** Read the cookie secret file */ +static int +cookie_secret_file_read(struct cookie_secrets* cookie_secrets, + char* cookie_secret_file) +{ + char secret[UNBOUND_COOKIE_SECRET_SIZE * 2 + 2/*'\n' and '\0'*/]; + FILE* f; + int corrupt = 0; + size_t count; + + log_assert(cookie_secret_file != NULL); + cookie_secrets->cookie_count = 0; + f = fopen(cookie_secret_file, "r"); + /* a non-existing cookie file is not an error */ + if( f == NULL ) { + if(errno != EPERM) { + log_err("Could not read cookie-secret-file '%s': %s", + cookie_secret_file, strerror(errno)); + return 0; + } + return 1; + } + /* cookie secret file exists and is readable */ + for( count = 0; count < UNBOUND_COOKIE_HISTORY_SIZE; count++ ) { + size_t secret_len = 0; + ssize_t decoded_len = 0; + if( fgets(secret, sizeof(secret), f) == NULL ) { break; } + secret_len = strlen(secret); + if( secret_len == 0 ) { break; } + log_assert( secret_len <= sizeof(secret) ); + secret_len = secret[secret_len - 1] == '\n' ? secret_len - 1 : secret_len; + if( secret_len != UNBOUND_COOKIE_SECRET_SIZE * 2 ) { corrupt++; break; } + /* needed for `hex_pton`; stripping potential `\n` */ + secret[secret_len] = '\0'; + decoded_len = hex_pton(secret, cookie_secrets->cookie_secrets[count].cookie_secret, + UNBOUND_COOKIE_SECRET_SIZE); + if( decoded_len != UNBOUND_COOKIE_SECRET_SIZE ) { corrupt++; break; } + cookie_secrets->cookie_count++; + } + fclose(f); + return corrupt == 0; +} + +int +cookie_secrets_apply_cfg(struct cookie_secrets* cookie_secrets, + char* cookie_secret_file) +{ + if(!cookie_secrets) { + if(!cookie_secret_file || !cookie_secret_file[0]) + return 1; /* There is nothing to read anyway */ + log_err("Could not read cookie secrets, no structure alloced"); + return 0; + } + if(!cookie_secret_file_read(cookie_secrets, cookie_secret_file)) + return 0; + return 1; +} + +enum edns_cookie_val_status +cookie_secrets_server_validate(const uint8_t* cookie, size_t cookie_len, + struct cookie_secrets* cookie_secrets, int v4, + const uint8_t* hash_input, uint32_t now) +{ + size_t i; + enum edns_cookie_val_status cookie_val_status, + last = COOKIE_STATUS_INVALID; + if(!cookie_secrets) + return COOKIE_STATUS_INVALID; /* There are no cookie secrets.*/ + lock_basic_lock(&cookie_secrets->lock); + if(cookie_secrets->cookie_count == 0) { + lock_basic_unlock(&cookie_secrets->lock); + return COOKIE_STATUS_INVALID; /* There are no cookie secrets.*/ + } + for(i=0; icookie_count; i++) { + cookie_val_status = edns_cookie_server_validate(cookie, + cookie_len, + cookie_secrets->cookie_secrets[i].cookie_secret, + UNBOUND_COOKIE_SECRET_SIZE, v4, hash_input, now); + if(cookie_val_status == COOKIE_STATUS_VALID || + cookie_val_status == COOKIE_STATUS_VALID_RENEW) { + lock_basic_unlock(&cookie_secrets->lock); + /* For staging cookies, write a fresh cookie. */ + if(i != 0) + return COOKIE_STATUS_VALID_RENEW; + return cookie_val_status; + } + if(last == COOKIE_STATUS_INVALID) + last = cookie_val_status; /* Store more interesting + failure to return. */ + } + lock_basic_unlock(&cookie_secrets->lock); + return last; +} + +void add_cookie_secret(struct cookie_secrets* cookie_secrets, + uint8_t* secret, size_t secret_len) +{ + log_assert(secret_len == UNBOUND_COOKIE_SECRET_SIZE); + (void)secret_len; + if(!cookie_secrets) + return; + + /* New cookie secret becomes the staging secret (position 1) + * unless there is no active cookie yet, then it becomes the active + * secret. If the UNBOUND_COOKIE_HISTORY_SIZE > 2 then all staging cookies + * are moved one position down. + */ + if(cookie_secrets->cookie_count == 0) { + memcpy( cookie_secrets->cookie_secrets->cookie_secret + , secret, UNBOUND_COOKIE_SECRET_SIZE); + cookie_secrets->cookie_count = 1; + explicit_bzero(secret, UNBOUND_COOKIE_SECRET_SIZE); + return; + } +#if UNBOUND_COOKIE_HISTORY_SIZE > 2 + memmove( &cookie_secrets->cookie_secrets[2], &cookie_secrets->cookie_secrets[1] + , sizeof(struct cookie_secret) * (UNBOUND_COOKIE_HISTORY_SIZE - 2)); +#endif + memcpy( cookie_secrets->cookie_secrets[1].cookie_secret + , secret, UNBOUND_COOKIE_SECRET_SIZE); + cookie_secrets->cookie_count = cookie_secrets->cookie_count < UNBOUND_COOKIE_HISTORY_SIZE + ? cookie_secrets->cookie_count + 1 : UNBOUND_COOKIE_HISTORY_SIZE; + explicit_bzero(secret, UNBOUND_COOKIE_SECRET_SIZE); +} + +void activate_cookie_secret(struct cookie_secrets* cookie_secrets) +{ + uint8_t active_secret[UNBOUND_COOKIE_SECRET_SIZE]; + if(!cookie_secrets) + return; + /* The staging secret becomes the active secret. + * The active secret becomes a staging secret. + * If the UNBOUND_COOKIE_HISTORY_SIZE > 2 then all staging secrets are moved + * one position up and the previously active secret becomes the last + * staging secret. + */ + if(cookie_secrets->cookie_count < 2) + return; + memcpy( active_secret, cookie_secrets->cookie_secrets[0].cookie_secret + , UNBOUND_COOKIE_SECRET_SIZE); + memmove( &cookie_secrets->cookie_secrets[0], &cookie_secrets->cookie_secrets[1] + , sizeof(struct cookie_secret) * (UNBOUND_COOKIE_HISTORY_SIZE - 1)); + memcpy( cookie_secrets->cookie_secrets[cookie_secrets->cookie_count - 1].cookie_secret + , active_secret, UNBOUND_COOKIE_SECRET_SIZE); + explicit_bzero(active_secret, UNBOUND_COOKIE_SECRET_SIZE); +} + +void drop_cookie_secret(struct cookie_secrets* cookie_secrets) +{ + if(!cookie_secrets) + return; + /* Drops a staging cookie secret. If there are more than one, it will + * drop the last staging secret. */ + if(cookie_secrets->cookie_count < 2) + return; + explicit_bzero( cookie_secrets->cookie_secrets[cookie_secrets->cookie_count - 1].cookie_secret + , UNBOUND_COOKIE_SECRET_SIZE); + cookie_secrets->cookie_count -= 1; +} diff --git a/util/edns.h b/util/edns.h index 5da0ecb29..47ccb1ad2 100644 --- a/util/edns.h +++ b/util/edns.h @@ -43,6 +43,7 @@ #define UTIL_EDNS_H #include "util/storage/dnstree.h" +#include "util/locks.h" struct edns_data; struct config_file; @@ -75,6 +76,31 @@ struct edns_string_addr { size_t string_len; }; +#define UNBOUND_COOKIE_HISTORY_SIZE 2 +#define UNBOUND_COOKIE_SECRET_SIZE 16 + +typedef struct cookie_secret cookie_secret_type; +struct cookie_secret { + /** cookie secret */ + uint8_t cookie_secret[UNBOUND_COOKIE_SECRET_SIZE]; +}; + +/** + * The cookie secrets from the cookie-secret-file. + */ +struct cookie_secrets { + /** lock on the structure, in case there are modifications + * from remote control, this avoids race conditions. */ + lock_basic_type lock; + + /** how many cookies are there in the cookies array */ + size_t cookie_count; + + /* keep track of the last `UNBOUND_COOKIE_HISTORY_SIZE` + * cookies as per rfc requirement .*/ + cookie_secret_type cookie_secrets[UNBOUND_COOKIE_HISTORY_SIZE]; +}; + enum edns_cookie_val_status { COOKIE_STATUS_CLIENT_ONLY = -3, COOKIE_STATUS_FUTURE = -2, @@ -165,4 +191,63 @@ enum edns_cookie_val_status edns_cookie_server_validate(const uint8_t* cookie, size_t cookie_len, const uint8_t* secret, size_t secret_len, int v4, const uint8_t* hash_input, uint32_t now); +/** + * Create the cookie secrets structure. + * @return the structure or NULL on failure. + */ +struct cookie_secrets* cookie_secrets_create(void); + +/** + * Delete the cookie secrets. + * @param cookie_secrets: the cookie secrets. + */ +void cookie_secrets_delete(struct cookie_secrets* cookie_secrets); + +/** + * Apply configuration to cookie secrets, read them from file. + * @param cookie_secrets: the cookie secrets structure. + * @param cookie_secret_file: the file name, it is read. + * @return false on failure. + */ +int cookie_secrets_apply_cfg(struct cookie_secrets* cookie_secrets, + char* cookie_secret_file); + +/** + * Validate the cookie secrets, try all of them. + * @param cookie: pointer to the cookie data. + * @param cookie_len: the length of the cookie data. + * @param cookie_secrets: struct of cookie secrets. + * @param v4: if the client IP is v4 or v6. + * @param hash_input: pointer to the hash input for validation. It needs to be: + * Client Cookie | Version | Reserved | Timestamp | Client-IP + * @param now: the current time. + * return edns_cookie_val_status with the cookie validation status i.e., + * <=0 for invalid, else valid. + */ +enum edns_cookie_val_status cookie_secrets_server_validate( + const uint8_t* cookie, size_t cookie_len, + struct cookie_secrets* cookie_secrets, int v4, + const uint8_t* hash_input, uint32_t now); + +/** + * Add a cookie secret. If there are no secrets yet, the secret will become + * the active secret. Otherwise it will become the staging secret. + * Active secrets are used to both verify and create new DNS Cookies. + * Staging secrets are only used to verify DNS Cookies. Caller has to lock. + */ +void add_cookie_secret(struct cookie_secrets* cookie_secrets, uint8_t* secret, + size_t secret_len); + +/** + * Makes the staging cookie secret active and the active secret staging. + * Caller has to lock. + */ +void activate_cookie_secret(struct cookie_secrets* cookie_secrets); + +/** + * Drop a cookie secret. Drops the staging secret. An active secret will not + * be dropped. Caller has to lock. + */ +void drop_cookie_secret(struct cookie_secrets* cookie_secrets); + #endif diff --git a/util/net_help.c b/util/net_help.c index 772333816..5cf702ef9 100644 --- a/util/net_help.c +++ b/util/net_help.c @@ -47,6 +47,7 @@ #ifdef HAVE_NETIOAPI_H #include #endif +#include #include "util/net_help.h" #include "util/log.h" #include "util/data/dname.h" @@ -1871,3 +1872,42 @@ sock_close(int socket) closesocket(socket); } # endif /* USE_WINSOCK */ + +ssize_t +hex_ntop(uint8_t const *src, size_t srclength, char *target, size_t targsize) +{ + static char hexdigits[] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + }; + size_t i; + + if (targsize < srclength * 2 + 1) { + return -1; + } + + for (i = 0; i < srclength; ++i) { + *target++ = hexdigits[src[i] >> 4U]; + *target++ = hexdigits[src[i] & 0xfU]; + } + *target = '\0'; + return 2 * srclength; +} + +ssize_t +hex_pton(const char* src, uint8_t* target, size_t targsize) +{ + uint8_t *t = target; + if(strlen(src) % 2 != 0 || strlen(src)/2 > targsize) { + return -1; + } + while(*src) { + if(!isxdigit((unsigned char)src[0]) || + !isxdigit((unsigned char)src[1])) + return -1; + *t++ = sldns_hexdigit_to_int(src[0]) * 16 + + sldns_hexdigit_to_int(src[1]) ; + src += 2; + } + return t-target; +} diff --git a/util/net_help.h b/util/net_help.h index 1c57b5b70..28245ea0c 100644 --- a/util/net_help.h +++ b/util/net_help.h @@ -572,4 +572,13 @@ char* sock_strerror(int errn); /** close the socket with close, or wsa closesocket */ void sock_close(int socket); +/** + * Convert binary data to a string of hexadecimal characters. + */ +ssize_t hex_ntop(uint8_t const *src, size_t srclength, char *target, + size_t targsize); + +/** Convert hexadecimal data to binary. */ +ssize_t hex_pton(const char* src, uint8_t* target, size_t targsize); + #endif /* NET_HELP_H */ From 3cbf554e3b7f49aff81cd4597ef9c8a93556c890 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 2 Aug 2024 13:36:06 +0200 Subject: [PATCH 59/66] Changelog note for #1090 - Merge #1090: Cookie secret file. Adds `cookie-secret-file: "unbound_cookiesecrets.txt"` option to store cookie secrets for EDNS COOKIE secret rollover. The remote control add_cookie_secret, activate_cookie_secret and drop_cookie_secret commands can be used for rollover, the command print_cookie_secrets shows the values in use. --- doc/Changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/Changelog b/doc/Changelog index 157d31a6d..0c0e376af 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -2,6 +2,12 @@ - Fix that alloc stats has strdup checks, it stops debuggers from complaining about mismatch at free time. - Fix testbound for alloc stats strdup in util/alloc.c. + - Merge #1090: Cookie secret file. Adds + `cookie-secret-file: "unbound_cookiesecrets.txt"` option to store + cookie secrets for EDNS COOKIE secret rollover. The remote control + add_cookie_secret, activate_cookie_secret and drop_cookie_secret + commands can be used for rollover, the command print_cookie_secrets + shows the values in use. 1 August 2024: Wouter - Fix dnstap test program, cleans up to have clean memory on exit, From 0f2f6025e7708e6a794890fa2148c6c3a7e0c755 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 2 Aug 2024 15:51:40 +0200 Subject: [PATCH 60/66] - Fix that alloc stats for forwards and hints are printed, and when alloc stats is enabled, the unit test for unbound control waits for reloads to complete. --- daemon/worker.c | 4 +- doc/Changelog | 3 + .../09-unbound-control.test | 66 ++++++++++++++----- 3 files changed, 55 insertions(+), 18 deletions(-) diff --git a/daemon/worker.c b/daemon/worker.c index 84e18f2d0..5e6b2a656 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -162,9 +162,9 @@ worker_mem_report(struct worker* ATTR_UNUSED(worker), + sizeof(*worker->env.scratch_buffer) + sldns_buffer_capacity(worker->env.scratch_buffer); if(worker->daemon->env->fwds) - me += forwards_get_mem(worker->env.fwds); + log_info("forwards=%u", (unsigned)forwards_get_mem(worker->env.fwds)); if(worker->daemon->env->hints) - me += hints_get_mem(worker->env.hints); + log_info("hints=%u", (unsigned)hints_get_mem(worker->env.hints)); if(worker->thread_num == 0) me += acl_list_get_mem(worker->daemon->acl); if(cur_serv) { diff --git a/doc/Changelog b/doc/Changelog index 0c0e376af..cec04793f 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -8,6 +8,9 @@ add_cookie_secret, activate_cookie_secret and drop_cookie_secret commands can be used for rollover, the command print_cookie_secrets shows the values in use. + - Fix that alloc stats for forwards and hints are printed, and when + alloc stats is enabled, the unit test for unbound control waits for + reloads to complete. 1 August 2024: Wouter - Fix dnstap test program, cleans up to have clean memory on exit, diff --git a/testdata/09-unbound-control.tdir/09-unbound-control.test b/testdata/09-unbound-control.tdir/09-unbound-control.test index 597487290..8bd2220f3 100644 --- a/testdata/09-unbound-control.tdir/09-unbound-control.test +++ b/testdata/09-unbound-control.tdir/09-unbound-control.test @@ -73,12 +73,53 @@ control_command () { $PRE/unbound-control $@ > outfile } +# Reload the server and check the reload has finished processing +# when a lot of debug is enabled, a lot of log needs to be printed. +control_reload () { + prelines=`wc -l unbound.log | awk '{print $1;}'` + cmd="$1" + if test -z "$cmd"; then cmd="reload"; fi + control_command -c ub.conf $cmd + expect_exit_value 0 + # see if the reload has completed. + lines1=`wc -l unbound.log | awk '{print $1;}'` + count=0 + lines2=`wc -l unbound.log | awk '{print $1;}'` + # See if the log finishes up without sleeping too long. + while test "$lines1" -ne "$lines2"; do + lines1=`wc -l unbound.log | awk '{print $1;}'` + # There is no sleep here. The add and compare are a + # brief wait. + count=`expr "$count" + 1` + if test "$count" -gt 30; then + break; + fi + lines2=`wc -l unbound.log | awk '{print $1;}'` + done + if test "$lines1" -ne "$lines2"; then + count=0 + while test "$lines1" -ne "$lines2"; do + tail -1 unbound.log + lines1=`wc -l unbound.log | awk '{print $1;}'` + sleep 1 + count=`expr "$count" + 1` + if test "$count" -gt 30; then + echo "reload is taking too long" + exit 1 + fi + lines2=`wc -l unbound.log | awk '{print $1;}'` + done + if test "$count" -ne "0"; then + echo "reload done with $count sec" + fi + fi +} + # Reload the server for a clean state clean_reload () { echo "> Reloading the server for a clean state" cp main.conf ub.conf - control_command -c ub.conf reload - expect_exit_value 0 + control_reload } # Reload the server for a clean state and populate the cache @@ -175,8 +216,7 @@ expect_exit_value 1 # local-data element in the server. teststep "reload the server" echo "server: local-data: 'afterreload. IN A 5.6.7.8'" >> ub.conf -control_command -c ub.conf reload -expect_exit_value 0 +control_reload query afterreload. expect_answer "5.6.7.8" @@ -336,16 +376,14 @@ fi clean_reload_and_fill_cache teststep "reload and check cache - should be empty" -control_command -c ub.conf reload -expect_exit_value 0 +control_reload query www.example.com +nordflag fail_answer "10.20.30.40" clean_reload_and_fill_cache teststep "reload_keep_cache and check cache - should not be empty" -control_command -c ub.conf reload_keep_cache -expect_exit_value 0 +control_reload reload_keep_cache query www.example.com +nordflag expect_answer "10.20.30.40" @@ -353,8 +391,7 @@ clean_reload_and_fill_cache teststep "change msg-cache-size and reload_keep_cache - should be empty" echo "server: msg-cache-size: 2m" >> ub.conf -control_command -c ub.conf reload_keep_cache -expect_exit_value 0 +control_reload reload_keep_cache query www.example.com +nordflag fail_answer "10.20.30.40" @@ -362,8 +399,7 @@ clean_reload_and_fill_cache teststep "change rrset-cache-size and reload_keep_cache - should be empty" echo "server: rrset-cache-size: 2m" >> ub.conf -control_command -c ub.conf reload_keep_cache -expect_exit_value 0 +control_reload reload_keep_cache query www.example.com +nordflag fail_answer "10.20.30.40" @@ -375,8 +411,7 @@ clean_reload_and_fill_cache teststep "change num-threads and reload_keep_cache - should be empty" echo "server: num-threads: 2" >> ub.conf -control_command -c ub.conf reload_keep_cache -expect_exit_value 0 +control_reload reload_keep_cache query www.example.com +nordflag fail_answer "10.20.30.40" @@ -384,8 +419,7 @@ clean_reload_and_fill_cache teststep "change minimal-responses and reload_keep_cache - should not be empty" echo "server: minimal-responses: no" >> ub.conf -control_command -c ub.conf reload_keep_cache -expect_exit_value 0 +control_reload reload_keep_cache query www.example.com +nordflag expect_answer "10.20.30.40" From ed883238fd347c8855a9eff53f54fa082c6d4d15 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 8 Aug 2024 09:27:45 +0200 Subject: [PATCH 61/66] - Fix CAMP issues with global quota. Thanks to Huayi Duan, Marco Bearzi, Jodok Vieli, and Cagin Tanir from NetSec group, ETH Zurich. --- doc/Changelog | 4 ++++ iterator/iterator.c | 19 +++++++++++++++++++ iterator/iterator.h | 6 ++++++ 3 files changed, 29 insertions(+) diff --git a/doc/Changelog b/doc/Changelog index cec04793f..c1a3ab8ea 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,7 @@ +8 August 2024: Wouter + - Fix CAMP issues with global quota. Thanks to Huayi Duan, Marco + Bearzi, Jodok Vieli, and Cagin Tanir from NetSec group, ETH Zurich. + 2 August 2024: Wouter - Fix that alloc stats has strdup checks, it stops debuggers from complaining about mismatch at free time. diff --git a/iterator/iterator.c b/iterator/iterator.c index b348e9867..228f5dfae 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -760,6 +760,14 @@ target_count_increase_nx(struct iter_qstate* iq, int num) iq->target_count[TARGET_COUNT_NX] += num; } +static void +target_count_increase_global_quota(struct iter_qstate* iq, int num) +{ + target_count_create(iq); + if(iq->target_count) + iq->target_count[TARGET_COUNT_GLOBAL_QUOTA] += num; +} + /** * Generate a subrequest. * Generate a local request event. Local events are tied to this module, and @@ -3013,6 +3021,17 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, } } + target_count_increase_global_quota(iq, 1); + if(iq->target_count && iq->target_count[TARGET_COUNT_GLOBAL_QUOTA] + > MAX_GLOBAL_QUOTA) { + char s[LDNS_MAX_DOMAINLEN+1]; + dname_str(qstate->qinfo.qname, s); + verbose(VERB_QUERY, "request %s has exceeded the maximum " + "global quota on number of upstream queries %d", s, + iq->target_count[TARGET_COUNT_GLOBAL_QUOTA]); + return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL); + } + /* Do not check ratelimit for forwarding queries or if we already got a * pass. */ sq_check_ratelimit = (!(iq->chase_flags & BIT_RD) && !iq->ratelimit_ok); diff --git a/iterator/iterator.h b/iterator/iterator.h index e253f3f7e..70b11df7e 100644 --- a/iterator/iterator.h +++ b/iterator/iterator.h @@ -55,6 +55,9 @@ struct rbtree_type; /** max number of targets spawned for a query and its subqueries */ #define MAX_TARGET_COUNT 64 +/** max number of upstream queries for a query and its subqueries, it is + * never reset. */ +#define MAX_GLOBAL_QUOTA 128 /** max number of target lookups per qstate, per delegation point */ #define MAX_DP_TARGET_COUNT 16 /** max number of nxdomains allowed for target lookups for a query and @@ -248,6 +251,9 @@ enum target_count_variables { TARGET_COUNT_QUERIES, /** Number of nxdomain responses encountered. */ TARGET_COUNT_NX, + /** Global quota on number of queries to upstream servers per + * client request, that is never reset. */ + TARGET_COUNT_GLOBAL_QUOTA, /** This should stay last here, it is used for the allocation */ TARGET_COUNT_MAX, From b4519012dc672e8a884f4f026c4982db6990710b Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 8 Aug 2024 09:28:44 +0200 Subject: [PATCH 62/66] - Fix CacheFlush issues with limit on NS RRs. Thanks to Yehuda Afek, Anat Bremler-Barr, Shoham Danino and Yuval Shavitt (Tel-Aviv University and Reichman University). --- doc/Changelog | 3 ++ iterator/iter_scrub.c | 82 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/doc/Changelog b/doc/Changelog index c1a3ab8ea..2bf71067b 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,6 +1,9 @@ 8 August 2024: Wouter - Fix CAMP issues with global quota. Thanks to Huayi Duan, Marco Bearzi, Jodok Vieli, and Cagin Tanir from NetSec group, ETH Zurich. + - Fix CacheFlush issues with limit on NS RRs. Thanks to Yehuda Afek, + Anat Bremler-Barr, Shoham Danino and Yuval Shavitt (Tel-Aviv + University and Reichman University). 2 August 2024: Wouter - Fix that alloc stats has strdup checks, it stops debuggers from diff --git a/iterator/iter_scrub.c b/iterator/iter_scrub.c index 48867e50c..f038ad69a 100644 --- a/iterator/iter_scrub.c +++ b/iterator/iter_scrub.c @@ -367,6 +367,47 @@ type_allowed_in_additional_section(uint16_t tp) return 0; } +/** Shorten RRset */ +static void +shorten_rrset(sldns_buffer* pkt, struct rrset_parse* rrset, int count) +{ + /* The too large NS RRset is shortened. This is so that too large + * content does not overwhelm the cache. It may make the rrset + * bogus if it was signed, and then the domain is not resolved any + * more, that is okay, the NS RRset was too large. During a referral + * it can be shortened and then the first part of the list could + * be used to resolve. The scrub continues to disallow glue for the + * removed nameserver RRs and removes that too. Because the glue + * is not marked as okay, since the RRs have been removed here. */ + int i; + struct rr_parse* rr = rrset->rr_first, *prev = NULL; + if(!rr) + return; + for(i=0; inext; + if(!rr) + return; /* The RRset is already short. */ + } + if(verbosity >= VERB_QUERY + && rrset->dname_len <= LDNS_MAX_DOMAINLEN) { + uint8_t buf[LDNS_MAX_DOMAINLEN+1]; + dname_pkt_copy(pkt, buf, rrset->dname); + log_nametypeclass(VERB_QUERY, "normalize: shorten RRset:", buf, + rrset->type, ntohs(rrset->rrset_class)); + } + /* remove further rrs */ + rrset->rr_last = prev; + rrset->rr_count = count; + while(rr) { + rrset->size -= rr->size; + rr = rr->next; + } + if(rrset->rr_last) + rrset->rr_last->next = NULL; + else rrset->rr_first = NULL; +} + /** * This routine normalizes a response. This includes removing "irrelevant" * records from the answer and additional sections and (re)synthesizing @@ -387,6 +428,7 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg, uint8_t* sname = qinfo->qname; size_t snamelen = qinfo->qname_len; struct rrset_parse* rrset, *prev, *nsset=NULL; + int cname_length = 0; /* number of CNAMEs, or DNAMEs */ if(FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NOERROR && FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NXDOMAIN) @@ -401,6 +443,16 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg, prev = NULL; rrset = msg->rrset_first; while(rrset && rrset->section == LDNS_SECTION_ANSWER) { + if(cname_length > 11 /* env->cfg.iter_scrub_cname */) { + /* Too many CNAMEs, or DNAMEs, from the authority + * server, scrub down the length to something + * shorter. This deletes everything after the limit + * is reached. The iterator is going to look up + * the content one by one anyway. */ + remove_rrset("normalize: removing because too many cnames:", + pkt, msg, prev, &rrset); + continue; + } if(rrset->type == LDNS_RR_TYPE_DNAME && pkt_strict_sub(pkt, sname, rrset->dname)) { /* check if next rrset is correct CNAME. else, @@ -420,6 +472,7 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg, "too long"); return 0; } + cname_length++; if(nx && nx->type == LDNS_RR_TYPE_CNAME && dname_pkt_compare(pkt, sname, nx->dname) == 0) { /* check next cname */ @@ -460,6 +513,7 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg, if(rrset->type == LDNS_RR_TYPE_CNAME) { struct rrset_parse* nx = rrset->rrset_all_next; uint8_t* oldsname = sname; + cname_length++; /* see if the next one is a DNAME, if so, swap them */ if(nx && nx->section == LDNS_SECTION_ANSWER && nx->type == LDNS_RR_TYPE_DNAME && @@ -507,6 +561,10 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg, LDNS_SECTION_ANSWER && dname_pkt_compare(pkt, oldsname, rrset->dname) == 0) { + if(rrset->type == LDNS_RR_TYPE_NS && + rrset->rr_count > 20 /* env->cfg->iter_scrub_ns */) { + shorten_rrset(pkt, rrset, 20 /* env->cfg->iter_scrub_ns */); + } prev = rrset; rrset = rrset->rrset_all_next; } @@ -522,6 +580,11 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg, continue; } + if(rrset->type == LDNS_RR_TYPE_NS && + rrset->rr_count > 20 /* env->cfg->iter_scrub_ns */) { + shorten_rrset(pkt, rrset, 20 /* env->cfg->iter_scrub_ns */); + } + /* Mark the additional names from relevant rrset as OK. */ /* only for RRsets that match the query name, other ones * will be removed by sanitize, so no additional for them */ @@ -578,6 +641,25 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg, "RRset:", pkt, msg, prev, &rrset); continue; } + if(rrset->rr_count > 20 /* env->cfg->iter_scrub_ns */) { + /* If this is not a referral, and the NS RRset + * is signed, then remove it entirely, so + * that when it becomes bogus it does not + * make the message that is otherwise fine + * into a bogus message. */ + if(!(msg->an_rrsets == 0 && + FLAGS_GET_RCODE(msg->flags) == + LDNS_RCODE_NOERROR && + !soa_in_auth(msg) && + !(msg->flags & BIT_AA)) && + rrset->rrsig_count != 0) { + remove_rrset("normalize: removing too large NS " + "RRset:", pkt, msg, prev, &rrset); + continue; + } else { + shorten_rrset(pkt, rrset, 20 /* env->cfg->iter_scrub_ns */); + } + } } /* if this is type DS and we query for type DS we just got * a referral answer for our type DS query, fix packet */ From 158c1defe3a81fa7f554ae9942d8f334201e4d15 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 8 Aug 2024 09:30:53 +0200 Subject: [PATCH 63/66] - Set version number to 1.21.0 for release. --- configure | 26 +++++++++++++------------- configure.ac | 6 +++--- doc/Changelog | 1 + 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/configure b/configure index daa37e504..63051eca8 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.71 for unbound 1.20.1. +# Generated by GNU Autoconf 2.71 for unbound 1.21.0. # # Report bugs to . # @@ -622,8 +622,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='unbound' PACKAGE_TARNAME='unbound' -PACKAGE_VERSION='1.20.1' -PACKAGE_STRING='unbound 1.20.1' +PACKAGE_VERSION='1.21.0' +PACKAGE_STRING='unbound 1.21.0' PACKAGE_BUGREPORT='unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues' PACKAGE_URL='' @@ -1508,7 +1508,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures unbound 1.20.1 to adapt to many kinds of systems. +\`configure' configures unbound 1.21.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1574,7 +1574,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of unbound 1.20.1:";; + short | recursive ) echo "Configuration of unbound 1.21.0:";; esac cat <<\_ACEOF @@ -1822,7 +1822,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -unbound configure 1.20.1 +unbound configure 1.21.0 generated by GNU Autoconf 2.71 Copyright (C) 2021 Free Software Foundation, Inc. @@ -2479,7 +2479,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by unbound $as_me 1.20.1, which was +It was created by unbound $as_me 1.21.0, which was generated by GNU Autoconf 2.71. Invocation command line was $ $0$ac_configure_args_raw @@ -3241,9 +3241,9 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu UNBOUND_VERSION_MAJOR=1 -UNBOUND_VERSION_MINOR=20 +UNBOUND_VERSION_MINOR=21 -UNBOUND_VERSION_MICRO=1 +UNBOUND_VERSION_MICRO=0 LIBUNBOUND_CURRENT=9 @@ -3342,7 +3342,7 @@ LIBUNBOUND_AGE=1 # 1.19.2 had 9:25:1 # 1.19.3 had 9:26:1 # 1.20.0 had 9:27:1 -# 1.20.1 had 9:28:1 +# 1.21.0 had 9:28:1 # Current -- the number of the binary API that we're implementing # Revision -- which iteration of the implementation of the binary @@ -24644,7 +24644,7 @@ printf "%s\n" "#define MAXSYSLOGMSGLEN 10240" >>confdefs.h -version=1.20.1 +version=1.21.0 date=`date +'%b %e, %Y'` @@ -25156,7 +25156,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by unbound $as_me 1.20.1, which was +This file was extended by unbound $as_me 1.21.0, which was generated by GNU Autoconf 2.71. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -25224,7 +25224,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ -unbound config.status 1.20.1 +unbound config.status 1.21.0 configured by $0, generated by GNU Autoconf 2.71, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index feeaf34c2..c30d931de 100644 --- a/configure.ac +++ b/configure.ac @@ -10,8 +10,8 @@ sinclude(dnscrypt/dnscrypt.m4) # must be numbers. ac_defun because of later processing m4_define([VERSION_MAJOR],[1]) -m4_define([VERSION_MINOR],[20]) -m4_define([VERSION_MICRO],[1]) +m4_define([VERSION_MINOR],[21]) +m4_define([VERSION_MICRO],[0]) AC_INIT([unbound],m4_defn([VERSION_MAJOR]).m4_defn([VERSION_MINOR]).m4_defn([VERSION_MICRO]),[unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues],[unbound]) AC_SUBST(UNBOUND_VERSION_MAJOR, [VERSION_MAJOR]) AC_SUBST(UNBOUND_VERSION_MINOR, [VERSION_MINOR]) @@ -113,7 +113,7 @@ LIBUNBOUND_AGE=1 # 1.19.2 had 9:25:1 # 1.19.3 had 9:26:1 # 1.20.0 had 9:27:1 -# 1.20.1 had 9:28:1 +# 1.21.0 had 9:28:1 # Current -- the number of the binary API that we're implementing # Revision -- which iteration of the implementation of the binary diff --git a/doc/Changelog b/doc/Changelog index 2bf71067b..64d2a77c3 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -4,6 +4,7 @@ - Fix CacheFlush issues with limit on NS RRs. Thanks to Yehuda Afek, Anat Bremler-Barr, Shoham Danino and Yuval Shavitt (Tel-Aviv University and Reichman University). + - Set version number to 1.21.0 for release. 2 August 2024: Wouter - Fix that alloc stats has strdup checks, it stops debuggers from From 5abdd09095171d7afdc6ae1f017f3391663c25a8 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 8 Aug 2024 16:14:09 +0200 Subject: [PATCH 64/66] - Fix that for windows the module startup is called and sets up the module-config. --- doc/Changelog | 2 ++ winrc/win_svc.c | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/doc/Changelog b/doc/Changelog index 64d2a77c3..510eb6673 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -5,6 +5,8 @@ Anat Bremler-Barr, Shoham Danino and Yuval Shavitt (Tel-Aviv University and Reichman University). - Set version number to 1.21.0 for release. + - Fix that for windows the module startup is called and sets up + the module-config. 2 August 2024: Wouter - Fix that alloc stats has strdup checks, it stops debuggers from diff --git a/winrc/win_svc.c b/winrc/win_svc.c index a87d73bf1..49d4251fa 100644 --- a/winrc/win_svc.c +++ b/winrc/win_svc.c @@ -352,6 +352,10 @@ service_init(int r, struct daemon** d, struct config_file** c) daemon_apply_cfg(daemon, cfg); if(!r) report_status(SERVICE_START_PENDING, NO_ERROR, 2300); + if(!r) { + if(!daemon_privileged(daemon)) + fatal_exit("could not do privileged setup"); + } if(!(daemon->rc = daemon_remote_create(cfg))) { log_err("could not set up remote-control"); daemon_delete(daemon); From 79e4c578518886a32475cfbb0de383ff3a905033 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 9 Aug 2024 14:04:25 +0200 Subject: [PATCH 65/66] - Fix spelling for the cache-min-negative-ttl entry in the example.conf. --- doc/Changelog | 4 ++++ doc/example.conf.in | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/Changelog b/doc/Changelog index 510eb6673..70860717a 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,7 @@ +9 August 2024: Wouter + - Fix spelling for the cache-min-negative-ttl entry in the + example.conf. + 8 August 2024: Wouter - Fix CAMP issues with global quota. Thanks to Huayi Duan, Marco Bearzi, Jodok Vieli, and Cagin Tanir from NetSec group, ETH Zurich. diff --git a/doc/example.conf.in b/doc/example.conf.in index 9f3fc0dce..d314d8ef2 100644 --- a/doc/example.conf.in +++ b/doc/example.conf.in @@ -228,7 +228,7 @@ server: # the time to live (TTL) value lower bound, in seconds. Default 0. # For negative responses in the cache. If disabled, default, - # cache-min-tll applies if configured. + # cache-min-ttl applies if configured. # cache-min-negative-ttl: 0 # the time to live (TTL) value for cached roundtrip times, lameness and From 5fa84d50bfe4d4abd8a055bd4140cd84fcbb05b5 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 15 Aug 2024 11:01:41 +0200 Subject: [PATCH 66/66] - Tag for release 1.21.0, the repository continues with 1.21.1 in development. --- configure | 25 +++++++++++++------------ configure.ac | 5 +++-- doc/Changelog | 4 +++- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/configure b/configure index 63051eca8..ef250d6c6 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.71 for unbound 1.21.0. +# Generated by GNU Autoconf 2.71 for unbound 1.21.1. # # Report bugs to . # @@ -622,8 +622,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='unbound' PACKAGE_TARNAME='unbound' -PACKAGE_VERSION='1.21.0' -PACKAGE_STRING='unbound 1.21.0' +PACKAGE_VERSION='1.21.1' +PACKAGE_STRING='unbound 1.21.1' PACKAGE_BUGREPORT='unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues' PACKAGE_URL='' @@ -1508,7 +1508,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures unbound 1.21.0 to adapt to many kinds of systems. +\`configure' configures unbound 1.21.1 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1574,7 +1574,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of unbound 1.21.0:";; + short | recursive ) echo "Configuration of unbound 1.21.1:";; esac cat <<\_ACEOF @@ -1822,7 +1822,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -unbound configure 1.21.0 +unbound configure 1.21.1 generated by GNU Autoconf 2.71 Copyright (C) 2021 Free Software Foundation, Inc. @@ -2479,7 +2479,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by unbound $as_me 1.21.0, which was +It was created by unbound $as_me 1.21.1, which was generated by GNU Autoconf 2.71. Invocation command line was $ $0$ac_configure_args_raw @@ -3243,11 +3243,11 @@ UNBOUND_VERSION_MAJOR=1 UNBOUND_VERSION_MINOR=21 -UNBOUND_VERSION_MICRO=0 +UNBOUND_VERSION_MICRO=1 LIBUNBOUND_CURRENT=9 -LIBUNBOUND_REVISION=28 +LIBUNBOUND_REVISION=29 LIBUNBOUND_AGE=1 # 1.0.0 had 0:12:0 # 1.0.1 had 0:13:0 @@ -3343,6 +3343,7 @@ LIBUNBOUND_AGE=1 # 1.19.3 had 9:26:1 # 1.20.0 had 9:27:1 # 1.21.0 had 9:28:1 +# 1.21.1 had 9:29:1 # Current -- the number of the binary API that we're implementing # Revision -- which iteration of the implementation of the binary @@ -24644,7 +24645,7 @@ printf "%s\n" "#define MAXSYSLOGMSGLEN 10240" >>confdefs.h -version=1.21.0 +version=1.21.1 date=`date +'%b %e, %Y'` @@ -25156,7 +25157,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by unbound $as_me 1.21.0, which was +This file was extended by unbound $as_me 1.21.1, which was generated by GNU Autoconf 2.71. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -25224,7 +25225,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ -unbound config.status 1.21.0 +unbound config.status 1.21.1 configured by $0, generated by GNU Autoconf 2.71, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index c30d931de..fdded4f50 100644 --- a/configure.ac +++ b/configure.ac @@ -11,14 +11,14 @@ sinclude(dnscrypt/dnscrypt.m4) # must be numbers. ac_defun because of later processing m4_define([VERSION_MAJOR],[1]) m4_define([VERSION_MINOR],[21]) -m4_define([VERSION_MICRO],[0]) +m4_define([VERSION_MICRO],[1]) AC_INIT([unbound],m4_defn([VERSION_MAJOR]).m4_defn([VERSION_MINOR]).m4_defn([VERSION_MICRO]),[unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues],[unbound]) AC_SUBST(UNBOUND_VERSION_MAJOR, [VERSION_MAJOR]) AC_SUBST(UNBOUND_VERSION_MINOR, [VERSION_MINOR]) AC_SUBST(UNBOUND_VERSION_MICRO, [VERSION_MICRO]) LIBUNBOUND_CURRENT=9 -LIBUNBOUND_REVISION=28 +LIBUNBOUND_REVISION=29 LIBUNBOUND_AGE=1 # 1.0.0 had 0:12:0 # 1.0.1 had 0:13:0 @@ -114,6 +114,7 @@ LIBUNBOUND_AGE=1 # 1.19.3 had 9:26:1 # 1.20.0 had 9:27:1 # 1.21.0 had 9:28:1 +# 1.21.1 had 9:29:1 # Current -- the number of the binary API that we're implementing # Revision -- which iteration of the implementation of the binary diff --git a/doc/Changelog b/doc/Changelog index 70860717a..8b05d3186 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,6 +1,8 @@ 9 August 2024: Wouter - Fix spelling for the cache-min-negative-ttl entry in the example.conf. + - Tag for release 1.21.0, the repository continues with 1.21.1 + in development. 8 August 2024: Wouter - Fix CAMP issues with global quota. Thanks to Huayi Duan, Marco @@ -8,7 +10,7 @@ - Fix CacheFlush issues with limit on NS RRs. Thanks to Yehuda Afek, Anat Bremler-Barr, Shoham Danino and Yuval Shavitt (Tel-Aviv University and Reichman University). - - Set version number to 1.21.0 for release. + - Set version number to 1.21.0 for release. This has tag 1.21.0rc1. - Fix that for windows the module startup is called and sets up the module-config.