Merge branch 'branch-1.23.1'

This commit is contained in:
W.C.A. Wijngaards 2025-07-16 11:34:44 +02:00
commit ad0e0fc290
2 changed files with 142 additions and 14 deletions

View file

@ -51,6 +51,7 @@
#include "services/cache/dns.h"
#include "util/module.h"
#include "util/regional.h"
#include "util/fptr_wlist.h"
#include "util/storage/slabhash.h"
#include "util/config_file.h"
#include "util/data/msgreply.h"
@ -155,7 +156,8 @@ int ecs_whitelist_check(struct query_info* qinfo,
/* Cache by default, might be disabled after parsing EDNS option
* received from nameserver. */
if(!iter_stub_fwd_no_cache(qstate, &qstate->qinfo, NULL, NULL, NULL, 0)) {
if(!iter_stub_fwd_no_cache(qstate, &qstate->qinfo, NULL, NULL, NULL, 0)
&& sq->ecs_client_in.subnet_validdata) {
qstate->no_cache_store = 0;
}
@ -522,6 +524,69 @@ common_prefix(uint8_t *a, uint8_t *b, uint8_t net)
return !memcmp(a, b, n) && ((net % 8) == 0 || a[n] == b[n]);
}
/**
* Create sub request that looks up the query.
* @param qstate: query state
* @param sq: subnet qstate
* @return false on failure.
*/
static int
generate_sub_request(struct module_qstate *qstate, struct subnet_qstate* sq)
{
struct module_qstate* subq = NULL;
uint16_t qflags = 0; /* OPCODE QUERY, no flags */
int prime = 0;
int valrec = 0;
struct query_info qinf;
qinf.qname = qstate->qinfo.qname;
qinf.qname_len = qstate->qinfo.qname_len;
qinf.qtype = qstate->qinfo.qtype;
qinf.qclass = qstate->qinfo.qclass;
qinf.local_alias = NULL;
qflags |= BIT_RD;
if((qstate->query_flags & BIT_CD)!=0) {
qflags |= BIT_CD;
valrec = 1;
}
fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub));
if(!(*qstate->env->attach_sub)(qstate, &qinf, qflags, prime, valrec,
&subq)) {
return 0;
}
if(subq) {
/* It is possible to access the subquery module state. */
if(sq->ecs_client_in.subnet_source_mask == 0 &&
edns_opt_list_find(qstate->edns_opts_front_in,
qstate->env->cfg->client_subnet_opcode)) {
subq->no_cache_store = 1;
}
}
return 1;
}
/**
* Perform the query without subnet
* @param qstate: query state
* @param sq: subnet qstate
* @return module state
*/
static enum module_ext_state
generate_lookup_without_subnet(struct module_qstate *qstate,
struct subnet_qstate* sq)
{
verbose(VERB_ALGO, "subnetcache: make subquery to look up without subnet");
if(!generate_sub_request(qstate, sq)) {
verbose(VERB_ALGO, "Could not generate sub query");
qstate->return_rcode = LDNS_RCODE_FORMERR;
qstate->return_msg = NULL;
return module_finished;
}
sq->wait_subquery = 1;
return module_wait_subquery;
}
static enum module_ext_state
eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq)
{
@ -557,14 +622,7 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq)
* is still useful to put it in the edns subnet cache for
* when a client explicitly asks for subnet specific answer. */
verbose(VERB_QUERY, "subnetcache: Authority indicates no support");
if(!sq->started_no_cache_store) {
lock_rw_wrlock(&sne->biglock);
update_cache(qstate, id);
lock_rw_unlock(&sne->biglock);
}
if (sq->subnet_downstream)
cp_edns_bad_response(c_out, c_in);
return module_finished;
return generate_lookup_without_subnet(qstate, sq);
}
/* Purposefully there was no sent subnet, and there is consequently
@ -589,14 +647,14 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq)
!common_prefix(s_out->subnet_addr, s_in->subnet_addr,
s_out->subnet_source_mask))
{
/* we can not accept, restart query without option */
/* we can not accept, perform query without option */
verbose(VERB_QUERY, "subnetcache: forged data");
s_out->subnet_validdata = 0;
(void)edns_opt_list_remove(&qstate->edns_opts_back_out,
qstate->env->cfg->client_subnet_opcode);
sq->subnet_sent = 0;
sq->subnet_sent_no_subnet = 0;
return module_restart_next;
return generate_lookup_without_subnet(qstate, sq);
}
lock_rw_wrlock(&sne->biglock);
@ -795,6 +853,9 @@ ecs_edns_back_parsed(struct module_qstate* qstate, int id,
} else if(sq->subnet_sent_no_subnet) {
/* The answer can be stored as scope 0, not in global cache. */
qstate->no_cache_store = 1;
} else if(sq->subnet_sent) {
/* Need another query to be able to store in global cache. */
qstate->no_cache_store = 1;
}
return 1;
@ -812,6 +873,32 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event,
strmodulevent(event));
log_query_info(VERB_QUERY, "subnetcache operate: query", &qstate->qinfo);
if(sq && sq->wait_subquery_done) {
/* The subquery lookup returned. */
if(sq->ecs_client_in.subnet_source_mask == 0 &&
edns_opt_list_find(qstate->edns_opts_front_in,
qstate->env->cfg->client_subnet_opcode)) {
if(!sq->started_no_cache_store &&
qstate->return_msg) {
lock_rw_wrlock(&sne->biglock);
update_cache(qstate, id);
lock_rw_unlock(&sne->biglock);
}
if (sq->subnet_downstream)
cp_edns_bad_response(&sq->ecs_client_out,
&sq->ecs_client_in);
/* It is a scope zero lookup, append edns subnet
* option to the querier. */
subnet_ecs_opt_list_append(&sq->ecs_client_out,
&qstate->edns_opts_front_out, qstate,
qstate->region);
}
sq->wait_subquery_done = 0;
qstate->ext_state[id] = module_finished;
qstate->no_cache_store = sq->started_no_cache_store;
qstate->no_cache_lookup = sq->started_no_cache_lookup;
return;
}
if((event == module_event_new || event == module_event_pass) &&
sq == NULL) {
struct edns_option* ecs_opt;
@ -822,6 +909,8 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event,
}
sq = (struct subnet_qstate*)qstate->minfo[id];
if(sq->wait_subquery)
return; /* Wait for that subquery to return */
if((ecs_opt = edns_opt_list_find(
qstate->edns_opts_front_in,
@ -851,6 +940,14 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event,
/* No clients are interested in result or we could not
* parse it, we don't do client subnet */
sq->ecs_server_out.subnet_validdata = 0;
if(edns_opt_list_find(qstate->edns_opts_front_in,
qstate->env->cfg->client_subnet_opcode)) {
/* aggregated this deaggregated state */
qstate->ext_state[id] =
generate_lookup_without_subnet(
qstate, sq);
return;
}
verbose(VERB_ALGO, "subnetcache: pass to next module");
qstate->ext_state[id] = module_wait_module;
return;
@ -891,6 +988,14 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event,
}
lock_rw_unlock(&sne->biglock);
}
if(sq->ecs_client_in.subnet_source_mask == 0 &&
edns_opt_list_find(qstate->edns_opts_front_in,
qstate->env->cfg->client_subnet_opcode)) {
/* client asked for resolution without edns subnet */
qstate->ext_state[id] = generate_lookup_without_subnet(
qstate, sq);
return;
}
sq->ecs_server_out.subnet_addr_fam =
sq->ecs_client_in.subnet_addr_fam;
@ -927,6 +1032,8 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event,
qstate->ext_state[id] = module_wait_module;
return;
}
if(sq && sq->wait_subquery)
return; /* Wait for that subquery to return */
/* Query handed back by next module, we have a 'final' answer */
if(sq && event == module_event_moddone) {
qstate->ext_state[id] = eval_response(qstate, id, sq);
@ -975,10 +1082,27 @@ subnetmod_clear(struct module_qstate *ATTR_UNUSED(qstate),
}
void
subnetmod_inform_super(struct module_qstate *ATTR_UNUSED(qstate),
int ATTR_UNUSED(id), struct module_qstate *ATTR_UNUSED(super))
subnetmod_inform_super(struct module_qstate *qstate, int id,
struct module_qstate *super)
{
/* Not used */
struct subnet_qstate* super_sq =
(struct subnet_qstate*)super->minfo[id];
log_query_info(VERB_ALGO, "subnetcache inform_super: query",
&super->qinfo);
super_sq->wait_subquery = 0;
super_sq->wait_subquery_done = 1;
if(qstate->return_rcode != LDNS_RCODE_NOERROR ||
!qstate->return_msg) {
super->return_msg = NULL;
super->return_rcode = LDNS_RCODE_SERVFAIL;
return;
}
super->return_rcode = LDNS_RCODE_NOERROR;
super->return_msg = dns_copy_msg(qstate->return_msg, super->region);
if(!super->return_msg) {
log_err("subnetcache: copy response, out of memory");
super->return_rcode = LDNS_RCODE_SERVFAIL;
}
}
size_t

View file

@ -102,6 +102,10 @@ struct subnet_qstate {
int started_no_cache_store;
/** has the subnet module been started with no_cache_lookup? */
int started_no_cache_lookup;
/** Wait for subquery that has been started for nonsubnet lookup. */
int wait_subquery;
/** The subquery waited for is done. */
int wait_subquery_done;
};
void subnet_data_delete(void* d, void* ATTR_UNUSED(arg));