rep->flags |= (BIT_RA | BIT_QR); /* fix flags to be sensible for */
rep->flags &= ~(BIT_AA | BIT_CD);/* a reply based on the cache */
addrtree_insert(tree, (addrkey_t*)edns->subnet_addr,
- edns->subnet_source_mask,
- sq->ecs_server_in.subnet_scope_mask, rep,
+ edns->subnet_source_mask, sq->max_scope, rep,
rep->ttl, *qstate->env->now);
lock_rw_unlock(&lru_entry->lock);
c_out->subnet_addr_fam = c_in->subnet_addr_fam;
c_out->subnet_source_mask = c_in->subnet_source_mask;
memcpy(&c_out->subnet_addr, &c_in->subnet_addr, INET6_SIZE);
- c_out->subnet_scope_mask = s_in->subnet_scope_mask;
+ c_out->subnet_scope_mask = sq->max_scope;
/* Limit scope returned to client to scope used for caching. */
if(c_out->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4) {
if(c_out->subnet_scope_mask >
qstate->env->cfg->client_subnet_opcode);
sq->subnet_sent = 0;
memset(&sq->ecs_server_out, 0, sizeof(sq->ecs_server_out));
+ } else if (!sq->track_max_scope &&
+ FLAGS_GET_RCODE(response->rep->flags) == LDNS_RCODE_NOERROR &&
+ response->rep->an_numrrsets > 0
+ ) {
+ struct ub_packed_rrset_key* s = response->rep->rrsets[0];
+ if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME &&
+ query_dname_compare(qstate->qinfo.qname,
+ s->rk.dname) == 0) {
+ /* CNAME response for QNAME. From now on keep track of
+ * longest received ECS prefix for all queries on this
+ * qstate. */
+ sq->track_max_scope = 1;
+ }
}
return 1;
}
return 1;
if((ecs_opt = edns_opt_list_find(
qstate->edns_opts_back_in,
- qstate->env->cfg->client_subnet_opcode))) {
- if(parse_subnet_option(ecs_opt, &sq->ecs_server_in) &&
- sq->subnet_sent &&
- sq->ecs_server_in.subnet_validdata)
+ qstate->env->cfg->client_subnet_opcode)) &&
+ parse_subnet_option(ecs_opt, &sq->ecs_server_in) &&
+ sq->subnet_sent && sq->ecs_server_in.subnet_validdata) {
/* Only skip global cache store if we sent an ECS option
* and received one back. Answers from non-whitelisted
* servers will end up in global cache. Answers for
* queries with 0 source will not (unless nameserver
* does not support ECS). */
qstate->no_cache_store = 1;
+ if(!sq->track_max_scope || (sq->track_max_scope &&
+ sq->ecs_server_in.subnet_scope_mask >
+ sq->max_scope))
+ sq->max_scope = sq->ecs_server_in.subnet_scope_mask;
}
return 1;
#include "util/alloc.h"
#include "util/net_help.h"
#include "util/storage/slabhash.h"
+#include "util/data/dname.h"
#include "edns-subnet/addrtree.h"
#include "edns-subnet/edns-subnet.h"
struct ecs_data ecs_server_out;
int subnet_downstream;
int subnet_sent;
+ /** keep track of longest received scope, set after receiving CNAME for
+ * incoming QNAME. */
+ int track_max_scope;
+ /** longest received scope mask since track_max_scope is set. This value
+ * is used for caching and answereing to client. */
+ uint8_t max_scope;
/** has the subnet module been started with no_cache_store? */
int started_no_cache_store;
};