/* This either results in a query restart (CNAME cache response), a
* terminating response (ANSWER), or a cache miss (null). */
-
+
+ /* Check RPZ for override */
+ if(qstate->env->auth_zones) {
+ /* apply rpz qname triggers, like after cname */
+ struct dns_msg* forged_response =
+ rpz_callback_from_iterator_cname(qstate, iq);
+ if(forged_response) {
+ uint8_t* sname = 0;
+ size_t slen = 0;
+ int count = 0;
+ while(forged_response && reply_find_rrset_section_an(
+ forged_response->rep, iq->qchase.qname,
+ iq->qchase.qname_len, LDNS_RR_TYPE_CNAME,
+ iq->qchase.qclass) &&
+ iq->qchase.qtype != LDNS_RR_TYPE_CNAME &&
+ count++ < ie->max_query_restarts) {
+ /* another cname to follow */
+ if(!handle_cname_response(qstate, iq, forged_response,
+ &sname, &slen)) {
+ errinf(qstate, "malloc failure, CNAME info");
+ return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+ }
+ iq->qchase.qname = sname;
+ iq->qchase.qname_len = slen;
+ forged_response =
+ rpz_callback_from_iterator_cname(qstate, iq);
+ }
+ if(forged_response != NULL) {
+ qstate->ext_state[id] = module_finished;
+ qstate->return_rcode = LDNS_RCODE_NOERROR;
+ qstate->return_msg = forged_response;
+ iq->response = forged_response;
+ next_state(iq, FINISHED_STATE);
+ if(!iter_prepend(iq, qstate->return_msg, qstate->region)) {
+ log_err("rpz: after cached cname, prepend rrsets: out of memory");
+ return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+ }
+ qstate->return_msg->qinfo = qstate->qinfo;
+ return 0;
+ }
+ /* Follow the CNAME response */
+ iq->dp = NULL;
+ iq->refetch_glue = 0;
+ iq->query_restart_count++;
+ iq->sent_count = 0;
+ iq->dp_target_count = 0;
+ sock_list_insert(&qstate->reply_origin, NULL, 0, qstate->region);
+ if(qstate->env->cfg->qname_minimisation)
+ iq->minimisation_state = INIT_MINIMISE_STATE;
+ return next_state(iq, INIT_REQUEST_STATE);
+ }
+ }
+
if (iter_stub_fwd_no_cache(qstate, &iq->qchase, &dpname, &dpnamelen)) {
/* Asked to not query cache. */
verbose(VERB_ALGO, "no-cache set, going to the network");
}
iq->qchase.qname = sname;
iq->qchase.qname_len = slen;
- if(qstate->env->auth_zones) {
- /* apply rpz qname triggers after cname */
- struct dns_msg* forged_response =
- rpz_callback_from_iterator_cname(qstate, iq);
- int count = 0;
- while(forged_response && reply_find_rrset_section_an(
- forged_response->rep, iq->qchase.qname,
- iq->qchase.qname_len, LDNS_RR_TYPE_CNAME,
- iq->qchase.qclass) &&
- iq->qchase.qtype != LDNS_RR_TYPE_CNAME &&
- count++ < ie->max_query_restarts) {
- /* another cname to follow */
- if(!handle_cname_response(qstate, iq, forged_response,
- &sname, &slen)) {
- errinf(qstate, "malloc failure, CNAME info");
- return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
- }
- iq->qchase.qname = sname;
- iq->qchase.qname_len = slen;
- forged_response =
- rpz_callback_from_iterator_cname(qstate, iq);
- }
- if(forged_response != NULL) {
- qstate->ext_state[id] = module_finished;
- qstate->return_rcode = LDNS_RCODE_NOERROR;
- qstate->return_msg = forged_response;
- iq->response = forged_response;
- next_state(iq, FINISHED_STATE);
- if(!iter_prepend(iq, qstate->return_msg, qstate->region)) {
- log_err("rpz: after cached cname, prepend rrsets: out of memory");
- return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
- }
- qstate->return_msg->qinfo = qstate->qinfo;
- return 0;
- }
- }
/* This *is* a query restart, even if it is a cheap
* one. */
iq->dp = NULL;
iq->minimisation_state = INIT_MINIMISE_STATE;
return next_state(iq, INIT_REQUEST_STATE);
}
-
/* if from cache, NULL, else insert 'cache IP' len=0 */
if(qstate->reply_origin)
sock_list_insert(&qstate->reply_origin, NULL, 0, qstate->region);
/** Find entry for RR type in the list of rrsets for the clientip. */
static struct local_rrset*
rpz_find_synthesized_rrset(uint16_t qtype,
- struct clientip_synthesized_rr* data)
+ struct clientip_synthesized_rr* data, int alias_ok)
{
- struct local_rrset* cursor = data->data;
+ struct local_rrset* cursor = data->data, *cname = NULL;
while( cursor != NULL) {
struct packed_rrset_key* packed_rrset = &cursor->rrset->rk;
if(htons(qtype) == packed_rrset->type) {
return cursor;
}
+ if(ntohs(packed_rrset->type) == LDNS_RR_TYPE_CNAME && alias_ok)
+ cname = cursor;
cursor = cursor->next;
}
+ if(alias_ok)
+ return cname;
return NULL;
}
struct local_rrset* rrset;
struct packed_rrset_data* d;
size_t index;
- rrset = rpz_find_synthesized_rrset(rr_type, node);
+ rrset = rpz_find_synthesized_rrset(rr_type, node, 0);
if(rrset == NULL)
return 0; /* type not found, ignore */
d = (struct packed_rrset_data*)rrset->rrset->entry.data;
}
/* check query type / rr type */
- rrset = rpz_find_synthesized_rrset(qinfo->qtype, raddr);
+ rrset = rpz_find_synthesized_rrset(qinfo->qtype, raddr, 1);
if(rrset == NULL) {
verbose(VERB_ALGO, "rpz: unable to find local-data for query");
rrset_count = 0;
{
struct local_rrset* rrset;
- rrset = rpz_find_synthesized_rrset(qi->qtype, data);
+ rrset = rpz_find_synthesized_rrset(qi->qtype, data, 1);
if(rrset == NULL) {
verbose(VERB_ALGO, "rpz: nsip: no matching local data found");
return NULL;
key.namelabs = dname_count_labels(qinfo->qname);
ld = (struct local_data*)rbtree_search(&z->data, &key.node);
if(ld == NULL) {
- verbose(VERB_ALGO, "rpz: qname after cname: name not found");
+ verbose(VERB_ALGO, "rpz: qname: name not found");
return NULL;
}
rrset = local_data_find_type(ld, qinfo->qtype, 1);
if(rrset == NULL) {
- verbose(VERB_ALGO, "rpz: qname after cname: type not found");
+ verbose(VERB_ALGO, "rpz: qname: type not found");
return NULL;
}
return rpz_synthesize_localdata_from_rrset(r, ms, qinfo, rrset, az);
dname_str(is->qchase.qname, nm);
dname_str(z->name, zn);
if(strcmp(zn, nm) != 0)
- verbose(VERB_ALGO, "rpz: qname trigger after cname %s on %s, with action=%s",
+ verbose(VERB_ALGO, "rpz: qname trigger %s on %s, with action=%s",
zn, nm, rpz_action_to_string(localzone_type_to_rpz_action(lzt)));
else
- verbose(VERB_ALGO, "rpz: qname trigger after cname %s, with action=%s",
+ verbose(VERB_ALGO, "rpz: qname trigger %s, with action=%s",
nm, rpz_action_to_string(localzone_type_to_rpz_action(lzt)));
}
switch(localzone_type_to_rpz_action(lzt)) {
ms->rpz_passthru = 1;
break;
default:
- verbose(VERB_ALGO, "rpz: qname trigger after cname: bug: unhandled or invalid action: '%s'",
+ verbose(VERB_ALGO, "rpz: qname trigger: bug: unhandled or invalid action: '%s'",
rpz_action_to_string(localzone_type_to_rpz_action(lzt)));
ret = NULL;
}
--- /dev/null
+; config options
+server:
+ module-config: "respip validator iterator"
+ target-fetch-policy: "0 0 0 0 0"
+ qname-minimisation: no
+ access-control: 192.0.0.0/8 allow
+
+rpz:
+ name: "rpz.example.com."
+ rpz-log: yes
+ rpz-log-name: "rpz.example.com"
+ zonefile:
+TEMPFILE_NAME rpz.example.com
+TEMPFILE_CONTENTS rpz.example.com
+$ORIGIN example.com.
+rpz 3600 IN SOA ns1.rpz.example.com. hostmaster.rpz.example.com. (
+ 1379078166 28800 7200 604800 7200 )
+ 3600 IN NS ns1.rpz.example.com.
+ 3600 IN NS ns2.rpz.example.com.
+$ORIGIN rpz.example.com.
+www.gotham.a A 1.2.3.61
+www.gotham2.a CNAME g2.target.a.
+g2.target.a A 1.2.3.62
+www.gotham3.a CNAME g3.target.a.
+g3.target.a CNAME g3b.target.a.
+g3b.target.a A 1.2.3.63
+www.gotham4.a CNAME g4.target.a.
+g4.target.a CNAME g4b.target.a.
+g4b.target.a CNAME g4c.target.a.
+g4c.target.a A 1.2.3.64
+w2.gotham5.a A 1.2.3.65
+w2.gotham6.a CNAME g6.target.a.
+g6.target.a A 1.2.3.66
+w2.gotham7.a CNAME g7.target.a.
+g7.target.a CNAME g7b.target.a.
+g7b.target.a A 1.2.3.66
+; ns1.gotham8.a
+32.48.30.20.10.rpz-nsip A 1.2.3.68
+; ns1.gotham9.a
+32.49.30.20.10.rpz-nsip CNAME g9.target.a.
+g9.target.a A 1.2.3.69
+; ns1.gotham10.a
+32.50.30.20.10.rpz-nsip CNAME g10.target.a.
+g10.target.a CNAME g10b.target.a.
+g10b.target.a A 1.2.3.70
+www.gotham11.a CNAME g11.target.a.
+www.gotham12.a CNAME g12.target.a.
+g12.target.a CNAME g12b.target.a.
+www.gotham13.a CNAME g13.target.a.
+g13.target.a CNAME g13b.target.a.
+g13b.target.a CNAME g13c.target.a.
+w2.gotham14.a CNAME g14.target.a.
+w2.gotham15.a CNAME g15.target.a.
+g15.target.a CNAME g15b.target.a.
+; ns1.gotham16.a
+32.56.30.20.10.rpz-nsip CNAME g16.target.a.
+; ns1.gotham17.a
+32.57.30.20.10.rpz-nsip CNAME g17.target.a.
+g17.target.a CNAME g17b.target.a.
+TEMPFILE_END
+
+stub-zone:
+ name: "a."
+ stub-addr: 10.20.30.40
+CONFIG_END
+
+SCENARIO_BEGIN Test RPZ handling of CNAMEs.
+
+; a.
+RANGE_BEGIN 0 1000
+ ADDRESS 10.20.30.40
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+gotham5.a. IN NS
+SECTION AUTHORITY
+gotham5.a. NS ns1.gotham5.a.
+SECTION ADDITIONAL
+ns1.gotham5.a. A 10.20.30.45
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+gotham6.a. IN NS
+SECTION AUTHORITY
+gotham6.a. NS ns1.gotham6.a.
+SECTION ADDITIONAL
+ns1.gotham6.a. A 10.20.30.46
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+gotham7.a. IN NS
+SECTION AUTHORITY
+gotham7.a. NS ns1.gotham7.a.
+SECTION ADDITIONAL
+ns1.gotham7.a. A 10.20.30.47
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+gotham8.a. IN NS
+SECTION AUTHORITY
+gotham8.a. NS ns1.gotham8.a.
+SECTION ADDITIONAL
+ns1.gotham8.a. A 10.20.30.48
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+gotham9.a. IN NS
+SECTION AUTHORITY
+gotham9.a. NS ns1.gotham9.a.
+SECTION ADDITIONAL
+ns1.gotham9.a. A 10.20.30.49
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+gotham10.a. IN NS
+SECTION AUTHORITY
+gotham10.a. NS ns1.gotham10.a.
+SECTION ADDITIONAL
+ns1.gotham10.a. A 10.20.30.50
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+gotham14.a. IN NS
+SECTION AUTHORITY
+gotham14.a. NS ns1.gotham14.a.
+SECTION ADDITIONAL
+ns1.gotham14.a. A 10.20.30.54
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+gotham15.a. IN NS
+SECTION AUTHORITY
+gotham15.a. NS ns1.gotham15.a.
+SECTION ADDITIONAL
+ns1.gotham15.a. A 10.20.30.55
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+gotham16.a. IN NS
+SECTION AUTHORITY
+gotham16.a. NS ns1.gotham16.a.
+SECTION ADDITIONAL
+ns1.gotham16.a. A 10.20.30.56
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+gotham17.a. IN NS
+SECTION AUTHORITY
+gotham17.a. NS ns1.gotham17.a.
+SECTION ADDITIONAL
+ns1.gotham17.a. A 10.20.30.57
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+target.a. IN A
+SECTION ANSWER
+target.a. IN A 1.2.3.6
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+g11.target.a. IN A
+SECTION ANSWER
+g11.target.a. IN A 1.2.3.11
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+g12b.target.a. IN A
+SECTION ANSWER
+g12b.target.a. A 1.2.3.12
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+g13c.target.a. IN A
+SECTION ANSWER
+g13c.target.a. A 1.2.3.13
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+g14.target.a. IN A
+SECTION ANSWER
+g14.target.a. A 1.2.3.14
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+g15b.target.a. IN A
+SECTION ANSWER
+g15b.target.a. A 1.2.3.15
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+g16.target.a. IN A
+SECTION ANSWER
+g16.target.a. A 1.2.3.16
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+g17b.target.a. IN A
+SECTION ANSWER
+g17b.target.a. A 1.2.3.17
+ENTRY_END
+RANGE_END
+
+; gotham5.a.
+RANGE_BEGIN 0 1000
+ ADDRESS 10.20.30.45
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+www.gotham5.a. IN A
+SECTION ANSWER
+www.gotham5.a. CNAME w2.gotham5.a.
+ENTRY_END
+RANGE_END
+
+; gotham6.a.
+RANGE_BEGIN 0 1000
+ ADDRESS 10.20.30.46
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.gotham6.a. IN A
+SECTION ANSWER
+www.gotham6.a. CNAME w2.gotham6.a.
+ENTRY_END
+RANGE_END
+
+; gotham7.a.
+RANGE_BEGIN 0 1000
+ ADDRESS 10.20.30.47
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+www.gotham7.a. IN A
+SECTION ANSWER
+www.gotham7.a. CNAME w2.gotham7.a.
+ENTRY_END
+RANGE_END
+
+; gotham14.a.
+RANGE_BEGIN 0 1000
+ ADDRESS 10.20.30.54
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+www.gotham14.a. IN A
+SECTION ANSWER
+www.gotham14.a. CNAME w2.gotham14.a.
+ENTRY_END
+RANGE_END
+
+; gotham15.a.
+RANGE_BEGIN 0 1000
+ ADDRESS 10.20.30.55
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+www.gotham15.a. IN A
+SECTION ANSWER
+www.gotham15.a. CNAME w2.gotham15.a.
+ENTRY_END
+RANGE_END
+
+; Test with zero rpz CNAMEs, rpz answer.
+STEP 10 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.gotham.a. IN A
+ENTRY_END
+
+STEP 11 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA AA NOERROR
+SECTION QUESTION
+www.gotham.a. IN A
+SECTION ANSWER
+www.gotham.a. A 1.2.3.61
+ENTRY_END
+
+; Test with one rpz CNAME, rpz answer.
+STEP 20 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.gotham2.a. IN A
+ENTRY_END
+
+STEP 21 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA AA NOERROR
+SECTION QUESTION
+www.gotham2.a. IN A
+SECTION ANSWER
+www.gotham2.a. CNAME g2.target.a.
+g2.target.a. A 1.2.3.62
+ENTRY_END
+
+; Test with two rpz CNAMEs, rpz answer.
+STEP 30 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.gotham3.a. IN A
+ENTRY_END
+
+STEP 31 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA AA NOERROR
+SECTION QUESTION
+www.gotham3.a. IN A
+SECTION ANSWER
+www.gotham3.a. CNAME g3.target.a.
+g3.target.a. CNAME g3b.target.a.
+g3b.target.a. A 1.2.3.63
+ENTRY_END
+
+; Test with three rpz CNAMEs, rpz answer.
+STEP 40 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.gotham4.a. IN A
+ENTRY_END
+
+STEP 41 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA AA NOERROR
+SECTION QUESTION
+www.gotham4.a. IN A
+SECTION ANSWER
+www.gotham4.a. CNAME g4.target.a.
+g4.target.a. CNAME g4b.target.a.
+g4b.target.a. CNAME g4c.target.a.
+g4c.target.a. A 1.2.3.64
+ENTRY_END
+
+; Test with a CNAME from upstream, zero rpz CNAMEs, rpz answer.
+STEP 50 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.gotham5.a. IN A
+ENTRY_END
+
+STEP 51 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA AA NOERROR
+SECTION QUESTION
+www.gotham5.a. IN A
+SECTION ANSWER
+www.gotham5.a. CNAME w2.gotham5.a.
+w2.gotham5.a. A 1.2.3.65
+ENTRY_END
+
+; Test with a CNAME from upstream, one rpz CNAME, rpz answer.
+STEP 60 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.gotham6.a. IN A
+ENTRY_END
+
+STEP 61 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA AA NOERROR
+SECTION QUESTION
+www.gotham6.a. IN A
+SECTION ANSWER
+www.gotham6.a. CNAME w2.gotham6.a.
+w2.gotham6.a. CNAME g6.target.a.
+g6.target.a. A 1.2.3.66
+ENTRY_END
+
+; Test with a CNAME from upstream, two rpz CNAMEs, rpz answer.
+STEP 70 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.gotham7.a. IN A
+ENTRY_END
+
+STEP 71 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA AA NOERROR
+SECTION QUESTION
+www.gotham7.a. IN A
+SECTION ANSWER
+www.gotham7.a. CNAME w2.gotham7.a.
+w2.gotham7.a. CNAME g7.target.a.
+g7.target.a. CNAME g7b.target.a.
+g7b.target.a. A 1.2.3.66
+ENTRY_END
+
+; Test with a CNAME from cache, zero rpz CNAMEs, rpz answer.
+STEP 80 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.gotham5.a. IN A
+ENTRY_END
+
+STEP 81 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA AA NOERROR
+SECTION QUESTION
+www.gotham5.a. IN A
+SECTION ANSWER
+www.gotham5.a. CNAME w2.gotham5.a.
+w2.gotham5.a. A 1.2.3.65
+ENTRY_END
+
+; Test with a CNAME from cache, one rpz CNAME, rpz answer.
+STEP 90 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.gotham6.a. IN A
+ENTRY_END
+
+STEP 91 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA AA NOERROR
+SECTION QUESTION
+www.gotham6.a. IN A
+SECTION ANSWER
+www.gotham6.a. CNAME w2.gotham6.a.
+w2.gotham6.a. CNAME g6.target.a.
+g6.target.a. A 1.2.3.66
+ENTRY_END
+
+; Test with a CNAME from cache, two rpz CNAMEs, rpz answer.
+STEP 100 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.gotham7.a. IN A
+ENTRY_END
+
+STEP 101 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA AA NOERROR
+SECTION QUESTION
+www.gotham7.a. IN A
+SECTION ANSWER
+www.gotham7.a. CNAME w2.gotham7.a.
+w2.gotham7.a. CNAME g7.target.a.
+g7.target.a. CNAME g7b.target.a.
+g7b.target.a. A 1.2.3.66
+ENTRY_END
+
+; Test with lookup from nameserver, zero rpz CNAMEs, rpz nsip answer.
+STEP 110 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.gotham8.a. IN A
+ENTRY_END
+
+STEP 111 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA AA NOERROR
+SECTION QUESTION
+www.gotham8.a. IN A
+SECTION ANSWER
+www.gotham8.a. A 1.2.3.68
+ENTRY_END
+
+; Test with lookup from nameserver, one rpz CNAME, rpz nsip answer.
+STEP 120 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.gotham9.a. IN A
+ENTRY_END
+
+STEP 121 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA AA NOERROR
+SECTION QUESTION
+www.gotham9.a. IN A
+SECTION ANSWER
+www.gotham9.a. CNAME g9.target.a.
+g9.target.a. A 1.2.3.69
+ENTRY_END
+
+; Test with lookup from nameserver, two rpz CNAMEs, rpz nsip answer.
+STEP 130 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.gotham10.a. IN A
+ENTRY_END
+
+STEP 131 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA AA NOERROR
+SECTION QUESTION
+www.gotham10.a. IN A
+SECTION ANSWER
+www.gotham10.a. CNAME g10.target.a.
+g10.target.a. CNAME g10b.target.a.
+g10b.target.a. A 1.2.3.70
+ENTRY_END
+
+; Test with one rpz CNAME, upstream answer.
+STEP 140 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.gotham11.a. IN A
+ENTRY_END
+
+STEP 141 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA AA NOERROR
+SECTION QUESTION
+www.gotham11.a. IN A
+SECTION ANSWER
+www.gotham11.a. CNAME g11.target.a.
+g11.target.a. A 1.2.3.11
+ENTRY_END
+
+; Test with two rpz CNAMEs, upstream answer.
+STEP 150 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.gotham12.a. IN A
+ENTRY_END
+
+STEP 151 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA AA NOERROR
+SECTION QUESTION
+www.gotham12.a. IN A
+SECTION ANSWER
+www.gotham12.a. CNAME g12.target.a.
+g12.target.a. CNAME g12b.target.a.
+g12b.target.a. A 1.2.3.12
+ENTRY_END
+
+; Test with three rpz CNAMEs, upstream answer.
+STEP 160 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.gotham13.a. IN A
+ENTRY_END
+
+STEP 161 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA AA NOERROR
+SECTION QUESTION
+www.gotham13.a. IN A
+SECTION ANSWER
+www.gotham13.a. CNAME g13.target.a.
+g13.target.a. CNAME g13b.target.a.
+g13b.target.a. CNAME g13c.target.a.
+g13c.target.a. A 1.2.3.13
+ENTRY_END
+
+; Test with a CNAME from upstream, one rpz CNAME, upstream answer.
+STEP 170 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.gotham14.a. IN A
+ENTRY_END
+
+STEP 171 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.gotham14.a. IN A
+SECTION ANSWER
+www.gotham14.a. CNAME w2.gotham14.a.
+w2.gotham14.a. CNAME g14.target.a.
+g14.target.a. A 1.2.3.14
+ENTRY_END
+
+; Test with a CNAME from upstream, two rpz CNAMEs, upstream answer.
+STEP 180 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.gotham15.a. IN A
+ENTRY_END
+
+STEP 181 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.gotham15.a. IN A
+SECTION ANSWER
+www.gotham15.a. CNAME w2.gotham15.a.
+w2.gotham15.a. CNAME g15.target.a.
+g15.target.a. CNAME g15b.target.a.
+g15b.target.a. A 1.2.3.15
+ENTRY_END
+
+; Test with a CNAME from cache, one rpz CNAME, upstream answer.
+STEP 190 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.gotham14.a. IN A
+ENTRY_END
+
+STEP 191 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.gotham14.a. IN A
+SECTION ANSWER
+www.gotham14.a. CNAME w2.gotham14.a.
+w2.gotham14.a. CNAME g14.target.a.
+g14.target.a. A 1.2.3.14
+ENTRY_END
+
+; Test with a CNAME from cache, two rpz CNAMEs, upstream answer.
+STEP 200 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.gotham15.a. IN A
+ENTRY_END
+
+STEP 201 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.gotham15.a. IN A
+SECTION ANSWER
+www.gotham15.a. CNAME w2.gotham15.a.
+w2.gotham15.a. CNAME g15.target.a.
+g15.target.a. CNAME g15b.target.a.
+g15b.target.a. A 1.2.3.15
+ENTRY_END
+
+; Test with lookup from nameserver, one rpz nsip CNAME, upstream answer.
+STEP 210 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.gotham16.a. IN A
+ENTRY_END
+
+STEP 211 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.gotham16.a. IN A
+SECTION ANSWER
+www.gotham16.a. CNAME g16.target.a.
+g16.target.a. A 1.2.3.16
+ENTRY_END
+
+; Test with lookup from nameserver, two rpz nsip CNAMEs, upstream answer.
+STEP 220 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.gotham17.a. IN A
+ENTRY_END
+
+STEP 221 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.gotham17.a. IN A
+SECTION ANSWER
+www.gotham17.a. CNAME g17.target.a.
+g17.target.a. CNAME g17b.target.a.
+g17b.target.a. A 1.2.3.17
+ENTRY_END
+
+SCENARIO_END