if(!qstate->return_msg || !qstate->return_msg->rep)
return 0;
+ /* do not store failures like SERVFAIL in the cachedb, this avoids
+ * overwriting expired, valid, content with broken content. */
+ if(FLAGS_GET_RCODE(qstate->return_msg->rep->flags) !=
+ LDNS_RCODE_NOERROR &&
+ FLAGS_GET_RCODE(qstate->return_msg->rep->flags) !=
+ LDNS_RCODE_NXDOMAIN &&
+ 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
--- /dev/null
+; config options
+server:
+ target-fetch-policy: "0 0 0 0 0"
+ qname-minimisation: no
+ minimal-responses: no
+ ;serve-expired: yes
+ module-config: "cachedb iterator"
+
+cachedb:
+ backend: "testframe"
+ secret-seed: "testvalue"
+
+stub-zone:
+ name: "."
+ stub-addr: 193.0.14.129
+CONFIG_END
+
+SCENARIO_BEGIN Test cachedb store and servfail reply from cname.
+; the servfail reply should not overwrite the cache contents.
+
+; 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 subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+com. IN NS
+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 subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN NS
+SECTION AUTHORITY
+example.com. IN NS ns2.example.com.
+SECTION ADDITIONAL
+ns2.example.com. IN A 1.2.3.5
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+foo.com. IN NS
+SECTION AUTHORITY
+foo.com. IN NS ns.example.com.
+ENTRY_END
+RANGE_END
+
+; ns2.example.com.
+RANGE_BEGIN 0 20
+ ADDRESS 1.2.3.5
+ENTRY_BEGIN
+MATCH opcode qname qtype
+REPLY QR AA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 10 IN A 1.2.3.4
+ENTRY_END
+RANGE_END
+
+; ns2.example.com., now failing
+RANGE_BEGIN 20 100
+ ADDRESS 1.2.3.5
+ENTRY_BEGIN
+MATCH opcode qname qtype
+REPLY QR AA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 10 IN CNAME foo.example.com.
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qname qtype
+REPLY QR AA SERVFAIL
+SECTION QUESTION
+foo.example.com. IN A
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qname qtype
+REPLY QR AA SERVFAIL
+SECTION QUESTION
+ns2.example.com. IN A
+SECTION ANSWER
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qname qtype
+REPLY QR AA SERVFAIL
+SECTION QUESTION
+ns2.example.com. IN AAAA
+SECTION ANSWER
+ENTRY_END
+RANGE_END
+
+; get and entry in cache, to make it expired.
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; get the answer for it
+STEP 10 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 10 IN A 1.2.3.4
+ENTRY_END
+
+; it is now expired
+STEP 20 TIME_PASSES ELAPSE 20
+
+; get a servfail in cache for the destination
+STEP 30 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+foo.example.com. IN A
+ENTRY_END
+
+STEP 40 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA SERVFAIL
+SECTION QUESTION
+foo.example.com. IN A
+ENTRY_END
+
+; the query is now a CNAME to servfail.
+; there is a valid, but expired, entry in cache.
+STEP 50 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+STEP 60 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA SERVFAIL
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 10 IN CNAME foo.example.com.
+ENTRY_END
+
+SCENARIO_END