From: W.C.A. Wijngaards Date: Wed, 27 May 2026 10:16:23 +0000 (+0200) Subject: - Fix that the ratelimit is decremented on successful X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=758c649611dbaae89964b4fb21c7ab7eea39c927;p=thirdparty%2Funbound.git - Fix that the ratelimit is decremented on successful referrals. Thanks to Qifan Zhang, Palo Alto Networks, for the report. --- diff --git a/daemon/worker.c b/daemon/worker.c index a5dd9bc02..8c695e1dc 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -2535,7 +2535,8 @@ worker_send_query(struct query_info* qinfo, uint16_t flags, int dnssec, int want_dnssec, int nocaps, int check_ratelimit, struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone, size_t zonelen, int tcp_upstream, int ssl_upstream, char* tls_auth_name, - struct module_qstate* q, int* was_ratelimited) + struct module_qstate* q, int* was_ratelimited, + int* ratelimit_incremented) { struct worker* worker = q->env->worker; struct outbound_entry* e = (struct outbound_entry*)regional_alloc( @@ -2547,7 +2548,7 @@ worker_send_query(struct query_info* qinfo, uint16_t flags, int dnssec, want_dnssec, nocaps, check_ratelimit, tcp_upstream, ssl_upstream, tls_auth_name, addr, addrlen, zone, zonelen, q, worker_handle_service_reply, e, worker->back->udp_buff, q->env, - was_ratelimited); + was_ratelimited, ratelimit_incremented); if(!e->qsent) { return NULL; } @@ -2596,7 +2597,8 @@ struct outbound_entry* libworker_send_query( struct sockaddr_storage* ATTR_UNUSED(addr), socklen_t ATTR_UNUSED(addrlen), uint8_t* ATTR_UNUSED(zone), size_t ATTR_UNUSED(zonelen), int ATTR_UNUSED(tcp_upstream), int ATTR_UNUSED(ssl_upstream), char* ATTR_UNUSED(tls_auth_name), - struct module_qstate* ATTR_UNUSED(q), int* ATTR_UNUSED(was_ratelimited)) + struct module_qstate* ATTR_UNUSED(q), int* ATTR_UNUSED(was_ratelimited), + int* ATTR_UNUSED(ratelimit_incremented)) { log_assert(0); return 0; diff --git a/dnstap/unbound-dnstap-socket.c b/dnstap/unbound-dnstap-socket.c index 90b0f6003..d6d5e2a7e 100644 --- a/dnstap/unbound-dnstap-socket.c +++ b/dnstap/unbound-dnstap-socket.c @@ -1659,7 +1659,8 @@ struct outbound_entry* worker_send_query( socklen_t ATTR_UNUSED(addrlen), uint8_t* ATTR_UNUSED(zone), size_t ATTR_UNUSED(zonelen), int ATTR_UNUSED(tcp_upstream), int ATTR_UNUSED(ssl_upstream), char* ATTR_UNUSED(tls_auth_name), - struct module_qstate* ATTR_UNUSED(q), int* ATTR_UNUSED(was_ratelimited)) + struct module_qstate* ATTR_UNUSED(q), int* ATTR_UNUSED(was_ratelimited), + int* ATTR_UNUSED(ratelimit_incremented)) { log_assert(0); return 0; @@ -1693,7 +1694,8 @@ struct outbound_entry* libworker_send_query( socklen_t ATTR_UNUSED(addrlen), uint8_t* ATTR_UNUSED(zone), size_t ATTR_UNUSED(zonelen), int ATTR_UNUSED(tcp_upstream), int ATTR_UNUSED(ssl_upstream), char* ATTR_UNUSED(tls_auth_name), - struct module_qstate* ATTR_UNUSED(q), int* ATTR_UNUSED(was_ratelimited)) + struct module_qstate* ATTR_UNUSED(q), int* ATTR_UNUSED(was_ratelimited), + int* ATTR_UNUSED(ratelimit_incremented)) { log_assert(0); return 0; diff --git a/doc/Changelog b/doc/Changelog index 5e398a34f..186a5cd8d 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -4,6 +4,9 @@ for the report. - Fix to limit the DSNS per-label walk in the iterator. Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix that the ratelimit is decremented on successful + referrals. Thanks to Qifan Zhang, Palo Alto Networks, for + the report. 26 May 2026: Wouter - Fix for mesh new client and mesh new callback to rollback the diff --git a/iterator/iterator.c b/iterator/iterator.c index ae54da0d9..25b4401bf 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -3054,7 +3054,9 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, /* 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); + sq_check_ratelimit = ((!(iq->chase_flags & BIT_RD) && + !iq->ratelimit_ok)); + iq->ratelimit_incremented = 0; /* We have a valid target. */ if(verbosity >= VERB_QUERY) { log_query_info(VERB_QUERY, "sending query:", &iq->qinfo_out); @@ -3080,7 +3082,8 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, iq->dp->name, iq->dp->namelen, (iq->dp->tcp_upstream || qstate->env->cfg->tcp_upstream), (iq->dp->ssl_upstream || qstate->env->cfg->ssl_upstream), - target->tls_auth_name, qstate, &sq_was_ratelimited); + target->tls_auth_name, qstate, &sq_was_ratelimited, + &iq->ratelimit_incremented); if(!outq) { if(sq_was_ratelimited) { lock_basic_lock(&ie->queries_ratelimit_lock); @@ -3439,6 +3442,12 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, iq->deleg_msg = iq->response; /* Keep current delegation point for label comparison */ old_dp = iq->dp; + /* A referral reply is "pleasant", refund the + * parent dp's rate charge before descending to the child. */ + if(iq->ratelimit_incremented) + infra_ratelimit_dec(qstate->env->infra_cache, + old_dp->name, old_dp->namelen, + *qstate->env->now); iq->dp = delegpt_from_message(iq->response, qstate->region); if (qstate->env->cfg->qname_minimisation) iq->minimisation_state = INIT_MINIMISE_STATE; diff --git a/iterator/iterator.h b/iterator/iterator.h index 87e54d41e..df5449c52 100644 --- a/iterator/iterator.h +++ b/iterator/iterator.h @@ -380,6 +380,10 @@ struct iter_qstate { /** if true, already tested for ratelimiting and passed the test */ int ratelimit_ok; + /** If the last query, that may be a referral, incremented the + * ratelimit counter. */ + int ratelimit_incremented; + /** * The query must store NS records from referrals as parentside RRs * Enabled once it hits resolution problems, to throttle retries. diff --git a/libunbound/libworker.c b/libunbound/libworker.c index 6e7244c03..c8692ba26 100644 --- a/libunbound/libworker.c +++ b/libunbound/libworker.c @@ -879,7 +879,8 @@ struct outbound_entry* libworker_send_query(struct query_info* qinfo, int check_ratelimit, struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone, size_t zonelen, int tcp_upstream, int ssl_upstream, char* tls_auth_name, - struct module_qstate* q, int* was_ratelimited) + struct module_qstate* q, int* was_ratelimited, + int* ratelimit_incremented) { struct libworker* w = (struct libworker*)q->env->worker; struct outbound_entry* e = (struct outbound_entry*)regional_alloc( @@ -891,7 +892,7 @@ struct outbound_entry* libworker_send_query(struct query_info* qinfo, want_dnssec, nocaps, check_ratelimit, tcp_upstream, ssl_upstream, tls_auth_name, addr, addrlen, zone, zonelen, q, libworker_handle_service_reply, e, w->back->udp_buff, q->env, - was_ratelimited); + was_ratelimited, ratelimit_incremented); if(!e->qsent) { return NULL; } @@ -976,7 +977,8 @@ struct outbound_entry* worker_send_query(struct query_info* ATTR_UNUSED(qinfo), struct sockaddr_storage* ATTR_UNUSED(addr), socklen_t ATTR_UNUSED(addrlen), uint8_t* ATTR_UNUSED(zone), size_t ATTR_UNUSED(zonelen), int ATTR_UNUSED(tcp_upstream), int ATTR_UNUSED(ssl_upstream), char* ATTR_UNUSED(tls_auth_name), - struct module_qstate* ATTR_UNUSED(q), int* ATTR_UNUSED(was_ratelimited)) + struct module_qstate* ATTR_UNUSED(q), int* ATTR_UNUSED(was_ratelimited), + int* ATTR_UNUSED(ratelimit_incremented)) { log_assert(0); return 0; diff --git a/libunbound/worker.h b/libunbound/worker.h index 0fa5bfa99..9ca4b2d96 100644 --- a/libunbound/worker.h +++ b/libunbound/worker.h @@ -70,6 +70,8 @@ struct query_info; * @param q: which query state to reactivate upon return. * @param was_ratelimited: it will signal back if the query failed to pass the * ratelimit check. + * @param ratelimit_incremented: set to true if the ratelimit counter + * was increased. * @return: false on failure (memory or socket related). no query was * sent. */ @@ -78,7 +80,8 @@ struct outbound_entry* libworker_send_query(struct query_info* qinfo, int check_ratelimit, struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone, size_t zonelen, int tcp_upstream, int ssl_upstream, char* tls_auth_name, - struct module_qstate* q, int* was_ratelimited); + struct module_qstate* q, int* was_ratelimited, + int* ratelimit_incremented); /** process incoming serviced query replies from the network */ int libworker_handle_service_reply(struct comm_point* c, void* arg, int error, @@ -126,6 +129,8 @@ void worker_sighandler(int sig, void* arg); * @param q: which query state to reactivate upon return. * @param was_ratelimited: it will signal back if the query failed to pass the * ratelimit check. + * @param ratelimit_incremented: set to true if the ratelimit counter + * was increased. * @return: false on failure (memory or socket related). no query was * sent. */ @@ -134,7 +139,8 @@ struct outbound_entry* worker_send_query(struct query_info* qinfo, int check_ratelimit, struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone, size_t zonelen, int tcp_upstream, int ssl_upstream, char* tls_auth_name, - struct module_qstate* q, int* was_ratelimited); + struct module_qstate* q, int* was_ratelimited, + int* ratelimit_incremented); /** * process control messages from the main thread. Frees the control diff --git a/pythonmod/interface.i b/pythonmod/interface.i index 7ac868df5..735f2ed50 100644 --- a/pythonmod/interface.i +++ b/pythonmod/interface.i @@ -729,7 +729,8 @@ struct module_env { int check_ratelimit, struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone, size_t zonelen, int tcp_upstream, int ssl_upstream, - char* tls_auth_name, struct module_qstate* q, int* was_ratelimited); + char* tls_auth_name, struct module_qstate* q, int* was_ratelimited, + int* ratelimit_incremented); void (*detach_subs)(struct module_qstate* qstate); int (*attach_sub)(struct module_qstate* qstate, struct query_info* qinfo, struct respip_client_info* cinfo, diff --git a/services/outside_network.c b/services/outside_network.c index be65c6114..38598ed48 100644 --- a/services/outside_network.c +++ b/services/outside_network.c @@ -3490,7 +3490,8 @@ outnet_serviced_query(struct outside_network* outnet, char* tls_auth_name, struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone, size_t zonelen, struct module_qstate* qstate, comm_point_callback_type* callback, void* callback_arg, - sldns_buffer* buff, struct module_env* env, int* was_ratelimited) + sldns_buffer* buff, struct module_env* env, int* was_ratelimited, + int* ratelimit_incremented) { struct serviced_query* sq; struct service_callback* cb; @@ -3562,6 +3563,7 @@ outnet_serviced_query(struct outside_network* outnet, "delegation point", zone, LDNS_RR_TYPE_NS, LDNS_RR_CLASS_IN); } + *ratelimit_incremented = 1; } /* make new serviced query entry */ sq = serviced_create(outnet, buff, dnssec, want_dnssec, nocaps, diff --git a/services/outside_network.h b/services/outside_network.h index 390d4befe..a2dd2c6d6 100644 --- a/services/outside_network.h +++ b/services/outside_network.h @@ -660,6 +660,8 @@ void pending_delete(struct outside_network* outnet, struct pending* p); * @param env: the module environment. * @param was_ratelimited: it will signal back if the query failed to pass the * ratelimit check. + * @param ratelimit_incremented: set to true if the ratelimit counter + * was increased. * @return 0 on error, or pointer to serviced query that is used to answer * this serviced query may be shared with other callbacks as well. */ @@ -669,7 +671,8 @@ struct serviced_query* outnet_serviced_query(struct outside_network* outnet, char* tls_auth_name, struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone, size_t zonelen, struct module_qstate* qstate, comm_point_callback_type* callback, void* callback_arg, - struct sldns_buffer* buff, struct module_env* env, int* was_ratelimited); + struct sldns_buffer* buff, struct module_env* env, int* was_ratelimited, + int* ratelimit_incremented); /** * Remove service query callback. diff --git a/smallapp/worker_cb.c b/smallapp/worker_cb.c index 92ebe386d..9807aac8e 100644 --- a/smallapp/worker_cb.c +++ b/smallapp/worker_cb.c @@ -102,7 +102,7 @@ struct outbound_entry* worker_send_query( socklen_t ATTR_UNUSED(addrlen), uint8_t* ATTR_UNUSED(zone), size_t ATTR_UNUSED(zonelen), int ATTR_UNUSED(tcp_upstream), int ATTR_UNUSED(ssl_upstream), char* ATTR_UNUSED(tls_auth_name), struct module_qstate* ATTR_UNUSED(q), - int* ATTR_UNUSED(was_ratelimited)) + int* ATTR_UNUSED(was_ratelimited), int* ATTR_UNUSED(ratelimit_incremented)) { log_assert(0); return 0; @@ -136,7 +136,7 @@ struct outbound_entry* libworker_send_query( socklen_t ATTR_UNUSED(addrlen), uint8_t* ATTR_UNUSED(zone), size_t ATTR_UNUSED(zonelen), int ATTR_UNUSED(tcp_upstream), int ATTR_UNUSED(ssl_upstream), char* ATTR_UNUSED(tls_auth_name), struct module_qstate* ATTR_UNUSED(q), - int* ATTR_UNUSED(was_ratelimited)) + int* ATTR_UNUSED(was_ratelimited), int* ATTR_UNUSED(ratelimit_incremented)) { log_assert(0); return 0; diff --git a/testcode/doqclient.c b/testcode/doqclient.c index 8a34ca31b..11064e486 100644 --- a/testcode/doqclient.c +++ b/testcode/doqclient.c @@ -2595,7 +2595,8 @@ struct outbound_entry* worker_send_query( socklen_t ATTR_UNUSED(addrlen), uint8_t* ATTR_UNUSED(zone), size_t ATTR_UNUSED(zonelen), int ATTR_UNUSED(tcp_upstream), int ATTR_UNUSED(ssl_upstream), char* ATTR_UNUSED(tls_auth_name), - struct module_qstate* ATTR_UNUSED(q), int* ATTR_UNUSED(was_ratelimited)) + struct module_qstate* ATTR_UNUSED(q), int* ATTR_UNUSED(was_ratelimited), + int* ATTR_UNUSED(ratelimit_incremented)) { log_assert(0); return 0; @@ -2629,7 +2630,8 @@ struct outbound_entry* libworker_send_query( socklen_t ATTR_UNUSED(addrlen), uint8_t* ATTR_UNUSED(zone), size_t ATTR_UNUSED(zonelen), int ATTR_UNUSED(tcp_upstream), int ATTR_UNUSED(ssl_upstream), char* ATTR_UNUSED(tls_auth_name), - struct module_qstate* ATTR_UNUSED(q), int* ATTR_UNUSED(was_ratelimited)) + struct module_qstate* ATTR_UNUSED(q), int* ATTR_UNUSED(was_ratelimited), + int* ATTR_UNUSED(ratelimit_incremented)) { log_assert(0); return 0; diff --git a/testcode/fake_event.c b/testcode/fake_event.c index ce439edd1..b457ba42e 100644 --- a/testcode/fake_event.c +++ b/testcode/fake_event.c @@ -1275,7 +1275,8 @@ struct serviced_query* outnet_serviced_query(struct outside_network* outnet, socklen_t addrlen, uint8_t* zone, size_t zonelen, struct module_qstate* qstate, comm_point_callback_type* callback, void* callback_arg, sldns_buffer* ATTR_UNUSED(buff), - struct module_env* env, int* ATTR_UNUSED(was_ratelimited)) + struct module_env* env, int* ATTR_UNUSED(was_ratelimited), + int* ATTR_UNUSED(ratelimit_incremented)) { struct replay_runtime* runtime = (struct replay_runtime*)outnet->base; struct fake_pending* pend = (struct fake_pending*)calloc(1, diff --git a/util/fptr_wlist.c b/util/fptr_wlist.c index a45134065..18a2fc11b 100644 --- a/util/fptr_wlist.c +++ b/util/fptr_wlist.c @@ -362,7 +362,7 @@ fptr_whitelist_modenv_send_query(struct outbound_entry* (*fptr)( int nocaps, int check_ratelimit, struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone, size_t zonelen, int tcp_upstream, int ssl_upstream, char* tls_auth_name, struct module_qstate* q, - int* was_ratelimited)) + int* was_ratelimited, int* ratelimit_incremented)) { if(fptr == &worker_send_query) return 1; else if(fptr == &libworker_send_query) return 1; diff --git a/util/fptr_wlist.h b/util/fptr_wlist.h index a1a75625d..f76d2c3a3 100644 --- a/util/fptr_wlist.h +++ b/util/fptr_wlist.h @@ -214,7 +214,7 @@ int fptr_whitelist_modenv_send_query(struct outbound_entry* (*fptr)( int nocaps, int check_ratelimit, struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone, size_t zonelen, int tcp_upstream, int ssl_upstream, char* tls_auth_name, struct module_qstate* q, - int* was_ratelimited)); + int* was_ratelimited, int* ratelimit_incremented)); /** * Check function pointer whitelist for module_env detach_subs callback values. diff --git a/util/module.h b/util/module.h index a6fa6be90..631eb530b 100644 --- a/util/module.h +++ b/util/module.h @@ -375,6 +375,8 @@ struct module_env { * @param q: which query state to reactivate upon return. * @param was_ratelimited: it will signal back if the query failed to pass the * ratelimit check. + * @param ratelimit_incremented: set to true if the ratelimit counter + * was increased. * @return: false on failure (memory or socket related). no query was * sent. Or returns an outbound entry with qsent and qstate set. * This outbound_entry will be used on later module invocations @@ -385,7 +387,8 @@ struct module_env { int check_ratelimit, struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone, size_t zonelen, int tcp_upstream, int ssl_upstream, - char* tls_auth_name, struct module_qstate* q, int* was_ratelimited); + char* tls_auth_name, struct module_qstate* q, int* was_ratelimited, + int* ratelimit_incremented); /** * Detach-subqueries.