if(!go_on)
return 1; /* skip this one, not all references satisfied */
- if(!dns_cache_store(&worker->env, &qinf, &rep, 0, 0)) {
+ if(!dns_cache_store(&worker->env, &qinf, &rep, 0, 0, NULL)) {
log_warn("error out of memory");
return 0;
}
+10 January 2012: Wouter
+ - Fix bug #425: unbound reports wrong TTL in reply, it reports a TTL
+ that would be permissible by the RFCs but it is not the TTL in the
+ cache.
+
2 January 2012: Wouter
- Fix to randomize hash function, based on 28c3 congress, reported
by Peter van Dijk.
ref.key = k;
ref.id = k->id;
/*ignore ret: it was in the cache, ref updated */
- (void)rrset_cache_update(env->rrset_cache, &ref,
- env->alloc, now);
+ (void)rrset_cache_update(env->rrset_cache, &ref, env->alloc, now);
}
/** Check if there are SOA records in the authority section (negative) */
int
iter_dns_store(struct module_env* env, struct query_info* msgqinf,
- struct reply_info* msgrep, int is_referral, uint32_t leeway)
+ struct reply_info* msgrep, int is_referral, uint32_t leeway,
+ struct regional* region)
{
- return dns_cache_store(env, msgqinf, msgrep, is_referral, leeway);
+ return dns_cache_store(env, msgqinf, msgrep, is_referral, leeway,
+ region);
}
int
* @param is_referral: If true, then the given message to be stored is a
* referral. The cache implementation may use this as a hint.
* @param leeway: prefetch TTL leeway to expire old rrsets quicker.
+ * @param region: to copy modified (cache is better) rrs back to.
* @return 0 on alloc error (out of memory).
*/
int iter_dns_store(struct module_env* env, struct query_info* qinf,
- struct reply_info* rep, int is_referral, uint32_t leeway);
+ struct reply_info* rep, int is_referral, uint32_t leeway,
+ struct regional* region);
/**
* Select randomly with n/m probability.
/* do not waste time trying to validate this servfail */
err.security = sec_status_indeterminate;
verbose(VERB_ALGO, "store error response in message cache");
- if(!iter_dns_store(qstate->env, &qstate->qinfo, &err, 0, 0)) {
+ if(!iter_dns_store(qstate->env, &qstate->qinfo, &err, 0, 0, NULL)) {
log_err("error_response_cache: could not store error (nomem)");
}
return error_response(qstate, id, rcode);
"nodata ANSWER"));
}
if(!iter_dns_store(qstate->env, &iq->response->qinfo,
- iq->response->rep, 0, qstate->prefetch_leeway))
+ iq->response->rep, 0, qstate->prefetch_leeway,
+ qstate->region))
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
/* close down outstanding requests to be discarded */
outbound_list_clear(&iq->outlist);
/* Store the referral under the current query */
/* no prefetch-leeway, since its not the answer */
if(!iter_dns_store(qstate->env, &iq->response->qinfo,
- iq->response->rep, 1, 0))
+ iq->response->rep, 1, 0, NULL))
return error_response(qstate, id,
LDNS_RCODE_SERVFAIL);
if(iq->store_parent_NS)
* the partial query answer (CNAME only). */
/* prefetchleeway applied because this updates answer parts */
if(!iter_dns_store(qstate->env, &iq->response->qinfo,
- iq->response->rep, 1, qstate->prefetch_leeway))
+ iq->response->rep, 1, qstate->prefetch_leeway, NULL))
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
/* set the current request's qname to the new value. */
iq->qchase.qname = sname;
* from cache does not need to be stored in the msg cache. */
if(qstate->query_flags&BIT_RD) {
if(!iter_dns_store(qstate->env, &qstate->qinfo,
- iq->response->rep, 0, qstate->prefetch_leeway))
+ iq->response->rep, 0, qstate->prefetch_leeway,
+ qstate->region))
return error_response(qstate, id,
LDNS_RCODE_SERVFAIL);
}
}
return dns_cache_store(qstate->env, qinfo, msgrep, is_referral,
- qstate->prefetch_leeway);
+ qstate->prefetch_leeway, NULL);
}
/* Invalidate the message associated with query_info stored in message cache */
* @param env: module environment with caches.
* @param rep: contains list of rrsets to store.
* @param now: current time.
+ * @param qrep: update rrsets here if cache is better
+ * @param region: for qrep allocs.
*/
static void
-store_rrsets(struct module_env* env, struct reply_info* rep, uint32_t now)
+store_rrsets(struct module_env* env, struct reply_info* rep, uint32_t now,
+ struct reply_info* qrep, struct regional* region)
{
size_t i;
/* see if rrset already exists in cache, if not insert it. */
for(i=0; i<rep->rrset_count; i++) {
rep->ref[i].key = rep->rrsets[i];
rep->ref[i].id = rep->rrsets[i]->id;
- if(rrset_cache_update(env->rrset_cache, &rep->ref[i],
- env->alloc, now)) /* it was in the cache */
+ /* update ref if it was in the cache */
+ switch(rrset_cache_update(env->rrset_cache, &rep->ref[i],
+ env->alloc, now)) {
+ case 0: /* ref unchanged, item inserted */
+ break;
+ case 2: /* ref updated, cache is superior */
+ if(region) {
+ struct ub_packed_rrset_key* ck;
+ lock_rw_rdlock(&rep->ref[i].key->entry.lock);
+ /* if deleted rrset, do not copy it */
+ if(rep->ref[i].key->id == 0)
+ ck = NULL;
+ else ck = packed_rrset_copy_region(
+ rep->ref[i].key, region, now);
+ lock_rw_unlock(&rep->ref[i].key->entry.lock);
+ if(ck) {
+ /* use cached copy if memory allows */
+ qrep->rrsets[i] = ck;
+ }
+ }
+ /* no break: also copy key item */
+ case 1: /* ref updated, item inserted */
rep->rrsets[i] = rep->ref[i].key;
+ }
}
}
void
dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
- hashvalue_t hash, struct reply_info* rep, uint32_t leeway)
+ hashvalue_t hash, struct reply_info* rep, uint32_t leeway,
+ struct reply_info* qrep, struct regional* region)
{
struct msgreply_entry* e;
uint32_t ttl = rep->ttl;
rep->ref[i].key = rep->rrsets[i];
rep->ref[i].id = rep->rrsets[i]->id;
}
- reply_info_sortref(rep);
+
+ /* 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);
- store_rrsets(env, rep, *env->now+leeway);
+ store_rrsets(env, rep, *env->now+leeway, qrep, region);
if(ttl == 0) {
/* we do not store the message, but we did store the RRs,
* which could be useful for delegation information */
int
dns_cache_store(struct module_env* env, struct query_info* msgqinf,
- struct reply_info* msgrep, int is_referral, uint32_t leeway)
+ struct reply_info* msgrep, int is_referral, uint32_t leeway,
+ struct regional* region)
{
struct reply_info* rep = NULL;
/* alloc, malloc properly (not in region, like msg is) */
rep->flags |= (BIT_RA | BIT_QR);
rep->flags &= ~(BIT_AA | BIT_CD);
h = query_info_hash(&qinf);
- dns_cache_store_msg(env, &qinf, h, rep, leeway);
+ dns_cache_store_msg(env, &qinf, h, rep, leeway, msgrep, region);
/* qname is used inside query_info_entrysetup, and set to
* NULL. If it has not been used, free it. free(0) is safe. */
free(qinf.qname);
* It will store only the RRsets, not the message.
* @param leeway: TTL value, if not 0, other rrsets are considered expired
* that many seconds before actual TTL expiry.
+ * @param region: region to allocate better entries from cache into.
+ * (used when is_referral is false).
* @return 0 on alloc error (out of memory).
*/
int dns_cache_store(struct module_env* env, struct query_info* qinf,
- struct reply_info* rep, int is_referral, uint32_t leeway);
+ struct reply_info* rep, int is_referral, uint32_t leeway,
+ struct regional* region);
/**
* Store message in the cache. Stores in message cache and rrset cache.
* Adjusts the reply info TTLs to absolute time.
* @param leeway: TTL value, if not 0, other rrsets are considered expired
* that many seconds before actual TTL expiry.
+ * @param qrep: message that can be altered with better rrs from cache.
+ * @param region: to allocate into for qmsg.
*/
void dns_cache_store_msg(struct module_env* env, struct query_info* qinfo,
- hashvalue_t hash, struct reply_info* rep, uint32_t leeway);
+ hashvalue_t hash, struct reply_info* rep, uint32_t leeway,
+ struct reply_info* qrep, struct regional* region);
/**
* Find a delegation from the cache.
/* cache is superior, return that value */
lock_rw_unlock(&e->lock);
ub_packed_rrset_parsedelete(k, alloc);
- return 1;
+ return 2;
}
lock_rw_unlock(&e->lock);
/* Go on and insert the passed item.
* @param alloc: how to allocate (and deallocate) the special rrset key.
* @param timenow: current time (to see if ttl in cache is expired).
* @return: true if the passed reference is updated, false if it is unchanged.
+ * 0: reference unchanged, inserted in cache.
+ * 1: reference updated, item is inserted in cache.
+ * 2: reference updated, item in cache is considered superior.
*/
int rrset_cache_update(struct rrset_cache* r, struct rrset_ref* ref,
struct alloc_cache* alloc, uint32_t timenow);
SECTION ANSWER
SECTION AUTHORITY
example.com. 3600 IN SOA a. b. 1 2 3 4 5
-example.com. 3600 IN NS ns.example.com.
+example.com. 1800 IN NS ns.example.com.
SECTION ADDITIONAL
-ns.example.com. 3600 IN A 1.2.3.4
+ns.example.com. 1800 IN A 1.2.3.4
ENTRY_END
; after another 1900 seconds the domain must have timed out.
--- /dev/null
+; config options
+server:
+ target-fetch-policy: "0 0 0 0 0"
+ prefetch: "yes"
+
+stub-zone:
+ name: "."
+ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
+CONFIG_END
+
+SCENARIO_BEGIN Test resolver prefetch and a moved domain
+; for bug #425.
+
+; K.ROOT-SERVERS.NET.
+RANGE_BEGIN 0 500
+ ADDRESS 193.0.14.129
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+. IN NS
+SECTION ANSWER
+. IN NS K.ROOT-SERVERS.NET.
+SECTION ADDITIONAL
+K.ROOT-SERVERS.NET. IN A 193.0.14.129
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+com. IN A
+SECTION AUTHORITY
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+RANGE_END
+
+; a.gtld-servers.net. (before sale of domain)
+RANGE_BEGIN 0 30
+ ADDRESS 192.5.6.30
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+com. IN NS
+SECTION ANSWER
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN A
+SECTION AUTHORITY
+example.com. 86400 IN NS old-ns.example.com.
+SECTION ADDITIONAL
+old-ns.example.com. 86400 IN A 192.168.0.1
+ENTRY_END
+RANGE_END
+
+; a.gtld-servers.net. (after sale of domain)
+RANGE_BEGIN 40 500
+ ADDRESS 192.5.6.30
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+com. IN NS
+SECTION ANSWER
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN A
+SECTION AUTHORITY
+example.com. 86400 IN NS new-ns.example.com.
+SECTION ADDITIONAL
+new-ns.example.com. 86400 IN A 172.16.0.1
+ENTRY_END
+RANGE_END
+
+; ns.example.com. first owner
+RANGE_BEGIN 0 500
+ ADDRESS 192.168.0.1
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+example.com. IN NS
+SECTION ANSWER
+example.com. 86400 IN NS old-ns.example.com.
+SECTION ADDITIONAL
+old-ns.example.com. 86400 IN A 192.168.0.1
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 3600 IN A 10.1.1.1
+SECTION AUTHORITY
+example.com. 86400 IN NS old-ns.example.com.
+SECTION ADDITIONAL
+old-ns.example.com. 86400 IN A 192.168.0.1
+ENTRY_END
+RANGE_END
+
+; ns.example.com. new owner
+RANGE_BEGIN 0 500
+ ADDRESS 172.16.0.1
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+example.com. IN NS
+SECTION ANSWER
+example.com. 86400 IN NS new-ns.example.com.
+SECTION ADDITIONAL
+new-ns.example.com. 86400 IN A 172.16.0.1
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+old-ns.example.com. IN A
+SECTION ANSWER
+old-ns.example.com. 86400 IN A 172.16.0.1
+SECTION AUTHORITY
+example.com. 86400 IN NS new-ns.example.com.
+SECTION ADDITIONAL
+new-ns.example.com. 86400 IN A 172.16.0.1
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 3600 IN A 10.2.2.2
+SECTION AUTHORITY
+example.com. 86400 IN NS new-ns.example.com.
+SECTION ADDITIONAL
+new-ns.example.com. 86400 IN A 172.16.0.1
+ENTRY_END
+RANGE_END
+
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+; recursion happens here.
+STEP 10 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ttl
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 3600 IN A 10.1.1.1
+SECTION AUTHORITY
+example.com. 86400 IN NS old-ns.example.com.
+SECTION ADDITIONAL
+old-ns.example.com. 86400 IN A 192.168.0.1
+ENTRY_END
+
+; after 1800 secs still the cached answer
+STEP 20 TIME_PASSES ELAPSE 1800
+
+STEP 30 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+; recursion happens here.
+STEP 40 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ttl
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 1800 IN A 10.1.1.1
+SECTION AUTHORITY
+example.com. 84600 IN NS old-ns.example.com.
+SECTION ADDITIONAL
+old-ns.example.com. 84600 IN A 192.168.0.1
+ENTRY_END
+
+; after 1440 we are 360 seconds before the expiry
+; but it still contacts the old-ns
+STEP 50 TIME_PASSES ELAPSE 1440
+
+STEP 60 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+; recursion happens here.
+STEP 70 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ttl
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 360 IN A 10.1.1.1
+SECTION AUTHORITY
+example.com. 83160 IN NS old-ns.example.com.
+SECTION ADDITIONAL
+old-ns.example.com. 83160 IN A 192.168.0.1
+ENTRY_END
+
+STEP 80 TRAFFIC
+; let traffic flow for prefetch to happen
+
+; we updated from the old-ns.
+STEP 90 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+; recursion happens here.
+STEP 100 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ttl
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 3600 IN A 10.1.1.1
+SECTION AUTHORITY
+example.com. 83160 IN NS old-ns.example.com.
+SECTION ADDITIONAL
+old-ns.example.com. 83160 IN A 192.168.0.1
+ENTRY_END
+
+; the NS record is now 10% from expiry (8640 TTL left).
+; and the A record has expired completely, retry.
+STEP 110 TIME_PASSES ELAPSE 74520
+
+; the NS record should have timed out.
+; but you see the full TTL here, this is only for *this query*
+; in the cache itself its 8640, not 86400.
+STEP 120 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+; recursion happens here.
+STEP 130 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ttl
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 3600 IN A 10.1.1.1
+SECTION AUTHORITY
+example.com. 8640 IN NS old-ns.example.com.
+SECTION ADDITIONAL
+old-ns.example.com. 8640 IN A 192.168.0.1
+ENTRY_END
+
+; get it from cache
+STEP 140 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+; recursion happens here.
+STEP 150 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ttl
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 3600 IN A 10.1.1.1
+SECTION AUTHORITY
+example.com. 8640 IN NS old-ns.example.com.
+SECTION ADDITIONAL
+old-ns.example.com. 8640 IN A 192.168.0.1
+ENTRY_END
+
+; the NS record times out after 8640 seconds.
+STEP 160 TIME_PASSES ELAPSE 8641
+
+; fetch it
+STEP 170 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+; recursion happens here.
+STEP 180 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ttl
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 3600 IN A 10.2.2.2
+SECTION AUTHORITY
+example.com. 86400 IN NS new-ns.example.com.
+SECTION ADDITIONAL
+new-ns.example.com. 86400 IN A 172.16.0.1
+ENTRY_END
+
+; a reply from cache
+STEP 190 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+; recursion happens here.
+STEP 200 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ttl
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 3600 IN A 10.2.2.2
+SECTION AUTHORITY
+example.com. 86400 IN NS new-ns.example.com.
+SECTION ADDITIONAL
+new-ns.example.com. 86400 IN A 172.16.0.1
+ENTRY_END
+
+SCENARIO_END
SECTION ANSWER
www.example.com. 3600 IN A 88.88.88.88
SECTION AUTHORITY
-example.com. 3600 IN NS ns.example.com.
+example.com. 1240 IN NS ns.example.com.
SECTION ADDITIONAL
-ns.example.com. 3600 IN A 8.8.8.8
+ns.example.com. 820 IN A 8.8.8.8
ENTRY_END
SCENARIO_END
SECTION ANSWER
bla.example.com. IN A 10.20.30.140
SECTION AUTHORITY
- example.com. 200 IN NS ns.example.com.
+ example.com. 100 IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. IN A 10.20.30.50
ENTRY_END
/* store results in cache */
if(qstate->query_flags&BIT_RD) {
if(!dns_cache_store(qstate->env, &vq->orig_msg->qinfo,
- vq->orig_msg->rep, 0, qstate->prefetch_leeway)) {
+ vq->orig_msg->rep, 0, qstate->prefetch_leeway, NULL)) {
log_err("out of memory caching validator results");
}
} else {
/* for a referral, store the verified RRsets */
/* and this does not get prefetched, so no leeway */
if(!dns_cache_store(qstate->env, &vq->orig_msg->qinfo,
- vq->orig_msg->rep, 1, 0)) {
+ vq->orig_msg->rep, 1, 0, NULL)) {
log_err("out of memory caching validator results");
}
}