From: Wouter Wijngaards Date: Mon, 20 Dec 2010 15:58:12 +0000 (+0000) Subject: Work on validation of multiple algorithms. X-Git-Tag: release-1.4.8rc1~23 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e9582487d9aa78bae26c1e0e9854a5853ea11a6e;p=thirdparty%2Funbound.git Work on validation of multiple algorithms. git-svn-id: file:///svn/unbound/trunk@2356 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/testcode/unitverify.c b/testcode/unitverify.c index ba5180e55..2062d6742 100644 --- a/testcode/unitverify.c +++ b/testcode/unitverify.c @@ -148,6 +148,33 @@ should_be_bogus(struct ub_packed_rrset_key* rrset, struct query_info* qinfo) return 0; } +/** return number of rrs in an rrset */ +static size_t +rrset_get_count(struct ub_packed_rrset_key* rrset) +{ + struct packed_rrset_data* d = (struct packed_rrset_data*) + rrset->entry.data; + if(!d) return 0; + return d->count; +} + +/** setup sig alg list from dnskey */ +static void +setup_sigalg(struct ub_packed_rrset_key* dnskey, uint8_t* sigalg) +{ + uint8_t a[ALGO_NEEDS_MAX]; + size_t i, n = 0; + memset(a, 0, sizeof(a)); + for(i=0; irk.dname, ntohs(rrset->rk.type), ntohs(rrset->rk.rrset_class)); } - sec = dnskeyset_verify_rrset(env, ve, rrset, dnskey, 1, &reason); + setup_sigalg(dnskey, sigalg); /* check all algorithms in the dnskey */ + sec = dnskeyset_verify_rrset(env, ve, rrset, dnskey, sigalg, &reason); if(vsig) { printf("verify outcome is: %s %s\n", sec_status_to_string(sec), reason?reason:""); diff --git a/validator/val_kentry.c b/validator/val_kentry.c index d58630fc5..8d7edc26f 100644 --- a/validator/val_kentry.c +++ b/validator/val_kentry.c @@ -58,6 +58,8 @@ key_entry_sizefunc(void* key, void* data) s += packed_rrset_sizeof(kd->rrset_data); if(kd->reason) s += strlen(kd->reason)+1; + if(kd->algo) + s += strlen((char*)kd->algo)+1; return s; } @@ -91,6 +93,7 @@ key_entry_deldatafunc(void* data, void* ATTR_UNUSED(userarg)) struct key_entry_data* kd = (struct key_entry_data*)data; free(kd->reason); free(kd->rrset_data); + free(kd->algo); free(kd); } @@ -136,6 +139,12 @@ key_entry_copy_toregion(struct key_entry_key* kkey, struct regional* region) if(!newd->reason) return NULL; } + if(d->algo) { + newd->algo = (uint8_t*)regional_strdup(region, + (char*)d->algo); + if(!newd->algo) + return NULL; + } newk->entry.data = newd; } return newk; @@ -190,6 +199,17 @@ key_entry_copy(struct key_entry_key* kkey) return NULL; } } + if(d->algo) { + newd->algo = (uint8_t*)strdup((char*)d->algo); + if(!newd->algo) { + free(newd->rrset_data); + free(newd->reason); + free(newd); + free(newk->name); + free(newk); + return NULL; + } + } newk->entry.data = newd; } return newk; @@ -267,13 +287,14 @@ key_entry_create_null(struct regional* region, d->reason = NULL; d->rrset_type = LDNS_RR_TYPE_DNSKEY; d->rrset_data = NULL; + d->algo = NULL; return k; } struct key_entry_key* key_entry_create_rrset(struct regional* region, uint8_t* name, size_t namelen, uint16_t dclass, - struct ub_packed_rrset_key* rrset, uint32_t now) + struct ub_packed_rrset_key* rrset, uint8_t* sigalg, uint32_t now) { struct key_entry_key* k; struct key_entry_data* d; @@ -289,6 +310,11 @@ key_entry_create_rrset(struct regional* region, rd, packed_rrset_sizeof(rd)); if(!d->rrset_data) return NULL; + if(sigalg) { + d->algo = (uint8_t*)regional_strdup(region, (char*)sigalg); + if(!d->algo) + return NULL; + } else d->algo = NULL; packed_rrset_ptr_fixup(d->rrset_data); return k; } @@ -307,6 +333,7 @@ key_entry_create_bad(struct regional* region, d->reason = NULL; d->rrset_type = LDNS_RR_TYPE_DNSKEY; d->rrset_data = NULL; + d->algo = NULL; return k; } diff --git a/validator/val_kentry.h b/validator/val_kentry.h index 07106e0a6..d14ffe588 100644 --- a/validator/val_kentry.h +++ b/validator/val_kentry.h @@ -80,6 +80,8 @@ struct key_entry_data { struct packed_rrset_data* rrset_data; /** not NULL sometimes to give reason why bogus */ char* reason; + /** list of algorithms signalled, ends with 0, or NULL */ + uint8_t* algo; /** DNS RR type of the rrset data (host order) */ uint16_t rrset_type; /** if the key is bad: Bogus or malformed */ @@ -177,12 +179,13 @@ struct key_entry_key* key_entry_create_null(struct regional* region, * @param namelen: length of name * @param dclass: class of key entry. (host order); * @param rrset: data for key entry. This is copied to the region. + * @param sigalg: signalled algorithm list (or NULL). * @param now: current time (added to ttl of rrset) * @return new key entry or NULL on alloc failure */ struct key_entry_key* key_entry_create_rrset(struct regional* region, uint8_t* name, size_t namelen, uint16_t dclass, - struct ub_packed_rrset_key* rrset, uint32_t now); + struct ub_packed_rrset_key* rrset, uint8_t* sigalg, uint32_t now); /** * Create a bad entry, in the given region. diff --git a/validator/val_sigcrypt.c b/validator/val_sigcrypt.c index dd3a694b6..9a603c708 100644 --- a/validator/val_sigcrypt.c +++ b/validator/val_sigcrypt.c @@ -473,8 +473,45 @@ void algo_needs_init_dnskey(struct algo_needs* n, n->num = total; } +void algo_needs_init_dnskey_add(struct algo_needs* n, + struct ub_packed_rrset_key* dnskey, uint8_t* sigalg) +{ + uint8_t algo; + size_t i, total = n->num; + size_t num = rrset_get_count(dnskey); + + for(i=0; ineeds[algo] == 0) { + n->needs[algo] = 1; + sigalg[total] = algo; + total++; + } + } + sigalg[total] = 0; + n->num = total; +} + +void algo_needs_init_list(struct algo_needs* n, uint8_t* sigalg) +{ + uint8_t algo; + size_t total = 0; + + memset(n->needs, 0, sizeof(uint8_t)*ALGO_NEEDS_MAX); + while( (algo=*sigalg) != 0) { + log_assert(dnskey_algo_id_is_supported((int)algo)); + log_assert(n->needs[algo] == 0); + n->needs[algo] = 1; + total++; + sigalg++; + } + n->num = total; +} + void algo_needs_init_ds(struct algo_needs* n, struct ub_packed_rrset_key* ds, - int fav_ds_algo) + int fav_ds_algo, uint8_t* sigalg) { uint8_t algo; size_t i, total = 0; @@ -487,11 +524,14 @@ void algo_needs_init_ds(struct algo_needs* n, struct ub_packed_rrset_key* ds, algo = (uint8_t)ds_get_key_algo(ds, i); if(!dnskey_algo_id_is_supported((int)algo)) continue; + log_assert(algo != 0); /* we do not support 0 and is EOS */ if(n->needs[algo] == 0) { n->needs[algo] = 1; + sigalg[total] = algo; total++; } } + sigalg[total] = 0; n->num = total; } @@ -533,7 +573,7 @@ int algo_needs_missing(struct algo_needs* n) enum sec_status dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, - int downprot, char** reason) + uint8_t* sigalg, char** reason) { enum sec_status sec; size_t i, num; @@ -550,30 +590,32 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, return sec_status_bogus; } - algo_needs_init_dnskey(&needs, dnskey); - if(algo_needs_num_missing(&needs) == 0) { - verbose(VERB_QUERY, "DNSKEY has no known algorithms"); - *reason = "DNSKEY has no known algorithms"; - return sec_status_insecure; + if(sigalg) { + algo_needs_init_list(&needs, sigalg); + if(algo_needs_num_missing(&needs) == 0) { + verbose(VERB_QUERY, "zone has no known algorithms"); + *reason = "zone has no known algorithms"; + return sec_status_insecure; + } } for(i=0; inow, rrset, dnskey, i, &sortree, reason); /* see which algorithm has been fixed up */ if(sec == sec_status_secure) { - if(!downprot) + if(!sigalg) return sec; /* done! */ else if(algo_needs_set_secure(&needs, (uint8_t)rrset_get_sig_algo(rrset, i))) return sec; /* done! */ - } else if(downprot && sec == sec_status_bogus) { + } else if(sigalg && sec == sec_status_bogus) { algo_needs_set_bogus(&needs, (uint8_t)rrset_get_sig_algo(rrset, i)); } } verbose(VERB_ALGO, "rrset failed to verify: no valid signatures for " "%d algorithms", (int)algo_needs_num_missing(&needs)); - if(downprot && (alg=algo_needs_missing(&needs)) != 0) { + if(sigalg && (alg=algo_needs_missing(&needs)) != 0) { algo_needs_reason(env, alg, reason, "no signatures"); } return sec_status_bogus; diff --git a/validator/val_sigcrypt.h b/validator/val_sigcrypt.h index d021e8b63..6cef3d817 100644 --- a/validator/val_sigcrypt.h +++ b/validator/val_sigcrypt.h @@ -52,6 +52,7 @@ struct regional; /** number of entries in algorithm needs array */ #define ALGO_NEEDS_MAX 256 + /** * Storage for algorithm needs. DNSKEY algorithms. */ @@ -75,14 +76,33 @@ struct algo_needs { void algo_needs_init_dnskey(struct algo_needs* n, struct ub_packed_rrset_key* dnskey); +/** + * Initialize algo needs structure, set algos from rrset as needed. + * Results are added to an existing need structure. + * @param n: struct with storage. + * @param dnskey: algos from this struct set as necessary. DNSKEY set. + * @param sigalg: adds to signalled algorithm list too. + */ +void algo_needs_init_dnskey_add(struct algo_needs* n, + struct ub_packed_rrset_key* dnskey, uint8_t* sigalg); + +/** + * Initialize algo needs structure from a signalled algo list. + * @param n: struct with storage. + * @param sigalg: signalled algorithm list, numbers ends with 0. + */ +void algo_needs_init_list(struct algo_needs* n, uint8_t* sigalg); + /** * Initialize algo needs structure, set algos from rrset as needed. * @param n: struct with storage. * @param ds: algos from this struct set as necessary. DS set. * @param fav_ds_algo: filter to use only this DS algo. + * @param sigalg: list of signalled algos, constructed as output, + * provide size ALGO_NEEDS_MAX+1. list of algonumbers, ends with a zero. */ void algo_needs_init_ds(struct algo_needs* n, struct ub_packed_rrset_key* ds, - int fav_ds_algo); + int fav_ds_algo, uint8_t* sigalg); /** * Mark this algorithm as a success, sec_secure, and see if we are done. @@ -221,7 +241,7 @@ uint16_t dnskey_get_flags(struct ub_packed_rrset_key* k, size_t idx); * @param ve: validator environment, date settings. * @param rrset: to be validated. * @param dnskey: DNSKEY rrset, keyset to try. - * @param downprot: if true provide downgrade protection otherwise one + * @param sigalg: if nonNULL provide downgrade protection otherwise one * algorithm is enough. * @param reason: if bogus, a string returned, fixed or alloced in scratch. * @return SECURE if one key in the set verifies one rrsig. @@ -230,7 +250,7 @@ uint16_t dnskey_get_flags(struct ub_packed_rrset_key* k, size_t idx); */ enum sec_status dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, - struct ub_packed_rrset_key* dnskey, int downprot, char** reason); + struct ub_packed_rrset_key* dnskey, uint8_t* sigalg, char** reason); /** * verify rrset against one specific dnskey (from rrset) diff --git a/validator/val_utils.c b/validator/val_utils.c index 98ff8a8c6..928af2b2a 100644 --- a/validator/val_utils.c +++ b/validator/val_utils.c @@ -310,7 +310,7 @@ rrset_get_ttl(struct ub_packed_rrset_key* rrset) enum sec_status val_verify_rrset(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* keys, - int downprot, char** reason) + uint8_t* sigalg, char** reason) { enum sec_status sec; struct packed_rrset_data* d = (struct packed_rrset_data*)rrset-> @@ -332,7 +332,7 @@ val_verify_rrset(struct module_env* env, struct val_env* ve, } log_nametypeclass(VERB_ALGO, "verify rrset", rrset->rk.dname, ntohs(rrset->rk.type), ntohs(rrset->rk.rrset_class)); - sec = dnskeyset_verify_rrset(env, ve, rrset, keys, downprot, reason); + sec = dnskeyset_verify_rrset(env, ve, rrset, keys, sigalg, reason); verbose(VERB_ALGO, "verify result: %s", sec_status_to_string(sec)); regional_free_all(env->scratch); @@ -378,7 +378,7 @@ val_verify_rrset_entry(struct module_env* env, struct val_env* ve, dnskey.rk.dname_len = kkey->namelen; dnskey.entry.key = &dnskey; dnskey.entry.data = kd->rrset_data; - sec = val_verify_rrset(env, ve, rrset, &dnskey, 1, reason); + sec = val_verify_rrset(env, ve, rrset, &dnskey, kd->algo, reason); return sec; } @@ -453,7 +453,7 @@ int val_favorite_ds_algo(struct ub_packed_rrset_key* ds_rrset) enum sec_status val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, - struct ub_packed_rrset_key* ds_rrset, int downprot, char** reason) + struct ub_packed_rrset_key* ds_rrset, uint8_t* sigalg, char** reason) { /* as long as this is false, we can consider this DS rrset to be * equivalent to no DS rrset. */ @@ -472,8 +472,8 @@ val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve, } digest_algo = val_favorite_ds_algo(ds_rrset); - if(downprot) - algo_needs_init_ds(&needs, ds_rrset, digest_algo); + if(sigalg) + algo_needs_init_ds(&needs, ds_rrset, digest_algo, sigalg); num = rrset_get_count(ds_rrset); for(i=0; irk.dname, ds_rrset->rk.dname_len, ntohs(ds_rrset->rk.rrset_class), dnskey_rrset, - *env->now); + downprot?sigalg:NULL, *env->now); } else if(sec == sec_status_insecure) { return key_entry_create_null(region, ds_rrset->rk.dname, ds_rrset->rk.dname_len, @@ -544,6 +545,147 @@ val_verify_new_DNSKEYs(struct regional* region, struct module_env* env, BOGUS_KEY_TTL, *env->now); } +enum sec_status +val_verify_DNSKEY_with_TA(struct module_env* env, struct val_env* ve, + struct ub_packed_rrset_key* dnskey_rrset, + struct ub_packed_rrset_key* ta_ds, + struct ub_packed_rrset_key* ta_dnskey, uint8_t* sigalg, char** reason) +{ + /* as long as this is false, we can consider this DS rrset to be + * equivalent to no DS rrset. */ + int has_useful_ta = 0, digest_algo = 0, alg; + struct algo_needs needs; + size_t i, num; + enum sec_status sec; + + if(ta_ds && (dnskey_rrset->rk.dname_len != ta_ds->rk.dname_len || + query_dname_compare(dnskey_rrset->rk.dname, ta_ds->rk.dname) + != 0)) { + verbose(VERB_QUERY, "DNSKEY RRset did not match DS RRset " + "by name"); + *reason = "DNSKEY RRset did not match DS RRset by name"; + return sec_status_bogus; + } + if(ta_dnskey && (dnskey_rrset->rk.dname_len != ta_dnskey->rk.dname_len + || query_dname_compare(dnskey_rrset->rk.dname, ta_dnskey->rk.dname) + != 0)) { + verbose(VERB_QUERY, "DNSKEY RRset did not match anchor RRset " + "by name"); + *reason = "DNSKEY RRset did not match anchor RRset by name"; + return sec_status_bogus; + } + + if(ta_ds) + digest_algo = val_favorite_ds_algo(ta_ds); + if(sigalg) { + if(ta_ds) + algo_needs_init_ds(&needs, ta_ds, digest_algo, sigalg); + else memset(&needs, 0, sizeof(needs)); + if(ta_dnskey) + algo_needs_init_dnskey_add(&needs, ta_dnskey, sigalg); + } + if(ta_ds) { + num = rrset_get_count(ta_ds); + for(i=0; irk.dname, dnskey_rrset->rk.dname_len, + ntohs(dnskey_rrset->rk.rrset_class), dnskey_rrset, + downprot?sigalg:NULL, *env->now); + } else if(sec == sec_status_insecure) { + return key_entry_create_null(region, dnskey_rrset->rk.dname, + dnskey_rrset->rk.dname_len, + ntohs(dnskey_rrset->rk.rrset_class), + rrset_get_ttl(dnskey_rrset), *env->now); + } + return key_entry_create_bad(region, dnskey_rrset->rk.dname, + dnskey_rrset->rk.dname_len, ntohs(dnskey_rrset->rk.rrset_class), + BOGUS_KEY_TTL, *env->now); +} + int val_dsset_isusable(struct ub_packed_rrset_key* ds_rrset) { diff --git a/validator/val_utils.h b/validator/val_utils.h index c78ec92b1..c38b82ce9 100644 --- a/validator/val_utils.h +++ b/validator/val_utils.h @@ -117,14 +117,14 @@ void val_find_signer(enum val_classification subtype, * @param ve: validator environment (verification settings) * @param rrset: what to verify * @param keys: dnskey rrset to verify with. - * @param downprot: if true provide downgrade protection otherwise one - * algorithm is enough. + * @param sigalg: if nonNULL provide downgrade protection otherwise one + * algorithm is enough. Algo list is constructed in here. * @param reason: reason of failure. Fixed string or alloced in scratch. * @return security status of verification. */ enum sec_status val_verify_rrset(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* keys, - int downprot, char** reason); + uint8_t* sigalg, char** reason); /** * Verify RRset with keys from a keyset. @@ -146,8 +146,9 @@ enum sec_status val_verify_rrset_entry(struct module_env* env, * @param ve: validator environment (verification settings) * @param dnskey_rrset: DNSKEY rrset to verify * @param ds_rrset: DS rrset to verify with. - * @param downprot: if true provide downgrade protection otherwise one - * algorithm is enough. + * @param sigalg: if nonNULL provide downgrade protection otherwise one + * algorithm is enough. The list of signalled algorithms is returned, + * must have enough space for ALGO_NEEDS_MAX+1. * @param reason: reason of failure. Fixed string or alloced in scratch. * @return: sec_status_secure if a DS matches. * sec_status_insecure if end of trust (i.e., unknown algorithms). @@ -155,7 +156,7 @@ enum sec_status val_verify_rrset_entry(struct module_env* env, */ enum sec_status val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, - struct ub_packed_rrset_key* ds_rrset, int downprot, char** reason); + struct ub_packed_rrset_key* ds_rrset, uint8_t* sigalg, char** reason); /** * Verify new DNSKEYs with DS rrset. The DS contains hash values that should @@ -178,12 +179,43 @@ enum sec_status val_verify_DNSKEY_with_DS(struct module_env* env, * generally only occur in a private algorithm scenario: normally * this sort of thing is checked before fetching the matching DNSKEY * rrset. + * if downprot is set, a key entry with an algo list is made. */ struct key_entry_key* val_verify_new_DNSKEYs(struct regional* region, struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, struct ub_packed_rrset_key* ds_rrset, int downprot, char** reason); + +/** + * Verify rrset with trust anchor: DS and DNSKEY rrset. + * + * @param region: where to allocate key entry result. + * @param env: module environment (scratch buffer) + * @param ve: validator environment (verification settings) + * @param dnskey_rrset: DNSKEY rrset to verify + * @param ta_ds_rrset: DS rrset to verify with. + * @param ta_dnskey_rrset: the DNSKEY rrset to verify with. + * @param downprot: if true provide downgrade protection otherwise one + * algorithm is enough. + * @param reason: reason of failure. Fixed string or alloced in scratch. + * @return a KeyEntry. This will either contain the now trusted + * dnskey_rrset, a "null" key entry indicating that this DS + * rrset/DNSKEY pair indicate an secure end to the island of trust + * (i.e., unknown algorithms), or a "bad" KeyEntry if the dnskey + * rrset fails to verify. Note that the "null" response should + * generally only occur in a private algorithm scenario: normally + * this sort of thing is checked before fetching the matching DNSKEY + * rrset. + * if downprot is set, a key entry with an algo list is made. + */ +struct key_entry_key* val_verify_new_DNSKEYs_with_ta(struct regional* region, + struct module_env* env, struct val_env* ve, + struct ub_packed_rrset_key* dnskey_rrset, + struct ub_packed_rrset_key* ta_ds_rrset, + struct ub_packed_rrset_key* ta_dnskey_rrset, + int downprot, char** reason); + /** * Determine if DS rrset is usable for validator or not. * Returns true if the algorithms for key and DShash are supported, diff --git a/validator/validator.c b/validator/validator.c index cfce63c8e..c907574d9 100644 --- a/validator/validator.c +++ b/validator/validator.c @@ -49,6 +49,7 @@ #include "validator/val_nsec.h" #include "validator/val_nsec3.h" #include "validator/val_neg.h" +#include "validator/val_sigcrypt.h" #include "validator/autotrust.h" #include "services/cache/dns.h" #include "util/data/dname.h" @@ -2260,35 +2261,18 @@ primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset, return kkey; } /* attempt to verify with trust anchor DS and DNSKEY */ - if(ta->ds_rrset) { - kkey = val_verify_new_DNSKEYs(qstate->region, qstate->env, ve, - dnskey_rrset, ta->ds_rrset, 0, &reason); - if(!kkey) { - log_err("out of memory: verifying prime DS"); - return NULL; - } - if(key_entry_isgood(kkey)) - sec = sec_status_secure; - else - sec = sec_status_bogus; - verbose(VERB_DETAIL, "validate keys with anchor(DS): %s", - sec_status_to_string(sec)); - } - if(sec != sec_status_secure && ta->dnskey_rrset) { - sec = val_verify_rrset(qstate->env, ve, dnskey_rrset, - ta->dnskey_rrset, 0, &reason); - verbose(VERB_DETAIL, "validate keys with anchor(DNSKEY): %s", - sec_status_to_string(sec)); - if(sec == sec_status_secure) { - kkey = key_entry_create_rrset(qstate->region, - ta->name, ta->namelen, ta->dclass, - dnskey_rrset, *qstate->env->now); - if(!kkey) { - log_err("out of memory: allocate primed key"); - return NULL; - } - } + kkey = val_verify_new_DNSKEYs_with_ta(qstate->region, qstate->env, ve, + dnskey_rrset, ta->ds_rrset, ta->dnskey_rrset, 0, &reason); + if(!kkey) { + log_err("out of memory: verifying prime TA"); + return NULL; } + if(key_entry_isgood(kkey)) + sec = sec_status_secure; + else + sec = sec_status_bogus; + verbose(VERB_DETAIL, "validate keys with anchor(DS): %s", + sec_status_to_string(sec)); if(sec != sec_status_secure) { log_nametypeclass(VERB_OPS, "failed to prime trust anchor -- " @@ -2390,7 +2374,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, log_query_info(VERB_DETAIL, "validated DS", qinfo); *ke = key_entry_create_rrset(qstate->region, qinfo->qname, qinfo->qname_len, qinfo->qclass, ds, - *qstate->env->now); + NULL, *qstate->env->now); return (*ke) != NULL; } else if(subtype == VAL_CLASS_NODATA || subtype == VAL_CLASS_NAMEERROR) {