if(!go_on)
return 1; /* skip this one, not all references satisfied */
- if(!dns_cache_store(&worker->env, &qinf, &rep, 0, 0, NULL)) {
+ if(!dns_cache_store(&worker->env, &qinf, &rep, 0, 0, 0, NULL)) {
log_warn("error out of memory");
return 0;
}
16 February 2012: Wouter
- iter_hints is now thread-owned in module env, and thus threadsafe.
+ - Fix prefetch and sticky NS, now the prefetch works. It picks
+ nameservers that 'would be valid in the future', and if this makes
+ the NS timeout, it updates that NS by asking delegation from the
+ parent again. If child NS has longer TTL, that TTL does not get
+ refreshed from the lookup to the child nameserver.
15 February 2012: Wouter
- Fix forward-zone memory, uses malloc and frees original root dp.
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, int pside,
struct regional* region)
{
return dns_cache_store(env, msgqinf, msgrep, is_referral, leeway,
- region);
+ pside, 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 pside: true if dp is parentside, thus message is 'fresh' and NS
+ * can be prefetch-updates.
* @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, int pside,
struct regional* region);
/**
/* 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, NULL)) {
+ if(!iter_dns_store(qstate->env, &qstate->qinfo, &err, 0, 0, 0, NULL)) {
log_err("error_response_cache: could not store error (nomem)");
}
return error_response(qstate, id, rcode);
if(delname)
iq->dp = dns_cache_find_delegation(qstate->env, delname,
delnamelen, iq->qchase.qtype, iq->qchase.qclass,
- qstate->region, &iq->deleg_msg, *qstate->env->now);
+ qstate->region, &iq->deleg_msg,
+ *qstate->env->now+qstate->prefetch_leeway);
else iq->dp = NULL;
/* If the cache has returned nothing, then we have a
} else {
subiq->dp = dns_cache_find_delegation(qstate->env,
name, namelen, qtype, qclass, subq->region,
- &subiq->deleg_msg, *qstate->env->now);
+ &subiq->deleg_msg,
+ *qstate->env->now+subq->prefetch_leeway);
/* if no dp, then it's from root, refetch unneeded */
if(subiq->dp) {
subiq->dnssec_expected = iter_indicates_dnssec(
}
if(!iter_dns_store(qstate->env, &iq->response->qinfo,
iq->response->rep, 0, qstate->prefetch_leeway,
+ iq->dp&&iq->dp->has_parent_side_NS,
qstate->region))
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
/* close down outstanding requests to be discarded */
/* 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, NULL))
+ iq->response->rep, 1, 0, 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, NULL))
+ iq->response->rep, 1, qstate->prefetch_leeway,
+ iq->dp&&iq->dp->has_parent_side_NS,
+ NULL))
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
/* set the current request's qname to the new value. */
iq->qchase.qname = sname;
if(qstate->query_flags&BIT_RD) {
if(!iter_dns_store(qstate->env, &qstate->qinfo,
iq->response->rep, 0, qstate->prefetch_leeway,
+ iq->dp&&iq->dp->has_parent_side_NS,
qstate->region))
return error_response(qstate, id,
LDNS_RCODE_SERVFAIL);
* 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.
+ * @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
* @param region: for qrep allocs.
*/
static void
store_rrsets(struct module_env* env, struct reply_info* rep, uint32_t now,
- uint32_t leeway, struct reply_info* qrep, struct regional* region)
+ uint32_t leeway, int pside, struct reply_info* qrep,
+ struct regional* region)
{
size_t i;
/* see if rrset already exists in cache, if not insert it. */
/* update ref if it was in the cache */
switch(rrset_cache_update(env->rrset_cache, &rep->ref[i],
env->alloc, now + ((ntohs(rep->ref[i].key->rk.type)==
- LDNS_RR_TYPE_NS)?0:leeway))) {
+ LDNS_RR_TYPE_NS && !pside)?0:leeway))) {
case 0: /* ref unchanged, item inserted */
break;
case 2: /* ref updated, cache is superior */
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, int pside,
struct reply_info* qrep, struct regional* region)
{
struct msgreply_entry* e;
/* 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, qrep, region);
+ store_rrsets(env, rep, *env->now, leeway, pside, 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, int pside,
struct regional* region)
{
struct reply_info* rep = NULL;
/* no leeway for typeNS */
(void)rrset_cache_update(env->rrset_cache, &ref,
env->alloc, *env->now +
- ((ntohs(ref.key->rk.type)==LDNS_RR_TYPE_NS)?
- 0:leeway));
+ ((ntohs(ref.key->rk.type)==LDNS_RR_TYPE_NS
+ && !pside) ? 0:leeway));
}
free(rep);
return 1;
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, msgrep, region);
+ dns_cache_store_msg(env, &qinf, h, rep, leeway, pside, 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 pside: if true, information came from a server which was fetched
+ * from the parentside of the zonecut. This means that the type NS
+ * can be updated to full TTL even in prefetch situations.
* @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, int pside,
struct regional* region);
/**
* 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 pside: if true, information came from a server which was fetched
+ * from the parentside of the zonecut. This means that the type NS
+ * can be updated to full TTL even in prefetch situations.
* @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, int pside,
struct reply_info* qrep, struct regional* region);
/**
SECTION ANSWER
www.example.com. 3600 IN A 10.20.30.40
SECTION AUTHORITY
-; NS rrset TTL not updated to avoid sticky-NS (ghost domain) problem.
-example.com. 360 IN NS ns.example.com.
+; NS rrset picked up from parent-NS (the child-NS timed out at now+prefetch)
+example.com. 3600 IN NS ns.example.com.
SECTION ADDITIONAL
ns.example.com. 3600 IN A 1.2.3.4
ENTRY_END
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
-www.example.com. 300 IN A 10.1.1.1
+www.example.com. 86400 IN A 10.2.2.2
SECTION AUTHORITY
-example.com. 30 IN NS old-ns.example.com.
+example.com. 86400 IN NS new-ns.example.com.
SECTION ADDITIONAL
-old-ns.example.com. 300 IN A 192.168.0.1
+new-ns.example.com. 86400 IN A 172.16.0.1
ENTRY_END
; the NS record times out after 31 seconds.
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
-www.example.com. 86400 IN A 10.2.2.2
+www.example.com. 86369 IN A 10.2.2.2
SECTION AUTHORITY
-example.com. 86400 IN NS new-ns.example.com.
+example.com. 86369 IN NS new-ns.example.com.
SECTION ADDITIONAL
-new-ns.example.com. 86400 IN A 172.16.0.1
+new-ns.example.com. 86369 IN A 172.16.0.1
ENTRY_END
; a reply from cache
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
-www.example.com. 86400 IN A 10.2.2.2
+www.example.com. 86369 IN A 10.2.2.2
SECTION AUTHORITY
-example.com. 86400 IN NS new-ns.example.com.
+example.com. 86369 IN NS new-ns.example.com.
SECTION ADDITIONAL
-new-ns.example.com. 86400 IN A 172.16.0.1
+new-ns.example.com. 86369 IN A 172.16.0.1
ENTRY_END
SCENARIO_END
--- /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 from child nameserver
+; child NS record has longer TTL than A record and is thus valid for prefetch.
+
+; K.ROOT-SERVERS.NET.
+RANGE_BEGIN 0 100
+ 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 qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.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.
+RANGE_BEGIN 0 100
+ 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 qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION AUTHORITY
+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 40
+ 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
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 1800 IN A 10.20.30.40
+SECTION AUTHORITY
+example.com. 3600 IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. 3600 IN A 1.2.3.4
+ENTRY_END
+RANGE_END
+
+; ns.example.com.
+RANGE_BEGIN 50 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
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 1800 IN A 10.20.30.40
+SECTION AUTHORITY
+example.com. 3600 IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. 3600 IN A 1.2.3.4
+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. 1800 IN A 10.20.30.40
+SECTION AUTHORITY
+example.com. 3600 IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. 3600 IN A 1.2.3.4
+ENTRY_END
+
+; after 900 secs still the cached answer
+STEP 20 TIME_PASSES ELAPSE 900
+
+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. 900 IN A 10.20.30.40
+SECTION AUTHORITY
+example.com. 2700 IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. 2700 IN A 1.2.3.4
+ENTRY_END
+
+; after 720 we are 180 seconds before the expiry
+; (the authority changes behind the scenes to detect new lookup)
+STEP 50 TIME_PASSES ELAPSE 720
+
+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. 180 IN A 10.20.30.40
+SECTION AUTHORITY
+example.com. 1980 IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. 1980 IN A 1.2.3.4
+ENTRY_END
+STEP 80 TRAFFIC
+; let traffic flow for prefetch to happen
+
+; above a cache reply with 10% of the original TTL
+; but the actual cache is changed, try to get that
+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. 1800 IN A 10.20.30.40
+SECTION AUTHORITY
+; The NS rrset (from the child-side NS) is not updated but keeps counting down
+example.com. 1980 IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. 1980 IN A 1.2.3.4
+ENTRY_END
+
+SCENARIO_END
ENTRY_END
ENTRY_BEGIN
-MATCH opcode qtype qname
-ADJUST copy_id
+MATCH opcode subdomain
+ADJUST copy_id copy_query
REPLY QR NOERROR
SECTION QUESTION
-www.example.com. IN A
+example.com. IN A
SECTION AUTHORITY
com. IN NS a.gtld-servers.net.
SECTION ADDITIONAL
example.com. IN NS
ENTRY_END
; recursion happens here.
+
+; because the prefetch+current makes old-NS expired, new delegation is picked up
STEP 91 CHECK_ANSWER
ENTRY_BEGIN
MATCH all ttl
SECTION QUESTION
example.com. IN NS
SECTION ANSWER
-; this record is unchanged even though it now points to the
-; new registrant. Thus it keeps counting down.
-example.com. 360 IN NS ns.example.com.
+example.com. 3600 IN NS ns.example.com.
SECTION AUTHORITY
SECTION ADDITIONAL
ns.example.com. 3600 IN A 8.8.8.8
ENTRY_END
-
-; after 360 + 2000 we are after the change to new owner.
STEP 100 TIME_PASSES ELAPSE 2360
-; the NS record should have timed out.
STEP 120 QUERY
ENTRY_BEGIN
REPLY RD
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. 1240 IN A 8.8.8.8
ENTRY_END
/* store results in cache */
if(qstate->query_flags&BIT_RD) {
+ /* if secure, this will override cache anyway, no need
+ * to check if from parentNS */
if(!dns_cache_store(qstate->env, &vq->orig_msg->qinfo,
- vq->orig_msg->rep, 0, qstate->prefetch_leeway, NULL)) {
+ vq->orig_msg->rep, 0, qstate->prefetch_leeway, 0, 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, NULL)) {
+ vq->orig_msg->rep, 1, 0, 0, NULL)) {
log_err("out of memory caching validator results");
}
}