From 56e60e37ae828399496677300de48a4923509efc Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 15 Jun 2026 16:16:50 +0200 Subject: [PATCH] - Fix that fast_reload when a zonemd verification lookup it in progress with subnet loaded, deregisters the callback. Thanks to Qifan Zhang, Palo Alto Networks, for the report. --- daemon/remote.c | 3 ++- doc/Changelog | 4 ++++ libunbound/context.h | 2 ++ libunbound/libworker.c | 9 +++++--- services/authzone.c | 9 +++++--- services/authzone.h | 6 ++++++ services/mesh.c | 47 ++++++++++++++++++++++++++++++++++++++---- services/mesh.h | 25 ++++++++++++++++++++-- validator/autotrust.c | 2 +- 9 files changed, 93 insertions(+), 14 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 4e4647237..bce73dc86 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -7612,7 +7612,8 @@ auth_zone_zonemd_stop_lookup(struct auth_zone* z, struct mesh_area* mesh) qinfo.local_alias = NULL; mesh_remove_callback(mesh, &qinfo, qflags, - &auth_zonemd_dnskey_lookup_callback, z); + &auth_zonemd_dnskey_lookup_callback, z, + z->zonemd_callback_unique_info); } /** Pick up the auth zone locks. */ diff --git a/doc/Changelog b/doc/Changelog index ac17fa191..e81c9aaf3 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -17,6 +17,10 @@ - Fix that misconfigured `iter-scrub-ns: 0` causes request failures. Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix that fast_reload when a zonemd verification lookup + it in progress with subnet loaded, deregisters the + callback. Thanks to Qifan Zhang, Palo Alto Networks, + for the report. 12 June 2026: Wouter - Fix that for auth-zone and rpz zones the allow-notify diff --git a/libunbound/context.h b/libunbound/context.h index c0fc80e57..090df345b 100644 --- a/libunbound/context.h +++ b/libunbound/context.h @@ -167,6 +167,8 @@ struct ctx_query { ub_event_callback_type cb_event; /** for async query, the callback user arg */ void* cb_arg; + /** for async query the unique info */ + void* unique_info; /** answer message, result from resolver lookup. */ uint8_t* msg; diff --git a/libunbound/libworker.c b/libunbound/libworker.c index c8692ba26..9ce747bce 100644 --- a/libunbound/libworker.c +++ b/libunbound/libworker.c @@ -642,7 +642,8 @@ int libworker_fg(struct ub_ctx* ctx, struct ctx_query* q) } /* process new query */ if(!mesh_new_callback(w->env->mesh, &qinfo, qflags, &edns, - w->back->udp_buff, qid, libworker_fg_done_cb, q, 0)) { + w->back->udp_buff, qid, libworker_fg_done_cb, q, 0, + &q->unique_info)) { free(qinfo.qname); return UB_NOMEM; } @@ -723,7 +724,8 @@ int libworker_attach_mesh(struct ub_ctx* ctx, struct ctx_query* q, if(async_id) *async_id = q->querynum; if(!mesh_new_callback(w->env->mesh, &qinfo, qflags, &edns, - w->back->udp_buff, qid, libworker_event_done_cb, q, 0)) { + w->back->udp_buff, qid, libworker_event_done_cb, q, 0, + &q->unique_info)) { free(qinfo.qname); return UB_NOMEM; } @@ -861,7 +863,8 @@ handle_newq(struct libworker* w, uint8_t* buf, uint32_t len) q->w = w; /* process new query */ if(!mesh_new_callback(w->env->mesh, &qinfo, qflags, &edns, - w->back->udp_buff, qid, libworker_bg_done_cb, q, 0)) { + w->back->udp_buff, qid, libworker_bg_done_cb, q, 0, + &q->unique_info)) { add_bg_result(w, q, NULL, UB_NOMEM, NULL, 0); } free(qinfo.qname); diff --git a/services/authzone.c b/services/authzone.c index 3d31e4674..f7224a3df 100644 --- a/services/authzone.c +++ b/services/authzone.c @@ -5558,7 +5558,8 @@ xfr_transfer_lookup_host(struct auth_xfer* xfr, struct module_env* env) * called straight away */ lock_basic_unlock(&xfr->lock); if(!mesh_new_callback(env->mesh, &qinfo, qflags, &edns, buf, 0, - &auth_xfer_transfer_lookup_callback, xfr, 0)) { + &auth_xfer_transfer_lookup_callback, xfr, 0, + &xfr->task_transfer->lookup_unique_info)) { lock_basic_lock(&xfr->lock); log_err("out of memory lookup up master %s", master->host); return 0; @@ -6804,7 +6805,8 @@ xfr_probe_lookup_host(struct auth_xfer* xfr, struct module_env* env) * called straight away */ lock_basic_unlock(&xfr->lock); if(!mesh_new_callback(env->mesh, &qinfo, qflags, &edns, buf, 0, - &auth_xfer_probe_lookup_callback, xfr, 0)) { + &auth_xfer_probe_lookup_callback, xfr, 0, + &xfr->task_probe->lookup_unique_info)) { lock_basic_lock(&xfr->lock); log_err("out of memory lookup up master %s", master->host); return 0; @@ -8640,7 +8642,8 @@ zonemd_lookup_dnskey(struct auth_zone* z, struct module_env* env) /* the callback can be called straight away */ lock_rw_unlock(&z->lock); if(!mesh_new_callback(env->mesh, &qinfo, qflags, &edns, buf, 0, - &auth_zonemd_dnskey_lookup_callback, z, 0)) { + &auth_zonemd_dnskey_lookup_callback, z, 0, + &z->zonemd_callback_unique_info)) { lock_rw_wrlock(&z->lock); log_err("out of memory lookup of %s for zonemd", (fetch_ds?"DS":"DNSKEY")); diff --git a/services/authzone.h b/services/authzone.h index 23a6b0f93..b55b711ae 100644 --- a/services/authzone.h +++ b/services/authzone.h @@ -144,6 +144,8 @@ struct auth_zone { struct module_env* zonemd_callback_env; /** for the zonemd callback, the type of data looked up */ uint16_t zonemd_callback_qtype; + /** for the zonemd callback, the unique info */ + void* zonemd_callback_unique_info; /** zone has been deleted */ int zone_deleted; /** deletelist pointer, unused normally except during delete */ @@ -344,6 +346,8 @@ struct auth_probe { /** for the hostname lookups, which master is current */ struct auth_master* lookup_target; + /** for the lookup, the callback unique info */ + void* lookup_unique_info; /** are we looking up A or AAAA, first A, then AAAA (if ip6 enabled) */ int lookup_aaaa; /** we only want to do lookups for making config work (for notify), @@ -402,6 +406,8 @@ struct auth_transfer { /** for the hostname lookups, which master is current */ struct auth_master* lookup_target; + /** for the lookup, the callback unique info */ + void* lookup_unique_info; /** are we looking up A or AAAA, first A, then AAAA (if ip6 enabled) */ int lookup_aaaa; diff --git a/services/mesh.c b/services/mesh.c index 5cb2cba4b..2347bcee2 100644 --- a/services/mesh.c +++ b/services/mesh.c @@ -662,7 +662,8 @@ servfail_mem: int mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo, uint16_t qflags, struct edns_data* edns, sldns_buffer* buf, - uint16_t qid, mesh_cb_func_type cb, void* cb_arg, int rpz_passthru) + uint16_t qid, mesh_cb_func_type cb, void* cb_arg, int rpz_passthru, + void** unique_info) { struct mesh_state* s = NULL; int unique = unique_mesh_state(edns->opt_list_in, mesh->env); @@ -751,6 +752,8 @@ mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo, mesh->num_reply_states ++; } mesh->num_reply_addrs++; + if(unique_info) + *unique_info = s->unique; if(added) mesh_run(mesh, s, module_event_new, NULL); return 1; @@ -1969,6 +1972,25 @@ struct mesh_state* mesh_area_find(struct mesh_area* mesh, return result; } +struct mesh_state* mesh_area_find_unique(struct mesh_area* mesh, + struct respip_client_info* cinfo, struct query_info* qinfo, + uint16_t qflags, int prime, int valrec, void* unique_info) +{ + struct mesh_state key; + struct mesh_state* result; + + key.node.key = &key; + key.s.is_priming = prime; + key.s.is_valrec = valrec; + key.s.qinfo = *qinfo; + key.s.query_flags = qflags; + key.unique = (struct mesh_state*)unique_info; + key.s.client_info = cinfo; + + result = (struct mesh_state*)rbtree_search(&mesh->all, &key); + return result; +} + /** remove mesh state callback */ int mesh_state_del_cb(struct mesh_state* s, mesh_cb_func_type cb, void* cb_arg) { @@ -2700,13 +2722,30 @@ int mesh_jostle_exceeded(struct mesh_area* mesh) } void mesh_remove_callback(struct mesh_area* mesh, struct query_info* qinfo, - uint16_t qflags, mesh_cb_func_type cb, void* cb_arg) + uint16_t qflags, mesh_cb_func_type cb, void* cb_arg, void* unique_info) { struct mesh_state* s = NULL; s = mesh_area_find(mesh, NULL, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0); - if(!s) return; - if(!mesh_state_del_cb(s, cb, cb_arg)) return; + if(s && mesh_state_del_cb(s, cb, cb_arg)) + goto removed; + if(unique_info) { + s = mesh_area_find_unique(mesh, NULL, qinfo, + qflags&(BIT_RD|BIT_CD), 0, 0, unique_info); + if(s && mesh_state_del_cb(s, cb, cb_arg)) + goto removed; + } + /* mesh_area_find builds key.unique=NULL and cannot match a state + * created with mesh_state_make_unique (e.g. subnetcache sets + * env->unique_mesh). Fall back to a linear scan; cb+cb_arg is an + * exact key (mesh_state_del_cb compares both). + * This works for both lookups for zonemd and for hostname authzone. */ + RBTREE_FOR(s, struct mesh_state*, &mesh->all) { + if(s->cb_list && mesh_state_del_cb(s, cb, cb_arg)) + goto removed; + } + return; +removed: /* It was in the list and removed. */ log_assert(mesh->num_reply_addrs > 0); mesh->num_reply_addrs--; diff --git a/services/mesh.h b/services/mesh.h index 8f8b8a169..77b3c7025 100644 --- a/services/mesh.h +++ b/services/mesh.h @@ -342,11 +342,14 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo, * @param cb_arg: callback user arg. * @param rpz_passthru: if true, the rpz passthru was previously found and * further rpz processing is stopped. + * @param unique_info: if nonnull, unique info is passed back to be used + * for the callback remove call. It does not need to be deallocated. * @return 0 on error. */ int mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo, uint16_t qflags, struct edns_data* edns, struct sldns_buffer* buf, - uint16_t qid, mesh_cb_func_type cb, void* cb_arg, int rpz_passthru); + uint16_t qid, mesh_cb_func_type cb, void* cb_arg, int rpz_passthru, + void** unique_info); /** * New prefetch message. Create new query state if needed. @@ -543,6 +546,23 @@ struct mesh_state* mesh_area_find(struct mesh_area* mesh, struct respip_client_info* cinfo, struct query_info* qinfo, uint16_t qflags, int prime, int valrec); +/** + * Find a unique mesh state in the mesh area. Pass relevant flags. + * + * @param mesh: the mesh area to look in. + * @param cinfo: if non-NULL client specific info that may affect IP-based + * actions that apply to the query result. + * @param qinfo: what query + * @param qflags: if RD / CD bit is set or not. + * @param prime: if it is a priming query. + * @param valrec: if it is a validation-recursion query. + * @param unique_info: the unique info for the state. NULL can be passed. + * @return: mesh state or NULL if not found. + */ +struct mesh_state* mesh_area_find_unique(struct mesh_area* mesh, + struct respip_client_info* cinfo, struct query_info* qinfo, + uint16_t qflags, int prime, int valrec, void* unique_info); + /** * Setup attachment super/sub relation between super and sub mesh state. * The relation must not be present when calling the function. @@ -736,8 +756,9 @@ void mesh_respond_serve_expired(struct mesh_state* mstate); * @param qflags: flags from client query. * @param cb: callback function. * @param cb_arg: callback user arg. + * @param unique_info: if not NULL, used to find a unique state for removal. */ void mesh_remove_callback(struct mesh_area* mesh, struct query_info* qinfo, - uint16_t qflags, mesh_cb_func_type cb, void* cb_arg); + uint16_t qflags, mesh_cb_func_type cb, void* cb_arg, void* unique_info); #endif /* SERVICES_MESH_H */ diff --git a/validator/autotrust.c b/validator/autotrust.c index 078007da5..4ee105371 100644 --- a/validator/autotrust.c +++ b/validator/autotrust.c @@ -2431,7 +2431,7 @@ probe_anchor(struct module_env* env, struct trust_anchor* tp) qinfo.qclass); if(!mesh_new_callback(env->mesh, &qinfo, qflags, &edns, buf, 0, - &probe_answer_cb, env, 0)) { + &probe_answer_cb, env, 0, NULL)) { log_err("out of memory making 5011 probe"); } } -- 2.47.3