]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
A few changes for TTL processing:
authorYorgos Thessalonikefs <yorgos@nlnetlabs.nl>
Mon, 15 Sep 2025 08:03:35 +0000 (10:03 +0200)
committerYorgos Thessalonikefs <yorgos@nlnetlabs.nl>
Mon, 15 Sep 2025 08:03:35 +0000 (10:03 +0200)
- 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.

43 files changed:
cachedb/cachedb.c
daemon/worker.c
doc/example.conf.in
doc/unbound.conf.rst
services/authzone.c
services/cache/dns.c
services/cache/dns.h
services/cache/rrset.c
services/localzone.c
services/mesh.c
services/rpz.c
testcode/unitmain.c
testdata/cachedb_expired.crpl
testdata/cachedb_expired_client_timeout.crpl
testdata/cachedb_expired_reply_ttl.crpl
testdata/cachedb_subnet_change.crpl
testdata/cachedb_val_expired.crpl
testdata/fwd_0ttlservfail.rpl
testdata/rrset_use_cached.rpl
testdata/serve_expired.rpl
testdata/serve_expired_0ttl_nodata.rpl
testdata/serve_expired_0ttl_nxdomain.rpl
testdata/serve_expired_0ttl_servfail.rpl
testdata/serve_expired_client_timeout.rpl
testdata/serve_expired_client_timeout_no_prefetch.rpl [deleted file]
testdata/serve_expired_client_timeout_servfail.rpl
testdata/serve_expired_reply_ttl.rpl
testdata/serve_expired_ttl_client_timeout.rpl
testdata/serve_expired_ttl_reset.rpl
testdata/serve_expired_zerottl.rpl
testdata/subnet_global_prefetch_always_forward.crpl
testdata/subnet_global_prefetch_expired.crpl
testdata/ttl_zero_cacherep.rpl
util/data/msgencode.c
util/data/msgencode.h
util/data/msgparse.c
util/data/msgparse.h
util/data/msgreply.c
util/data/msgreply.h
util/data/packed_rrset.c
util/data/packed_rrset.h
validator/val_neg.c
validator/val_utils.c

index 8f2e66342f018ec6828dd19cb5a8cf9e7b017205..b45c0a3ea69f017e9970fc36fda8c7cb14a2a48b 100644 (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;
index 77417c2492098fe9962bcae8959c3404cbb79b59..0f0af7457c807751f8c1375d38e9ad0e9df19eda 100644 (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,
index b33e65bfeae2748946095da67cce53bebdc9eeff..02f6ec7740ffdd920047a91ce2bc68feda136f82 100644 (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.
index ad8404e113cf28793f0e2a7fe6891d6d1c4abf89..72522af702cdd4a5f5412890a64998aa59ee857d 100644 (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
 
 
index 6a9de52f83ddd1782e9576685f13bd834edaf215..e4dd32465b9f338bd8698c04784f5a806225ff25 100644 (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);
index 351b3568c80b71f9ef130b6344b1463fa16c4b9b..325faa0b2139e3be8de172a0a534c22b3b31e2ea 100644 (file)
  * @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.
index 8aa6b44bc341e517e0793e1bf587f20ae7fdd08b..41d42e60d07021471c18efbba62eae63ee16d2ec 100644 (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
index 6d5c24f8053e04e384c73eddafac4e146985beb9..200e3c701f785f1da132941d3665348308d2a0f4 100644 (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);
index ce796c34915eccad39dc2c65cd8a87875361045c..9ea98c250907a03f2fc3e025e4febc62b1cfe9ab 100644 (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);
index 30040f75242d6212a1f777489461425c23948ec8..a0bb9ba2f00b48b67656ed2d83fc1b816804ffae 100644 (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))
index b77f47c9bffe49621dc404bb441aac35897828ff..df39e75b0596a12e8f489921cd9f53d7ce807e94 100644 (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);
index 14e31927b834dcc89c55d34f65e8004c69c37c90..07c016d7ba7431a3960716cfdd6fb9627faab037 100644 (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);
index d3bf06fe1a1b2fce38b3c0cb0b0f7b3e9d5ca7d8..697e88e264acddd70d00278b911a993a43782265 100644 (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
index 78ddf4d8f698c019091b3a4efe37ddfe9efba667..16d8cc30d67c32f7281cd43873fa34066a30b7c9 100644 (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
index 03fd01add476f7fe302a3203672e754e10f1b0bf..b8adbe738a7fbb4800a46f32546f7b68c61287e6 100644 (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
index 73584305ce6024af0486cd23b8818f7c07d22ba8..87903132c373144a31785b848833efa0630305a5 100644 (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
index 741445ce851541d4969927217694a867ec05a47a..aa98b5fe621c595c1193276474bfa64862b45168 100644 (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
index d50d386d4137d2e4021edb626b877edfa7d0e257..635ee6db696f85af0ece6419f8507d39e7e16203 100644 (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
index 8420ae02afe6dda90d1f2ebf58d3a3fcf7ee23ab..7af5c85d2e27466fc70e7edabe73ddc28fd01b27 100644 (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
index 990a562c7191ac657dbeeb8e1c49c525ad1a110a..4aa65e1676a155252eb01b3b6f0b3a885c7e08c0 100644 (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
index 8ca461be2c7b38b289a56f8cda6c6abebb05a8fb..a53df437aeb76df8891950c9563ba5f75e1f890d 100644 (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
index 7cf26aedda0a27458542448758ec2f244da5f81e..7dd27b8d54c18b7856c1eb1475d3184b12ea8c74 100644 (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
index e9d4c4884e9f15fe478fba66149d26f05bc1d1a8..9e5a17a094dcd73d382f38dc1ebd36da74a13076 100644 (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
index 5560aa05a8dd6db40fabc7ff0078e2dd423ce3aa..c445436929d0160b26e5c64460a8975002777c48 100644 (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
diff --git a/testdata/serve_expired_client_timeout_no_prefetch.rpl b/testdata/serve_expired_client_timeout_no_prefetch.rpl
deleted file mode 100644 (file)
index 0177dd1..0000000
+++ /dev/null
@@ -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
index 3c5b35e1793a5be346b749ce8c4af52c82b1efdf..46fe032fb0b842c95fff2963964609697632888a 100644 (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
index e76976bde07dd649ea2cf84519c26a151165a5c7..c7859e00e1e01c8409aaea46e1d0d43243c33eee 100644 (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
+
+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
-STEP 11 TIME_PASSES ELAPSE 3601
+; 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
index 169d070ead14a306a572533b5a39695ccf682389..d1aef32afc1b178e6de6b50888c3db19e3ac6f91 100644 (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
index 9f215c96bcd197766d7c405065175894d0c7ac75..f732fddcf2e6f521dcae06d76f9c73d95c42ce7f 100644 (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
index 1411cb8e77a588161fb5ad3491399bd6d786cdec..50a50bb3be783085777c23a99f6fb13028a57ba1 100644 (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
index 775474cbcfeb27011d837caf46400b65e1f46417..f2c8bdb60fcec94c84a1b906ff0e7a33058f4d8c 100644 (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
index 374bf3e693aa7d4c61d4e5e99f65e35c288d057b..a347550ba0a3333b18ea9ec988529cae8afc5890 100644 (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
index 7e9eb53943a76c28ba7e3c6a378a1aa39630d021..fe6f9dcc7e1b8163398fe6ee8156813d2c29042d 100644 (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
index b389800d01938330448f24275bab236bdd2cd388..22c3ba9d74dde4737908b6c1a3f75c973ddefcbe 100644 (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,
+                                       EXPIRED_REPLY_TTL_CALC(
+                                           data->rr_ttl[i], data->ttl_add));
+                       } else {
                                sldns_buffer_write_u32(pkt,
-                                       SERVE_EXPIRED?SERVE_EXPIRED_REPLY_TTL:0);
-                       else    sldns_buffer_write_u32(pkt, data->rr_ttl[i]-adjust);
+                                       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;
index 2313616806c631769939dcc435256bf193acddfb..0363a90bf06f47c36a82326df6bfc67e7927f6d6 100644 (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.
index 6963d850171ee5d59736004e05419b74f153b277..fc7018def6c8a4af994a05610157c5a6fb3142ef 100644 (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
index 7de4e394f2aed23f26c5b7b05ddff96eaf396f11..373de677d27c8782563cc66806837917915fdb20 100644 (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.
index 6b65e1eb18829809f88cc6000bdce367542152d1..d67c1d0ca0307e2d01283f4c4edee3cc1ad75475 100644 (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)
 {
index b65abd922b05478a2374a407ae7667e17c4cea6e..a63ac49a3ced71f4682b2671c445b650a1cea938 100644 (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.
index d18486cc5b3647f59e9dde0af05226bd529f5ad5..89ece3c031f565c4e4c666d2c79afd06c7603bc1 100644 (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;
 }
 
index 776e8d0923cc5aadbe78e6ec33edd0bf1d429ed8..4e0911ccf0a293332d91153166b8e92169bb8f5e 100644 (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 */
index bc3a83aeb4c92c400a67a87950066d7aeb3a1fff..7817d56fcc2e631b848c21d5c86a7d6a3654cd07 100644 (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 &&
index 549264d76a1f1ac0db92dd0fe26044c46736366f..1a5f19673bcf1ebdb8f682dd1af06bfc78a6f51b 100644 (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 */