A few changes for TTL processing:

- Cached messages that reach 0 TTL are considered expired. This prevents
  Unbound itself from issuing replies with TTL 0 and possibly causing a
  thundering herd at the last second. Upstream replies of TTL 0 still
  get the usual pass-through but they are not considered for caching
  from Unbound or any of its caching modules.
- 'serve-expired-reply-ttl' is changed and is now capped by the original
  TTL value of the record to try and make some sense when replying
  with expired records.
- TTL decoding was updated to adhere to RFC8767 section 4 where a set
  high-order bit means the value is positive instead of 0.
This commit is contained in:
Yorgos Thessalonikefs 2025-09-15 10:03:35 +02:00
parent d521135f66
commit 73e408f1d0
43 changed files with 619 additions and 546 deletions

View file

@ -401,12 +401,9 @@ prep_data(struct module_qstate* qstate, struct sldns_buffer* buf)
FLAGS_GET_RCODE(qstate->return_msg->rep->flags) !=
LDNS_RCODE_YXDOMAIN)
return 0;
/* We don't store the reply if its TTL is 0 unless serve-expired is
* enabled. Such a reply won't be reusable and simply be a waste for
* the backend. It's also compatible with the default behavior of
* dns_cache_store_msg(). */
if(qstate->return_msg->rep->ttl == 0 &&
!qstate->env->cfg->serve_expired)
/* We don't store the reply if its TTL is 0. This is probably coming
* from upstream and it is not meant to be stored. */
if(qstate->return_msg->rep->ttl == 0)
return 0;
/* The EDE is added to the out-list so it is encoded in the cached message */
@ -421,7 +418,7 @@ prep_data(struct module_qstate* qstate, struct sldns_buffer* buf)
qstate->return_msg->rep);
if(!reply_info_answer_encode(&qstate->return_msg->qinfo,
qstate->return_msg->rep, 0, qstate->query_flags,
buf, 0, 1, qstate->env->scratch, 65535, &edns, 1, 0, 1))
buf, 0, 1, qstate->env->scratch, 65535, &edns, 1, 0))
return 0;
/* TTLs in the return_msg are relative to time(0) so we have to
@ -460,7 +457,7 @@ good_expiry_and_qinfo(struct module_qstate* qstate, struct sldns_buffer* buf)
* - serve_expired needs to be set
* - if SERVE_EXPIRED_TTL is set make sure that the record is not older
* than that. */
if((time_t)expiry < *qstate->env->now &&
if(TTL_IS_EXPIRED((time_t)expiry, *qstate->env->now) &&
(!qstate->env->cfg->serve_expired ||
(SERVE_EXPIRED_TTL &&
*qstate->env->now - (time_t)expiry > SERVE_EXPIRED_TTL)))
@ -472,7 +469,8 @@ good_expiry_and_qinfo(struct module_qstate* qstate, struct sldns_buffer* buf)
/* Adjust the TTL of the given RRset by 'subtract'. If 'subtract' is
* negative, set the TTL to 0. */
static void
packed_rrset_ttl_subtract(struct packed_rrset_data* data, time_t subtract)
packed_rrset_ttl_subtract(struct packed_rrset_data* data, time_t subtract,
time_t timestamp)
{
size_t i;
size_t total = data->count + data->rrsig_count;
@ -484,13 +482,13 @@ packed_rrset_ttl_subtract(struct packed_rrset_data* data, time_t subtract)
data->rr_ttl[i] -= subtract;
else data->rr_ttl[i] = 0;
}
data->ttl_add = (subtract < data->ttl_add) ? (data->ttl_add - subtract) : 0;
data->ttl_add = timestamp;
}
/* Adjust the TTL of a DNS message and its RRs by 'adjust'. If 'adjust' is
* negative, set the TTLs to 0. */
static void
adjust_msg_ttl(struct dns_msg* msg, time_t adjust)
adjust_msg_ttl(struct dns_msg* msg, time_t adjust, time_t timestamp)
{
size_t i;
if(adjust >= 0 && msg->rep->ttl > adjust)
@ -502,13 +500,13 @@ adjust_msg_ttl(struct dns_msg* msg, time_t adjust)
for(i=0; i<msg->rep->rrset_count; i++) {
packed_rrset_ttl_subtract((struct packed_rrset_data*)msg->
rep->rrsets[i]->entry.data, adjust);
rep->rrsets[i]->entry.data, adjust, timestamp);
}
}
/* Set the TTL of the given RRset to fixed value. */
static void
packed_rrset_ttl_set(struct packed_rrset_data* data, time_t ttl)
packed_rrset_ttl_set(struct packed_rrset_data* data, time_t ttl, time_t timestamp)
{
size_t i;
size_t total = data->count + data->rrsig_count;
@ -516,12 +514,12 @@ packed_rrset_ttl_set(struct packed_rrset_data* data, time_t ttl)
for(i=0; i<total; i++) {
data->rr_ttl[i] = ttl;
}
data->ttl_add = 0;
data->ttl_add = timestamp;
}
/* Set the TTL of a DNS message and its RRs by to a fixed value. */
static void
set_msg_ttl(struct dns_msg* msg, time_t ttl)
set_msg_ttl(struct dns_msg* msg, time_t ttl, time_t timestamp)
{
size_t i;
msg->rep->ttl = ttl;
@ -530,14 +528,14 @@ set_msg_ttl(struct dns_msg* msg, time_t ttl)
for(i=0; i<msg->rep->rrset_count; i++) {
packed_rrset_ttl_set((struct packed_rrset_data*)msg->
rep->rrsets[i]->entry.data, ttl);
rep->rrsets[i]->entry.data, ttl, timestamp);
}
}
/** convert dns message in buffer to return_msg */
static int
parse_data(struct module_qstate* qstate, struct sldns_buffer* buf,
int* msg_expired)
int* msg_expired, time_t* msg_timestamp, time_t* msg_expiry)
{
struct msg_parse* prs;
struct edns_data edns;
@ -554,6 +552,9 @@ parse_data(struct module_qstate* qstate, struct sldns_buffer* buf,
&timestamp, sizeof(timestamp));
expiry = be64toh(expiry);
timestamp = be64toh(timestamp);
log_assert(timestamp <= expiry);
*msg_expiry = (time_t)expiry;
*msg_timestamp = (time_t)timestamp;
/* parse DNS packet */
regional_free_all(qstate->env->scratch);
@ -605,11 +606,9 @@ parse_data(struct module_qstate* qstate, struct sldns_buffer* buf,
return 1; /* message from the future (clock skew?) */
}
adjust = *qstate->env->now - (time_t)timestamp;
if(qstate->return_msg->rep->ttl < adjust) {
if(TTL_IS_EXPIRED((time_t)expiry, *qstate->env->now)) {
verbose(VERB_ALGO, "cachedb msg expired");
*msg_expired = 1;
/* If serve-expired is enabled, we still use an expired message
* setting the TTL to 0. */
if(!qstate->env->cfg->serve_expired ||
(FLAGS_GET_RCODE(qstate->return_msg->rep->flags)
!= LDNS_RCODE_NOERROR &&
@ -618,23 +617,21 @@ parse_data(struct module_qstate* qstate, struct sldns_buffer* buf,
FLAGS_GET_RCODE(qstate->return_msg->rep->flags)
!= LDNS_RCODE_YXDOMAIN))
return 0; /* message expired */
else
adjust = -1;
/* If serve-expired is enabled, we still use an expired message.
* Set the TTL to 0 now and it will be handled specially later
* when we need to store it internally. */
adjust = -1;
}
adjust_msg_ttl(qstate->return_msg, adjust, timestamp);
verbose(VERB_ALGO, "cachedb msg adjusted down by %d", (int)adjust);
adjust_msg_ttl(qstate->return_msg, adjust);
if(qstate->env->cfg->aggressive_nsec) {
limit_nsec_ttl(qstate->return_msg);
}
/* Similar to the unbound worker, if serve-expired is enabled and
* the msg would be considered to be expired, mark the state so a
* refetch will be scheduled. The comparison between 'expiry' and
* 'now' should be redundant given how these values were calculated,
* but we check it just in case as does good_expiry_and_qinfo(). */
if(qstate->env->cfg->serve_expired &&
!qstate->env->cfg->serve_expired_client_timeout &&
(adjust == -1 || (time_t)expiry < *qstate->env->now)) {
* refetch will be scheduled. */
if(*msg_expired && !qstate->env->cfg->serve_expired_client_timeout) {
qstate->need_refetch = 1;
}
@ -647,7 +644,7 @@ parse_data(struct module_qstate* qstate, struct sldns_buffer* buf,
*/
static int
cachedb_extcache_lookup(struct module_qstate* qstate, struct cachedb_env* ie,
int* msg_expired)
int* msg_expired, time_t* msg_timestamp, time_t* msg_expiry)
{
char key[(CACHEDB_HASHSIZE/8)*2+1];
calc_hash(&qstate->qinfo, qstate->env, key, sizeof(key));
@ -664,7 +661,8 @@ cachedb_extcache_lookup(struct module_qstate* qstate, struct cachedb_env* ie,
}
/* parse dns message into return_msg */
if( !parse_data(qstate, qstate->env->scratch_buffer, msg_expired) ) {
if( !parse_data(qstate, qstate->env->scratch_buffer, msg_expired,
msg_timestamp, msg_expiry) ) {
return 0;
}
return 1;
@ -736,20 +734,24 @@ cachedb_intcache_lookup(struct module_qstate* qstate, struct cachedb_env* cde)
* Store query into the internal cache of unbound.
*/
static void
cachedb_intcache_store(struct module_qstate* qstate, int msg_expired)
cachedb_intcache_store(struct module_qstate* qstate, int msg_expired,
time_t msg_timestamp, time_t msg_expiry)
{
uint32_t store_flags = qstate->query_flags;
int serve_expired = qstate->env->cfg->serve_expired;
if(qstate->env->cfg->serve_expired)
store_flags |= DNSCACHE_STORE_ZEROTTL;
if(!qstate->return_msg)
return;
if(serve_expired && msg_expired) {
/* Set TTLs to a value such that value + *env->now is
* going to be now-3 seconds. Making it expired
* in the cache. */
set_msg_ttl(qstate->return_msg, (time_t)-3);
time_t original_ttl = msg_expiry - msg_timestamp;
store_flags |= DNSCACHE_STORE_EXPIRED_MSG_CACHEDB;
/* Pass the original TTL of the expired message and signal with
* the DNSCACHE_STORE_EXPIRED_MSG_CACHEDB flag that
* dns_cache_store_msg() needs to set absolute expired TTLs
* based on the original message TTL.
* Results as expired message in the cache */
set_msg_ttl(qstate->return_msg, original_ttl, 0);
verbose(VERB_ALGO, "cachedb expired msg set to be expired now "
"(original ttl: %d)", (int)original_ttl);
/* The expired entry does not get checked by the validator
* and we need a validation value for it. */
if(qstate->env->cfg->cachedb_check_when_serve_expired)
@ -767,12 +769,14 @@ cachedb_intcache_store(struct module_qstate* qstate, int msg_expired)
* of cache. */
return;
}
/* set TTLs to zero again */
adjust_msg_ttl(qstate->return_msg, -1);
/* Send serve expired responses based on the cachedb
* returned message, that was just stored in the cache.
* It can then continue to work on this query. */
mesh_respond_serve_expired(qstate->mesh_info);
/* set TTLs as expired for this return_msg in case it is used
* later on */
set_msg_ttl(qstate->return_msg,
EXPIRED_REPLY_TTL_CALC(msg_expiry, msg_timestamp), 0);
}
}
@ -790,6 +794,7 @@ cachedb_handle_query(struct module_qstate* qstate,
struct cachedb_env* ie, int id)
{
int msg_expired = 0;
time_t msg_timestamp, msg_expiry;
qstate->is_cachedb_answer = 0;
/* check if we are enabled, and skip if so */
if(!ie->enabled) {
@ -824,13 +829,15 @@ cachedb_handle_query(struct module_qstate* qstate,
}
/* ask backend cache to see if we have data */
if(cachedb_extcache_lookup(qstate, ie, &msg_expired)) {
if(cachedb_extcache_lookup(qstate, ie, &msg_expired, &msg_timestamp,
&msg_expiry)) {
if(verbosity >= VERB_ALGO)
log_dns_msg(ie->backend->name,
&qstate->return_msg->qinfo,
qstate->return_msg->rep);
/* store this result in internal cache */
cachedb_intcache_store(qstate, msg_expired);
cachedb_intcache_store(qstate,
msg_expired, msg_timestamp, msg_expiry);
/* In case we have expired data but there is a client timer for expired
* answers, pass execution to next module in order to try updating the
* data first.
@ -850,6 +857,8 @@ cachedb_handle_query(struct module_qstate* qstate,
qstate->ext_state[id] = module_wait_module;
return;
}
/* No 0TTL answers escaping from external cache. */
log_assert(qstate->return_msg->rep->ttl > 0);
qstate->is_cachedb_answer = 1;
/* we are done with the query */
qstate->ext_state[id] = module_finished;

View file

@ -651,7 +651,7 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo,
}
if(!reply_info_answer_encode(&msg->qinfo, msg->rep, id, flags,
repinfo->c->buffer, 0, 1, worker->scratchpad,
udpsize, edns, (int)(edns->bits & EDNS_DO), secure, 1)) {
udpsize, edns, (int)(edns->bits & EDNS_DO), secure)) {
if(!inplace_cb_reply_servfail_call(&worker->env, qinfo, NULL, NULL,
LDNS_RCODE_SERVFAIL, edns, repinfo, worker->scratchpad,
worker->env.now_tv))
@ -746,7 +746,7 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo,
*partial_repp = NULL; /* avoid accidental further pass */
/* Check TTL */
if(rep->ttl < timenow) {
if(TTL_IS_EXPIRED(rep->ttl, timenow)) {
/* Check if we need to serve expired now */
if(worker->env.cfg->serve_expired &&
/* if serve-expired-client-timeout is set, serve
@ -891,7 +891,7 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo,
if(!reply_info_answer_encode(qinfo, encode_rep, id, flags,
repinfo->c->buffer, timenow, 1, worker->scratchpad,
udpsize, edns, (int)(edns->bits & EDNS_DO),
*is_secure_answer, 1)) {
*is_secure_answer)) {
if(!inplace_cb_reply_servfail_call(&worker->env, qinfo,
NULL, NULL, LDNS_RCODE_SERVFAIL, edns, repinfo,
worker->scratchpad, worker->env.now_tv))
@ -1929,11 +1929,11 @@ lookup_cache:
if((worker->env.cfg->prefetch &&
rep->prefetch_ttl <= *worker->env.now) ||
(worker->env.cfg->serve_expired &&
rep->ttl < *worker->env.now &&
TTL_IS_EXPIRED(rep->ttl, *worker->env.now) &&
!(*worker->env.now < rep->serve_expired_norec_ttl))) {
time_t leeway = rep->ttl - *worker->env.now;
if(rep->ttl < *worker->env.now)
leeway = 0;
time_t leeway =
TTL_IS_EXPIRED(rep->ttl, *worker->env.now)
? 0 : rep->ttl - *worker->env.now;
lock_rw_unlock(&e->lock);
reply_and_prefetch(worker, lookup_qinfo,

View file

@ -752,6 +752,7 @@ server:
# serve-expired-ttl-reset: no
#
# TTL value to use when replying with expired data.
# Capped by the original TTL of the record.
# serve-expired-reply-ttl: 30
#
# Time in milliseconds before replying to the client with expired data.

View file

@ -2306,6 +2306,12 @@ These options are part of the **server:** clause.
:ref:`serve-expired-client-timeout<unbound.conf.serve-expired-client-timeout>`
is also used then it is RECOMMENDED to use 30 as the value (:rfc:`8767`).
This value is capped by the original TTL of the record.
This means that records with higher original TTL than this value will use
this value for expired replies.
Records with lower original TTL than this value will use their original TTL
for expired replies.
Default: 30

View file

@ -171,7 +171,7 @@ get_rrset_ttl(struct ub_packed_rrset_key* k)
/** Copy rrset into region from domain-datanode and packet rrset */
static struct ub_packed_rrset_key*
auth_packed_rrset_copy_region(struct auth_zone* z, struct auth_data* node,
struct auth_rrset* rrset, struct regional* region, time_t adjust)
struct auth_rrset* rrset, struct regional* region)
{
struct ub_packed_rrset_key key;
memset(&key, 0, sizeof(key));
@ -182,7 +182,7 @@ auth_packed_rrset_copy_region(struct auth_zone* z, struct auth_data* node,
key.rk.type = htons(rrset->type);
key.rk.rrset_class = htons(z->dclass);
key.entry.hash = rrset_key_hash(&key.rk);
return packed_rrset_copy_region(&key, region, adjust);
return packed_rrset_copy_region(&key, region, 0);
}
/** fix up msg->rep TTL and prefetch ttl */
@ -236,7 +236,7 @@ msg_add_rrset_an(struct auth_zone* z, struct regional* region,
return 0;
/* copy it */
if(!(msg->rep->rrsets[msg->rep->rrset_count] =
auth_packed_rrset_copy_region(z, node, rrset, region, 0)))
auth_packed_rrset_copy_region(z, node, rrset, region)))
return 0;
msg->rep->rrset_count++;
msg->rep->an_numrrsets++;
@ -260,7 +260,7 @@ msg_add_rrset_ns(struct auth_zone* z, struct regional* region,
return 0;
/* copy it */
if(!(msg->rep->rrsets[msg->rep->rrset_count] =
auth_packed_rrset_copy_region(z, node, rrset, region, 0)))
auth_packed_rrset_copy_region(z, node, rrset, region)))
return 0;
msg->rep->rrset_count++;
msg->rep->ns_numrrsets++;
@ -283,7 +283,7 @@ msg_add_rrset_ar(struct auth_zone* z, struct regional* region,
return 0;
/* copy it */
if(!(msg->rep->rrsets[msg->rep->rrset_count] =
auth_packed_rrset_copy_region(z, node, rrset, region, 0)))
auth_packed_rrset_copy_region(z, node, rrset, region)))
return 0;
msg->rep->rrset_count++;
msg->rep->ar_numrrsets++;
@ -3530,7 +3530,7 @@ auth_answer_encode(struct query_info* qinfo, struct module_env* env,
*(uint16_t*)sldns_buffer_begin(buf),
sldns_buffer_read_u16_at(buf, 2),
buf, 0, 0, temp, udpsize, edns,
(int)(edns->bits&EDNS_DO), 0, 0)) {
(int)(edns->bits&EDNS_DO), 0)) {
error_encode(buf, (LDNS_RCODE_SERVFAIL|BIT_AA), qinfo,
*(uint16_t*)sldns_buffer_begin(buf),
sldns_buffer_read_u16_at(buf, 2), edns);

86
services/cache/dns.c vendored
View file

@ -60,10 +60,10 @@
* @param rep: contains list of rrsets to store.
* @param now: current time.
* @param leeway: during prefetch how much leeway to update TTLs.
* This makes rrsets (other than type NS) timeout sooner so they get
* updated with a new full TTL.
* Type NS does not get this, because it must not be refreshed from the
* child domain, but keep counting down properly.
* This makes rrsets expire sooner so they get updated with a new full
* TTL.
* Child side type NS does get this but TTL checks are done using the time
* the query was created rather than the time the answer was received.
* @param pside: if from parentside discovered NS, so that its NS is okay
* in a prefetch situation to be updated (without becoming sticky).
* @param qrep: update rrsets here if cache is better
@ -100,11 +100,20 @@ store_rrsets(struct module_env* env, struct reply_info* rep, time_t now,
rep->ref[i].id != rep->ref[i].key->id)
ck = NULL;
else ck = packed_rrset_copy_region(
rep->ref[i].key, region, now);
rep->ref[i].key, region,
((ntohs(rep->ref[i].key->rk.type)==
LDNS_RR_TYPE_NS && !pside)?qstarttime:now));
lock_rw_unlock(&rep->ref[i].key->entry.lock);
if(ck) {
/* use cached copy if memory allows */
qrep->rrsets[i] = ck;
ttl = ((struct packed_rrset_data*)
ck->entry.data)->ttl;
if(ttl < qrep->ttl) {
qrep->ttl = ttl;
qrep->prefetch_ttl = PREFETCH_TTL_CALC(qrep->ttl);
qrep->serve_expired_ttl = qrep->ttl + SERVE_EXPIRED_TTL;
}
}
}
/* no break: also copy key item */
@ -169,10 +178,12 @@ dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
/* there was a reply_info_sortref(rep) here but it seems to be
* unnecessary, because the cache gets locked per rrset. */
reply_info_set_ttls(rep, *env->now);
if((flags & DNSCACHE_STORE_EXPIRED_MSG_CACHEDB)) {
reply_info_absolute_ttls(rep, *env->now, *env->now - ttl);
} else reply_info_set_ttls(rep, *env->now);
store_rrsets(env, rep, *env->now, leeway, pside, qrep, region,
qstarttime);
if(ttl == 0 && !(flags & DNSCACHE_STORE_ZEROTTL)) {
if(ttl == 0) {
/* we do not store the message, but we did store the RRs,
* which could be useful for delegation information */
verbose(VERB_ALGO, "TTL 0: dropped msg from cache");
@ -272,8 +283,10 @@ addr_to_additional(struct ub_packed_rrset_key* rrset, struct regional* region,
{
if((msg->rep->rrsets[msg->rep->rrset_count] =
packed_rrset_copy_region(rrset, region, now))) {
struct packed_rrset_data* d = rrset->entry.data;
msg->rep->ar_numrrsets++;
msg->rep->rrset_count++;
UPDATE_TTL_FROM_RRSET(msg->rep->ttl, d->ttl);
}
}
@ -456,8 +469,10 @@ find_add_ds(struct module_env* env, struct regional* region,
/* add it to auth section. This is the second rrset. */
if((msg->rep->rrsets[msg->rep->rrset_count] =
packed_rrset_copy_region(rrset, region, now))) {
struct packed_rrset_data* d = rrset->entry.data;
msg->rep->ns_numrrsets++;
msg->rep->rrset_count++;
UPDATE_TTL_FROM_RRSET(msg->rep->ttl, d->ttl);
}
lock_rw_unlock(&rrset->entry.lock);
}
@ -487,6 +502,8 @@ dns_msg_create(uint8_t* qname, size_t qnamelen, uint16_t qtype,
return NULL; /* integer overflow protection */
msg->rep->flags = BIT_QR; /* with QR, no AA */
msg->rep->qdcount = 1;
msg->rep->ttl = MAX_TTL; /* will be updated (brought down) while we add
* rrsets to the message */
msg->rep->reason_bogus = LDNS_EDE_NONE;
msg->rep->rrsets = (struct ub_packed_rrset_key**)
regional_alloc(region,
@ -497,24 +514,28 @@ dns_msg_create(uint8_t* qname, size_t qnamelen, uint16_t qtype,
}
int
dns_msg_authadd(struct dns_msg* msg, struct regional* region,
dns_msg_authadd(struct dns_msg* msg, struct regional* region,
struct ub_packed_rrset_key* rrset, time_t now)
{
if(!(msg->rep->rrsets[msg->rep->rrset_count++] =
struct packed_rrset_data* d = rrset->entry.data;
if(!(msg->rep->rrsets[msg->rep->rrset_count++] =
packed_rrset_copy_region(rrset, region, now)))
return 0;
msg->rep->ns_numrrsets++;
UPDATE_TTL_FROM_RRSET(msg->rep->ttl, d->ttl);
return 1;
}
int
dns_msg_ansadd(struct dns_msg* msg, struct regional* region,
dns_msg_ansadd(struct dns_msg* msg, struct regional* region,
struct ub_packed_rrset_key* rrset, time_t now)
{
if(!(msg->rep->rrsets[msg->rep->rrset_count++] =
struct packed_rrset_data* d = rrset->entry.data;
if(!(msg->rep->rrsets[msg->rep->rrset_count++] =
packed_rrset_copy_region(rrset, region, now)))
return 0;
msg->rep->an_numrrsets++;
UPDATE_TTL_FROM_RRSET(msg->rep->ttl, d->ttl);
return 1;
}
@ -585,6 +606,7 @@ gen_dns_msg(struct regional* region, struct query_info* q, size_t num)
sizeof(struct reply_info) - sizeof(struct rrset_ref));
if(!msg->rep)
return NULL;
msg->rep->ttl = MAX_TTL;
msg->rep->reason_bogus = LDNS_EDE_NONE;
msg->rep->reason_bogus_str = NULL;
if(num > RR_COUNT_MAX)
@ -606,13 +628,13 @@ tomsg(struct module_env* env, struct query_info* q, struct reply_info* r,
size_t i;
int is_expired = 0;
time_t now_control = now;
if(now > r->ttl) {
if(TTL_IS_EXPIRED(r->ttl, now)) {
/* Check if we are allowed to serve expired */
if(!allow_expired || !reply_info_can_answer_expired(r, now))
return NULL;
/* Change the current time so we can pass the below TTL checks when
* serving expired data. */
now_control = r->ttl - env->cfg->serve_expired_reply_ttl;
/* Change the current time so we can pass the below TTL checks
* when serving expired data. */
now_control = 0;
is_expired = 1;
}
@ -620,15 +642,6 @@ tomsg(struct module_env* env, struct query_info* q, struct reply_info* r,
if(!msg) return NULL;
msg->rep->flags = r->flags;
msg->rep->qdcount = r->qdcount;
msg->rep->ttl = is_expired
?SERVE_EXPIRED_REPLY_TTL
:r->ttl - now;
if(r->prefetch_ttl > now)
msg->rep->prefetch_ttl = r->prefetch_ttl - now;
else
msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl);
msg->rep->serve_expired_ttl = msg->rep->ttl + SERVE_EXPIRED_TTL;
msg->rep->serve_expired_norec_ttl = 0;
msg->rep->security = r->security;
msg->rep->an_numrrsets = r->an_numrrsets;
msg->rep->ns_numrrsets = r->ns_numrrsets;
@ -656,13 +669,30 @@ tomsg(struct module_env* env, struct query_info* q, struct reply_info* r,
return NULL;
}
for(i=0; i<msg->rep->rrset_count; i++) {
struct packed_rrset_data* d;
msg->rep->rrsets[i] = packed_rrset_copy_region(r->rrsets[i],
region, now);
if(!msg->rep->rrsets[i]) {
rrset_array_unlock(r->ref, r->rrset_count);
return NULL;
}
d = msg->rep->rrsets[i]->entry.data;
UPDATE_TTL_FROM_RRSET(msg->rep->ttl, d->ttl);
}
if(msg->rep->rrset_count < 1) {
msg->rep->ttl = is_expired
?SERVE_EXPIRED_REPLY_TTL
:r->ttl - now;
if(r->prefetch_ttl > now)
msg->rep->prefetch_ttl = r->prefetch_ttl - now;
else
msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl);
} else {
/* msg->rep->ttl has been updated through the RRSets above */
msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl);
}
msg->rep->serve_expired_ttl = msg->rep->ttl + SERVE_EXPIRED_TTL;
msg->rep->serve_expired_norec_ttl = 0;
if(env)
rrset_array_unlock_touch(env->rrset_cache, scratch, r->ref,
r->rrset_count);
@ -701,7 +731,7 @@ rrset_msg(struct ub_packed_rrset_key* rrset, struct regional* region,
struct dns_msg* msg;
struct packed_rrset_data* d = (struct packed_rrset_data*)
rrset->entry.data;
if(now > d->ttl)
if(TTL_IS_EXPIRED(d->ttl, now))
return NULL;
msg = gen_dns_msg(region, q, 1); /* only the CNAME (or other) RRset */
if(!msg)
@ -736,7 +766,7 @@ synth_dname_msg(struct ub_packed_rrset_key* rrset, struct regional* region,
rrset->entry.data;
uint8_t* newname, *dtarg = NULL;
size_t newlen, dtarglen;
if(now > d->ttl)
if(TTL_IS_EXPIRED(d->ttl, now))
return NULL;
/* only allow validated (with DNSSEC) DNAMEs used from cache
* for insecure DNAMEs, query again. */
@ -844,6 +874,8 @@ fill_any(struct module_env* env,
/* set NOTIMPL for RFC 8482 */
msg->rep->flags |= LDNS_RCODE_NOTIMPL;
msg->rep->security = sec_status_indeterminate;
msg->rep->ttl = 1; /* empty NOTIMPL response will never be
* updated with rrsets, set TTL to 1 */
return msg;
}
@ -1069,7 +1101,7 @@ dns_cache_store(struct module_env* env, struct query_info* msgqinf,
msgqinf->qclass, flags, 0, 1);
if(e) {
struct reply_info* cached = e->entry.data;
if(cached->ttl < *env->now
if(TTL_IS_EXPIRED(cached->ttl, *env->now)
&& reply_info_could_use_expired(cached, *env->now)
/* If we are validating make sure only
* validating modules can update such messages.

View file

@ -53,7 +53,7 @@ struct delegpt;
* Must be an unsigned 32-bit value larger than 0xffff */
/** Allow caching a DNS message with a zero TTL. */
#define DNSCACHE_STORE_ZEROTTL 0x100000
#define DNSCACHE_STORE_EXPIRED_MSG_CACHEDB 0x100000
/**
* Region allocated message reply

View file

@ -131,7 +131,7 @@ need_to_update_rrset(void* nd, void* cd, time_t timenow, int equal, int ns)
struct packed_rrset_data* newd = (struct packed_rrset_data*)nd;
struct packed_rrset_data* cached = (struct packed_rrset_data*)cd;
/* o if new data is expired, cached data is better */
if( newd->ttl < timenow && timenow <= cached->ttl)
if( TTL_IS_EXPIRED(newd->ttl, timenow) && !TTL_IS_EXPIRED(cached->ttl, timenow))
return 0;
/* o store if rrset has been validated
* everything better than bogus data
@ -146,13 +146,13 @@ need_to_update_rrset(void* nd, void* cd, time_t timenow, int equal, int ns)
if( newd->trust > cached->trust ) {
/* if the cached rrset is bogus, and new is equal,
* do not update the TTL - let it expire. */
if(equal && cached->ttl >= timenow &&
if(equal && !TTL_IS_EXPIRED(cached->ttl, timenow) &&
cached->security == sec_status_bogus)
return 0;
return 1;
}
/* o item in cache has expired */
if( cached->ttl < timenow )
if( TTL_IS_EXPIRED(cached->ttl, timenow) )
return 1;
/* o same trust, but different in data - insert it */
if( newd->trust == cached->trust && !equal ) {
@ -300,7 +300,7 @@ rrset_cache_lookup(struct rrset_cache* r, uint8_t* qname, size_t qnamelen,
/* check TTL */
struct packed_rrset_data* data =
(struct packed_rrset_data*)e->data;
if(timenow > data->ttl) {
if(TTL_IS_EXPIRED(data->ttl, timenow)) {
lock_rw_unlock(&e->lock);
return NULL;
}
@ -310,17 +310,18 @@ rrset_cache_lookup(struct rrset_cache* r, uint8_t* qname, size_t qnamelen,
return NULL;
}
int
int
rrset_array_lock(struct rrset_ref* ref, size_t count, time_t timenow)
{
size_t i;
struct packed_rrset_data* d;
for(i=0; i<count; i++) {
if(i>0 && ref[i].key == ref[i-1].key)
continue; /* only lock items once */
lock_rw_rdlock(&ref[i].key->entry.lock);
if(ref[i].id != ref[i].key->id || timenow >
((struct packed_rrset_data*)(ref[i].key->entry.data))
->ttl) {
d = ref[i].key->entry.data;
if(ref[i].id != ref[i].key->id ||
TTL_IS_EXPIRED(d->ttl, timenow)) {
/* failure! rollback our readlocks */
rrset_array_unlock(ref, i+1);
return 0;
@ -511,7 +512,7 @@ rrset_cache_expired_above(struct rrset_cache* r, uint8_t** qname, size_t*
*qnamelen, searchtype, qclass, 0, 0, 0))) {
struct packed_rrset_data* data =
(struct packed_rrset_data*)rrset->entry.data;
if(now > data->ttl) {
if(TTL_IS_EXPIRED(data->ttl, now)) {
/* it is expired, this is not wanted */
lock_rw_unlock(&rrset->entry.lock);
log_nametypeclass(VERB_ALGO, "this rrset is expired", *qname, searchtype, qclass);

View file

@ -1332,7 +1332,7 @@ local_encode(struct query_info* qinfo, struct module_env* env,
if(!inplace_cb_reply_local_call(env, qinfo, NULL, &rep, rcode, edns,
repinfo, temp, env->now_tv) || !reply_info_answer_encode(qinfo, &rep,
*(uint16_t*)sldns_buffer_begin(buf), sldns_buffer_read_u16_at(buf, 2),
buf, 0, 0, temp, udpsize, edns, (int)(edns->bits&EDNS_DO), 0, 0)) {
buf, 0, 0, temp, udpsize, edns, (int)(edns->bits&EDNS_DO), 0)) {
error_encode(buf, (LDNS_RCODE_SERVFAIL|BIT_AA), qinfo,
*(uint16_t*)sldns_buffer_begin(buf),
sldns_buffer_read_u16_at(buf, 2), edns);

View file

@ -348,7 +348,7 @@ mesh_serve_expired_lookup(struct module_qstate* qstate,
key = (struct msgreply_entry*)e->key;
data = (struct reply_info*)e->data;
if(data->ttl < timenow) *is_expired = 1;
if(TTL_IS_EXPIRED(data->ttl, timenow)) *is_expired = 1;
msg = tomsg(qstate->env, &key->key, data, qstate->region, timenow,
qstate->env->cfg->serve_expired, qstate->env->scratch);
if(!msg)
@ -1351,7 +1351,7 @@ mesh_do_callback(struct mesh_state* m, int rcode, struct reply_info* rep,
!reply_info_answer_encode(&m->s.qinfo, rep, r->qid,
r->qflags, r->buf, 0, 1,
m->s.env->scratch, udp_size, &r->edns,
(int)(r->edns.bits & EDNS_DO), secure, 0))
(int)(r->edns.bits & EDNS_DO), secure))
{
fptr_ok(fptr_whitelist_mesh_cb(r->cb));
(*r->cb)(r->cb_arg, LDNS_RCODE_SERVFAIL, r->buf,
@ -1539,7 +1539,7 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
!reply_info_answer_encode(&m->s.qinfo, rep, r->qid,
r->qflags, r_buffer, 0, 1, m->s.env->scratch,
udp_size, &r->edns, (int)(r->edns.bits & EDNS_DO),
secure, 0))
secure))
{
if(!inplace_cb_reply_servfail_call(m->s.env, &m->s.qinfo, &m->s,
rep, LDNS_RCODE_SERVFAIL, &r->edns, &r->query_reply, m->s.region, &r->start_time))

View file

@ -1807,7 +1807,7 @@ rpz_local_encode(struct module_env* env, struct query_info* qinfo,
repinfo, temp, env->now_tv) ||
!reply_info_answer_encode(qinfo, &rep,
*(uint16_t*)sldns_buffer_begin(buf), sldns_buffer_read_u16_at(buf, 2),
buf, 0, 0, temp, udpsize, edns, (int)(edns->bits&EDNS_DO), 0, 0)) {
buf, 0, 0, temp, udpsize, edns, (int)(edns->bits&EDNS_DO), 0)) {
error_encode(buf, (LDNS_RCODE_SERVFAIL|BIT_AA), qinfo,
*(uint16_t*)sldns_buffer_begin(buf),
sldns_buffer_read_u16_at(buf, 2), edns);

View file

@ -1037,7 +1037,7 @@ static void edns_ede_encode_encodedecode(struct query_info* qinfo,
/* encode */
unit_assert(
reply_info_answer_encode(qinfo, rep, 1, rep->flags, pkt,
0, 0, region, 65535, edns, 0, 0, 0));
0, 0, region, 65535, edns, 0, 0));
/* buffer ready for reading; skip after the question section */
sldns_buffer_skip(pkt, LDNS_HEADER_SIZE);
(void)query_dname_len(pkt);

View file

@ -5,7 +5,10 @@ server:
minimal-responses: no
serve-expired: yes
serve-expired-client-timeout: 0
serve-expired-reply-ttl: 123
module-config: "cachedb iterator"
ede: yes
ede-serve-expired: yes
cachedb:
backend: "testframe"
@ -82,7 +85,7 @@ REPLY QR AA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 10 IN A 1.2.3.4
www.example.com. 200 IN A 1.2.3.4
ENTRY_END
ENTRY_BEGIN
@ -91,7 +94,8 @@ REPLY QR AA NOERROR
SECTION QUESTION
www2.example.com. IN A
SECTION ANSWER
www2.example.com. 10 IN A 1.2.3.5
; TTL lower than serve-expired-reply-ttl on purpose
www2.example.com. 100 IN A 1.2.3.5
ENTRY_END
RANGE_END
@ -111,7 +115,7 @@ REPLY QR RD RA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 10 IN A 1.2.3.4
www.example.com. 200 IN A 1.2.3.4
ENTRY_END
; Get another query in cache to make it expired.
@ -130,46 +134,46 @@ REPLY QR RD RA NOERROR
SECTION QUESTION
www2.example.com. IN A
SECTION ANSWER
www2.example.com. 10 IN A 1.2.3.5
www2.example.com. 100 IN A 1.2.3.5
ENTRY_END
; it is now expired
STEP 40 TIME_PASSES ELAPSE 20
STEP 40 TIME_PASSES ELAPSE 200
; cache is expired, and cachedb is expired.
STEP 50 QUERY
ENTRY_BEGIN
REPLY RD
REPLY RD DO
SECTION QUESTION
www2.example.com. IN A
ENTRY_END
STEP 60 CHECK_ANSWER
ENTRY_BEGIN
MATCH all ttl
REPLY QR RD RA NOERROR
MATCH all ttl ede=3
REPLY QR RD RA DO NOERROR
SECTION QUESTION
www2.example.com. IN A
SECTION ANSWER
www2.example.com. 30 IN A 1.2.3.5
www2.example.com. 100 IN A 1.2.3.5
ENTRY_END
; cache is expired, cachedb has no answer
STEP 70 QUERY
ENTRY_BEGIN
REPLY RD
REPLY RD DO
SECTION QUESTION
www.example.com. IN A
ENTRY_END
STEP 80 CHECK_ANSWER
ENTRY_BEGIN
MATCH all ttl
REPLY QR RD RA NOERROR
MATCH all ttl ede=3
REPLY QR RD RA DO NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 30 IN A 1.2.3.4
www.example.com. 123 IN A 1.2.3.4
ENTRY_END
STEP 90 TRAFFIC
@ -189,7 +193,7 @@ REPLY QR RD RA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 10 IN A 1.2.3.4
www.example.com. 200 IN A 1.2.3.4
ENTRY_END
; flush the entry from cache
@ -210,30 +214,30 @@ REPLY QR RD RA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 10 IN A 1.2.3.4
www.example.com. 200 IN A 1.2.3.4
ENTRY_END
; it is now expired
STEP 150 TIME_PASSES ELAPSE 20
STEP 150 TIME_PASSES ELAPSE 200
; flush the entry from cache
STEP 160 FLUSH_MESSAGE www.example.com. IN A
; cache has no answer, cachedb is expired
STEP 170 QUERY
ENTRY_BEGIN
REPLY RD
REPLY RD DO
SECTION QUESTION
www.example.com. IN A
ENTRY_END
STEP 180 CHECK_ANSWER
ENTRY_BEGIN
MATCH all ttl
REPLY QR RD RA NOERROR
MATCH all ttl ede=3
REPLY QR RD RA DO NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 30 IN A 1.2.3.4
www.example.com. 123 IN A 1.2.3.4
ENTRY_END
STEP 190 TRAFFIC
@ -254,7 +258,7 @@ REPLY QR RD RA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 10 IN A 1.2.3.4
www.example.com. 200 IN A 1.2.3.4
ENTRY_END
; expire the entry in cache
@ -275,30 +279,30 @@ REPLY QR RD RA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 10 IN A 1.2.3.4
www.example.com. 200 IN A 1.2.3.4
ENTRY_END
; it is now expired
STEP 250 TIME_PASSES ELAPSE 20
STEP 250 TIME_PASSES ELAPSE 200
; expire the entry in cache
STEP 260 EXPIRE_MESSAGE www.example.com. IN A
; cache is expired, cachedb is expired
STEP 270 QUERY
ENTRY_BEGIN
REPLY RD
REPLY RD DO
SECTION QUESTION
www.example.com. IN A
ENTRY_END
STEP 280 CHECK_ANSWER
ENTRY_BEGIN
MATCH all ttl
REPLY QR RD RA NOERROR
MATCH all ttl ede=3
REPLY QR RD RA DO NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 30 IN A 1.2.3.4
www.example.com. 123 IN A 1.2.3.4
ENTRY_END
STEP 290 TRAFFIC
@ -319,7 +323,7 @@ REPLY QR RD RA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 10 IN A 1.2.3.4
www.example.com. 200 IN A 1.2.3.4
ENTRY_END
SCENARIO_END

View file

@ -4,12 +4,14 @@ server:
qname-minimisation: no
minimal-responses: no
serve-expired: yes
serve-expired-reply-ttl: 30
serve-expired-reply-ttl: 123
; at least one second, so we can time skip past the timer in the
; testbound script steps, but also reply within the time.
serve-expired-client-timeout: 1200
module-config: "cachedb iterator"
discard-timeout: 3000
ede: yes
ede-serve-expired: yes
cachedb:
backend: "testframe"
@ -86,7 +88,7 @@ REPLY QR AA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 10 IN A 1.2.3.4
www.example.com. 200 IN A 1.2.3.4
ENTRY_END
ENTRY_BEGIN
@ -95,7 +97,8 @@ REPLY QR AA NOERROR
SECTION QUESTION
www2.example.com. IN A
SECTION ANSWER
www2.example.com. 10 IN A 1.2.3.5
; TTL lower than serve-expired-reply-ttl on purpose
www2.example.com. 100 IN A 1.2.3.5
ENTRY_END
RANGE_END
@ -108,7 +111,7 @@ REPLY QR AA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 10 IN A 1.2.3.6
www.example.com. 200 IN A 1.2.3.6
ENTRY_END
ENTRY_BEGIN
@ -117,7 +120,8 @@ REPLY QR AA NOERROR
SECTION QUESTION
www2.example.com. IN A
SECTION ANSWER
www2.example.com. 10 IN A 1.2.3.7
; TTL lower than serve-expired-reply-ttl on purpose
www2.example.com. 100 IN A 1.2.3.7
ENTRY_END
RANGE_END
@ -132,7 +136,7 @@ REPLY QR AA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 10 IN A 1.2.3.8
www.example.com. 200 IN A 1.2.3.8
ENTRY_END
ENTRY_BEGIN
@ -141,7 +145,8 @@ REPLY QR AA NOERROR
SECTION QUESTION
www2.example.com. IN A
SECTION ANSWER
www2.example.com. 10 IN A 1.2.3.9
; TTL lower than serve-expired-reply-ttl on purpose
www2.example.com. 100 IN A 1.2.3.9
ENTRY_END
RANGE_END
@ -156,7 +161,7 @@ REPLY QR AA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 10 IN A 1.2.3.10
www.example.com. 200 IN A 1.2.3.10
ENTRY_END
ENTRY_BEGIN
@ -165,7 +170,7 @@ REPLY QR AA NOERROR
SECTION QUESTION
www2.example.com. IN A
SECTION ANSWER
www2.example.com. 10 IN A 1.2.3.11
www2.example.com. 100 IN A 1.2.3.11
ENTRY_END
RANGE_END
@ -188,7 +193,7 @@ REPLY QR RD RA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 10 IN A 1.2.3.4
www.example.com. 200 IN A 1.2.3.4
ENTRY_END
; Get another query in cache.
@ -207,7 +212,7 @@ REPLY QR RD RA NOERROR
SECTION QUESTION
www2.example.com. IN A
SECTION ANSWER
www2.example.com. 10 IN A 1.2.3.5
www2.example.com. 100 IN A 1.2.3.5
ENTRY_END
; www.example.com and www2.example.com are in cache, www2 in cachedb.
@ -217,7 +222,7 @@ STEP 40 FLUSH_MESSAGE www2.example.com. IN A
; response from cachedb for www2.
; make 2 seconds pass to decrement the TTL on the response,
; the upstream TTL would be 10, cachedb 8.
; the upstream TTL would be 200, cachedb 198.
STEP 48 TIME_PASSES ELAPSE 2
STEP 50 QUERY
@ -234,11 +239,11 @@ REPLY QR RD RA NOERROR
SECTION QUESTION
www2.example.com. IN A
SECTION ANSWER
www2.example.com. 8 IN A 1.2.3.5
www2.example.com. 98 IN A 1.2.3.5
ENTRY_END
; make both cache and cachedb expired
STEP 70 TIME_PASSES ELAPSE 20
STEP 70 TIME_PASSES ELAPSE 200
; www and www2 expired in cache, www2 expired in cachedb.
; the query should now try to resolve and complete within the
@ -258,11 +263,11 @@ REPLY QR RD RA NOERROR
SECTION QUESTION
www2.example.com. IN A
SECTION ANSWER
www2.example.com. 10 IN A 1.2.3.7
www2.example.com. 100 IN A 1.2.3.7
ENTRY_END
; expire the data again
STEP 100 TIME_PASSES ELAPSE 20
STEP 100 TIME_PASSES ELAPSE 200
; the query should now try to resolve, but the upstream is not
; responsive for several testbound steps. When the timer expires,
@ -271,7 +276,7 @@ STEP 100 TIME_PASSES ELAPSE 20
; www2 expired in cache and www2 expired in cachedb.
STEP 110 QUERY
ENTRY_BEGIN
REPLY RD
REPLY RD DO
SECTION QUESTION
www2.example.com. IN A
ENTRY_END
@ -281,26 +286,26 @@ STEP 112 TIME_PASSES ELAPSE 2
STEP 120 CHECK_ANSWER
ENTRY_BEGIN
MATCH all ttl
REPLY QR RD RA NOERROR
MATCH all ttl ede=3
REPLY QR RD RA DO NOERROR
SECTION QUESTION
www2.example.com. IN A
SECTION ANSWER
www2.example.com. 30 IN A 1.2.3.7
www2.example.com. 100 IN A 1.2.3.7
ENTRY_END
; make traffic flow to resolve the query, server responds.
STEP 130 TRAFFIC
; expire the data again
STEP 140 TIME_PASSES ELAPSE 20
STEP 140 TIME_PASSES ELAPSE 200
; The client query tries to resolve, but gets no immediate answer,
; so the expired data is used. But the expired data is in cache and
; the query is not in cachedb.
STEP 150 QUERY
ENTRY_BEGIN
REPLY RD
REPLY RD DO
SECTION QUESTION
www.example.com. IN A
ENTRY_END
@ -310,12 +315,12 @@ STEP 152 TIME_PASSES ELAPSE 2
STEP 160 CHECK_ANSWER
ENTRY_BEGIN
MATCH all ttl
REPLY QR RD RA NOERROR
MATCH all ttl ede=3
REPLY QR RD RA DO NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 30 IN A 1.2.3.4
www.example.com. 123 IN A 1.2.3.4
ENTRY_END
; make traffic flow to resolve the query, server responds.
@ -337,7 +342,7 @@ REPLY QR RD RA NOERROR
SECTION QUESTION
www2.example.com. IN A
SECTION ANSWER
www2.example.com. 10 IN A 1.2.3.11
www2.example.com. 100 IN A 1.2.3.11
ENTRY_END
SCENARIO_END

View file

@ -5,8 +5,10 @@ server:
minimal-responses: no
serve-expired: yes
serve-expired-client-timeout: 0
serve-expired-reply-ttl: 30
serve-expired-reply-ttl: 123
module-config: "cachedb iterator"
ede: yes
ede-serve-expired: yes
cachedb:
backend: "testframe"
@ -83,7 +85,7 @@ REPLY QR AA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 10 IN A 1.2.3.4
www.example.com. 200 IN A 1.2.3.4
ENTRY_END
ENTRY_BEGIN
@ -92,7 +94,8 @@ REPLY QR AA NOERROR
SECTION QUESTION
www2.example.com. IN A
SECTION ANSWER
www2.example.com. 10 IN A 1.2.3.5
; TTL lower than serve-expired-reply-ttl on purpose
www2.example.com. 100 IN A 1.2.3.5
ENTRY_END
RANGE_END
@ -115,7 +118,7 @@ REPLY QR RD RA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 10 IN A 1.2.3.4
www.example.com. 200 IN A 1.2.3.4
ENTRY_END
; Get another query in cache to make it expired.
@ -134,28 +137,28 @@ REPLY QR RD RA NOERROR
SECTION QUESTION
www2.example.com. IN A
SECTION ANSWER
www2.example.com. 10 IN A 1.2.3.5
www2.example.com. 100 IN A 1.2.3.5
ENTRY_END
; it is now expired
STEP 40 TIME_PASSES ELAPSE 20
STEP 40 TIME_PASSES ELAPSE 200
; cache is expired, and cachedb is expired.
STEP 50 QUERY
ENTRY_BEGIN
REPLY RD
REPLY RD DO
SECTION QUESTION
www2.example.com. IN A
ENTRY_END
STEP 60 CHECK_ANSWER
ENTRY_BEGIN
MATCH all ttl
REPLY QR RD RA NOERROR
MATCH all ttl ede=3
REPLY QR RD RA DO NOERROR
SECTION QUESTION
www2.example.com. IN A
SECTION ANSWER
www2.example.com. 30 IN A 1.2.3.5
www2.example.com. 100 IN A 1.2.3.5
ENTRY_END
; got an answer from upstream
@ -173,25 +176,25 @@ REPLY QR RD RA NOERROR
SECTION QUESTION
www2.example.com. IN A
SECTION ANSWER
www2.example.com. 10 IN A 1.2.3.5
www2.example.com. 100 IN A 1.2.3.5
ENTRY_END
; cache is expired, cachedb has no answer
STEP 70 QUERY
ENTRY_BEGIN
REPLY RD
REPLY RD DO
SECTION QUESTION
www.example.com. IN A
ENTRY_END
STEP 80 CHECK_ANSWER
ENTRY_BEGIN
MATCH all ttl
REPLY QR RD RA NOERROR
MATCH all ttl ede=3
REPLY QR RD RA DO NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 30 IN A 1.2.3.4
www.example.com. 123 IN A 1.2.3.4
ENTRY_END
STEP 90 TRAFFIC
@ -211,29 +214,29 @@ REPLY QR RD RA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 10 IN A 1.2.3.4
www.example.com. 200 IN A 1.2.3.4
ENTRY_END
; make both cache and cachedb expired.
STEP 120 TIME_PASSES ELAPSE 20
STEP 120 TIME_PASSES ELAPSE 200
STEP 130 FLUSH_MESSAGE www.example.com. IN A
; cache has no entry and cachedb is expired.
STEP 140 QUERY
ENTRY_BEGIN
REPLY RD
REPLY RD DO
SECTION QUESTION
www.example.com. IN A
ENTRY_END
STEP 150 CHECK_ANSWER
ENTRY_BEGIN
MATCH all ttl
REPLY QR RD RA NOERROR
MATCH all ttl ede=3
REPLY QR RD RA DO NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 30 IN A 1.2.3.4
www.example.com. 123 IN A 1.2.3.4
ENTRY_END
; the name is resolved
@ -254,7 +257,7 @@ REPLY QR RD RA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 10 IN A 1.2.3.4
www.example.com. 200 IN A 1.2.3.4
ENTRY_END
SCENARIO_END

View file

@ -4,7 +4,7 @@ server:
qname-minimisation: no
minimal-responses: no
serve-expired: yes
serve-expired-reply-ttl: 30
serve-expired-reply-ttl: 123
; disable the serve expired client timeout.
serve-expired-client-timeout: 0
@ -14,6 +14,8 @@ server:
; store for edns subnet content for modules to the right of it.
; this keeps subnet content out of cachedb as global content.
module-config: "subnetcache cachedb iterator"
ede: yes
ede-serve-expired: yes
cachedb:
backend: "testframe"
@ -105,7 +107,7 @@ REPLY QR AA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 10 IN CNAME www.initial.com.
www.example.com. 200 IN CNAME www.initial.com.
ENTRY_END
RANGE_END
@ -118,7 +120,7 @@ REPLY QR AA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 10 IN CNAME example.foo.com.
www.example.com. 200 IN CNAME example.foo.com.
ENTRY_END
RANGE_END
@ -131,7 +133,7 @@ REPLY QR AA NOERROR
SECTION QUESTION
www.initial.com. IN A
SECTION ANSWER
www.initial.com. 10 IN A 1.2.3.4
www.initial.com. 200 IN A 1.2.3.4
ENTRY_END
RANGE_END
@ -144,7 +146,7 @@ REPLY QR AA NOERROR
SECTION QUESTION
example.foo.com. IN A
SECTION ANSWER
example.foo.com. 10 IN A 1.2.3.5
example.foo.com. 200 IN A 1.2.3.5
SECTION ADDITIONAL
HEX_EDNSDATA_BEGIN
; client is 127.0.0.1
@ -166,7 +168,7 @@ REPLY QR AA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 10 IN CNAME example.foo.com.
www.example.com. 200 IN CNAME example.foo.com.
ENTRY_END
RANGE_END
@ -179,7 +181,7 @@ REPLY QR AA NOERROR
SECTION QUESTION
example.foo.com. IN A
SECTION ANSWER
example.foo.com. 10 IN A 1.2.3.6
example.foo.com. 200 IN A 1.2.3.6
SECTION ADDITIONAL
HEX_EDNSDATA_BEGIN
; client is 127.0.0.1
@ -211,19 +213,19 @@ REPLY QR RD RA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 10 IN CNAME www.initial.com.
www.initial.com. 10 IN A 1.2.3.4
www.example.com. 200 IN CNAME www.initial.com.
www.initial.com. 200 IN A 1.2.3.4
ENTRY_END
; now valid in cache and valid in cachedb, without subnet.
STEP 30 TIME_PASSES ELAPSE 20
STEP 30 TIME_PASSES ELAPSE 200
; now the cache and cachedb have an expired entry.
; the upstream is updated to CNAME to a subnet zone A record.
STEP 40 QUERY ADDRESS 127.0.0.1
ENTRY_BEGIN
REPLY RD
REPLY RD DO
SECTION QUESTION
www.example.com. IN A
ENTRY_END
@ -231,13 +233,13 @@ ENTRY_END
; the expired answer, while the ECS answer is looked up.
STEP 50 CHECK_ANSWER
ENTRY_BEGIN
MATCH all ttl
REPLY QR RD RA NOERROR
MATCH all ttl ede=3
REPLY QR RD RA DO NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 30 IN CNAME www.initial.com.
www.initial.com. 30 IN A 1.2.3.4
www.example.com. 123 IN CNAME www.initial.com.
www.initial.com. 123 IN A 1.2.3.4
ENTRY_END
; check that subnet has the query in cache.
@ -256,12 +258,12 @@ REPLY QR RD RA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 8 IN CNAME example.foo.com.
example.foo.com. 8 IN A 1.2.3.5
www.example.com. 198 IN CNAME example.foo.com.
example.foo.com. 198 IN A 1.2.3.5
ENTRY_END
; everything is expired, cache, subnetcache and cachedb.
STEP 80 TIME_PASSES ELAPSE 20
STEP 80 TIME_PASSES ELAPSE 200
STEP 90 QUERY ADDRESS 127.0.0.1
ENTRY_BEGIN
@ -277,8 +279,8 @@ REPLY QR RD RA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 10 IN CNAME example.foo.com.
example.foo.com. 10 IN A 1.2.3.6
www.example.com. 200 IN CNAME example.foo.com.
example.foo.com. 200 IN A 1.2.3.6
ENTRY_END
; see the entry now in cache, from the subnetcache.
@ -297,8 +299,8 @@ REPLY QR RD RA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 8 IN CNAME example.foo.com.
example.foo.com. 8 IN A 1.2.3.6
www.example.com. 198 IN CNAME example.foo.com.
example.foo.com. 198 IN A 1.2.3.6
ENTRY_END
SCENARIO_END

View file

@ -5,8 +5,11 @@ server:
minimal-responses: yes
serve-expired: yes
serve-expired-client-timeout: 0
serve-expired-reply-ttl: 123
;module-config: "subnetcache validator cachedb iterator"
module-config: "validator cachedb iterator"
ede: yes
ede-serve-expired: yes
cachedb:
backend: "testframe"
@ -83,7 +86,7 @@ REPLY QR AA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 10 IN A 1.2.3.4
www.example.com. 200 IN A 1.2.3.4
ENTRY_END
ENTRY_BEGIN
@ -92,7 +95,8 @@ REPLY QR AA NOERROR
SECTION QUESTION
www2.example.com. IN A
SECTION ANSWER
www2.example.com. 10 IN A 1.2.3.5
; TTL lower than serve-expired-reply-ttl on purpose
www2.example.com. 100 IN A 1.2.3.5
ENTRY_END
RANGE_END
@ -112,7 +116,7 @@ REPLY QR RD RA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 10 IN A 1.2.3.4
www.example.com. 200 IN A 1.2.3.4
ENTRY_END
; Get another query in cache to make it expired.
@ -131,48 +135,48 @@ REPLY QR RD RA NOERROR
SECTION QUESTION
www2.example.com. IN A
SECTION ANSWER
www2.example.com. 10 IN A 1.2.3.5
www2.example.com. 100 IN A 1.2.3.5
ENTRY_END
; it is now expired
STEP 40 TIME_PASSES ELAPSE 20
STEP 40 TIME_PASSES ELAPSE 200
; cache is expired, and cachedb is expired.
; The expired reply, from cachedb, needs a validation status,
; because the validator module set that validation is needed.
STEP 50 QUERY
ENTRY_BEGIN
REPLY RD
REPLY RD DO
SECTION QUESTION
www2.example.com. IN A
ENTRY_END
STEP 60 CHECK_ANSWER
ENTRY_BEGIN
MATCH all ttl
REPLY QR RD RA NOERROR
MATCH all ttl ede=3
REPLY QR RD RA DO NOERROR
SECTION QUESTION
www2.example.com. IN A
SECTION ANSWER
www2.example.com. 30 IN A 1.2.3.5
www2.example.com. 100 IN A 1.2.3.5
ENTRY_END
; cache is expired, cachedb has no answer
STEP 70 QUERY
ENTRY_BEGIN
REPLY RD
REPLY RD DO
SECTION QUESTION
www.example.com. IN A
ENTRY_END
STEP 80 CHECK_ANSWER
ENTRY_BEGIN
MATCH all ttl
REPLY QR RD RA NOERROR
MATCH all ttl ede=3
REPLY QR RD RA DO NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 30 IN A 1.2.3.4
www.example.com. 123 IN A 1.2.3.4
ENTRY_END
STEP 90 TRAFFIC
@ -192,7 +196,7 @@ REPLY QR RD RA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 10 IN A 1.2.3.4
www.example.com. 200 IN A 1.2.3.4
ENTRY_END
; flush the entry from cache
@ -213,30 +217,30 @@ REPLY QR RD RA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 10 IN A 1.2.3.4
www.example.com. 200 IN A 1.2.3.4
ENTRY_END
; it is now expired
STEP 150 TIME_PASSES ELAPSE 20
STEP 150 TIME_PASSES ELAPSE 200
; flush the entry from cache
STEP 160 FLUSH_MESSAGE www.example.com. IN A
; cache has no answer, cachedb is expired
STEP 170 QUERY
ENTRY_BEGIN
REPLY RD
REPLY RD DO
SECTION QUESTION
www.example.com. IN A
ENTRY_END
STEP 180 CHECK_ANSWER
ENTRY_BEGIN
MATCH all ttl
REPLY QR RD RA NOERROR
MATCH all ttl ede=3
REPLY QR RD RA DO NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 30 IN A 1.2.3.4
www.example.com. 123 IN A 1.2.3.4
ENTRY_END
STEP 190 TRAFFIC
@ -257,7 +261,7 @@ REPLY QR RD RA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 10 IN A 1.2.3.4
www.example.com. 200 IN A 1.2.3.4
ENTRY_END
; expire the entry in cache
@ -278,30 +282,30 @@ REPLY QR RD RA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 10 IN A 1.2.3.4
www.example.com. 200 IN A 1.2.3.4
ENTRY_END
; it is now expired
STEP 250 TIME_PASSES ELAPSE 20
STEP 250 TIME_PASSES ELAPSE 200
; expire the entry in cache
STEP 260 EXPIRE_MESSAGE www.example.com. IN A
; cache is expired, cachedb is expired
STEP 270 QUERY
ENTRY_BEGIN
REPLY RD
REPLY RD DO
SECTION QUESTION
www.example.com. IN A
ENTRY_END
STEP 280 CHECK_ANSWER
ENTRY_BEGIN
MATCH all ttl
REPLY QR RD RA NOERROR
MATCH all ttl ede=3
REPLY QR RD RA DO NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 30 IN A 1.2.3.4
www.example.com. 123 IN A 1.2.3.4
ENTRY_END
STEP 290 TRAFFIC
@ -322,7 +326,7 @@ REPLY QR RD RA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 10 IN A 1.2.3.4
www.example.com. 200 IN A 1.2.3.4
ENTRY_END
SCENARIO_END

View file

@ -46,25 +46,10 @@ www.example.com. IN A
SECTION ANSWER
ENTRY_END
; enough to pass by the TTL of the servfail answer in cache
; enough to expire the servfail answer in cache
STEP 50 TIME_PASSES ELAPSE 5
; this query triggers a prefetch
STEP 210 QUERY
ENTRY_BEGIN
REPLY RD
SECTION QUESTION
www.example.com. IN A
ENTRY_END
STEP 220 CHECK_ANSWER
ENTRY_BEGIN
MATCH all
REPLY QR RD RA SERVFAIL
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
ENTRY_END
; Expired SERVFAILS are no longer served from Unbound
; this query gets the 0ttl answer
STEP 230 QUERY
@ -76,7 +61,7 @@ ENTRY_END
STEP 240 CHECK_ANSWER
ENTRY_BEGIN
MATCH all
MATCH all ttl
REPLY QR RD RA NOERROR
SECTION QUESTION
www.example.com. IN A

View file

@ -5,8 +5,11 @@ server:
# We do not want only serve-expired because fetches from that
# apply a generous PREFETCH_LEEWAY.
serve-expired-client-timeout: 1000
serve-expired-reply-ttl: 123
# So that we can only have to give one SERVFAIL answer.
outbound-msg-retry: 0
ede: yes
ede-serve-expired: yes
forward-zone: name: "." forward-addr: 216.0.0.1
CONFIG_END
@ -35,11 +38,11 @@ ENTRY_BEGIN
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 5 IN A 10.20.30.40
www.example.com. 205 IN A 10.20.30.40
SECTION AUTHORITY
example.com. 10 IN NS ns.example.com.
example.com. 210 IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. 10 IN A 10.20.30.50
ns.example.com. 210 IN A 10.20.30.50
ENTRY_END
STEP 4 CHECK_ANSWER
ENTRY_BEGIN
@ -48,15 +51,15 @@ ENTRY_BEGIN
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 5 IN A 10.20.30.40
www.example.com. 205 IN A 10.20.30.40
SECTION AUTHORITY
example.com. 10 IN NS ns.example.com.
example.com. 210 IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. 10 IN A 10.20.30.50
ns.example.com. 210 IN A 10.20.30.50
ENTRY_END
; Wait for the A RRSET to expire.
STEP 5 TIME_PASSES ELAPSE 6
STEP 5 TIME_PASSES ELAPSE 205
STEP 6 QUERY
ENTRY_BEGIN
@ -80,14 +83,14 @@ ENTRY_BEGIN
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 5 IN A 10.20.30.40
www.example.com. 205 IN A 10.20.30.40
SECTION AUTHORITY
example.com. 10 IN NS ns.example.com.
example.com. 210 IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. 10 IN A 10.20.30.50
ns.example.com. 210 IN A 10.20.30.50
ENTRY_END
; The cached NS related RRSETs will not be overwritten by the fresh answer.
; The message should have a TTL of 4 instead of 5 from above.
; The message should have a TTL of 5 instead of 205 from above.
STEP 9 CHECK_ANSWER
ENTRY_BEGIN
MATCH all ttl
@ -95,11 +98,11 @@ ENTRY_BEGIN
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 5 IN A 10.20.30.40
www.example.com. 205 IN A 10.20.30.40
SECTION AUTHORITY
example.com. 4 IN NS ns.example.com.
example.com. 5 IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. 4 IN A 10.20.30.50
ns.example.com. 5 IN A 10.20.30.50
ENTRY_END
; Wait for the NS RRSETs to expire.
@ -107,7 +110,7 @@ STEP 10 TIME_PASSES ELAPSE 5
STEP 11 QUERY
ENTRY_BEGIN
REPLY RD
REPLY RD DO
SECTION QUESTION
www.example.com. IN A
ENTRY_END
@ -129,23 +132,23 @@ ENTRY_BEGIN
ENTRY_END
; The SERVFAIL will trigger the serve-expired-client-timeout logic to try and
; replace the SERVFAIL with a possible cached (expired) answer.
; The A RRSET would be at 0TTL left (not expired) but the message should have
; been updated to use a TTL of 4 so expired by now.
; The A RRSET would be at 200 left but the message should have
; been updated to use a TTL of 5 so expired by now.
; If the message TTL was not updated (bug), this message would be treated as
; non-expired and the now expired NS related RRSETs would fail sanity checks
; for non-expired messages. The result would be SERVFAIL here.
STEP 14 CHECK_ANSWER
ENTRY_BEGIN
MATCH all ttl
REPLY QR RD RA
MATCH all ttl ede=3
REPLY QR RD RA DO
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 0 IN A 10.20.30.40
www.example.com. 200 IN A 10.20.30.40
SECTION AUTHORITY
example.com. 30 IN NS ns.example.com.
example.com. 123 IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. 30 IN A 10.20.30.50
ns.example.com. 123 IN A 10.20.30.50
ENTRY_END
SCENARIO_END

View file

@ -5,6 +5,7 @@ server:
minimal-responses: no
serve-expired: yes
serve-expired-client-timeout: 0
serve-expired-reply-ttl: 123
access-control: 127.0.0.1/32 allow_snoop
ede: yes
ede-serve-expired: yes
@ -44,7 +45,7 @@ RANGE_BEGIN 0 100
SECTION QUESTION
example.com. IN A
SECTION ANSWER
example.com. 10 IN A 5.6.7.8
example.com. 200 IN A 5.6.7.8
SECTION AUTHORITY
example.com. IN NS ns.example.com.
SECTION ADDITIONAL
@ -68,15 +69,15 @@ ENTRY_BEGIN
SECTION QUESTION
example.com. IN A
SECTION ANSWER
example.com. 10 IN A 5.6.7.8
example.com. 200 IN A 5.6.7.8
SECTION AUTHORITY
example.com. IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. IN A 1.2.3.4
ENTRY_END
; Wait for the TTL to expire
STEP 11 TIME_PASSES ELAPSE 3601
; Wait for the TTL to expire (for all RRSets; default 3600)
STEP 11 TIME_PASSES ELAPSE 3600
; Query again without RD bit
STEP 30 QUERY
@ -94,11 +95,11 @@ ENTRY_BEGIN
SECTION QUESTION
example.com. IN A
SECTION ANSWER
example.com. 30 IN A 5.6.7.8
example.com. 123 IN A 5.6.7.8
SECTION AUTHORITY
example.com. 30 IN NS ns.example.com.
example.com. 123 IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. 30 IN A 1.2.3.4
ns.example.com. 123 IN A 1.2.3.4
ENTRY_END
; Query with RD bit (the record should have been prefetched)
@ -116,7 +117,7 @@ ENTRY_BEGIN
SECTION QUESTION
example.com. IN A
SECTION ANSWER
example.com. 10 IN A 5.6.7.8
example.com. 200 IN A 5.6.7.8
SECTION AUTHORITY
example.com. IN NS ns.example.com.
SECTION ADDITIONAL

View file

@ -5,6 +5,7 @@ server:
minimal-responses: no
serve-expired: yes
serve-expired-client-timeout: 0
serve-expired-reply-ttl: 123
ede: yes
ede-serve-expired: yes
@ -48,9 +49,9 @@ RANGE_BEGIN 30 100
SECTION QUESTION
example.com. IN NS
SECTION ANSWER
example.com. 10 IN NS ns.example.com.
example.com. 200 IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. 10 IN A 1.2.3.4
ns.example.com. 200 IN A 1.2.3.4
ENTRY_END
ENTRY_BEGIN
@ -62,9 +63,9 @@ RANGE_BEGIN 30 100
SECTION ANSWER
0ttl.example.com. 0 IN A 5.6.7.8
SECTION AUTHORITY
example.com. 10 IN NS ns.example.com.
example.com. 200 IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. 10 IN A 1.2.3.4
ns.example.com. 200 IN A 1.2.3.4
ENTRY_END
RANGE_END
@ -106,13 +107,13 @@ ENTRY_BEGIN
example.com IN SOA ns.example.com dns.example.com 1 7200 3600 2419200 10
ENTRY_END
; Wait for the NXDOMAIN to expire
STEP 31 TIME_PASSES ELAPSE 32
; Wait for the NODATA to expire
STEP 31 TIME_PASSES ELAPSE 10
; Query again
STEP 40 QUERY
ENTRY_BEGIN
REPLY RD
REPLY RD DO
SECTION QUESTION
0ttl.example.com. IN A
ENTRY_END
@ -120,8 +121,8 @@ ENTRY_END
; Check that we get the cached NODATA
STEP 50 CHECK_ANSWER
ENTRY_BEGIN
MATCH all
REPLY QR RD RA NOERROR
MATCH all ede=3
REPLY QR RD RA DO NOERROR
SECTION QUESTION
0ttl.example.com. IN A
SECTION AUTHORITY
@ -146,9 +147,9 @@ ENTRY_BEGIN
SECTION ANSWER
0ttl.example.com. 0 IN A 5.6.7.8
SECTION AUTHORITY
example.com. 10 IN NS ns.example.com.
example.com. 200 IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. 10 IN A 1.2.3.4
ns.example.com. 200 IN A 1.2.3.4
ENTRY_END
SCENARIO_END

View file

@ -107,7 +107,7 @@ ENTRY_BEGIN
ENTRY_END
; Wait for the NXDOMAIN to expire
STEP 31 TIME_PASSES ELAPSE 32
STEP 31 TIME_PASSES ELAPSE 10
; Query again
STEP 40 QUERY
@ -117,7 +117,7 @@ ENTRY_BEGIN
0ttl.example.com. IN A
ENTRY_END
; Check that we get the cached NXDOMAIN
; Check that we get the newly cached NXDOMAIN
STEP 50 CHECK_ANSWER
ENTRY_BEGIN
MATCH all

View file

@ -101,7 +101,7 @@ ENTRY_BEGIN
ENTRY_END
; Wait for the SERVFAIL to expire
STEP 31 TIME_PASSES ELAPSE 32
STEP 31 TIME_PASSES ELAPSE 10
; Query again
STEP 40 QUERY

View file

@ -48,7 +48,7 @@ RANGE_BEGIN 0 20
SECTION QUESTION
example.com. IN A
SECTION ANSWER
example.com. 10 IN A 5.6.7.8
example.com. 200 IN A 5.6.7.8
SECTION AUTHORITY
example.com. IN NS ns.example.com.
SECTION ADDITIONAL
@ -72,7 +72,7 @@ ENTRY_BEGIN
SECTION QUESTION
example.com. IN A
SECTION ANSWER
example.com. 10 IN A 5.6.7.8
example.com. 200 IN A 5.6.7.8
SECTION AUTHORITY
example.com. IN NS ns.example.com.
SECTION ADDITIONAL

View file

@ -1,110 +0,0 @@
; config options
server:
module-config: "validator iterator"
qname-minimisation: "no"
minimal-responses: no
serve-expired: yes
serve-expired-client-timeout: 1
serve-expired-reply-ttl: 123
ede: yes
ede-serve-expired: yes
stub-zone:
name: "example.com"
stub-addr: 1.2.3.4
CONFIG_END
SCENARIO_BEGIN Test that no prefetch is triggered for 0TTL records with serve-expired and client-timeout enabled
; Scenario overview:
; - query for example.com. IN A
; - check that we get an answer for example.com. IN A with the correct TTL
; - query again right at the 0TTL cached entry
; - check that we get the cached answer with no prefetching triggered
; ns.example.com.
RANGE_BEGIN 0 100
ADDRESS 1.2.3.4
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR NOERROR
SECTION QUESTION
example.com. IN NS
SECTION ANSWER
example.com. IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. IN A 1.2.3.4
ENTRY_END
RANGE_END
; ns.example.com.
RANGE_BEGIN 0 10
ADDRESS 1.2.3.4
; response to A query
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR NOERROR
SECTION QUESTION
example.com. IN A
SECTION ANSWER
example.com. 10 IN A 5.6.7.8
SECTION AUTHORITY
example.com. IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. IN A 1.2.3.4
ENTRY_END
RANGE_END
; Query with RD flag
STEP 0 QUERY
ENTRY_BEGIN
REPLY RD
SECTION QUESTION
example.com. IN A
ENTRY_END
; Check that we got the correct answer (should be cached)
STEP 1 CHECK_ANSWER
ENTRY_BEGIN
MATCH all ttl
REPLY QR RD RA NOERROR
SECTION QUESTION
example.com. IN A
SECTION ANSWER
example.com. 10 IN A 5.6.7.8
SECTION AUTHORITY
example.com. IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. IN A 1.2.3.4
ENTRY_END
; Wait for the TTL to expire and produce a 0 TTL cached record.
STEP 10 TIME_PASSES ELAPSE 10
; Query again
STEP 20 QUERY
ENTRY_BEGIN
REPLY RD DO
SECTION QUESTION
example.com. IN A
ENTRY_END
; This should come from the cache with no prefetch triggered (earlier bug).
STEP 21 CHECK_ANSWER
ENTRY_BEGIN
MATCH all ttl
REPLY QR RD RA DO NOERROR
SECTION QUESTION
example.com. IN A
SECTION ANSWER
example.com. 1 IN A 5.6.7.8
SECTION AUTHORITY
example.com. 3591 IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. 3591 IN A 1.2.3.4
ENTRY_END
; If a prefetch triggers the test will fail with 'messages pending'.
SCENARIO_END

View file

@ -40,9 +40,9 @@ RANGE_BEGIN 0 20
SECTION QUESTION
example.com. IN NS
SECTION ANSWER
example.com. 10 IN NS ns.example.com.
example.com. 200 IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. 10 IN A 1.2.3.4
ns.example.com. 200 IN A 1.2.3.4
ENTRY_END
ENTRY_BEGIN
@ -52,11 +52,11 @@ RANGE_BEGIN 0 20
SECTION QUESTION
example.com. IN A
SECTION ANSWER
example.com. 10 IN A 5.6.7.8
example.com. 200 IN A 5.6.7.8
SECTION AUTHORITY
example.com. 10 IN NS ns.example.com.
example.com. 200 IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. 10 IN A 1.2.3.4
ns.example.com. 200 IN A 1.2.3.4
ENTRY_END
RANGE_END
@ -84,11 +84,11 @@ RANGE_BEGIN 50 100
SECTION QUESTION
example.com. IN A
SECTION ANSWER
example.com. 10 IN A 5.6.7.8
example.com. 200 IN A 5.6.7.8
SECTION AUTHORITY
example.com. 10 IN NS ns.example.com.
example.com. 200 IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. 10 IN A 1.2.3.4
ns.example.com. 200 IN A 1.2.3.4
ENTRY_END
RANGE_END
@ -108,15 +108,15 @@ ENTRY_BEGIN
SECTION QUESTION
example.com. IN A
SECTION ANSWER
example.com. 10 IN A 5.6.7.8
example.com. 200 IN A 5.6.7.8
SECTION AUTHORITY
example.com. 10 IN NS ns.example.com.
example.com. 200 IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. 10 IN A 1.2.3.4
ns.example.com. 200 IN A 1.2.3.4
ENTRY_END
; Wait for the TTL to expire
STEP 11 TIME_PASSES ELAPSE 11
STEP 11 TIME_PASSES ELAPSE 200
; Query again
STEP 30 QUERY
@ -209,11 +209,11 @@ ENTRY_BEGIN
SECTION QUESTION
example.com. IN A
SECTION ANSWER
example.com. 10 IN A 5.6.7.8
example.com. 200 IN A 5.6.7.8
SECTION AUTHORITY
example.com. 10 IN NS ns.example.com.
example.com. 200 IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. 10 IN A 1.2.3.4
ns.example.com. 200 IN A 1.2.3.4
ENTRY_END
SCENARIO_END

View file

@ -18,8 +18,11 @@ SCENARIO_BEGIN Test serve-expired with reply-ttl
; Scenario overview:
; - query for example.com. IN A
; - check that we get an answer for example.com. IN A with the correct TTL
; - query for shorterttl.example.com. IN A
; - check that we get an answer for shorterttl.example.com. IN A with the correct TTL
; - query again right after the TTL expired
; - check that we get the expired cached answer with the configured TTL
; - check that we get the expired cached answer for example.com. with the configured TTL
; - check that we get the expired cached answer for shorterttl.example.com. with its own original TTL since it is shorter than the configured one
; ns.example.com.
RANGE_BEGIN 0 100
@ -43,7 +46,21 @@ RANGE_BEGIN 0 100
SECTION QUESTION
example.com. IN A
SECTION ANSWER
example.com. 10 IN A 5.6.7.8
example.com. 200 IN A 5.6.7.8
SECTION AUTHORITY
example.com. IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. IN A 1.2.3.4
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR NOERROR
SECTION QUESTION
shorterttl.example.com. IN A
SECTION ANSWER
shorterttl.example.com. 121 IN A 5.6.7.8
SECTION AUTHORITY
example.com. IN NS ns.example.com.
SECTION ADDITIONAL
@ -67,15 +84,37 @@ ENTRY_BEGIN
SECTION QUESTION
example.com. IN A
SECTION ANSWER
example.com. 10 IN A 5.6.7.8
example.com. 200 IN A 5.6.7.8
SECTION AUTHORITY
example.com. IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. IN A 1.2.3.4
ENTRY_END
; Wait for the TTL to expire
STEP 11 TIME_PASSES ELAPSE 3601
STEP 11 QUERY
ENTRY_BEGIN
REPLY RD
SECTION QUESTION
shorterttl.example.com. IN A
ENTRY_END
STEP 12 CHECK_ANSWER
ENTRY_BEGIN
MATCH all ttl
REPLY QR RD RA NOERROR
SECTION QUESTION
shorterttl.example.com. IN A
SECTION ANSWER
shorterttl.example.com. 121 IN A 5.6.7.8
SECTION AUTHORITY
example.com. IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. IN A 1.2.3.4
ENTRY_END
; Wait for the TTL to expire (for all rrsets; default 3600)
STEP 20 TIME_PASSES ELAPSE 3600
; Query again
STEP 30 QUERY
@ -100,7 +139,31 @@ ENTRY_BEGIN
ns.example.com. 123 A 1.2.3.4
ENTRY_END
; Query again for shorter ttl
STEP 50 QUERY
ENTRY_BEGIN
REPLY RD DO
SECTION QUESTION
shorterttl.example.com. IN A
ENTRY_END
; Check that we got a stale answer
; Note: auth, additional rrsets are already updated from previous recursion.
STEP 60 CHECK_ANSWER
ENTRY_BEGIN
MATCH all ttl ede=3
REPLY QR RD RA DO NOERROR
SECTION QUESTION
shorterttl.example.com. IN A
SECTION ANSWER
shorterttl.example.com. 121 A 5.6.7.8
SECTION AUTHORITY
example.com. 3600 NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. 3600 A 1.2.3.4
ENTRY_END
; Give time for the pending query to get answered
STEP 41 TRAFFIC
STEP 61 TRAFFIC
SCENARIO_END

View file

@ -78,7 +78,7 @@ ENTRY_BEGIN
ENTRY_END
; Wait for the TTL to expire + serve-expired-ttl
STEP 11 TIME_PASSES ELAPSE 3611
STEP 11 TIME_PASSES ELAPSE 3610
; Query again
STEP 30 QUERY

View file

@ -36,7 +36,7 @@ REPLY QR AA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 10 IN A 0.0.0.0
www.example.com. 200 IN A 0.0.0.0
ENTRY_END
STEP 3 CHECK_ANSWER
@ -46,11 +46,11 @@ REPLY QR RA RD NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 10 IN A 0.0.0.0
www.example.com. 200 IN A 0.0.0.0
ENTRY_END
; Expire the record (+ serve-expired-ttl)
STEP 4 TIME_PASSES ELAPSE 12
STEP 4 TIME_PASSES ELAPSE 201
STEP 5 QUERY
ENTRY_BEGIN

View file

@ -65,11 +65,11 @@ RANGE_BEGIN 11 100
SECTION QUESTION
example.com. IN A
SECTION ANSWER
example.com. 10 IN A 5.6.7.8
example.com. 200 IN A 5.6.7.8
SECTION AUTHORITY
example.com. 10 IN NS ns.example.com.
example.com. 200 IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. 10 IN A 1.2.3.4
ns.example.com. 200 IN A 1.2.3.4
ENTRY_END
RANGE_END
@ -118,15 +118,15 @@ ENTRY_BEGIN
SECTION QUESTION
example.com. IN A
SECTION ANSWER
example.com. 10 IN A 5.6.7.8
example.com. 200 IN A 5.6.7.8
SECTION AUTHORITY
example.com. 10 IN NS ns.example.com.
example.com. 200 IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. 10 IN A 1.2.3.4
ns.example.com. 200 IN A 1.2.3.4
ENTRY_END
; Wait for the TTL to expire
STEP 30 TIME_PASSES ELAPSE 11
STEP 30 TIME_PASSES ELAPSE 200
; Query with RD flag
STEP 40 QUERY

View file

@ -7,12 +7,15 @@ server:
target-fetch-policy: "0 0 0 0 0"
serve-expired: yes
serve-expired-client-timeout: 0
serve-expired-reply-ttl: 123
client-subnet-always-forward: yes
module-config: "subnetcache iterator"
verbosity: 3
access-control: 127.0.0.1 allow_snoop
qname-minimisation: no
minimal-responses: no
ede: yes
ede-serve-expired: yes
stub-zone:
name: "."
@ -100,7 +103,7 @@ RANGE_BEGIN 0 100
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 10 IN A 10.20.30.40
www.example.com. 200 IN A 10.20.30.40
SECTION AUTHORITY
example.com. IN NS ns.example.com.
SECTION ADDITIONAL
@ -131,11 +134,11 @@ ns.example.com. IN A 1.2.3.4
ENTRY_END
; Wait for the TTL to expire
STEP 3 TIME_PASSES ELAPSE 20
STEP 3 TIME_PASSES ELAPSE 200
STEP 11 QUERY
ENTRY_BEGIN
REPLY RD
REPLY RD DO
SECTION QUESTION
www.example.com. IN A
ENTRY_END
@ -143,16 +146,16 @@ ENTRY_END
; This record came from the global cache and a prefetch was triggered
STEP 12 CHECK_ANSWER
ENTRY_BEGIN
MATCH all ttl
REPLY QR RD RA NOERROR
MATCH all ttl ede=3
REPLY QR RD RA DO NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 30 IN A 10.20.30.40
www.example.com. 123 IN A 10.20.30.40
SECTION AUTHORITY
example.com. 3580 IN NS ns.example.com.
example.com. 3400 IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. 3580 IN A 1.2.3.4
ns.example.com. 3400 IN A 1.2.3.4
ENTRY_END
STEP 13 CHECK_OUT_QUERY

View file

@ -16,7 +16,11 @@ server:
serve-expired: yes
serve-expired-client-timeout: 0
serve-expired-ttl: 1
prefetch: yes
serve-expired-client-timeout: 0
serve-expired-reply-ttl: 123
#prefetch: yes #not needed, expired answers also trigger refetch
ede: yes
ede-serve-expired: yes
stub-zone:
name: "."
@ -113,7 +117,7 @@ RANGE_BEGIN 0 10
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 10 IN A 10.20.30.40
www.example.com. 200 IN A 10.20.30.40
SECTION AUTHORITY
example.com. IN NS ns.example.com.
SECTION ADDITIONAL
@ -147,7 +151,7 @@ RANGE_BEGIN 11 100
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 10 IN A 10.20.30.40
www.example.com. 200 IN A 10.20.30.40
SECTION AUTHORITY
example.com. IN NS ns.example.com.
SECTION ADDITIONAL
@ -178,7 +182,7 @@ REPLY QR RD RA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. IN A 10.20.30.40
www.example.com. 200 IN A 10.20.30.40
SECTION AUTHORITY
example.com. IN NS ns.example.com.
SECTION ADDITIONAL
@ -186,11 +190,11 @@ ns.example.com. IN A 1.2.3.4
ENTRY_END
; Try to trigger a prefetch with expired data
STEP 3 TIME_PASSES ELAPSE 11
STEP 3 TIME_PASSES ELAPSE 200
STEP 11 QUERY
ENTRY_BEGIN
REPLY RD
REPLY RD DO
SECTION QUESTION
www.example.com. IN A
ENTRY_END
@ -198,19 +202,18 @@ ENTRY_END
; This expired record came from the global cache and a prefetch is triggered.
STEP 12 CHECK_ANSWER
ENTRY_BEGIN
MATCH all ttl
REPLY QR RD RA NOERROR
MATCH all ttl ede=3
REPLY QR RD RA DO NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 30 IN A 10.20.30.40
www.example.com. 123 IN A 10.20.30.40
SECTION AUTHORITY
example.com. 3589 IN NS ns.example.com.
example.com. 3400 IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. 3589 IN A 1.2.3.4
ns.example.com. 3400 IN A 1.2.3.4
ENTRY_END
;STEP 13 TRAFFIC
; Allow enough time to pass so that the expired record from the global cache
; cannot be used anymore.
STEP 14 TIME_PASSES ELAPSE 1
@ -232,7 +235,7 @@ REPLY QR RD RA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. 9 IN A 10.20.30.40
www.example.com. 199 IN A 10.20.30.40
SECTION AUTHORITY
example.com. 3599 IN NS ns.example.com.
SECTION ADDITIONAL

View file

@ -260,27 +260,6 @@ ENTRY_END
STEP 140 CHECK_ANSWER
ENTRY_BEGIN
MATCH all ttl
REPLY QR RD RA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
; note that it did not send 0 TTL. The message can be cached by the receiver
; during the last second of the TTL.
www.example.com. 1 IN A 1.2.3.4
ENTRY_END
STEP 150 TIME_PASSES ELAPSE 1
STEP 160 QUERY
ENTRY_BEGIN
REPLY RD
SECTION QUESTION
www.example.com. IN A
ENTRY_END
STEP 170 CHECK_ANSWER
ENTRY_BEGIN
MATCH all ttl
REPLY QR RD RA SERVFAIL
SECTION QUESTION
www.example.com. IN A

View file

@ -496,10 +496,18 @@ packed_rrset_encode(struct ub_packed_rrset_key* key, sldns_buffer* pkt,
return r;
sldns_buffer_write(pkt, &key->rk.type, 2);
sldns_buffer_write(pkt, &key->rk.rrset_class, 2);
if(data->rr_ttl[j] < adjust)
if(key->rk.flags & PACKED_RRSET_UPSTREAM_0TTL) {
sldns_buffer_write_u32(pkt, 0);
} else if(adjust == 0) {
sldns_buffer_write_u32(pkt, data->rr_ttl[i]);
} else if(TTL_IS_EXPIRED(data->rr_ttl[j], adjust)) {
sldns_buffer_write_u32(pkt,
SERVE_EXPIRED?SERVE_EXPIRED_REPLY_TTL:0);
else sldns_buffer_write_u32(pkt, data->rr_ttl[j]-adjust);
EXPIRED_REPLY_TTL_CALC(
data->rr_ttl[i], data->ttl_add));
} else {
sldns_buffer_write_u32(pkt,
data->rr_ttl[i] - adjust);
}
if(c) {
if((r=compress_rdata(pkt, data->rr_data[j],
data->rr_len[j], region, tree, c,
@ -533,10 +541,18 @@ packed_rrset_encode(struct ub_packed_rrset_key* key, sldns_buffer* pkt,
}
sldns_buffer_write_u16(pkt, LDNS_RR_TYPE_RRSIG);
sldns_buffer_write(pkt, &key->rk.rrset_class, 2);
if(data->rr_ttl[i] < adjust)
if(key->rk.flags & PACKED_RRSET_UPSTREAM_0TTL) {
sldns_buffer_write_u32(pkt, 0);
} else if(adjust == 0) {
sldns_buffer_write_u32(pkt, data->rr_ttl[i]);
} else if(TTL_IS_EXPIRED(data->rr_ttl[i], adjust)) {
sldns_buffer_write_u32(pkt,
SERVE_EXPIRED?SERVE_EXPIRED_REPLY_TTL:0);
else sldns_buffer_write_u32(pkt, data->rr_ttl[i]-adjust);
EXPIRED_REPLY_TTL_CALC(
data->rr_ttl[i], data->ttl_add));
} else {
sldns_buffer_write_u32(pkt,
data->rr_ttl[i] - adjust);
}
/* rrsig rdata cannot be compressed, perform 100+ byte
* memcopy. */
sldns_buffer_write(pkt, data->rr_data[i],
@ -993,11 +1009,11 @@ attach_edns_record(sldns_buffer* pkt, struct edns_data* edns)
attach_edns_record_max_msg_sz(pkt, edns, edns->udp_size);
}
int
reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep,
int
reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep,
uint16_t id, uint16_t qflags, sldns_buffer* pkt, time_t timenow,
int cached, struct regional* region, uint16_t udpsize,
struct edns_data* edns, int dnssec, int secure, int cached_ttl)
int cached, struct regional* region, uint16_t udpsize,
struct edns_data* edns, int dnssec, int secure)
{
uint16_t flags;
unsigned int attach_edns = 0;
@ -1022,17 +1038,6 @@ reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep,
flags &= ~BIT_AD;
}
log_assert((flags & BIT_QR)); /* QR bit must be on in our replies */
if(cached_ttl && rep->ttl - timenow == 0) {
/* The last remaining second of the TTL for a cached response
* is replied. This makes a 0 in the protocol message. The
* response is valid for the cache, but the DNS TTL 0 item
* causes the received to drop the contents. Even though the
* contents are cachable, so the time used is decremented
* to change that into 1 second, and it can be cached, and
* used for expired response generation, and does not give
* repeated queries during that last second. */
timenow --;
}
if(udpsize < LDNS_HEADER_SIZE)
return 0;
/* currently edns does not change during calculations;

View file

@ -64,16 +64,12 @@ struct edns_data;
* or if edns_present = 0, it is not included.
* @param dnssec: if 0 DNSSEC records are omitted from the answer.
* @param secure: if 1, the AD bit is set in the reply.
* @param cached_ttl: the ttl is from a cache response. So that means it
* was some value minus the current time, and not an authoritative
* response with an autoritative TTL or a direct upstream response,
* that could have upstream TTL 0 items.
* @return: 0 on error (server failure).
*/
int reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep,
int reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep,
uint16_t id, uint16_t qflags, struct sldns_buffer* dest, time_t timenow,
int cached, struct regional* region, uint16_t udpsize,
struct edns_data* edns, int dnssec, int secure, int cached_ttl);
int cached, struct regional* region, uint16_t udpsize,
struct edns_data* edns, int dnssec, int secure);
/**
* Regenerate the wireformat from the stored msg reply.

View file

@ -1361,3 +1361,13 @@ msgparse_rrset_remove_rr(const char* str, sldns_buffer* pkt, struct rrset_parse*
* the rr->next works fine to continue. */
return rrset->rr_count == 0;
}
#ifdef UNBOUND_DEBUG
time_t debug_expired_reply_ttl_calc(time_t ttl, time_t ttl_add) {
/* Check that we are serving expired when this is called */
/* ttl (absolute) should be later than ttl_add */
log_assert(SERVE_EXPIRED && ttl_add <= ttl);
return (SERVE_EXPIRED_REPLY_TTL < (ttl) - (ttl_add) ?
SERVE_EXPIRED_REPLY_TTL : (ttl) - (ttl_add));
}
#endif

View file

@ -98,6 +98,31 @@ extern time_t SERVE_EXPIRED_REPLY_TTL;
/** If we serve the original TTL or decrementing TTLs */
extern int SERVE_ORIGINAL_TTL;
/** calculate the prefetch TTL as 90% of original. Calculation
* without numerical overflow (uin32_t) */
#define PREFETCH_TTL_CALC(ttl) ((ttl) - (ttl)/10)
/* caclulate the TTL used for expired answers to somewhat make sense wrt the
* original TTL; don't reply with higher TTL than the original */
#ifdef UNBOUND_DEBUG
time_t debug_expired_reply_ttl_calc(time_t ttl, time_t ttl_add);
#define EXPIRED_REPLY_TTL_CALC(ttl, ttl_add) \
debug_expired_reply_ttl_calc(ttl, ttl_add)
#else
#define EXPIRED_REPLY_TTL_CALC(ttl, ttl_add) \
(SERVE_EXPIRED_REPLY_TTL < (ttl) - (ttl_add) ? \
SERVE_EXPIRED_REPLY_TTL : (ttl) - (ttl_add))
#endif
/** Update the reply_info TTL from an RRSet's TTL, essentially keeping the TTL
* sane with all the (progressively added) rrsets to the message */
#define UPDATE_TTL_FROM_RRSET(ttl, rrsetttl) \
((ttl) = ((ttl) < (rrsetttl)) ? (ttl) : (rrsetttl))
/** Check if TTL is expired. 0 TTL is considered expired.
* Used mainly to identify parts of the code that do this comparison. */
#define TTL_IS_EXPIRED(ttl, now) ((ttl) <= (now))
/**
* Data stored in scratch pad memory during parsing.
* Stores the data that will enter into the msgreply and packet result.

View file

@ -178,9 +178,9 @@ reply_info_alloc_rrset_keys(struct reply_info* rep, struct alloc_cache* alloc,
int
reply_info_can_answer_expired(struct reply_info* rep, time_t timenow)
{
log_assert(rep->ttl < timenow);
log_assert(TTL_IS_EXPIRED(rep->ttl, timenow));
/* Really expired */
if(SERVE_EXPIRED_TTL && rep->serve_expired_ttl < timenow) return 0;
if(SERVE_EXPIRED_TTL && TTL_IS_EXPIRED(rep->serve_expired_ttl, timenow)) return 0;
/* Ignore expired failure answers */
if(FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_NOERROR &&
FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_NXDOMAIN &&
@ -188,12 +188,13 @@ reply_info_can_answer_expired(struct reply_info* rep, time_t timenow)
return 1;
}
int reply_info_could_use_expired(struct reply_info* rep, time_t timenow)
int
reply_info_could_use_expired(struct reply_info* rep, time_t timenow)
{
log_assert(rep->ttl < timenow);
log_assert(TTL_IS_EXPIRED(rep->ttl, timenow));
/* Really expired */
if(SERVE_EXPIRED_TTL && rep->serve_expired_ttl < timenow &&
!SERVE_EXPIRED_TTL_RESET) return 0;
if(SERVE_EXPIRED_TTL && TTL_IS_EXPIRED(rep->serve_expired_ttl, timenow)
&& !SERVE_EXPIRED_TTL_RESET) return 0;
/* Ignore expired failure answers */
if(FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_NOERROR &&
FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_NXDOMAIN &&
@ -229,7 +230,7 @@ make_new_reply_info(const struct reply_info* rep, struct regional* region,
}
/** find the minimumttl in the rdata of SOA record */
static time_t
static uint32_t
soa_find_minttl(struct rr_parse* rr)
{
uint16_t rlen = sldns_read_uint16(rr->ttl_data+4);
@ -237,7 +238,7 @@ soa_find_minttl(struct rr_parse* rr)
return 0; /* rdata too small for SOA (dname, dname, 5*32bit) */
/* minimum TTL is the last 32bit value in the rdata of the record */
/* at position ttl_data + 4(ttl) + 2(rdatalen) + rdatalen - 4(timeval)*/
return (time_t)sldns_read_uint32(rr->ttl_data+6+rlen-4);
return sldns_read_uint32(rr->ttl_data+6+rlen-4);
}
/** do the rdata copy */
@ -247,37 +248,40 @@ rdata_copy(sldns_buffer* pkt, struct packed_rrset_data* data, uint8_t* to,
sldns_pkt_section section)
{
uint16_t pkt_len;
uint32_t ttl;
const sldns_rr_descriptor* desc;
*rr_ttl = sldns_read_uint32(rr->ttl_data);
ttl = sldns_read_uint32(rr->ttl_data);
/* RFC 2181 Section 8. if msb of ttl is set treat as if zero. */
if((*rr_ttl & 0x80000000U))
*rr_ttl = 0;
/* RFC 8767 Section 4. values with high-order bit as positive, not 0.
+ * As such, it will be capped by MAX_TTL below. */
if(type == LDNS_RR_TYPE_SOA && section == LDNS_SECTION_AUTHORITY) {
/* negative response. see if TTL of SOA record larger than the
* minimum-ttl in the rdata of the SOA record */
if(*rr_ttl > soa_find_minttl(rr)) *rr_ttl = soa_find_minttl(rr);
if(ttl > soa_find_minttl(rr)) ttl = soa_find_minttl(rr);
if(!SERVE_ORIGINAL_TTL) {
/* If MIN_NEG_TTL is configured skip setting MIN_TTL */
if(MIN_NEG_TTL <= 0 && *rr_ttl < MIN_TTL) {
*rr_ttl = MIN_TTL;
if(MIN_NEG_TTL <= 0 && ttl < MIN_TTL) {
ttl = MIN_TTL;
}
if(*rr_ttl > MAX_TTL) *rr_ttl = MAX_TTL;
if(ttl > MAX_TTL) ttl = MAX_TTL;
}
/* MAX_NEG_TTL overrides the min and max ttl of everything
* else; it is for a more specific record */
if(*rr_ttl > MAX_NEG_TTL) *rr_ttl = MAX_NEG_TTL;
if(ttl > MAX_NEG_TTL) ttl = MAX_NEG_TTL;
/* MIN_NEG_TTL overrides the min and max ttl of everything
* else if configured; it is for a more specific record */
if(MIN_NEG_TTL > 0 && *rr_ttl < MIN_NEG_TTL) {
*rr_ttl = MIN_NEG_TTL;
if(MIN_NEG_TTL > 0 && ttl < MIN_NEG_TTL) {
ttl = MIN_NEG_TTL;
}
} else if(!SERVE_ORIGINAL_TTL) {
if(*rr_ttl < MIN_TTL) *rr_ttl = MIN_TTL;
if(*rr_ttl > MAX_TTL) *rr_ttl = MAX_TTL;
if(ttl < MIN_TTL) ttl = MIN_TTL;
if(ttl > MAX_TTL) ttl = MAX_TTL;
}
if(*rr_ttl < data->ttl)
data->ttl = *rr_ttl;
if(ttl < data->ttl)
data->ttl = ttl;
/* We have concluded the TTL checks */
*rr_ttl = (time_t)ttl;
if(rr->outside_packet) {
/* uncompressed already, only needs copy */
@ -481,6 +485,7 @@ parse_copy_decompress_rrset(sldns_buffer* pkt, struct msg_parse* msg,
pk->entry.key = (void*)pk;
pk->entry.hash = pset->hash;
data->trust = get_rrset_trust(msg, pset);
pk->rk.flags |= (data->ttl == 0) ? PACKED_RRSET_UPSTREAM_0TTL : 0;
return 1;
}
@ -617,6 +622,29 @@ reply_info_set_ttls(struct reply_info* rep, time_t timenow)
}
}
void
reply_info_absolute_ttls(struct reply_info* rep, time_t ttl, time_t ttl_add)
{
size_t i, j;
rep->ttl = ttl;
rep->prefetch_ttl = PREFETCH_TTL_CALC(ttl);
rep->serve_expired_ttl = ttl + SERVE_EXPIRED_TTL;
/* Don't set rep->serve_expired_norec_ttl; this should only be set
* on cached records when encountering an error */
log_assert(rep->serve_expired_norec_ttl == 0);
for(i=0; i<rep->rrset_count; i++) {
struct packed_rrset_data* data = (struct packed_rrset_data*)
rep->ref[i].key->entry.data;
if(i>0 && rep->ref[i].key == rep->ref[i-1].key)
continue;
data->ttl = ttl;
for(j=0; j<data->count + data->rrsig_count; j++) {
data->rr_ttl[j] = ttl;
}
data->ttl_add = ttl_add;
}
}
void
reply_info_parsedelete(struct reply_info* rep, struct alloc_cache* alloc)
{

View file

@ -60,10 +60,6 @@ struct local_rrset;
struct dns_msg;
enum comm_point_type;
/** calculate the prefetch TTL as 90% of original. Calculation
* without numerical overflow (uin32_t) */
#define PREFETCH_TTL_CALC(ttl) ((ttl) - (ttl)/10)
/**
* Structure to store query information that makes answers to queries
* different.
@ -340,6 +336,15 @@ void reply_info_sortref(struct reply_info* rep);
*/
void reply_info_set_ttls(struct reply_info* rep, time_t timenow);
/**
* Set TTLs inside the replyinfo to the given absolute values.
* @param rep: reply info. rrsets must be filled in.
* Also refs must be filled in.
* @param ttl: absolute ttl value to be set.
* @param ttl_add: the current time to be used verbatim for ttl_add in the rrsets.
*/
void reply_info_absolute_ttls(struct reply_info* rep, time_t ttl, time_t ttl_add);
/**
* Delete reply_info and packed_rrsets (while they are not yet added to the
* hashtables.). Returns rrsets to the alloc cache.

View file

@ -336,10 +336,9 @@ packed_rrset_copy_region(struct ub_packed_rrset_key* key,
struct ub_packed_rrset_key* ck = regional_alloc(region,
sizeof(struct ub_packed_rrset_key));
struct packed_rrset_data* d;
struct packed_rrset_data* data = (struct packed_rrset_data*)
key->entry.data;
struct packed_rrset_data* data = key->entry.data;
size_t dsize, i;
time_t adjust = 0;
time_t now_control;
if(!ck)
return NULL;
ck->id = key->id;
@ -352,22 +351,31 @@ packed_rrset_copy_region(struct ub_packed_rrset_key* key,
if(!ck->rk.dname)
return NULL;
dsize = packed_rrset_sizeof(data);
d = (struct packed_rrset_data*)regional_alloc_init(region, data, dsize);
d = regional_alloc_init(region, data, dsize);
if(!d)
return NULL;
ck->entry.data = d;
packed_rrset_ptr_fixup(d);
/* make TTLs relative - once per rrset */
adjust = SERVE_ORIGINAL_TTL ? data->ttl_add : now;
for(i=0; i<d->count + d->rrsig_count; i++) {
if(d->rr_ttl[i] < adjust)
d->rr_ttl[i] = SERVE_EXPIRED?SERVE_EXPIRED_REPLY_TTL:0;
else d->rr_ttl[i] -= adjust;
/* make TTLs relative - once per rr */
if(now > 0) {
/* NS RRSets may be here with ttl_add higher than now because
* of the novel ghost attack mitigation i.e., using the
* qstarttime for NS RRSets. In that case make sure that the
* returned TTL is not higher than the original one. */
log_assert(d->ttl_add <= now ||
(ntohs(key->rk.type) == LDNS_RR_TYPE_NS));
now_control = SERVE_ORIGINAL_TTL ? data->ttl_add
: (d->ttl_add > now ? d->ttl_add : now );
for(i=0; i<d->count + d->rrsig_count; i++) {
if(TTL_IS_EXPIRED(d->rr_ttl[i], now_control)) {
d->rr_ttl[i] = EXPIRED_REPLY_TTL_CALC(d->rr_ttl[i], data->ttl_add);
} else d->rr_ttl[i] -= now_control;
}
if(TTL_IS_EXPIRED(d->ttl, now_control)) {
d->ttl = EXPIRED_REPLY_TTL_CALC(d->ttl, data->ttl_add);
} else d->ttl -= now_control;
d->ttl_add = 0; /* TTLs have been made relative */
}
if(d->ttl < adjust)
d->ttl = SERVE_EXPIRED?SERVE_EXPIRED_REPLY_TTL:0;
else d->ttl -= adjust;
d->ttl_add = 0; /* TTLs have been made relative */
return ck;
}

View file

@ -70,6 +70,8 @@ typedef uint64_t rrset_id_type;
#define PACKED_RRSET_RPZ 0x8
/** this rrset is A/AAAA and is an unverified glue record */
#define PACKED_RRSET_UNVERIFIED_GLUE 0x10
/** this rrset has a 0TTL from upstream */
#define PACKED_RRSET_UPSTREAM_0TTL 0x20
/** number of rrs and rrsets for integer overflow protection. More than
* this is not really possible (64K packet has much less RRs and RRsets) in
@ -99,6 +101,7 @@ struct packed_rrset_key {
* o PACKED_RRSET_FIXEDTTL (not supposed to be cached)
* o PACKED_RRSET_RPZ
* o PACKED_RRSET_UNVERIFIED_GLUE
* o PACKED_RRSET_UPSTREAM_0TTL (not supposed to be cached)
*/
uint32_t flags;
/** the rrset type in network format */

View file

@ -1066,11 +1066,7 @@ grab_nsec(struct rrset_cache* rrset_cache, uint8_t* qname, size_t qname_len,
qname, qname_len, qtype, qclass, flags, now, 0);
struct packed_rrset_data* d;
if(!k) return NULL;
d = (struct packed_rrset_data*)k->entry.data;
if(d->ttl < now) {
lock_rw_unlock(&k->entry.lock);
return NULL;
}
d = k->entry.data;
/* only secure or unchecked records that have signatures. */
if( ! ( d->security == sec_status_secure ||
(d->security == sec_status_unchecked &&

View file

@ -1310,6 +1310,7 @@ val_find_DS(struct module_env* env, uint8_t* nm, size_t nmlen, uint16_t c,
/* DS rrset exists. Return it to the validator immediately*/
struct ub_packed_rrset_key* copy = packed_rrset_copy_region(
rrset, region, *env->now);
struct packed_rrset_data* d = copy->entry.data;
lock_rw_unlock(&rrset->entry.lock);
if(!copy)
return NULL;
@ -1319,6 +1320,7 @@ val_find_DS(struct module_env* env, uint8_t* nm, size_t nmlen, uint16_t c,
msg->rep->rrsets[0] = copy;
msg->rep->rrset_count++;
msg->rep->an_numrrsets++;
UPDATE_TTL_FROM_RRSET(msg->rep->ttl, d->ttl);
return msg;
}
/* lookup in rrset and negative cache for NSEC/NSEC3 */