Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove child delegations from cache when grandchild delegations are returned from parent #1053

Merged
merged 1 commit into from
Apr 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions iterator/iterator.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
#include "iterator/iter_priv.h"
#include "validator/val_neg.h"
#include "services/cache/dns.h"
#include "services/cache/rrset.h"
#include "services/cache/infra.h"
#include "services/authzone.h"
#include "util/module.h"
Expand Down Expand Up @@ -3255,6 +3256,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
}
return final_state(iq);
} else if(type == RESPONSE_TYPE_REFERRAL) {
struct delegpt* old_dp = NULL;
/* REFERRAL type responses get a reset of the
* delegation point, and back to the QUERYTARGETS_STATE. */
verbose(VERB_DETAIL, "query response was REFERRAL");
Expand Down Expand Up @@ -3306,13 +3308,29 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
/* Reset the event state, setting the current delegation
* point to the referral. */
iq->deleg_msg = iq->response;
/* Keep current delegation point for label comparison */
old_dp = iq->dp;
iq->dp = delegpt_from_message(iq->response, qstate->region);
if (qstate->env->cfg->qname_minimisation)
iq->minimisation_state = INIT_MINIMISE_STATE;
if(!iq->dp) {
errinf(qstate, "malloc failure, for delegation point");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
if(old_dp->namelabs + 1 < iq->dp->namelabs) {
/* We got a grandchild delegation (more than one label
* difference) than expected. Check for in-between
* delegations in the cache and remove them.
* They could prove problematic when they expire
* and rrset_expired_above() encounters them during
* delegation cache lookups. */
uint8_t* qname = iq->dp->name;
size_t qnamelen = iq->dp->namelen;
rrset_cache_remove_above(qstate->env->rrset_cache,
&qname, &qnamelen, LDNS_RR_TYPE_NS,
iq->qchase.qclass, *qstate->env->now,
old_dp->name, old_dp->namelen);
}
if(!cache_fill_missing(qstate->env, iq->qchase.qclass,
qstate->region, iq->dp)) {
errinf(qstate, "malloc failure, copy extra info into delegation point");
Expand Down
52 changes: 6 additions & 46 deletions services/cache/dns.c
Original file line number Diff line number Diff line change
Expand Up @@ -193,46 +193,6 @@ dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
slabhash_insert(env->msg_cache, hash, &e->entry, rep, env->alloc);
}

/** see if an rrset is expired above the qname, return upper qname. */
static int
rrset_expired_above(struct module_env* env, uint8_t** qname, size_t* qnamelen,
uint16_t searchtype, uint16_t qclass, time_t now, uint8_t* expiretop,
size_t expiretoplen)
{
struct ub_packed_rrset_key *rrset;
uint8_t lablen;

while(*qnamelen > 0) {
/* look one label higher */
lablen = **qname;
*qname += lablen + 1;
*qnamelen -= lablen + 1;
if(*qnamelen <= 0)
break;

/* looks up with a time of 0, to see expired entries */
if((rrset = rrset_cache_lookup(env->rrset_cache, *qname,
*qnamelen, searchtype, qclass, 0, 0, 0))) {
struct packed_rrset_data* data =
(struct packed_rrset_data*)rrset->entry.data;
if(now > data->ttl) {
/* it is expired, this is not wanted */
lock_rw_unlock(&rrset->entry.lock);
log_nametypeclass(VERB_ALGO, "this rrset is expired", *qname, searchtype, qclass);
return 1;
}
/* it is not expired, continue looking */
lock_rw_unlock(&rrset->entry.lock);
}

/* do not look above the expiretop. */
if(expiretop && *qnamelen == expiretoplen &&
query_dname_compare(*qname, expiretop)==0)
break;
}
return 0;
}

/** find closest NS or DNAME and returns the rrset (locked) */
static struct ub_packed_rrset_key*
find_closest_of_type(struct module_env* env, uint8_t* qname, size_t qnamelen,
Expand Down Expand Up @@ -266,12 +226,12 @@ find_closest_of_type(struct module_env* env, uint8_t* qname, size_t qnamelen,
/* check for expiry, but we have to let go of the rrset
* for the lock ordering */
lock_rw_unlock(&rrset->entry.lock);
/* the expired_above function always takes off one
* label (if qnamelen>0) and returns the final qname
* where it searched, so we can continue from there
* turning the O N*N search into O N. */
if(!rrset_expired_above(env, &qname, &qnamelen,
searchtype, qclass, now, expiretop,
/* the rrset_cache_expired_above function always takes
* off one label (if qnamelen>0) and returns the final
* qname where it searched, so we can continue from
* there turning the O N*N search into O N. */
if(!rrset_cache_expired_above(env->rrset_cache, &qname,
&qnamelen, searchtype, qclass, now, expiretop,
expiretoplen)) {
/* we want to return rrset, but it may be
* gone from cache, if so, just loop like
Expand Down
84 changes: 84 additions & 0 deletions services/cache/rrset.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
#include "util/data/packed_rrset.h"
#include "util/data/msgreply.h"
#include "util/data/msgparse.h"
#include "util/data/dname.h"
#include "util/regional.h"
#include "util/alloc.h"
#include "util/net_help.h"
Expand Down Expand Up @@ -443,6 +444,89 @@ rrset_check_sec_status(struct rrset_cache* r,
lock_rw_unlock(&e->lock);
}

void
rrset_cache_remove_above(struct rrset_cache* r, uint8_t** qname, size_t*
qnamelen, uint16_t searchtype, uint16_t qclass, time_t now, uint8_t*
qnametop, size_t qnametoplen)
{
struct ub_packed_rrset_key *rrset;
uint8_t lablen;

while(*qnamelen > 0) {
/* look one label higher */
lablen = **qname;
*qname += lablen + 1;
*qnamelen -= lablen + 1;
if(*qnamelen <= 0)
return;

/* stop at qnametop */
if(qnametop && *qnamelen == qnametoplen &&
query_dname_compare(*qname, qnametop)==0)
return;

if(verbosity >= VERB_ALGO) {
/* looks up with a time of 0, to see expired entries */
if((rrset = rrset_cache_lookup(r, *qname,
*qnamelen, searchtype, qclass, 0, 0, 0))) {
struct packed_rrset_data* data =
(struct packed_rrset_data*)rrset->entry.data;
int expired = (now > data->ttl);
lock_rw_unlock(&rrset->entry.lock);
if(expired)
log_nametypeclass(verbosity, "this "
"(grand)parent rrset will be "
"removed (expired)",
*qname, searchtype, qclass);
else log_nametypeclass(verbosity, "this "
"(grand)parent rrset will be "
"removed",
*qname, searchtype, qclass);
}
}
rrset_cache_remove(r, *qname, *qnamelen, searchtype, qclass, 0);
}
}

int
rrset_cache_expired_above(struct rrset_cache* r, uint8_t** qname, size_t*
qnamelen, uint16_t searchtype, uint16_t qclass, time_t now, uint8_t*
qnametop, size_t qnametoplen)
{
struct ub_packed_rrset_key *rrset;
uint8_t lablen;

while(*qnamelen > 0) {
/* look one label higher */
lablen = **qname;
*qname += lablen + 1;
*qnamelen -= lablen + 1;
if(*qnamelen <= 0)
break;

/* looks up with a time of 0, to see expired entries */
if((rrset = rrset_cache_lookup(r, *qname,
*qnamelen, searchtype, qclass, 0, 0, 0))) {
struct packed_rrset_data* data =
(struct packed_rrset_data*)rrset->entry.data;
if(now > data->ttl) {
/* it is expired, this is not wanted */
lock_rw_unlock(&rrset->entry.lock);
log_nametypeclass(VERB_ALGO, "this rrset is expired", *qname, searchtype, qclass);
return 1;
}
/* it is not expired, continue looking */
lock_rw_unlock(&rrset->entry.lock);
}

/* do not look above the qnametop. */
if(qnametop && *qnamelen == qnametoplen &&
query_dname_compare(*qname, qnametop)==0)
break;
}
return 0;
}

void rrset_cache_remove(struct rrset_cache* r, uint8_t* nm, size_t nmlen,
uint16_t type, uint16_t dclass, uint32_t flags)
{
Expand Down
31 changes: 31 additions & 0 deletions services/cache/rrset.h
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,37 @@ void rrset_update_sec_status(struct rrset_cache* r,
void rrset_check_sec_status(struct rrset_cache* r,
struct ub_packed_rrset_key* rrset, time_t now);

/**
* Removes rrsets above the qname, returns upper qname.
* @param r: the rrset cache.
* @param qname: the start qname, also used as the output.
* @param qnamelen: length of qname, updated when it returns.
* @param searchtype: qtype to search for.
* @param qclass: qclass to search for.
* @param now: current time.
* @param qnametop: the top qname to stop removal (it is not removed).
* @param qnametoplen: length of qnametop.
*/
void rrset_cache_remove_above(struct rrset_cache* r, uint8_t** qname,
size_t* qnamelen, uint16_t searchtype, uint16_t qclass, time_t now,
uint8_t* qnametop, size_t qnametoplen);

/**
* Sees if an rrset is expired above the qname, returns upper qname.
* @param r: the rrset cache.
* @param qname: the start qname, also used as the output.
* @param qnamelen: length of qname, updated when it returns.
* @param searchtype: qtype to search for.
* @param qclass: qclass to search for.
* @param now: current time.
* @param qnametop: the top qname, don't look farther than that.
* @param qnametoplen: length of qnametop.
* @return true if there is an expired rrset above, false otherwise.
*/
int rrset_cache_expired_above(struct rrset_cache* r, uint8_t** qname,
size_t* qnamelen, uint16_t searchtype, uint16_t qclass, time_t now,
uint8_t* qnametop, size_t qnametoplen);

/**
* Remove an rrset from the cache, by name and type and flags
* @param r: rrset cache
Expand Down
Loading