finish up adding validator EDEs and other TODOs and fix tests with more specific EDE codes

This commit is contained in:
TCY16
2021-12-06 14:19:28 +01:00
parent db98a8b825
commit 65ee2f2c39
13 changed files with 91 additions and 64 deletions
+3 -3
View File
@@ -1582,12 +1582,12 @@ lookup_cache:
leeway = 0;
lock_rw_unlock(&e->lock);
// @TODO test this
// // stale answer?
// if (worker->env.cfg->serve_expired &&
// *worker->env.now >= ((struct reply_info*)e->data)->ttl) {
// // EDE Error Code 3 - Stale Answer
// EDNS_OPT_LIST_APPEND_EDE(&edns.opt_list_out, worker->scratchpad,
// LDNS_EDE_STALE_ANSWER, "");
// EDNS_OPT_LIST_APPEND_EDE(&edns.opt_list_out,
// worker->scratchpad, LDNS_EDE_STALE_ANSWER, "");
// }
// add EDNS struct?
+2 -3
View File
@@ -1813,9 +1813,8 @@ local_data_answer(struct local_zone* z, struct module_env* env,
return local_encode(qinfo, env, edns, repinfo, buf, temp, &r, 1,
LDNS_RCODE_NOERROR);
}
// @TODO add EDE?
return local_encode(qinfo, env, edns, repinfo, buf, temp, lr->rrset, 1,
LDNS_RCODE_NOERROR);
return local_encode_ede(z, qinfo, env, edns, repinfo, buf, temp, lr->rrset, 1,
LDNS_RCODE_NOERROR, LDNS_EDE_FORGED_ANSWER, "", do_ede);
}
/**
+2 -1
View File
@@ -1329,7 +1329,8 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
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))
r->edns.opt_list_inplace_cb_out = NULL;
// @TODO EDE?
/* internal server error (probably malloc failure) so no
* EDE (RFC8914) needed */
error_encode(r_buffer, LDNS_RCODE_SERVFAIL,
&m->s.qinfo, r->qid, r->qflags, &r->edns);
}
+2 -1
View File
@@ -1549,7 +1549,8 @@ find_match(struct entry* entries, uint8_t* query_pkt, size_t len,
int info_code = get_ede_info_code(query_pkt, len);
if(info_code == -1 ||
(uint16_t)info_code != p->ede_info_code) {
verbose(3, "bad EDE INFO-CODE\n");
verbose(3, "bad EDE INFO-CODE. Expected: %d, and got: %d\n",
p->ede_info_code, info_code);
continue;
}
}
+1 -1
View File
@@ -578,7 +578,7 @@ ENTRY_END
; recursion happens here.
STEP 10 CHECK_ANSWER
ENTRY_BEGIN
MATCH all_noedns ede=6
MATCH all_noedns ede=7
REPLY QR RD RA DO SERVFAIL
SECTION QUESTION
www.sub.example.com. IN A
+1 -1
View File
@@ -560,7 +560,7 @@ ENTRY_END
; recursion happens here.
STEP 10 CHECK_ANSWER
ENTRY_BEGIN
MATCH all_noedns ede=6
MATCH all_noedns ede=7
REPLY QR RD RA DO SERVFAIL
SECTION QUESTION
www.sub.example.com. IN A
+1 -1
View File
@@ -285,7 +285,7 @@ ENTRY_END
; recursion happens here.
STEP 10 CHECK_ANSWER
ENTRY_BEGIN
MATCH all_noedns ede=6
MATCH all_noedns ede=7
REPLY QR RD RA DO SERVFAIL
SECTION QUESTION
www.example.com. IN A
+1 -1
View File
@@ -127,7 +127,7 @@ ENTRY_END
; recursion happens here.
STEP 10 CHECK_ANSWER
ENTRY_BEGIN
MATCH all_noedns ede=6
MATCH all_noedns ede=12
REPLY QR RD RA DO SERVFAIL
SECTION QUESTION
ns1.example. IN MX
+1 -1
View File
@@ -223,7 +223,7 @@ ENTRY_END
; recursion happens here.
STEP 10 CHECK_ANSWER
ENTRY_BEGIN
MATCH all_noedns ede=6
MATCH all_noedns ede=10
REPLY QR RD RA DO SERVFAIL
SECTION QUESTION
www.sub.example.com. IN A
+6 -2
View File
@@ -1281,8 +1281,12 @@ void errinf_dname(struct module_qstate* qstate, const char* str,
* This string is malloced and has to be freed by caller.
*/
char* errinf_to_str_bogus(struct module_qstate* qstate);
// @TODO write this
/**
* Check the sldns_ede_code of the qstate.
* @param qstate: query state.
* @return LDNS_EDE_DNSSEC_BOGUS by default, or another sldns_ede_code
* if this is set.
*/
sldns_ede_code errinf_to_reason_bogus(struct module_qstate* qstate);
/**
+42 -16
View File
@@ -627,6 +627,15 @@ dnskey_verify_rrset(struct module_env* env, struct val_env* ve,
reason, NULL, section, qstate);
}
static enum sec_status
dnskey_verify_rrset_sig_ede(struct regional* region, sldns_buffer* buf,
struct val_env* ve, time_t now,
struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey,
size_t dnskey_idx, size_t sig_idx,
struct rbtree_type** sortree, int* buf_canon,
char** reason, sldns_ede_code *reason_bogus,
sldns_pkt_section section, struct module_qstate* qstate);
enum sec_status
dnskey_verify_rrset_ede(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey,
@@ -655,11 +664,10 @@ dnskey_verify_rrset_ede(struct module_env* env, struct val_env* ve,
tag != rrset_get_sig_keytag(rrset, i))
continue;
buf_canon = 0;
// @TODO should this be _ede verion as well?
sec = dnskey_verify_rrset_sig(env->scratch,
sec = dnskey_verify_rrset_sig_ede(env->scratch,
env->scratch_buffer, ve, *env->now, rrset,
dnskey, dnskey_idx, i, &sortree, &buf_canon, reason,
section, qstate);
reason_bogus, section, qstate);
if(sec == sec_status_secure)
return sec;
numchecked ++;
@@ -669,15 +677,6 @@ dnskey_verify_rrset_ede(struct module_env* env, struct val_env* ve,
return sec_status_bogus;
}
static enum sec_status
dnskey_verify_rrset_sig_ede(struct regional* region, sldns_buffer* buf,
struct val_env* ve, time_t now,
struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey,
size_t dnskey_idx, size_t sig_idx,
struct rbtree_type** sortree, int* buf_canon,
char** reason, sldns_ede_code *reason_bogus,
sldns_pkt_section section, struct module_qstate* qstate);
static enum sec_status
dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve,
time_t now, struct ub_packed_rrset_key* rrset,
@@ -695,9 +694,8 @@ dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve,
int buf_canon = 0;
verbose(VERB_ALGO, "verify sig %d %d", (int)tag, algo);
if(!dnskey_algo_id_is_supported(algo)) {
// @TODO do we do LDNS_EDE_UNSUPPORTED_DNSKEY_ALG here?
// if (reason_bogus)
// *reason_bogus = LDNS_EDE_UNSUPPORTED_DNSKEY_ALG;
if (reason_bogus)
*reason_bogus = LDNS_EDE_UNSUPPORTED_DNSKEY_ALG;
verbose(VERB_QUERY, "verify sig: unknown algorithm");
return sec_status_insecure;
}
@@ -719,6 +717,8 @@ dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve,
}
if(numchecked == 0) {
*reason = "signatures from unknown keys";
if (reason_bogus)
*reason_bogus = LDNS_EDE_DNSKEY_MISSING;
verbose(VERB_QUERY, "verify: could not find appropriate key");
return sec_status_bogus;
}
@@ -1436,7 +1436,13 @@ check_dates(struct val_env* ve, uint32_t unow, uint8_t* expi_p,
sigdate_error("verify: inception after expiration, "
"signature bad", expi, incep, now);
*reason = "signature inception after expiration";
/* @TODO Tom, wat hier te doen? */
if (reason_bogus){
/* from RFC8914 on Signature Not Yet Valid: The resolver
* attempted to perform DNSSEC validation, but no
* signatures are presently valid and at least some are
* not yet valid. */
*reason_bogus = LDNS_EDE_SIGNATURE_NOT_YET_VALID;
}
return 0;
}
@@ -1562,6 +1568,8 @@ dnskey_verify_rrset_sig_ede(struct regional* region, sldns_buffer* buf,
if(siglen < 2+20) {
verbose(VERB_QUERY, "verify: signature too short");
*reason = "signature too short";
if(reason_bogus)
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus;
}
@@ -1577,6 +1585,8 @@ dnskey_verify_rrset_sig_ede(struct regional* region, sldns_buffer* buf,
/* RFC 4034 says DNSKEY PROTOCOL MUST be 3 */
verbose(VERB_QUERY, "verify: dnskey has wrong key protocol");
*reason = "dnskey has wrong protocolnumber";
if(reason_bogus)
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus;
}
@@ -1586,17 +1596,23 @@ dnskey_verify_rrset_sig_ede(struct regional* region, sldns_buffer* buf,
if(!signer_len) {
verbose(VERB_QUERY, "verify: malformed signer name");
*reason = "signer name malformed";
if(reason_bogus)
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus; /* signer name invalid */
}
if(!dname_subdomain_c(rrset->rk.dname, signer)) {
verbose(VERB_QUERY, "verify: signer name is off-tree");
*reason = "signer name off-tree";
if(reason_bogus)
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus; /* signer name offtree */
}
sigblock = (unsigned char*)signer+signer_len;
if(siglen < 2+18+signer_len+1) {
verbose(VERB_QUERY, "verify: too short, no signature data");
*reason = "signature too short, no signature data";
if(reason_bogus)
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus; /* sig rdf is < 1 byte */
}
sigblock_len = (unsigned int)(siglen - 2 - 18 - signer_len);
@@ -1609,6 +1625,8 @@ dnskey_verify_rrset_sig_ede(struct regional* region, sldns_buffer* buf,
log_nametypeclass(VERB_QUERY, "the key name is",
dnskey->rk.dname, 0, 0);
*reason = "signer name mismatches key name";
if(reason_bogus)
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus;
}
@@ -1617,18 +1635,24 @@ dnskey_verify_rrset_sig_ede(struct regional* region, sldns_buffer* buf,
if(memcmp(sig+2, &rrset->rk.type, 2) != 0) {
verbose(VERB_QUERY, "verify: wrong type covered");
*reason = "signature covers wrong type";
if(reason_bogus)
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus;
}
/* verify keytag and sig algo (possibly again) */
if((int)sig[2+2] != dnskey_get_algo(dnskey, dnskey_idx)) {
verbose(VERB_QUERY, "verify: wrong algorithm");
*reason = "signature has wrong algorithm";
if(reason_bogus)
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus;
}
ktag = htons(dnskey_calc_keytag(dnskey, dnskey_idx));
if(memcmp(sig+2+16, &ktag, 2) != 0) {
verbose(VERB_QUERY, "verify: wrong keytag");
*reason = "signature has wrong keytag";
if(reason_bogus)
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus;
}
@@ -1636,6 +1660,8 @@ dnskey_verify_rrset_sig_ede(struct regional* region, sldns_buffer* buf,
if((int)sig[2+3] > dname_signame_label_count(rrset->rk.dname)) {
verbose(VERB_QUERY, "verify: labelcount out of range");
*reason = "signature labelcount out of range";
if(reason_bogus)
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus;
}
+1 -4
View File
@@ -593,8 +593,7 @@ val_verify_DNSKEY_with_DS_ede(struct module_env* env, struct val_env* ve,
if(!has_useful_ds) {
verbose(VERB_ALGO, "No usable DS records were found -- "
"treating as insecure.");
// @TODO add ede DNSSEC Indeterminate?
return sec_status_insecure;
return sec_status_insecure;
}
/* If any were understandable, then it is bad. */
verbose(VERB_QUERY, "Failed to match any usable DS to a DNSKEY.");
@@ -850,8 +849,6 @@ val_dsset_isusable(struct ub_packed_rrset_key* ds_rrset)
else snprintf(aerr, sizeof(aerr), "%d",
(int)ds_get_key_algo(ds_rrset, i));
// @TODO do we want to add EDE Unsupported DS Digest Type here?
verbose(VERB_ALGO, "DS unsupported, hash %s %s, "
"key algorithm %s %s", herr,
(ds_digest_algo_is_supported(ds_rrset, 0)?
+28 -29
View File
@@ -672,6 +672,7 @@ validate_msg_signatures(struct module_qstate* qstate, struct module_env* env,
/* only validate rrs that have signatures with the key */
/* leave others unchecked, those get removed later on too */
val_find_rrset_signer(s, &sname, &slen);
if(sname && query_dname_compare(sname, key_entry->name)==0)
(void)val_verify_rrset_entry(env, ve, s, key_entry,
&reason, LDNS_SECTION_ADDITIONAL, qstate);
@@ -1560,6 +1561,7 @@ processInit(struct module_qstate* qstate, struct val_qstate* vq,
verbose(VERB_QUERY, "unsigned parent zone denies"
" trust anchor, indeterminate");
vq->chase_reply->security = sec_status_indeterminate;
vq->chase_reply->reason_bogus = LDNS_EDE_DNSSEC_INDETERMINATE;
vq->state = VAL_FINISHED_STATE;
return 1;
}
@@ -1591,6 +1593,7 @@ processInit(struct module_qstate* qstate, struct val_qstate* vq,
if(vq->key_entry == NULL && anchor == NULL) {
/*response isn't under a trust anchor, so we cannot validate.*/
vq->chase_reply->security = sec_status_indeterminate;
vq->chase_reply->reason_bogus = LDNS_EDE_DNSSEC_INDETERMINATE;
/* go to finished state to cache this result */
vq->state = VAL_FINISHED_STATE;
return 1;
@@ -1637,11 +1640,8 @@ processInit(struct module_qstate* qstate, struct val_qstate* vq,
return 1;
} else if(key_entry_isbad(vq->key_entry)) {
/* key is bad, chain is bad, reply is bogus */
// @TODO ADD Error Code 6 - DNSSEC Bogus + text chain of trust?
errinf_dname(qstate, "key for validation", vq->key_entry->name);
errinf(qstate, "is marked as invalid");
errinf_ede(qstate, "is marked as invalid", LDNS_EDE_DNSSEC_BOGUS);
if(key_entry_get_reason(vq->key_entry)) {
errinf(qstate, "because of a previous");
errinf(qstate, key_entry_get_reason(vq->key_entry));
@@ -1649,6 +1649,7 @@ processInit(struct module_qstate* qstate, struct val_qstate* vq,
/* no retries, stop bothering the authority until timeout */
vq->restart_count = ve->max_restart;
vq->chase_reply->security = sec_status_bogus;
vq->chase_reply->reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
vq->state = VAL_FINISHED_STATE;
return 1;
}
@@ -1719,11 +1720,10 @@ processFindKey(struct module_qstate* qstate, struct val_qstate* vq, int id)
vq->empty_DS_name) == 0) {
/* do not query for empty_DS_name again */
verbose(VERB_ALGO, "Cannot retrieve DS for signature");
errinf(qstate, "no signatures");
vq->chase_reply->reason_bogus = LDNS_EDE_RRSIGS_MISSING;
errinf_ede(qstate, "no signatures", LDNS_EDE_RRSIGS_MISSING);
errinf_origin(qstate, qstate->reply_origin);
vq->chase_reply->security = sec_status_bogus;
// @TODO add EDE 10 - RRSIGs Missing
vq->chase_reply->reason_bogus = LDNS_EDE_RRSIGS_MISSING;
vq->state = VAL_FINISHED_STATE;
return 1;
}
@@ -1856,10 +1856,9 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq,
"of trust to keys for", vq->key_entry->name,
LDNS_RR_TYPE_DNSKEY, vq->key_entry->key_class);
vq->chase_reply->security = sec_status_bogus;
errinf(qstate, "while building chain of trust");
// @TODO ADD Error Code 6 - DNSSEC Bogus + text chain of trust?
vq->chase_reply->reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
errinf_ede(qstate, "while building chain of trust",
LDNS_EDE_DNSSEC_BOGUS);
if(vq->restart_count >= ve->max_restart)
key_cache_insert(ve->kcache, vq->key_entry, qstate);
return 1;
@@ -1872,10 +1871,10 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq,
"signer name", &vq->qchase);
verbose(VERB_DETAIL, "Could not establish validation of "
"INSECURE status of unsigned response.");
errinf(qstate, "no signatures");
vq->chase_reply->reason_bogus = LDNS_EDE_RRSIGS_MISSING;
errinf_ede(qstate, "no signatures", LDNS_EDE_RRSIGS_MISSING);
errinf_origin(qstate, qstate->reply_origin);
vq->chase_reply->security = sec_status_bogus;
vq->chase_reply->reason_bogus = LDNS_EDE_RRSIGS_MISSING;
return 1;
}
subtype = val_classify_response(qstate->query_flags, &qstate->qinfo,
@@ -2363,7 +2362,6 @@ primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset,
/* NOTE: in this case, we should probably reject the trust
* anchor for longer, perhaps forever. */
if(qstate->env->cfg->harden_dnssec_stripped) {
// errinf(qstate, reason);
errinf_ede(qstate, reason, reason_bogus);
kkey = key_entry_create_bad(qstate->region, ta->name,
ta->namelen, ta->dclass, BOGUS_KEY_TTL,
@@ -2407,6 +2405,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
{
struct val_env* ve = (struct val_env*)qstate->env->modinfo[id];
char* reason = NULL;
sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
enum val_classification subtype;
if(rcode != LDNS_RCODE_NOERROR) {
char rc[16];
@@ -2415,7 +2414,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
/* errors here pretty much break validation */
verbose(VERB_DETAIL, "DS response was error, thus bogus");
errinf(qstate, rc);
errinf(qstate, "no DS");
errinf_ede(qstate, "no DS", reason_bogus);
goto return_bogus;
}
@@ -2430,17 +2429,17 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
if(!ds) {
log_warn("internal error: POSITIVE DS response was "
"missing DS.");
errinf(qstate, "no DS record");
errinf_ede(qstate, "no DS record", reason_bogus);
goto return_bogus;
}
/* Verify only returns BOGUS or SECURE. If the rrset is
* bogus, then we are done. */
sec = val_verify_rrset_entry(qstate->env, ve, ds,
vq->key_entry, &reason, LDNS_SECTION_ANSWER, qstate);
sec = val_verify_rrset_entry_ede(qstate->env, ve, ds,
vq->key_entry, &reason, &reason_bogus, LDNS_SECTION_ANSWER, qstate);
if(sec != sec_status_secure) {
verbose(VERB_DETAIL, "DS rrset in DS response did "
"not verify");
errinf(qstate, reason);
errinf_ede(qstate, reason, reason_bogus);
goto return_bogus;
}
@@ -2449,6 +2448,9 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
if(!val_dsset_isusable(ds)) {
/* If they aren't usable, then we treat it like
* there was no DS. */
// @TODO add EDE Unsupported DS Digest Type
*ke = key_entry_create_null(qstate->region,
qinfo->qname, qinfo->qname_len, qinfo->qclass,
ub_packed_rrset_ttl(ds), *qstate->env->now);
@@ -2471,7 +2473,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
/* make sure there are NSECs or NSEC3s with signatures */
if(!val_has_signed_nsecs(msg->rep, &reason)) {
verbose(VERB_ALGO, "no NSECs: %s", reason);
errinf(qstate, reason);
errinf_ede(qstate, reason, LDNS_EDE_NSEC_MISSING);
goto return_bogus;
}
@@ -2599,9 +2601,6 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
goto return_bogus;
}
return_bogus:
// @TODO add EDE NSEC MISSING
*ke = key_entry_create_bad(qstate->region, qinfo->qname,
qinfo->qname_len, qinfo->qclass,
BOGUS_KEY_TTL, *qstate->env->now);
@@ -2707,6 +2706,7 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq,
struct ub_packed_rrset_key* dnskey = NULL;
int downprot;
char* reason = NULL;
sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
if(rcode == LDNS_RCODE_NOERROR)
dnskey = reply_find_answer_rrset(qinfo, msg->rep);
@@ -2716,7 +2716,7 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq,
verbose(VERB_DETAIL, "Missing DNSKEY RRset in response to "
"DNSKEY query.");
// @TODO add EDE 9 - DNSKEY Missing
reason_bogus = LDNS_EDE_DNSKEY_MISSING;
if(vq->restart_count < ve->max_restart) {
val_blacklist(&vq->chain_blacklist, qstate->region,
@@ -2732,7 +2732,7 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq,
log_err("alloc failure in missing dnskey response");
/* key_entry is NULL for failure in Validate */
}
errinf(qstate, "No DNSKEY record");
errinf_ede(qstate, "No DNSKEY record", reason_bogus);
errinf_origin(qstate, origin);
errinf_dname(qstate, "for key", qinfo->qname);
vq->state = VAL_VALIDATE_STATE;
@@ -2745,9 +2745,8 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq,
return;
}
downprot = qstate->env->cfg->harden_algo_downgrade;
// @TODO add _ede
vq->key_entry = val_verify_new_DNSKEYs(qstate->region, qstate->env,
ve, dnskey, vq->ds_rrset, downprot, &reason, qstate);
vq->key_entry = val_verify_new_DNSKEYs_ede(qstate->region, qstate->env,
ve, dnskey, vq->ds_rrset, downprot, &reason, &reason_bogus, qstate);
if(!vq->key_entry) {
log_err("out of memory in verify new DNSKEYs");
@@ -2768,7 +2767,7 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq,
}
verbose(VERB_DETAIL, "Did not match a DS to a DNSKEY, "
"thus bogus.");
errinf(qstate, reason);
errinf_ede(qstate, reason, reason_bogus);
errinf_origin(qstate, origin);
errinf_dname(qstate, "for key", qinfo->qname);
}