Skip to content

Commit 3ec74d1

Browse files
committed
- When a granchild delegation is returned, remove any cached child delegations
up to parent to not cause delegation invalidation because of an expired child delegation that would never be updated. Most likely to happen without qname-minimisation. Reported by Roland van Rijswijk-Deij.
1 parent 52aff65 commit 3ec74d1

File tree

5 files changed

+395
-46
lines changed

5 files changed

+395
-46
lines changed

iterator/iterator.c

+18
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
#include "iterator/iter_priv.h"
5353
#include "validator/val_neg.h"
5454
#include "services/cache/dns.h"
55+
#include "services/cache/rrset.h"
5556
#include "services/cache/infra.h"
5657
#include "services/authzone.h"
5758
#include "util/module.h"
@@ -3255,6 +3256,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
32553256
}
32563257
return final_state(iq);
32573258
} else if(type == RESPONSE_TYPE_REFERRAL) {
3259+
struct delegpt* old_dp = NULL;
32583260
/* REFERRAL type responses get a reset of the
32593261
* delegation point, and back to the QUERYTARGETS_STATE. */
32603262
verbose(VERB_DETAIL, "query response was REFERRAL");
@@ -3306,13 +3308,29 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
33063308
/* Reset the event state, setting the current delegation
33073309
* point to the referral. */
33083310
iq->deleg_msg = iq->response;
3311+
/* Keep current delegation point for label comparison */
3312+
old_dp = iq->dp;
33093313
iq->dp = delegpt_from_message(iq->response, qstate->region);
33103314
if (qstate->env->cfg->qname_minimisation)
33113315
iq->minimisation_state = INIT_MINIMISE_STATE;
33123316
if(!iq->dp) {
33133317
errinf(qstate, "malloc failure, for delegation point");
33143318
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
33153319
}
3320+
if(old_dp->namelabs + 1 < iq->dp->namelabs) {
3321+
/* We got a grandchild delegation (more than one label
3322+
* difference) than expected. Check for in-between
3323+
* delegations in the cache and remove them.
3324+
* They could prove problematic when they expire
3325+
* and rrset_expired_above() encounters them during
3326+
* delegation cache lookups. */
3327+
uint8_t* qname = iq->dp->name;
3328+
size_t qnamelen = iq->dp->namelen;
3329+
rrset_cache_remove_above(qstate->env->rrset_cache,
3330+
&qname, &qnamelen, LDNS_RR_TYPE_NS,
3331+
iq->qchase.qclass, *qstate->env->now,
3332+
old_dp->name, old_dp->namelen);
3333+
}
33163334
if(!cache_fill_missing(qstate->env, iq->qchase.qclass,
33173335
qstate->region, iq->dp)) {
33183336
errinf(qstate, "malloc failure, copy extra info into delegation point");

services/cache/dns.c

+6-46
Original file line numberDiff line numberDiff line change
@@ -193,46 +193,6 @@ dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
193193
slabhash_insert(env->msg_cache, hash, &e->entry, rep, env->alloc);
194194
}
195195

196-
/** see if an rrset is expired above the qname, return upper qname. */
197-
static int
198-
rrset_expired_above(struct module_env* env, uint8_t** qname, size_t* qnamelen,
199-
uint16_t searchtype, uint16_t qclass, time_t now, uint8_t* expiretop,
200-
size_t expiretoplen)
201-
{
202-
struct ub_packed_rrset_key *rrset;
203-
uint8_t lablen;
204-
205-
while(*qnamelen > 0) {
206-
/* look one label higher */
207-
lablen = **qname;
208-
*qname += lablen + 1;
209-
*qnamelen -= lablen + 1;
210-
if(*qnamelen <= 0)
211-
break;
212-
213-
/* looks up with a time of 0, to see expired entries */
214-
if((rrset = rrset_cache_lookup(env->rrset_cache, *qname,
215-
*qnamelen, searchtype, qclass, 0, 0, 0))) {
216-
struct packed_rrset_data* data =
217-
(struct packed_rrset_data*)rrset->entry.data;
218-
if(now > data->ttl) {
219-
/* it is expired, this is not wanted */
220-
lock_rw_unlock(&rrset->entry.lock);
221-
log_nametypeclass(VERB_ALGO, "this rrset is expired", *qname, searchtype, qclass);
222-
return 1;
223-
}
224-
/* it is not expired, continue looking */
225-
lock_rw_unlock(&rrset->entry.lock);
226-
}
227-
228-
/* do not look above the expiretop. */
229-
if(expiretop && *qnamelen == expiretoplen &&
230-
query_dname_compare(*qname, expiretop)==0)
231-
break;
232-
}
233-
return 0;
234-
}
235-
236196
/** find closest NS or DNAME and returns the rrset (locked) */
237197
static struct ub_packed_rrset_key*
238198
find_closest_of_type(struct module_env* env, uint8_t* qname, size_t qnamelen,
@@ -266,12 +226,12 @@ find_closest_of_type(struct module_env* env, uint8_t* qname, size_t qnamelen,
266226
/* check for expiry, but we have to let go of the rrset
267227
* for the lock ordering */
268228
lock_rw_unlock(&rrset->entry.lock);
269-
/* the expired_above function always takes off one
270-
* label (if qnamelen>0) and returns the final qname
271-
* where it searched, so we can continue from there
272-
* turning the O N*N search into O N. */
273-
if(!rrset_expired_above(env, &qname, &qnamelen,
274-
searchtype, qclass, now, expiretop,
229+
/* the rrset_cache_expired_above function always takes
230+
* off one label (if qnamelen>0) and returns the final
231+
* qname where it searched, so we can continue from
232+
* there turning the O N*N search into O N. */
233+
if(!rrset_cache_expired_above(env->rrset_cache, &qname,
234+
&qnamelen, searchtype, qclass, now, expiretop,
275235
expiretoplen)) {
276236
/* we want to return rrset, but it may be
277237
* gone from cache, if so, just loop like

services/cache/rrset.c

+84
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#include "util/data/packed_rrset.h"
4747
#include "util/data/msgreply.h"
4848
#include "util/data/msgparse.h"
49+
#include "util/data/dname.h"
4950
#include "util/regional.h"
5051
#include "util/alloc.h"
5152
#include "util/net_help.h"
@@ -443,6 +444,89 @@ rrset_check_sec_status(struct rrset_cache* r,
443444
lock_rw_unlock(&e->lock);
444445
}
445446

447+
void
448+
rrset_cache_remove_above(struct rrset_cache* r, uint8_t** qname, size_t*
449+
qnamelen, uint16_t searchtype, uint16_t qclass, time_t now, uint8_t*
450+
qnametop, size_t qnametoplen)
451+
{
452+
struct ub_packed_rrset_key *rrset;
453+
uint8_t lablen;
454+
455+
while(*qnamelen > 0) {
456+
/* look one label higher */
457+
lablen = **qname;
458+
*qname += lablen + 1;
459+
*qnamelen -= lablen + 1;
460+
if(*qnamelen <= 0)
461+
return;
462+
463+
/* stop at qnametop */
464+
if(qnametop && *qnamelen == qnametoplen &&
465+
query_dname_compare(*qname, qnametop)==0)
466+
return;
467+
468+
if(verbosity >= VERB_ALGO) {
469+
/* looks up with a time of 0, to see expired entries */
470+
if((rrset = rrset_cache_lookup(r, *qname,
471+
*qnamelen, searchtype, qclass, 0, 0, 0))) {
472+
struct packed_rrset_data* data =
473+
(struct packed_rrset_data*)rrset->entry.data;
474+
int expired = (now > data->ttl);
475+
lock_rw_unlock(&rrset->entry.lock);
476+
if(expired)
477+
log_nametypeclass(verbosity, "this "
478+
"(grand)parent rrset will be "
479+
"removed (expired)",
480+
*qname, searchtype, qclass);
481+
else log_nametypeclass(verbosity, "this "
482+
"(grand)parent rrset will be "
483+
"removed",
484+
*qname, searchtype, qclass);
485+
}
486+
}
487+
rrset_cache_remove(r, *qname, *qnamelen, searchtype, qclass, 0);
488+
}
489+
}
490+
491+
int
492+
rrset_cache_expired_above(struct rrset_cache* r, uint8_t** qname, size_t*
493+
qnamelen, uint16_t searchtype, uint16_t qclass, time_t now, uint8_t*
494+
qnametop, size_t qnametoplen)
495+
{
496+
struct ub_packed_rrset_key *rrset;
497+
uint8_t lablen;
498+
499+
while(*qnamelen > 0) {
500+
/* look one label higher */
501+
lablen = **qname;
502+
*qname += lablen + 1;
503+
*qnamelen -= lablen + 1;
504+
if(*qnamelen <= 0)
505+
break;
506+
507+
/* looks up with a time of 0, to see expired entries */
508+
if((rrset = rrset_cache_lookup(r, *qname,
509+
*qnamelen, searchtype, qclass, 0, 0, 0))) {
510+
struct packed_rrset_data* data =
511+
(struct packed_rrset_data*)rrset->entry.data;
512+
if(now > data->ttl) {
513+
/* it is expired, this is not wanted */
514+
lock_rw_unlock(&rrset->entry.lock);
515+
log_nametypeclass(VERB_ALGO, "this rrset is expired", *qname, searchtype, qclass);
516+
return 1;
517+
}
518+
/* it is not expired, continue looking */
519+
lock_rw_unlock(&rrset->entry.lock);
520+
}
521+
522+
/* do not look above the qnametop. */
523+
if(qnametop && *qnamelen == qnametoplen &&
524+
query_dname_compare(*qname, qnametop)==0)
525+
break;
526+
}
527+
return 0;
528+
}
529+
446530
void rrset_cache_remove(struct rrset_cache* r, uint8_t* nm, size_t nmlen,
447531
uint16_t type, uint16_t dclass, uint32_t flags)
448532
{

services/cache/rrset.h

+31
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,37 @@ void rrset_update_sec_status(struct rrset_cache* r,
231231
void rrset_check_sec_status(struct rrset_cache* r,
232232
struct ub_packed_rrset_key* rrset, time_t now);
233233

234+
/**
235+
* Removes rrsets above the qname, returns upper qname.
236+
* @param r: the rrset cache.
237+
* @param qname: the start qname, also used as the output.
238+
* @param qnamelen: length of qname, updated when it returns.
239+
* @param searchtype: qtype to search for.
240+
* @param qclass: qclass to search for.
241+
* @param now: current time.
242+
* @param qnametop: the top qname to stop removal (it is not removed).
243+
* @param qnametoplen: length of qnametop.
244+
*/
245+
void rrset_cache_remove_above(struct rrset_cache* r, uint8_t** qname,
246+
size_t* qnamelen, uint16_t searchtype, uint16_t qclass, time_t now,
247+
uint8_t* qnametop, size_t qnametoplen);
248+
249+
/**
250+
* Sees if an rrset is expired above the qname, returns upper qname.
251+
* @param r: the rrset cache.
252+
* @param qname: the start qname, also used as the output.
253+
* @param qnamelen: length of qname, updated when it returns.
254+
* @param searchtype: qtype to search for.
255+
* @param qclass: qclass to search for.
256+
* @param now: current time.
257+
* @param qnametop: the top qname, don't look farther than that.
258+
* @param qnametoplen: length of qnametop.
259+
* @return true if there is an expired rrset above, false otherwise.
260+
*/
261+
int rrset_cache_expired_above(struct rrset_cache* r, uint8_t** qname,
262+
size_t* qnamelen, uint16_t searchtype, uint16_t qclass, time_t now,
263+
uint8_t* qnametop, size_t qnametoplen);
264+
234265
/**
235266
* Remove an rrset from the cache, by name and type and flags
236267
* @param r: rrset cache

0 commit comments

Comments
 (0)