#include "config.h"
#include <ctype.h>
#include "validator/validator.h"
#include "validator/val_anchor.h"
#include "validator/val_kcache.h"
#include "validator/val_kentry.h"
#include "validator/val_utils.h"
#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 "services/cache/rrset.h"
#include "util/data/dname.h"
#include "util/module.h"
#include "util/log.h"
#include "util/net_help.h"
#include "util/regional.h"
#include "util/config_file.h"
#include "util/fptr_wlist.h"
#include "sldns/rrdef.h"
#include "sldns/wire2str.h"
#include "sldns/str2wire.h"
#define MAX_VALIDATE_AT_ONCE 8
#define MAX_VALIDATION_SUSPENDS 16
static void process_ds_response(struct module_qstate* qstate,
struct val_qstate* vq, int id, int rcode, struct dns_msg* msg,
struct query_info* qinfo, struct sock_list* origin, int* suspend,
struct module_qstate* sub_qstate);
static void
update_reason_bogus(struct reply_info* rep, sldns_ede_code reason_bogus)
{
if(reason_bogus == LDNS_EDE_NONE) return;
if(reason_bogus == LDNS_EDE_DNSSEC_BOGUS
&& rep->reason_bogus != LDNS_EDE_NONE
&& rep->reason_bogus != LDNS_EDE_DNSSEC_BOGUS) return;
rep->reason_bogus = reason_bogus;
}
static int
fill_nsec3_iter(size_t** keysize, size_t** maxiter, char* s, int c)
{
char* e;
int i;
*keysize = (size_t*)calloc((size_t)c, sizeof(size_t));
*maxiter = (size_t*)calloc((size_t)c, sizeof(size_t));
if(!*keysize || !*maxiter) {
free(*keysize);
*keysize = NULL;
free(*maxiter);
*maxiter = NULL;
log_err("out of memory");
return 0;
}
for(i=0; i<c; i++) {
(*keysize)[i] = (size_t)strtol(s, &e, 10);
if(s == e) {
log_err("cannot parse: %s", s);
free(*keysize);
*keysize = NULL;
free(*maxiter);
*maxiter = NULL;
return 0;
}
s = e;
(*maxiter)[i] = (size_t)strtol(s, &e, 10);
if(s == e) {
log_err("cannot parse: %s", s);
free(*keysize);
*keysize = NULL;
free(*maxiter);
*maxiter = NULL;
return 0;
}
s = e;
if(i>0 && (*keysize)[i-1] >= (*keysize)[i]) {
log_err("nsec3 key iterations not ascending: %d %d",
(int)(*keysize)[i-1], (int)(*keysize)[i]);
free(*keysize);
*keysize = NULL;
free(*maxiter);
*maxiter = NULL;
return 0;
}
verbose(VERB_ALGO, "validator nsec3cfg keysz %d mxiter %d",
(int)(*keysize)[i], (int)(*maxiter)[i]);
}
return 1;
}
int
val_env_parse_key_iter(char* val_nsec3_key_iterations, size_t** keysize,
size_t** maxiter, int* keyiter_count)
{
int c;
c = cfg_count_numbers(val_nsec3_key_iterations);
if(c < 1 || (c&1)) {
log_err("validator: unparsable or odd nsec3 key "
"iterations: %s", val_nsec3_key_iterations);
return 0;
}
*keyiter_count = c/2;
if(!fill_nsec3_iter(keysize, maxiter, val_nsec3_key_iterations, c/2)) {
log_err("validator: cannot apply nsec3 key iterations");
return 0;
}
return 1;
}
void
val_env_apply_cfg(struct val_env* val_env, struct config_file* cfg,
size_t* keysize, size_t* maxiter, int keyiter_count)
{
free(val_env->nsec3_keysize);
free(val_env->nsec3_maxiter);
val_env->nsec3_keysize = keysize;
val_env->nsec3_maxiter = maxiter;
val_env->nsec3_keyiter_count = keyiter_count;
val_env->bogus_ttl = (uint32_t)cfg->bogus_ttl;
val_env->date_override = cfg->val_date_override;
val_env->skew_min = cfg->val_sig_skew_min;
val_env->skew_max = cfg->val_sig_skew_max;
val_env->max_restart = cfg->val_max_restart;
}
static int
val_apply_cfg(struct module_env* env, struct val_env* val_env,
struct config_file* cfg)
{
size_t* keysize=NULL, *maxiter=NULL;
int keyiter_count = 0;
if(!env->anchors)
env->anchors = anchors_create();
if(!env->anchors) {
log_err("out of memory");
return 0;
}
if (env->key_cache)
val_env->kcache = env->key_cache;
if(!val_env->kcache)
val_env->kcache = key_cache_create(cfg);
if(!val_env->kcache) {
log_err("out of memory");
return 0;
}
env->key_cache = val_env->kcache;
if(!anchors_apply_cfg(env->anchors, cfg)) {
log_err("validator: error in trustanchors config");
return 0;
}
if(!val_env_parse_key_iter(cfg->val_nsec3_key_iterations,
&keysize, &maxiter, &keyiter_count)) {
return 0;
}
val_env_apply_cfg(val_env, cfg, keysize, maxiter, keyiter_count);
if (env->neg_cache)
val_env->neg_cache = env->neg_cache;
if(!val_env->neg_cache)
val_env->neg_cache = val_neg_create(cfg,
val_env->nsec3_maxiter[val_env->nsec3_keyiter_count-1]);
if(!val_env->neg_cache) {
log_err("out of memory");
return 0;
}
env->neg_cache = val_env->neg_cache;
return 1;
}
#ifdef USE_ECDSA_EVP_WORKAROUND
void ecdsa_evp_workaround_init(void);
#endif
int
val_init(struct module_env* env, int id)
{
struct val_env* val_env = (struct val_env*)calloc(1,
sizeof(struct val_env));
if(!val_env) {
log_err("malloc failure");
return 0;
}
env->modinfo[id] = (void*)val_env;
env->need_to_validate = 1;
lock_basic_init(&val_env->bogus_lock);
lock_protect(&val_env->bogus_lock, &val_env->num_rrset_bogus,
sizeof(val_env->num_rrset_bogus));
#ifdef USE_ECDSA_EVP_WORKAROUND
ecdsa_evp_workaround_init();
#endif
if(!val_apply_cfg(env, val_env, env->cfg)) {
log_err("validator: could not apply configuration settings.");
return 0;
}
if(env->cfg->disable_edns_do) {
struct trust_anchor* anchor = anchors_find_any_noninsecure(
env->anchors);
if(anchor) {
char b[LDNS_MAX_DOMAINLEN];
dname_str(anchor->name, b);
log_warn("validator: disable-edns-do is enabled, but there is a trust anchor for '%s'. Since DNSSEC could not work, the disable-edns-do setting is turned off. Continuing without it.", b);
lock_basic_unlock(&anchor->lock);
env->cfg->disable_edns_do = 0;
}
}
return 1;
}
void
val_deinit(struct module_env* env, int id)
{
struct val_env* val_env;
if(!env || !env->modinfo[id])
return;
val_env = (struct val_env*)env->modinfo[id];
lock_basic_destroy(&val_env->bogus_lock);
anchors_delete(env->anchors);
env->anchors = NULL;
key_cache_delete(val_env->kcache);
env->key_cache = NULL;
neg_cache_delete(val_env->neg_cache);
env->neg_cache = NULL;
free(val_env->nsec3_keysize);
free(val_env->nsec3_maxiter);
free(val_env);
env->modinfo[id] = NULL;
}
static struct val_qstate*
val_new_getmsg(struct module_qstate* qstate, struct val_qstate* vq)
{
if(!qstate->return_msg || qstate->return_rcode != LDNS_RCODE_NOERROR) {
verbose(VERB_ALGO, "constructing reply for validation");
vq->orig_msg = (struct dns_msg*)regional_alloc(qstate->region,
sizeof(struct dns_msg));
if(!vq->orig_msg)
return NULL;
vq->orig_msg->qinfo = qstate->qinfo;
vq->orig_msg->rep = (struct reply_info*)regional_alloc(
qstate->region, sizeof(struct reply_info));
if(!vq->orig_msg->rep)
return NULL;
memset(vq->orig_msg->rep, 0, sizeof(struct reply_info));
vq->orig_msg->rep->flags = (uint16_t)(qstate->return_rcode&0xf)
|BIT_QR|BIT_RA|(qstate->query_flags|(BIT_CD|BIT_RD));
vq->orig_msg->rep->qdcount = 1;
vq->orig_msg->rep->reason_bogus = LDNS_EDE_NONE;
} else {
vq->orig_msg = qstate->return_msg;
}
vq->qchase = qstate->qinfo;
vq->chase_reply = regional_alloc_init(qstate->region,
vq->orig_msg->rep,
sizeof(struct reply_info) - sizeof(struct rrset_ref));
if(!vq->chase_reply)
return NULL;
if(vq->orig_msg->rep->rrset_count > RR_COUNT_MAX)
return NULL;
vq->chase_reply->rrsets = regional_alloc(qstate->region,
sizeof(struct ub_packed_rrset_key*) *
(vq->orig_msg->rep->rrset_count
+ vq->orig_msg->rep->an_numrrsets));
if(!vq->chase_reply->rrsets)
return NULL;
memmove(vq->chase_reply->rrsets, vq->orig_msg->rep->rrsets,
sizeof(struct ub_packed_rrset_key*) *
vq->orig_msg->rep->rrset_count);
vq->rrset_skip = 0;
return vq;
}
static struct val_qstate*
val_new(struct module_qstate* qstate, int id)
{
struct val_qstate* vq = (struct val_qstate*)regional_alloc(
qstate->region, sizeof(*vq));
log_assert(!qstate->minfo[id]);
if(!vq)
return NULL;
memset(vq, 0, sizeof(*vq));
qstate->minfo[id] = vq;
vq->state = VAL_INIT_STATE;
return val_new_getmsg(qstate, vq);
}
static void
val_restart(struct val_qstate* vq)
{
struct comm_timer* temp_timer;
int restart_count;
if(!vq) return;
temp_timer = vq->suspend_timer;
restart_count = vq->restart_count+1;
memset(vq, 0, sizeof(*vq));
vq->suspend_timer = temp_timer;
vq->restart_count = restart_count;
vq->state = VAL_INIT_STATE;
}
static int
val_error(struct module_qstate* qstate, int id)
{
qstate->ext_state[id] = module_error;
qstate->return_rcode = LDNS_RCODE_SERVFAIL;
return 0;
}
static int
needs_validation(struct module_qstate* qstate, int ret_rc,
struct dns_msg* ret_msg)
{
int rcode;
if(qstate->is_valrec) {
verbose(VERB_ALGO, "not validating response, is valrec"
"(validation recursion lookup)");
return 0;
}
if(ret_rc != LDNS_RCODE_NOERROR || !ret_msg)
rcode = ret_rc;
else rcode = (int)FLAGS_GET_RCODE(ret_msg->rep->flags);
if(rcode != LDNS_RCODE_NOERROR && rcode != LDNS_RCODE_NXDOMAIN) {
if(verbosity >= VERB_ALGO) {
char rc[16];
rc[0]=0;
(void)sldns_wire2str_rcode_buf(rcode, rc, sizeof(rc));
verbose(VERB_ALGO, "cannot validate non-answer, rcode %s", rc);
}
return 0;
}
if(qstate->qinfo.qtype == LDNS_RR_TYPE_RRSIG &&
rcode == LDNS_RCODE_NOERROR && ret_msg &&
ret_msg->rep->an_numrrsets > 0) {
verbose(VERB_ALGO, "cannot validate RRSIG, no sigs on sigs.");
return 0;
}
return 1;
}
static int
already_validated(struct dns_msg* ret_msg)
{
if (ret_msg && ret_msg->rep->security > sec_status_bogus)
{
verbose(VERB_ALGO, "response has already been validated: %s",
sec_status_to_string(ret_msg->rep->security));
return 1;
}
return 0;
}
static int
generate_request(struct module_qstate* qstate, int id, uint8_t* name,
size_t namelen, uint16_t qtype, uint16_t qclass, uint16_t flags,
struct module_qstate** newq, int detached)
{
struct val_qstate* vq = (struct val_qstate*)qstate->minfo[id];
struct query_info ask;
int valrec;
ask.qname = name;
ask.qname_len = namelen;
ask.qtype = qtype;
ask.qclass = qclass;
ask.local_alias = NULL;
log_query_info(VERB_ALGO, "generate request", &ask);
valrec = 1;
fptr_ok(fptr_whitelist_modenv_detect_cycle(qstate->env->detect_cycle));
if((*qstate->env->detect_cycle)(qstate, &ask,
(uint16_t)(BIT_RD|flags), 0, valrec)) {
verbose(VERB_ALGO, "Could not generate request: cycle detected");
return 0;
}
if(detached) {
struct mesh_state* sub = NULL;
fptr_ok(fptr_whitelist_modenv_add_sub(
qstate->env->add_sub));
if(!(*qstate->env->add_sub)(qstate, &ask,
(uint16_t)(BIT_RD|flags), 0, valrec, newq, &sub)){
log_err("Could not generate request: out of memory");
return 0;
}
}
else {
fptr_ok(fptr_whitelist_modenv_attach_sub(
qstate->env->attach_sub));
if(!(*qstate->env->attach_sub)(qstate, &ask,
(uint16_t)(BIT_RD|flags), 0, valrec, newq)){
log_err("Could not generate request: out of memory");
return 0;
}
}
if(*newq) {
sock_list_merge(&(*newq)->blacklist, (*newq)->region,
vq->chain_blacklist);
}
qstate->ext_state[id] = module_wait_subquery;
return 1;
}
static int
generate_keytag_query(struct module_qstate* qstate, int id,
struct trust_anchor* ta)
{
#define MAX_LABEL_TAGS (LDNS_MAX_LABELLEN-3)/5
size_t i, numtag;
uint16_t tags[MAX_LABEL_TAGS];
char tagstr[LDNS_MAX_LABELLEN+1] = "_ta";
size_t tagstr_left = sizeof(tagstr) - strlen(tagstr);
char* tagstr_pos = tagstr + strlen(tagstr);
uint8_t dnamebuf[LDNS_MAX_DOMAINLEN+1];
size_t dnamebuf_len = sizeof(dnamebuf);
uint8_t* keytagdname;
struct module_qstate* newq = NULL;
enum module_ext_state ext_state = qstate->ext_state[id];
numtag = anchor_list_keytags(ta, tags, MAX_LABEL_TAGS);
if(numtag == 0)
return 0;
for(i=0; i<numtag; i++) {
snprintf(tagstr_pos, tagstr_left, "-%04x", (unsigned)tags[i]);
tagstr_left -= strlen(tagstr_pos);
tagstr_pos += strlen(tagstr_pos);
}
sldns_str2wire_dname_buf_origin(tagstr, dnamebuf, &dnamebuf_len,
ta->name, ta->namelen);
if(!(keytagdname = (uint8_t*)regional_alloc_init(qstate->region,
dnamebuf, dnamebuf_len))) {
log_err("could not generate key tag query: out of memory");
return 0;
}
log_nametypeclass(VERB_OPS, "generate keytag query", keytagdname,
LDNS_RR_TYPE_NULL, ta->dclass);
if(!generate_request(qstate, id, keytagdname, dnamebuf_len,
LDNS_RR_TYPE_NULL, ta->dclass, 0, &newq, 1)) {
verbose(VERB_ALGO, "failed to generate key tag signaling request");
return 0;
}
qstate->ext_state[id] = ext_state;
return 1;
}
static int
sentinel_get_keytag(char* start, uint16_t* keytag) {
char* keytag_str;
char* e = NULL;
keytag_str = calloc(1, SENTINEL_KEYTAG_LEN + 1 );
if(!keytag_str)
return 0;
memmove(keytag_str, start, SENTINEL_KEYTAG_LEN);
keytag_str[SENTINEL_KEYTAG_LEN] = '\0';
*keytag = (uint16_t)strtol(keytag_str, &e, 10);
if(!e || *e != '\0') {
free(keytag_str);
return 0;
}
free(keytag_str);
return 1;
}
static int
prime_trust_anchor(struct module_qstate* qstate, struct val_qstate* vq,
int id, struct trust_anchor* toprime)
{
struct module_qstate* newq = NULL;
int ret = generate_request(qstate, id, toprime->name, toprime->namelen,
LDNS_RR_TYPE_DNSKEY, toprime->dclass, BIT_CD, &newq, 0);
if(newq && qstate->env->cfg->trust_anchor_signaling &&
!generate_keytag_query(qstate, id, toprime)) {
verbose(VERB_ALGO, "keytag signaling query failed");
return 0;
}
if(!ret) {
verbose(VERB_ALGO, "Could not prime trust anchor");
return 0;
}
vq->wait_prime_ta = 1;
vq->trust_anchor_name = regional_alloc_init(qstate->region,
toprime->name, toprime->namelen);
vq->trust_anchor_len = toprime->namelen;
vq->trust_anchor_labs = toprime->namelabs;
if(!vq->trust_anchor_name) {
log_err("Could not prime trust anchor: out of memory");
return 0;
}
return 1;
}
static int
validate_msg_signatures(struct module_qstate* qstate, struct val_qstate* vq,
struct module_env* env, struct val_env* ve,
struct reply_info* chase_reply, struct key_entry_key* key_entry,
int* suspend)
{
uint8_t* sname;
size_t i, slen;
struct ub_packed_rrset_key* s;
enum sec_status sec;
int num_verifies = 0, verified, have_state = 0;
char reasonbuf[256];
char* reason = NULL;
sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
*suspend = 0;
if(vq->msg_signatures_state) {
vq->msg_signatures_state = 0;
have_state = 1;
}
for(i=0; i<chase_reply->an_numrrsets; i++) {
if(have_state && i <= vq->msg_signatures_index)
continue;
s = chase_reply->rrsets[i];
if(i>0 && ntohs(chase_reply->rrsets[i-1]->rk.type) ==
LDNS_RR_TYPE_DNAME &&
ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME &&
((struct packed_rrset_data*)chase_reply->rrsets[i-1]->entry.data)->security == sec_status_secure &&
dname_strict_subdomain_c(s->rk.dname, chase_reply->rrsets[i-1]->rk.dname)
) {
((struct packed_rrset_data*)s->entry.data)->security =
sec_status_secure;
((struct packed_rrset_data*)s->entry.data)->trust =
rrset_trust_validated;
continue;
}
sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason,
&reason_bogus, LDNS_SECTION_ANSWER, qstate, &verified,
reasonbuf, sizeof(reasonbuf));
if(sec != sec_status_secure) {
log_nametypeclass(VERB_QUERY, "validator: response "
"has failed ANSWER rrset:", s->rk.dname,
ntohs(s->rk.type), ntohs(s->rk.rrset_class));
errinf_ede(qstate, reason, reason_bogus);
if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME)
errinf(qstate, "for CNAME");
else if(ntohs(s->rk.type) == LDNS_RR_TYPE_DNAME)
errinf(qstate, "for DNAME");
errinf_origin(qstate, qstate->reply_origin);
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, reason_bogus);
return 0;
}
num_verifies += verified;
if(num_verifies > MAX_VALIDATE_AT_ONCE &&
i+1 < (env->cfg->val_clean_additional?
chase_reply->an_numrrsets+chase_reply->ns_numrrsets:
chase_reply->rrset_count)) {
*suspend = 1;
vq->msg_signatures_state = 1;
vq->msg_signatures_index = i;
verbose(VERB_ALGO, "msg signature validation "
"suspended");
return 0;
}
}
for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+
chase_reply->ns_numrrsets; i++) {
if(have_state && i <= vq->msg_signatures_index)
continue;
s = chase_reply->rrsets[i];
sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason,
&reason_bogus, LDNS_SECTION_AUTHORITY, qstate,
&verified, reasonbuf, sizeof(reasonbuf));
if(sec != sec_status_secure) {
log_nametypeclass(VERB_QUERY, "validator: response "
"has failed AUTHORITY rrset:", s->rk.dname,
ntohs(s->rk.type), ntohs(s->rk.rrset_class));
errinf_ede(qstate, reason, reason_bogus);
errinf_origin(qstate, qstate->reply_origin);
errinf_rrset(qstate, s);
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, reason_bogus);
return 0;
}
num_verifies += verified;
if(num_verifies > MAX_VALIDATE_AT_ONCE &&
i+1 < (env->cfg->val_clean_additional?
chase_reply->an_numrrsets+chase_reply->ns_numrrsets:
chase_reply->rrset_count)) {
*suspend = 1;
vq->msg_signatures_state = 1;
vq->msg_signatures_index = i;
verbose(VERB_ALGO, "msg signature validation "
"suspended");
return 0;
}
}
if(!env->cfg->val_clean_additional)
return 1;
for(i=chase_reply->an_numrrsets+chase_reply->ns_numrrsets;
i<chase_reply->rrset_count; i++) {
if(have_state && i <= vq->msg_signatures_index)
continue;
s = chase_reply->rrsets[i];
val_find_rrset_signer(s, &sname, &slen);
verified = 0;
if(sname && query_dname_compare(sname, key_entry->name)==0)
(void)val_verify_rrset_entry(env, ve, s, key_entry,
&reason, NULL, LDNS_SECTION_ADDITIONAL, qstate,
&verified, reasonbuf, sizeof(reasonbuf));
num_verifies += verified;
if(num_verifies > MAX_VALIDATE_AT_ONCE &&
i+1 < chase_reply->rrset_count) {
*suspend = 1;
vq->msg_signatures_state = 1;
vq->msg_signatures_index = i;
verbose(VERB_ALGO, "msg signature validation "
"suspended");
return 0;
}
}
return 1;
}
void
validate_suspend_timer_cb(void* arg)
{
struct module_qstate* qstate = (struct module_qstate*)arg;
verbose(VERB_ALGO, "validate_suspend timer, continue");
mesh_run(qstate->env->mesh, qstate->mesh_info, module_event_pass,
NULL);
}
static int
validate_suspend_setup_timer(struct module_qstate* qstate,
struct val_qstate* vq, int id, enum val_state resume_state)
{
struct timeval tv;
int usec, slack, base;
if(vq->suspend_count >= MAX_VALIDATION_SUSPENDS) {
verbose(VERB_ALGO, "validate_suspend timer: "
"reached MAX_VALIDATION_SUSPENDS (%d); error out",
MAX_VALIDATION_SUSPENDS);
errinf(qstate, "max validation suspends reached, "
"too many RRSIG validations");
return 0;
}
verbose(VERB_ALGO, "validate_suspend timer, set for suspend");
vq->state = resume_state;
qstate->ext_state[id] = module_wait_reply;
if(!vq->suspend_timer) {
vq->suspend_timer = comm_timer_create(
qstate->env->worker_base,
validate_suspend_timer_cb, qstate);
if(!vq->suspend_timer) {
log_err("validate_suspend_setup_timer: "
"out of memory for comm_timer_create");
return 0;
}
}
usec = 50000;
slack = 0;
if(qstate->env->mesh->all.count >= qstate->env->mesh->max_reply_states)
slack += 3;
else if(qstate->env->mesh->all.count >= qstate->env->mesh->max_reply_states/2)
slack += 2;
else if(qstate->env->mesh->all.count >= qstate->env->mesh->max_reply_states/4)
slack += 1;
if(vq->suspend_count > 3)
slack += 3;
else if(vq->suspend_count > 0)
slack += vq->suspend_count;
if(slack != 0 && slack <= 12 ) {
usec = usec << slack;
}
base = usec * 9/10;
usec = base + ub_random_max(qstate->env->rnd, usec-base);
tv.tv_usec = (usec % 1000000);
tv.tv_sec = (usec / 1000000);
vq->suspend_count ++;
comm_timer_set(vq->suspend_timer, &tv);
return 1;
}
static int
detect_wrongly_truncated(struct reply_info* rep)
{
size_t i;
if(rep->ns_numrrsets != 1 || rep->an_numrrsets == 0)
return 0;
if(ntohs(rep->rrsets[ rep->an_numrrsets ]->rk.type) != LDNS_RR_TYPE_NS)
return 0;
if(((struct packed_rrset_data*)rep->rrsets[ rep->an_numrrsets ]
->entry.data)->security == sec_status_secure)
return 0;
for(i=0; i<rep->an_numrrsets; i++) {
if(((struct packed_rrset_data*)rep->rrsets[ i ]
->entry.data)->security != sec_status_secure)
return 0;
}
verbose(VERB_ALGO, "truncating to minimal response");
return 1;
}
static void
remove_spurious_authority(struct reply_info* chase_reply,
struct reply_info* orig_reply)
{
size_t i, found = 0;
int remove = 0;
if(chase_reply->an_numrrsets == 0 && chase_reply->ns_numrrsets == 1)
return;
for(i = chase_reply->an_numrrsets;
i < chase_reply->an_numrrsets+chase_reply->ns_numrrsets; i++) {
struct packed_rrset_data* d = (struct packed_rrset_data*)
chase_reply->rrsets[i]->entry.data;
if(ntohs(chase_reply->rrsets[i]->rk.type) == LDNS_RR_TYPE_NS
&& d->rrsig_count == 0) {
found = i;
remove = 1;
break;
}
}
if(!remove) return;
log_rrset_key(VERB_ALGO, "Removing spurious unsigned NS record "
"(likely inserted by forwarder)", chase_reply->rrsets[found]);
for(i = orig_reply->an_numrrsets;
i < orig_reply->an_numrrsets+orig_reply->ns_numrrsets; i++) {
if(ntohs(orig_reply->rrsets[i]->rk.type) == LDNS_RR_TYPE_NS
&& query_dname_compare(orig_reply->rrsets[i]->rk.dname,
chase_reply->rrsets[found]->rk.dname) == 0) {
val_reply_remove_auth(orig_reply, i);
break;
}
}
val_reply_remove_auth(chase_reply, found);
}
static void
validate_positive_response(struct module_env* env, struct val_env* ve,
struct query_info* qchase, struct reply_info* chase_reply,
struct key_entry_key* kkey, struct module_qstate* qstate,
struct val_qstate* vq, int* nsec3_calculations, int* suspend)
{
uint8_t* wc = NULL;
size_t wl;
int wc_cached = 0;
int wc_NSEC_ok = 0;
int nsec3s_seen = 0;
size_t i;
struct ub_packed_rrset_key* s;
*suspend = 0;
for(i=0; i<chase_reply->an_numrrsets; i++) {
s = chase_reply->rrsets[i];
if(!val_rrset_wildcard(s, &wc, &wl)) {
log_nametypeclass(VERB_QUERY, "Positive response has "
"inconsistent wildcard sigs:", s->rk.dname,
ntohs(s->rk.type), ntohs(s->rk.rrset_class));
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
return;
}
if(wc && !wc_cached && env->cfg->aggressive_nsec) {
rrset_cache_update_wildcard(env->rrset_cache, s, wc, wl,
env->alloc, *env->now);
wc_cached = 1;
}
}
for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+
chase_reply->ns_numrrsets; i++) {
s = chase_reply->rrsets[i];
if(wc != NULL && ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) {
if(val_nsec_proves_positive_wildcard(s, qchase, wc)) {
wc_NSEC_ok = 1;
}
}
if(wc != NULL && ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) {
nsec3s_seen = 1;
}
}
if(wc != NULL && !wc_NSEC_ok && nsec3s_seen &&
nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) {
enum sec_status sec = nsec3_prove_wildcard(env, ve,
chase_reply->rrsets+chase_reply->an_numrrsets,
chase_reply->ns_numrrsets, qchase, kkey, wc,
&vq->nsec3_cache_table, nsec3_calculations);
if(sec == sec_status_insecure) {
verbose(VERB_ALGO, "Positive wildcard response is "
"insecure");
chase_reply->security = sec_status_insecure;
return;
} else if(sec == sec_status_secure) {
wc_NSEC_ok = 1;
} else if(sec == sec_status_unchecked) {
*suspend = 1;
return;
}
}
if(wc != NULL && !wc_NSEC_ok) {
verbose(VERB_QUERY, "positive response was wildcard "
"expansion and did not prove original data "
"did not exist");
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
return;
}
verbose(VERB_ALGO, "Successfully validated positive response");
chase_reply->security = sec_status_secure;
}
static void
validate_nodata_response(struct module_env* env, struct val_env* ve,
struct query_info* qchase, struct reply_info* chase_reply,
struct key_entry_key* kkey, struct module_qstate* qstate,
struct val_qstate* vq, int* nsec3_calculations, int* suspend)
{
int has_valid_nsec = 0;
uint8_t* ce = NULL;
uint8_t* wc = NULL;
int nsec3s_seen = 0;
struct ub_packed_rrset_key* s;
size_t i;
*suspend = 0;
for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+
chase_reply->ns_numrrsets; i++) {
s = chase_reply->rrsets[i];
if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) {
if(nsec_proves_nodata(s, qchase, &wc)) {
has_valid_nsec = 1;
}
if(val_nsec_proves_name_error(s, qchase->qname)) {
ce = nsec_closest_encloser(qchase->qname, s);
}
if(val_nsec_proves_insecuredelegation(s, qchase)) {
verbose(VERB_ALGO, "delegation is insecure");
chase_reply->security = sec_status_insecure;
return;
}
} else if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) {
nsec3s_seen = 1;
}
}
if(wc && !ce)
has_valid_nsec = 0;
else if(wc && ce) {
if(query_dname_compare(wc, ce) != 0) {
has_valid_nsec = 0;
}
}
if(!has_valid_nsec && nsec3s_seen &&
nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) {
enum sec_status sec = nsec3_prove_nodata(env, ve,
chase_reply->rrsets+chase_reply->an_numrrsets,
chase_reply->ns_numrrsets, qchase, kkey,
&vq->nsec3_cache_table, nsec3_calculations);
if(sec == sec_status_insecure) {
verbose(VERB_ALGO, "NODATA response is insecure");
chase_reply->security = sec_status_insecure;
return;
} else if(sec == sec_status_secure) {
has_valid_nsec = 1;
} else if(sec == sec_status_unchecked) {
*suspend = 1;
return;
}
}
if(!has_valid_nsec) {
verbose(VERB_QUERY, "NODATA response failed to prove NODATA "
"status with NSEC/NSEC3");
if(verbosity >= VERB_ALGO)
log_dns_msg("Failed NODATA", qchase, chase_reply);
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
return;
}
verbose(VERB_ALGO, "successfully validated NODATA response.");
chase_reply->security = sec_status_secure;
}
static void
validate_nameerror_response(struct module_env* env, struct val_env* ve,
struct query_info* qchase, struct reply_info* chase_reply,
struct key_entry_key* kkey, int* rcode,
struct module_qstate* qstate, struct val_qstate* vq,
int* nsec3_calculations, int* suspend)
{
int has_valid_nsec = 0;
int has_valid_wnsec = 0;
int nsec3s_seen = 0;
struct ub_packed_rrset_key* s;
size_t i;
uint8_t* ce;
int ce_labs = 0;
int prev_ce_labs = 0;
*suspend = 0;
for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+
chase_reply->ns_numrrsets; i++) {
s = chase_reply->rrsets[i];
if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) {
if(val_nsec_proves_name_error(s, qchase->qname))
has_valid_nsec = 1;
ce = nsec_closest_encloser(qchase->qname, s);
ce_labs = dname_count_labels(ce);
if(ce_labs > prev_ce_labs ||
(ce_labs == prev_ce_labs &&
has_valid_wnsec == 0)) {
if(val_nsec_proves_no_wc(s, qchase->qname,
qchase->qname_len))
has_valid_wnsec = 1;
else
has_valid_wnsec = 0;
}
prev_ce_labs = ce_labs;
if(val_nsec_proves_insecuredelegation(s, qchase)) {
verbose(VERB_ALGO, "delegation is insecure");
chase_reply->security = sec_status_insecure;
return;
}
} else if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3)
nsec3s_seen = 1;
}
if((!has_valid_nsec || !has_valid_wnsec) && nsec3s_seen &&
nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) {
chase_reply->security = nsec3_prove_nameerror(env, ve,
chase_reply->rrsets, chase_reply->an_numrrsets+
chase_reply->ns_numrrsets, qchase, kkey,
&vq->nsec3_cache_table, nsec3_calculations);
if(chase_reply->security == sec_status_unchecked) {
*suspend = 1;
return;
} else if(chase_reply->security != sec_status_secure) {
verbose(VERB_QUERY, "NameError response failed nsec, "
"nsec3 proof was %s", sec_status_to_string(
chase_reply->security));
return;
}
has_valid_nsec = 1;
has_valid_wnsec = 1;
}
if(!has_valid_nsec) {
validate_nodata_response(env, ve, qchase, chase_reply, kkey,
qstate, vq, nsec3_calculations, suspend);
if(*suspend) return;
verbose(VERB_QUERY, "NameError response has failed to prove: "
"qname does not exist");
if(chase_reply->security == sec_status_secure) {
*rcode = LDNS_RCODE_NOERROR;
} else {
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
}
return;
}
if(!has_valid_wnsec) {
validate_nodata_response(env, ve, qchase, chase_reply, kkey,
qstate, vq, nsec3_calculations, suspend);
if(*suspend) return;
verbose(VERB_QUERY, "NameError response has failed to prove: "
"covering wildcard does not exist");
if (chase_reply->security == sec_status_secure) {
*rcode = LDNS_RCODE_NOERROR;
} else {
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
}
return;
}
verbose(VERB_ALGO, "successfully validated NAME ERROR response.");
chase_reply->security = sec_status_secure;
}
static void
validate_referral_response(struct reply_info* chase_reply)
{
size_t i;
enum sec_status s;
chase_reply->security = sec_status_secure;
for(i=0; i<chase_reply->rrset_count; i++) {
s = ((struct packed_rrset_data*)chase_reply->rrsets[i]
->entry.data)->security;
if(s < chase_reply->security)
chase_reply->security = s;
}
verbose(VERB_ALGO, "validated part of referral response as %s",
sec_status_to_string(chase_reply->security));
}
static void
validate_any_response(struct module_env* env, struct val_env* ve,
struct query_info* qchase, struct reply_info* chase_reply,
struct key_entry_key* kkey, struct module_qstate* qstate,
struct val_qstate* vq, int* nsec3_calculations, int* suspend)
{
uint8_t* wc = NULL;
size_t wl;
int wc_NSEC_ok = 0;
int nsec3s_seen = 0;
size_t i;
struct ub_packed_rrset_key* s;
*suspend = 0;
if(qchase->qtype != LDNS_RR_TYPE_ANY) {
log_err("internal error: ANY validation called for non-ANY");
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
return;
}
for(i=0; i<chase_reply->an_numrrsets; i++) {
s = chase_reply->rrsets[i];
if(!val_rrset_wildcard(s, &wc, &wl)) {
log_nametypeclass(VERB_QUERY, "Positive ANY response"
" has inconsistent wildcard sigs:",
s->rk.dname, ntohs(s->rk.type),
ntohs(s->rk.rrset_class));
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
return;
}
}
if(wc != NULL)
for(i=0; i<chase_reply->an_numrrsets+chase_reply->ns_numrrsets;
i++) {
s = chase_reply->rrsets[i];
if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) {
if(val_nsec_proves_positive_wildcard(s, qchase, wc)) {
wc_NSEC_ok = 1;
}
}
if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) {
nsec3s_seen = 1;
}
}
if(wc != NULL && !wc_NSEC_ok && nsec3s_seen &&
nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) {
enum sec_status sec = nsec3_prove_wildcard(env, ve,
chase_reply->rrsets,
chase_reply->an_numrrsets+chase_reply->ns_numrrsets,
qchase, kkey, wc, &vq->nsec3_cache_table,
nsec3_calculations);
if(sec == sec_status_insecure) {
verbose(VERB_ALGO, "Positive ANY wildcard response is "
"insecure");
chase_reply->security = sec_status_insecure;
return;
} else if(sec == sec_status_secure) {
wc_NSEC_ok = 1;
} else if(sec == sec_status_unchecked) {
*suspend = 1;
return;
}
}
if(wc != NULL && !wc_NSEC_ok) {
verbose(VERB_QUERY, "positive ANY response was wildcard "
"expansion and did not prove original data "
"did not exist");
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
return;
}
verbose(VERB_ALGO, "Successfully validated positive ANY response");
chase_reply->security = sec_status_secure;
}
static void
validate_cname_response(struct module_env* env, struct val_env* ve,
struct query_info* qchase, struct reply_info* chase_reply,
struct key_entry_key* kkey, struct module_qstate* qstate,
struct val_qstate* vq, int* nsec3_calculations, int* suspend)
{
uint8_t* wc = NULL;
size_t wl;
int wc_NSEC_ok = 0;
int nsec3s_seen = 0;
size_t i;
struct ub_packed_rrset_key* s;
*suspend = 0;
for(i=0; i<chase_reply->an_numrrsets; i++) {
s = chase_reply->rrsets[i];
if(!val_rrset_wildcard(s, &wc, &wl)) {
log_nametypeclass(VERB_QUERY, "Cname response has "
"inconsistent wildcard sigs:", s->rk.dname,
ntohs(s->rk.type), ntohs(s->rk.rrset_class));
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
return;
}
if(qchase->qtype != LDNS_RR_TYPE_DNAME &&
ntohs(s->rk.type) == LDNS_RR_TYPE_DNAME && wc) {
log_nametypeclass(VERB_QUERY, "cannot validate a "
"wildcarded DNAME:", s->rk.dname,
ntohs(s->rk.type), ntohs(s->rk.rrset_class));
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
return;
}
if (ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME) {
break;
}
}
for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+
chase_reply->ns_numrrsets; i++) {
s = chase_reply->rrsets[i];
if(wc != NULL && ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) {
if(val_nsec_proves_positive_wildcard(s, qchase, wc)) {
wc_NSEC_ok = 1;
}
}
if(wc != NULL && ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) {
nsec3s_seen = 1;
}
}
if(wc != NULL && !wc_NSEC_ok && nsec3s_seen &&
nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) {
enum sec_status sec = nsec3_prove_wildcard(env, ve,
chase_reply->rrsets+chase_reply->an_numrrsets,
chase_reply->ns_numrrsets, qchase, kkey, wc,
&vq->nsec3_cache_table, nsec3_calculations);
if(sec == sec_status_insecure) {
verbose(VERB_ALGO, "wildcard CNAME response is "
"insecure");
chase_reply->security = sec_status_insecure;
return;
} else if(sec == sec_status_secure) {
wc_NSEC_ok = 1;
} else if(sec == sec_status_unchecked) {
*suspend = 1;
return;
}
}
if(wc != NULL && !wc_NSEC_ok) {
verbose(VERB_QUERY, "CNAME response was wildcard "
"expansion and did not prove original data "
"did not exist");
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
return;
}
verbose(VERB_ALGO, "Successfully validated CNAME response");
chase_reply->security = sec_status_secure;
}
static void
validate_cname_noanswer_response(struct module_env* env, struct val_env* ve,
struct query_info* qchase, struct reply_info* chase_reply,
struct key_entry_key* kkey, struct module_qstate* qstate,
struct val_qstate* vq, int* nsec3_calculations, int* suspend)
{
int nodata_valid_nsec = 0;
uint8_t* ce = NULL;
uint8_t* wc = NULL;
int nxdomain_valid_nsec = 0;
int nxdomain_valid_wnsec = 0;
int nsec3s_seen = 0;
struct ub_packed_rrset_key* s;
size_t i;
uint8_t* nsec_ce;
int ce_labs = 0;
int prev_ce_labs = 0;
*suspend = 0;
for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+
chase_reply->ns_numrrsets; i++) {
s = chase_reply->rrsets[i];
if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) {
if(nsec_proves_nodata(s, qchase, &wc)) {
nodata_valid_nsec = 1;
}
if(val_nsec_proves_name_error(s, qchase->qname)) {
ce = nsec_closest_encloser(qchase->qname, s);
nxdomain_valid_nsec = 1;
}
nsec_ce = nsec_closest_encloser(qchase->qname, s);
ce_labs = dname_count_labels(nsec_ce);
if(ce_labs > prev_ce_labs ||
(ce_labs == prev_ce_labs &&
nxdomain_valid_wnsec == 0)) {
if(val_nsec_proves_no_wc(s, qchase->qname,
qchase->qname_len))
nxdomain_valid_wnsec = 1;
else
nxdomain_valid_wnsec = 0;
}
prev_ce_labs = ce_labs;
if(val_nsec_proves_insecuredelegation(s, qchase)) {
verbose(VERB_ALGO, "delegation is insecure");
chase_reply->security = sec_status_insecure;
return;
}
} else if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) {
nsec3s_seen = 1;
}
}
if(wc && !ce)
nodata_valid_nsec = 0;
else if(wc && ce) {
if(query_dname_compare(wc, ce) != 0) {
nodata_valid_nsec = 0;
}
}
if(nxdomain_valid_nsec && !nxdomain_valid_wnsec) {
nxdomain_valid_nsec = 0;
}
if(nodata_valid_nsec && nxdomain_valid_nsec) {
verbose(VERB_QUERY, "CNAMEchain to noanswer proves that name "
"exists and not exists, bogus");
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
return;
}
if(!nodata_valid_nsec && !nxdomain_valid_nsec && nsec3s_seen &&
nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) {
int nodata;
enum sec_status sec = nsec3_prove_nxornodata(env, ve,
chase_reply->rrsets+chase_reply->an_numrrsets,
chase_reply->ns_numrrsets, qchase, kkey, &nodata,
&vq->nsec3_cache_table, nsec3_calculations);
if(sec == sec_status_insecure) {
verbose(VERB_ALGO, "CNAMEchain to noanswer response "
"is insecure");
chase_reply->security = sec_status_insecure;
return;
} else if(sec == sec_status_secure) {
if(nodata)
nodata_valid_nsec = 1;
else nxdomain_valid_nsec = 1;
} else if(sec == sec_status_unchecked) {
*suspend = 1;
return;
}
}
if(!nodata_valid_nsec && !nxdomain_valid_nsec) {
verbose(VERB_QUERY, "CNAMEchain to noanswer response failed "
"to prove status with NSEC/NSEC3");
if(verbosity >= VERB_ALGO)
log_dns_msg("Failed CNAMEnoanswer", qchase, chase_reply);
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
return;
}
if(nodata_valid_nsec)
verbose(VERB_ALGO, "successfully validated CNAME chain to a "
"NODATA response.");
else verbose(VERB_ALGO, "successfully validated CNAME chain to a "
"NAMEERROR response.");
chase_reply->security = sec_status_secure;
}
static int
processInit(struct module_qstate* qstate, struct val_qstate* vq,
struct val_env* ve, int id)
{
uint8_t* lookup_name;
size_t lookup_len;
struct trust_anchor* anchor;
enum val_classification subtype = val_classify_response(
qstate->query_flags, &qstate->qinfo, &vq->qchase,
vq->orig_msg->rep, vq->rrset_skip);
if(vq->restart_count > ve->max_restart) {
verbose(VERB_ALGO, "restart count exceeded");
return val_error(qstate, id);
}
update_reason_bogus(vq->chase_reply, LDNS_EDE_DNSSEC_BOGUS);
verbose(VERB_ALGO, "validator classification %s",
val_classification_to_string(subtype));
if(subtype == VAL_CLASS_REFERRAL &&
vq->rrset_skip < vq->orig_msg->rep->rrset_count) {
vq->qchase.qname = vq->orig_msg->rep->
rrsets[vq->rrset_skip]->rk.dname;
vq->qchase.qname_len = vq->orig_msg->rep->
rrsets[vq->rrset_skip]->rk.dname_len;
vq->qchase.qtype = ntohs(vq->orig_msg->rep->
rrsets[vq->rrset_skip]->rk.type);
vq->qchase.qclass = ntohs(vq->orig_msg->rep->
rrsets[vq->rrset_skip]->rk.rrset_class);
}
lookup_name = vq->qchase.qname;
lookup_len = vq->qchase.qname_len;
if(vq->qchase.qtype == LDNS_RR_TYPE_DS ||
(vq->qchase.qtype == LDNS_RR_TYPE_NSEC &&
vq->orig_msg->rep->rrset_count > vq->rrset_skip &&
ntohs(vq->orig_msg->rep->rrsets[vq->rrset_skip]->rk.type) ==
LDNS_RR_TYPE_NSEC &&
!(vq->orig_msg->rep->rrsets[vq->rrset_skip]->
rk.flags&PACKED_RRSET_NSEC_AT_APEX))) {
dname_remove_label(&lookup_name, &lookup_len);
}
val_mark_indeterminate(vq->chase_reply, qstate->env->anchors,
qstate->env->rrset_cache, qstate->env);
vq->key_entry = NULL;
vq->empty_DS_name = NULL;
vq->ds_rrset = 0;
anchor = anchors_lookup(qstate->env->anchors,
lookup_name, lookup_len, vq->qchase.qclass);
val_find_signer(subtype, &vq->qchase, vq->orig_msg->rep,
vq->rrset_skip, &vq->signer_name, &vq->signer_len);
if(vq->signer_name != NULL &&
!dname_subdomain_c(lookup_name, vq->signer_name)) {
log_nametypeclass(VERB_ALGO, "this signer name is not a parent "
"of lookupname, omitted", vq->signer_name, 0, 0);
vq->signer_name = NULL;
}
if(vq->signer_name == NULL) {
log_nametypeclass(VERB_ALGO, "no signer, using", lookup_name,
0, 0);
} else {
lookup_name = vq->signer_name;
lookup_len = vq->signer_len;
log_nametypeclass(VERB_ALGO, "signer is", lookup_name, 0, 0);
}
if(subtype == VAL_CLASS_NAMEERROR && vq->signer_name &&
anchor && dname_strict_subdomain_c(anchor->name, lookup_name)){
lock_basic_unlock(&anchor->lock);
anchor = anchors_lookup(qstate->env->anchors,
lookup_name, lookup_len, vq->qchase.qclass);
if(!anchor) {
verbose(VERB_QUERY, "unsigned parent zone denies"
" trust anchor, indeterminate");
vq->chase_reply->security = sec_status_indeterminate;
update_reason_bogus(vq->chase_reply, LDNS_EDE_DNSSEC_INDETERMINATE);
vq->state = VAL_FINISHED_STATE;
return 1;
}
verbose(VERB_ALGO, "trust anchor NXDOMAIN by signed parent");
} else if(subtype == VAL_CLASS_POSITIVE &&
qstate->qinfo.qtype == LDNS_RR_TYPE_DNSKEY &&
query_dname_compare(lookup_name, qstate->qinfo.qname) == 0) {
dname_remove_label(&lookup_name, &lookup_len);
}
if(vq->rrset_skip > 0 || subtype == VAL_CLASS_CNAME ||
subtype == VAL_CLASS_REFERRAL) {
val_fill_reply(vq->chase_reply, vq->orig_msg->rep,
vq->rrset_skip, lookup_name, lookup_len,
vq->signer_name);
if(verbosity >= VERB_ALGO)
log_dns_msg("chased extract", &vq->qchase,
vq->chase_reply);
}
vq->key_entry = key_cache_obtain(ve->kcache, lookup_name, lookup_len,
vq->qchase.qclass, qstate->region, *qstate->env->now);
if(vq->key_entry == NULL && anchor == NULL) {
vq->chase_reply->security = sec_status_indeterminate;
update_reason_bogus(vq->chase_reply, LDNS_EDE_DNSSEC_INDETERMINATE);
vq->state = VAL_FINISHED_STATE;
return 1;
}
else if(vq->key_entry == NULL || (anchor &&
dname_strict_subdomain_c(anchor->name, vq->key_entry->name))) {
if(anchor && anchor->numDS == 0 && anchor->numDNSKEY == 0) {
vq->chase_reply->security = sec_status_insecure;
val_mark_insecure(vq->chase_reply, anchor->name,
qstate->env->rrset_cache, qstate->env);
lock_basic_unlock(&anchor->lock);
vq->state = VAL_FINISHED_STATE;
return 1;
}
verbose(VERB_DETAIL, "prime trust anchor");
if(!prime_trust_anchor(qstate, vq, id, anchor)) {
lock_basic_unlock(&anchor->lock);
return val_error(qstate, id);
}
lock_basic_unlock(&anchor->lock);
vq->state = VAL_FINDKEY_STATE;
return 0;
}
if(anchor) {
lock_basic_unlock(&anchor->lock);
}
if(key_entry_isnull(vq->key_entry)) {
vq->chase_reply->security = sec_status_insecure;
val_mark_insecure(vq->chase_reply, vq->key_entry->name,
qstate->env->rrset_cache, qstate->env);
vq->state = VAL_FINISHED_STATE;
return 1;
} else if(key_entry_isbad(vq->key_entry)) {
sldns_ede_code ede = key_entry_get_reason_bogus(vq->key_entry);
errinf_dname(qstate, "key for validation", vq->key_entry->name);
errinf_ede(qstate, "is marked as invalid", ede);
errinf(qstate, "because of a previous");
errinf(qstate, key_entry_get_reason(vq->key_entry));
vq->restart_count = ve->max_restart;
vq->chase_reply->security = sec_status_bogus;
update_reason_bogus(vq->chase_reply, ede);
vq->state = VAL_FINISHED_STATE;
return 1;
}
vq->state = VAL_FINDKEY_STATE;
return 1;
}
static int
processFindKey(struct module_qstate* qstate, struct val_qstate* vq, int id)
{
uint8_t* target_key_name, *current_key_name;
size_t target_key_len;
int strip_lab;
struct module_qstate* newq = NULL;
log_query_info(VERB_ALGO, "validator: FindKey", &vq->qchase);
log_assert(vq->key_entry && !key_entry_isbad(vq->key_entry));
if(key_entry_isnull(vq->key_entry)) {
if(!generate_request(qstate, id, vq->ds_rrset->rk.dname,
vq->ds_rrset->rk.dname_len, LDNS_RR_TYPE_DNSKEY,
vq->qchase.qclass, BIT_CD, &newq, 0)) {
verbose(VERB_ALGO, "error generating DNSKEY request");
return val_error(qstate, id);
}
return 0;
}
target_key_name = vq->signer_name;
target_key_len = vq->signer_len;
if(!target_key_name) {
target_key_name = vq->qchase.qname;
target_key_len = vq->qchase.qname_len;
}
current_key_name = vq->key_entry->name;
if(query_dname_compare(target_key_name, current_key_name) == 0) {
vq->state = VAL_VALIDATE_STATE;
return 1;
}
if(vq->empty_DS_name) {
if(query_dname_compare(target_key_name,
vq->empty_DS_name) == 0) {
verbose(VERB_ALGO, "Cannot retrieve DS for signature");
errinf_ede(qstate, "no signatures", LDNS_EDE_RRSIGS_MISSING);
errinf_origin(qstate, qstate->reply_origin);
vq->chase_reply->security = sec_status_bogus;
update_reason_bogus(vq->chase_reply, LDNS_EDE_RRSIGS_MISSING);
vq->state = VAL_FINISHED_STATE;
return 1;
}
current_key_name = vq->empty_DS_name;
}
log_nametypeclass(VERB_ALGO, "current keyname", current_key_name,
LDNS_RR_TYPE_DNSKEY, LDNS_RR_CLASS_IN);
log_nametypeclass(VERB_ALGO, "target keyname", target_key_name,
LDNS_RR_TYPE_DNSKEY, LDNS_RR_CLASS_IN);
if(!dname_subdomain_c(target_key_name, current_key_name)) {
verbose(VERB_ALGO, "bad signer name");
vq->chase_reply->security = sec_status_bogus;
vq->state = VAL_FINISHED_STATE;
return 1;
}
strip_lab = dname_count_labels(target_key_name) -
dname_count_labels(current_key_name) - 1;
log_assert(strip_lab >= -1);
verbose(VERB_ALGO, "striplab %d", strip_lab);
if(strip_lab > 0) {
dname_remove_labels(&target_key_name, &target_key_len,
strip_lab);
}
log_nametypeclass(VERB_ALGO, "next keyname", target_key_name,
LDNS_RR_TYPE_DNSKEY, LDNS_RR_CLASS_IN);
if(vq->ds_rrset)
log_nametypeclass(VERB_ALGO, "DS RRset", vq->ds_rrset->rk.dname, LDNS_RR_TYPE_DS, LDNS_RR_CLASS_IN);
else verbose(VERB_ALGO, "No DS RRset");
if(vq->ds_rrset && query_dname_compare(vq->ds_rrset->rk.dname,
vq->key_entry->name) != 0) {
if(!generate_request(qstate, id, vq->ds_rrset->rk.dname,
vq->ds_rrset->rk.dname_len, LDNS_RR_TYPE_DNSKEY,
vq->qchase.qclass, BIT_CD, &newq, 0)) {
verbose(VERB_ALGO, "error generating DNSKEY request");
return val_error(qstate, id);
}
return 0;
}
if(!vq->ds_rrset || query_dname_compare(vq->ds_rrset->rk.dname,
target_key_name) != 0) {
struct dns_msg* msg;
int suspend;
if(vq->sub_ds_msg) {
verbose(VERB_ALGO, "Process suspended sub DS response");
msg = vq->sub_ds_msg;
process_ds_response(qstate, vq, id, LDNS_RCODE_NOERROR,
msg, &msg->qinfo, NULL, &suspend, NULL);
if(suspend) {
if(!validate_suspend_setup_timer(qstate, vq,
id, VAL_FINDKEY_STATE))
return val_error(qstate, id);
return 0;
}
vq->sub_ds_msg = NULL;
return 1;
} else if(!qstate->blacklist && !vq->chain_blacklist &&
(msg=val_find_DS(qstate->env, target_key_name,
target_key_len, vq->qchase.qclass, qstate->region,
vq->key_entry->name)) ) {
verbose(VERB_ALGO, "Process cached DS response");
process_ds_response(qstate, vq, id, LDNS_RCODE_NOERROR,
msg, &msg->qinfo, NULL, &suspend, NULL);
if(suspend) {
if(!validate_suspend_setup_timer(qstate, vq,
id, VAL_FINDKEY_STATE))
return val_error(qstate, id);
return 0;
}
return 1;
}
if(!generate_request(qstate, id, target_key_name,
target_key_len, LDNS_RR_TYPE_DS, vq->qchase.qclass,
BIT_CD, &newq, 0)) {
verbose(VERB_ALGO, "error generating DS request");
return val_error(qstate, id);
}
return 0;
}
if(!generate_request(qstate, id, vq->ds_rrset->rk.dname,
vq->ds_rrset->rk.dname_len, LDNS_RR_TYPE_DNSKEY,
vq->qchase.qclass, BIT_CD, &newq, 0)) {
verbose(VERB_ALGO, "error generating DNSKEY request");
return val_error(qstate, id);
}
return 0;
}
static int
processValidate(struct module_qstate* qstate, struct val_qstate* vq,
struct val_env* ve, int id)
{
enum val_classification subtype;
int rcode, suspend, nsec3_calculations = 0;
if(!vq->key_entry) {
verbose(VERB_ALGO, "validate: no key entry, failed");
return val_error(qstate, id);
}
vq->state = VAL_FINISHED_STATE;
if(key_entry_isnull(vq->key_entry)) {
verbose(VERB_DETAIL, "Verified that %sresponse is INSECURE",
vq->signer_name?"":"unsigned ");
vq->chase_reply->security = sec_status_insecure;
val_mark_insecure(vq->chase_reply, vq->key_entry->name,
qstate->env->rrset_cache, qstate->env);
key_cache_insert(ve->kcache, vq->key_entry,
qstate->env->cfg->val_log_level >= 2);
return 1;
}
if(key_entry_isbad(vq->key_entry)) {
log_nametypeclass(VERB_DETAIL, "Could not establish a chain "
"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;
update_reason_bogus(vq->chase_reply,
key_entry_get_reason_bogus(vq->key_entry));
errinf_ede(qstate, "while building chain of trust",
key_entry_get_reason_bogus(vq->key_entry));
if(vq->restart_count >= ve->max_restart)
key_cache_insert(ve->kcache, vq->key_entry,
qstate->env->cfg->val_log_level >= 2);
return 1;
}
if(vq->signer_name == NULL) {
log_query_info(VERB_ALGO, "processValidate: state has no "
"signer name", &vq->qchase);
verbose(VERB_DETAIL, "Could not establish validation of "
"INSECURE status of unsigned response.");
errinf_ede(qstate, "no signatures", LDNS_EDE_RRSIGS_MISSING);
errinf_origin(qstate, qstate->reply_origin);
vq->chase_reply->security = sec_status_bogus;
update_reason_bogus(vq->chase_reply, LDNS_EDE_RRSIGS_MISSING);
return 1;
}
subtype = val_classify_response(qstate->query_flags, &qstate->qinfo,
&vq->qchase, vq->orig_msg->rep, vq->rrset_skip);
if(subtype != VAL_CLASS_REFERRAL)
remove_spurious_authority(vq->chase_reply, vq->orig_msg->rep);
if(!validate_msg_signatures(qstate, vq, qstate->env, ve,
vq->chase_reply, vq->key_entry, &suspend)) {
if(suspend) {
if(!validate_suspend_setup_timer(qstate, vq,
id, VAL_VALIDATE_STATE))
return val_error(qstate, id);
return 0;
}
if((subtype == VAL_CLASS_POSITIVE || subtype == VAL_CLASS_ANY
|| subtype == VAL_CLASS_CNAME) &&
detect_wrongly_truncated(vq->orig_msg->rep)) {
vq->orig_msg->rep->ns_numrrsets = 0;
vq->orig_msg->rep->ar_numrrsets = 0;
vq->orig_msg->rep->rrset_count =
vq->orig_msg->rep->an_numrrsets;
vq->chase_reply->ns_numrrsets = 0;
vq->chase_reply->ar_numrrsets = 0;
vq->chase_reply->rrset_count =
vq->chase_reply->an_numrrsets;
qstate->errinf = NULL;
}
else {
verbose(VERB_DETAIL, "Validate: message contains "
"bad rrsets");
return 1;
}
}
switch(subtype) {
case VAL_CLASS_POSITIVE:
verbose(VERB_ALGO, "Validating a positive response");
validate_positive_response(qstate->env, ve,
&vq->qchase, vq->chase_reply, vq->key_entry,
qstate, vq, &nsec3_calculations, &suspend);
if(suspend) {
if(!validate_suspend_setup_timer(qstate,
vq, id, VAL_VALIDATE_STATE))
return val_error(qstate, id);
return 0;
}
verbose(VERB_DETAIL, "validate(positive): %s",
sec_status_to_string(
vq->chase_reply->security));
break;
case VAL_CLASS_NODATA:
verbose(VERB_ALGO, "Validating a nodata response");
validate_nodata_response(qstate->env, ve,
&vq->qchase, vq->chase_reply, vq->key_entry,
qstate, vq, &nsec3_calculations, &suspend);
if(suspend) {
if(!validate_suspend_setup_timer(qstate,
vq, id, VAL_VALIDATE_STATE))
return val_error(qstate, id);
return 0;
}
verbose(VERB_DETAIL, "validate(nodata): %s",
sec_status_to_string(
vq->chase_reply->security));
break;
case VAL_CLASS_NAMEERROR:
rcode = (int)FLAGS_GET_RCODE(vq->orig_msg->rep->flags);
verbose(VERB_ALGO, "Validating a nxdomain response");
validate_nameerror_response(qstate->env, ve,
&vq->qchase, vq->chase_reply, vq->key_entry, &rcode,
qstate, vq, &nsec3_calculations, &suspend);
if(suspend) {
if(!validate_suspend_setup_timer(qstate,
vq, id, VAL_VALIDATE_STATE))
return val_error(qstate, id);
return 0;
}
verbose(VERB_DETAIL, "validate(nxdomain): %s",
sec_status_to_string(
vq->chase_reply->security));
FLAGS_SET_RCODE(vq->orig_msg->rep->flags, rcode);
FLAGS_SET_RCODE(vq->chase_reply->flags, rcode);
break;
case VAL_CLASS_CNAME:
verbose(VERB_ALGO, "Validating a cname response");
validate_cname_response(qstate->env, ve,
&vq->qchase, vq->chase_reply, vq->key_entry,
qstate, vq, &nsec3_calculations, &suspend);
if(suspend) {
if(!validate_suspend_setup_timer(qstate,
vq, id, VAL_VALIDATE_STATE))
return val_error(qstate, id);
return 0;
}
verbose(VERB_DETAIL, "validate(cname): %s",
sec_status_to_string(
vq->chase_reply->security));
break;
case VAL_CLASS_CNAMENOANSWER:
verbose(VERB_ALGO, "Validating a cname noanswer "
"response");
validate_cname_noanswer_response(qstate->env, ve,
&vq->qchase, vq->chase_reply, vq->key_entry,
qstate, vq, &nsec3_calculations, &suspend);
if(suspend) {
if(!validate_suspend_setup_timer(qstate,
vq, id, VAL_VALIDATE_STATE))
return val_error(qstate, id);
return 0;
}
verbose(VERB_DETAIL, "validate(cname_noanswer): %s",
sec_status_to_string(
vq->chase_reply->security));
break;
case VAL_CLASS_REFERRAL:
verbose(VERB_ALGO, "Validating a referral response");
validate_referral_response(vq->chase_reply);
verbose(VERB_DETAIL, "validate(referral): %s",
sec_status_to_string(
vq->chase_reply->security));
break;
case VAL_CLASS_ANY:
verbose(VERB_ALGO, "Validating a positive ANY "
"response");
validate_any_response(qstate->env, ve, &vq->qchase,
vq->chase_reply, vq->key_entry, qstate, vq,
&nsec3_calculations, &suspend);
if(suspend) {
if(!validate_suspend_setup_timer(qstate,
vq, id, VAL_VALIDATE_STATE))
return val_error(qstate, id);
return 0;
}
verbose(VERB_DETAIL, "validate(positive_any): %s",
sec_status_to_string(
vq->chase_reply->security));
break;
default:
log_err("validate: unhandled response subtype: %d",
subtype);
}
if(vq->chase_reply->security == sec_status_bogus) {
if(subtype == VAL_CLASS_POSITIVE)
errinf(qstate, "wildcard");
else errinf(qstate, val_classification_to_string(subtype));
errinf(qstate, "proof failed");
errinf_origin(qstate, qstate->reply_origin);
}
return 1;
}
static int
processFinished(struct module_qstate* qstate, struct val_qstate* vq,
struct val_env* ve, int id)
{
enum val_classification subtype = val_classify_response(
qstate->query_flags, &qstate->qinfo, &vq->qchase,
vq->orig_msg->rep, vq->rrset_skip);
if(vq->rrset_skip == 0) {
vq->orig_msg->rep->security = vq->chase_reply->security;
update_reason_bogus(vq->orig_msg->rep, vq->chase_reply->reason_bogus);
} else if(subtype != VAL_CLASS_REFERRAL ||
vq->rrset_skip < vq->orig_msg->rep->an_numrrsets +
vq->orig_msg->rep->ns_numrrsets) {
if(vq->chase_reply->security < vq->orig_msg->rep->security) {
vq->orig_msg->rep->security =
vq->chase_reply->security;
update_reason_bogus(vq->orig_msg->rep, vq->chase_reply->reason_bogus);
}
}
if(subtype == VAL_CLASS_REFERRAL) {
vq->rrset_skip = val_next_unchecked(vq->orig_msg->rep,
vq->rrset_skip);
if(vq->rrset_skip < vq->orig_msg->rep->rrset_count) {
verbose(VERB_ALGO, "validator: go to next rrset");
vq->chase_reply->security = sec_status_unchecked;
vq->state = VAL_INIT_STATE;
return 1;
}
}
if(vq->chase_reply->security != sec_status_bogus &&
subtype == VAL_CLASS_CNAME) {
if(!val_chase_cname(&vq->qchase, vq->orig_msg->rep,
&vq->rrset_skip)) {
verbose(VERB_ALGO, "validator: failed to chase CNAME");
vq->orig_msg->rep->security = sec_status_bogus;
update_reason_bogus(vq->orig_msg->rep, LDNS_EDE_DNSSEC_BOGUS);
} else {
log_query_info(VERB_ALGO, "validator: chased to",
&vq->qchase);
vq->chase_reply->security = sec_status_unchecked;
vq->state = VAL_INIT_STATE;
return 1;
}
}
if(vq->orig_msg->rep->security == sec_status_secure) {
val_check_nonsecure(qstate->env, vq->orig_msg->rep);
if(vq->orig_msg->rep->security == sec_status_secure) {
log_query_info(VERB_DETAIL, "validation success",
&qstate->qinfo);
if(!qstate->no_cache_store) {
val_neg_addreply(qstate->env->neg_cache,
vq->orig_msg->rep);
}
}
}
if(vq->orig_msg->rep->security == sec_status_bogus) {
struct msgreply_entry* e;
if(vq->restart_count < ve->max_restart) {
verbose(VERB_ALGO, "validation failed, "
"blacklist and retry to fetch data");
val_blacklist(&qstate->blacklist, qstate->region,
qstate->reply_origin, 0);
qstate->reply_origin = NULL;
qstate->errinf = NULL;
val_restart(vq);
verbose(VERB_ALGO, "pass back to next module");
qstate->ext_state[id] = module_restart_next;
return 0;
}
if(qstate->env->cfg->serve_expired &&
(e=msg_cache_lookup(qstate->env, qstate->qinfo.qname,
qstate->qinfo.qname_len, qstate->qinfo.qtype,
qstate->qinfo.qclass, qstate->query_flags,
0 ,
1 ))) {
struct reply_info* rep = (struct reply_info*)e->entry.data;
if(rep && rep->security > sec_status_bogus &&
(!qstate->env->cfg->serve_expired_ttl ||
qstate->env->cfg->serve_expired_ttl_reset ||
*qstate->env->now <= rep->serve_expired_ttl)) {
verbose(VERB_ALGO, "validation failed but "
"previously cached valid response "
"exists; set serve-expired-norec-ttl "
"for response in cache");
rep->serve_expired_norec_ttl = NORR_TTL +
*qstate->env->now;
if(qstate->env->cfg->serve_expired_ttl_reset &&
*qstate->env->now + qstate->env->cfg->serve_expired_ttl
> rep->serve_expired_ttl) {
verbose(VERB_ALGO, "reset serve-expired-ttl for "
"valid response in cache");
rep->serve_expired_ttl = *qstate->env->now +
qstate->env->cfg->serve_expired_ttl;
}
qstate->return_rcode = LDNS_RCODE_SERVFAIL;
qstate->return_msg = NULL;
qstate->ext_state[id] = module_finished;
lock_rw_unlock(&e->entry.lock);
return 0;
}
lock_rw_unlock(&e->entry.lock);
}
vq->orig_msg->rep->ttl = ve->bogus_ttl;
vq->orig_msg->rep->prefetch_ttl =
PREFETCH_TTL_CALC(vq->orig_msg->rep->ttl);
vq->orig_msg->rep->serve_expired_ttl =
vq->orig_msg->rep->ttl + qstate->env->cfg->serve_expired_ttl;
if((qstate->env->cfg->val_log_level >= 1 ||
qstate->env->cfg->log_servfail) &&
!qstate->env->cfg->val_log_squelch) {
if(qstate->env->cfg->val_log_level < 2 &&
!qstate->env->cfg->log_servfail)
log_query_info(NO_VERBOSE, "validation failure",
&qstate->qinfo);
else {
char* err_str = errinf_to_str_bogus(qstate,
qstate->region);
if(err_str) {
log_info("%s", err_str);
vq->orig_msg->rep->reason_bogus_str = err_str;
}
}
}
if(qstate->env->cfg->val_permissive_mode)
vq->orig_msg->rep->security = sec_status_indeterminate;
}
if(vq->orig_msg->rep->security == sec_status_secure &&
qstate->env->cfg->root_key_sentinel &&
(qstate->qinfo.qtype == LDNS_RR_TYPE_A ||
qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA)) {
char* keytag_start;
uint16_t keytag;
if(*qstate->qinfo.qname == strlen(SENTINEL_IS) +
SENTINEL_KEYTAG_LEN &&
dname_lab_startswith(qstate->qinfo.qname, SENTINEL_IS,
&keytag_start)) {
if(sentinel_get_keytag(keytag_start, &keytag) &&
!anchor_has_keytag(qstate->env->anchors,
(uint8_t*)"", 1, 0, vq->qchase.qclass, keytag)) {
vq->orig_msg->rep->security =
sec_status_secure_sentinel_fail;
}
} else if(*qstate->qinfo.qname == strlen(SENTINEL_NOT) +
SENTINEL_KEYTAG_LEN &&
dname_lab_startswith(qstate->qinfo.qname, SENTINEL_NOT,
&keytag_start)) {
if(sentinel_get_keytag(keytag_start, &keytag) &&
anchor_has_keytag(qstate->env->anchors,
(uint8_t*)"", 1, 0, vq->qchase.qclass, keytag)) {
vq->orig_msg->rep->security =
sec_status_secure_sentinel_fail;
}
}
}
update_reason_bogus(vq->orig_msg->rep, errinf_to_reason_bogus(qstate));
if(vq->orig_msg->rep->security != sec_status_bogus &&
vq->orig_msg->rep->security != sec_status_secure_sentinel_fail
&& vq->orig_msg->rep->reason_bogus == LDNS_EDE_DNSSEC_BOGUS) {
vq->orig_msg->rep->reason_bogus = LDNS_EDE_NONE;
}
if((qstate->query_flags&BIT_RD)) {
if(!qstate->no_cache_store) {
if(!dns_cache_store(qstate->env, &vq->orig_msg->qinfo,
vq->orig_msg->rep, 0, qstate->prefetch_leeway,
0, qstate->region, qstate->query_flags,
qstate->qstarttime, qstate->is_valrec)) {
log_err("out of memory caching validator results");
}
}
} else {
if(!dns_cache_store(qstate->env, &vq->orig_msg->qinfo,
vq->orig_msg->rep, 1, 0, 0, qstate->region,
qstate->query_flags, qstate->qstarttime,
qstate->is_valrec)) {
log_err("out of memory caching validator results");
}
}
qstate->return_rcode = LDNS_RCODE_NOERROR;
qstate->return_msg = vq->orig_msg;
qstate->ext_state[id] = module_finished;
return 0;
}
static void
val_handle(struct module_qstate* qstate, struct val_qstate* vq,
struct val_env* ve, int id)
{
int cont = 1;
while(cont) {
verbose(VERB_ALGO, "val handle processing q with state %s",
val_state_to_string(vq->state));
switch(vq->state) {
case VAL_INIT_STATE:
cont = processInit(qstate, vq, ve, id);
break;
case VAL_FINDKEY_STATE:
cont = processFindKey(qstate, vq, id);
break;
case VAL_VALIDATE_STATE:
cont = processValidate(qstate, vq, ve, id);
break;
case VAL_FINISHED_STATE:
cont = processFinished(qstate, vq, ve, id);
break;
default:
log_warn("validator: invalid state %d",
vq->state);
cont = 0;
break;
}
}
}
void
val_operate(struct module_qstate* qstate, enum module_ev event, int id,
struct outbound_entry* outbound)
{
struct val_env* ve = (struct val_env*)qstate->env->modinfo[id];
struct val_qstate* vq = (struct val_qstate*)qstate->minfo[id];
verbose(VERB_QUERY, "validator[module %d] operate: extstate:%s "
"event:%s", id, strextstate(qstate->ext_state[id]),
strmodulevent(event));
log_query_info(VERB_QUERY, "validator operate: query",
&qstate->qinfo);
if(vq && qstate->qinfo.qname != vq->qchase.qname)
log_query_info(VERB_QUERY, "validator operate: chased to",
&vq->qchase);
(void)outbound;
if(event == module_event_new ||
(event == module_event_pass && vq == NULL)) {
verbose(VERB_ALGO, "validator: pass to next module");
qstate->ext_state[id] = module_wait_module;
return;
}
if(event == module_event_moddone) {
verbose(VERB_ALGO, "validator: nextmodule returned");
if(!needs_validation(qstate, qstate->return_rcode,
qstate->return_msg)) {
if(qstate->return_msg)
qstate->return_msg->rep->security =
sec_status_indeterminate;
qstate->ext_state[id] = module_finished;
return;
}
if(already_validated(qstate->return_msg)) {
qstate->ext_state[id] = module_finished;
return;
}
if(qstate->rpz_applied) {
verbose(VERB_ALGO, "rpz applied, mark it as insecure");
if(qstate->return_msg)
qstate->return_msg->rep->security =
sec_status_insecure;
qstate->ext_state[id] = module_finished;
return;
}
if(qstate->qinfo.qclass == LDNS_RR_CLASS_ANY) {
verbose(VERB_ALGO, "cannot validate classANY: bogus");
if(qstate->return_msg) {
qstate->return_msg->rep->security =
sec_status_bogus;
update_reason_bogus(qstate->return_msg->rep, LDNS_EDE_DNSSEC_BOGUS);
}
qstate->ext_state[id] = module_finished;
return;
}
qstate->ext_state[id] = module_error;
if(!vq) {
vq = val_new(qstate, id);
if(!vq) {
log_err("validator: malloc failure");
qstate->ext_state[id] = module_error;
return;
}
} else if(!vq->orig_msg) {
if(!val_new_getmsg(qstate, vq)) {
log_err("validator: malloc failure");
qstate->ext_state[id] = module_error;
return;
}
}
val_handle(qstate, vq, ve, id);
return;
}
if(event == module_event_pass) {
qstate->ext_state[id] = module_error;
val_handle(qstate, vq, ve, id);
return;
}
log_err("validator: bad event %s", strmodulevent(event));
qstate->ext_state[id] = module_error;
return;
}
static struct key_entry_key*
primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset,
struct trust_anchor* ta, struct module_qstate* qstate, int id,
struct module_qstate* sub_qstate)
{
struct val_env* ve = (struct val_env*)qstate->env->modinfo[id];
struct key_entry_key* kkey = NULL;
enum sec_status sec = sec_status_unchecked;
char reasonbuf[256];
char* reason = NULL;
sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
int downprot = qstate->env->cfg->harden_algo_downgrade;
if(!dnskey_rrset) {
char* err = errinf_to_str_misc(sub_qstate);
char rstr[1024];
log_nametypeclass(VERB_OPS, "failed to prime trust anchor -- "
"could not fetch DNSKEY rrset",
ta->name, LDNS_RR_TYPE_DNSKEY, ta->dclass);
reason_bogus = LDNS_EDE_DNSKEY_MISSING;
if(!err) {
snprintf(rstr, sizeof(rstr), "no DNSKEY rrset");
} else {
snprintf(rstr, sizeof(rstr), "no DNSKEY rrset "
"[%s]", err);
}
if(qstate->env->cfg->harden_dnssec_stripped) {
errinf_ede(qstate, rstr, reason_bogus);
kkey = key_entry_create_bad(qstate->region, ta->name,
ta->namelen, ta->dclass, BOGUS_KEY_TTL,
reason_bogus, rstr, *qstate->env->now);
} else kkey = key_entry_create_null(qstate->region, ta->name,
ta->namelen, ta->dclass, NULL_KEY_TTL,
reason_bogus, rstr, *qstate->env->now);
if(!kkey) {
log_err("out of memory: allocate fail prime key");
return NULL;
}
return kkey;
}
kkey = val_verify_new_DNSKEYs_with_ta(qstate->region, qstate->env, ve,
dnskey_rrset, ta->ds_rrset, ta->dnskey_rrset, downprot,
&reason, &reason_bogus, qstate, reasonbuf, sizeof(reasonbuf));
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 -- "
"DNSKEY rrset is not secure",
ta->name, LDNS_RR_TYPE_DNSKEY, ta->dclass);
if(qstate->env->cfg->harden_dnssec_stripped) {
errinf_ede(qstate, reason, reason_bogus);
kkey = key_entry_create_bad(qstate->region, ta->name,
ta->namelen, ta->dclass, BOGUS_KEY_TTL,
reason_bogus, reason,
*qstate->env->now);
} else kkey = key_entry_create_null(qstate->region, ta->name,
ta->namelen, ta->dclass, NULL_KEY_TTL,
reason_bogus, reason,
*qstate->env->now);
if(!kkey) {
log_err("out of memory: allocate null prime key");
return NULL;
}
return kkey;
}
log_nametypeclass(VERB_DETAIL, "Successfully primed trust anchor",
ta->name, LDNS_RR_TYPE_DNSKEY, ta->dclass);
return kkey;
}
static int
ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
int id, int rcode, struct dns_msg* msg, struct query_info* qinfo,
struct key_entry_key** ke, struct module_qstate* sub_qstate)
{
struct val_env* ve = (struct val_env*)qstate->env->modinfo[id];
char reasonbuf[256];
char* reason = NULL;
sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
enum val_classification subtype;
int verified;
if(rcode != LDNS_RCODE_NOERROR) {
char rc[16];
rc[0]=0;
(void)sldns_wire2str_rcode_buf(rcode, rc, sizeof(rc));
verbose(VERB_DETAIL, "DS response was error, thus bogus");
errinf(qstate, rc);
reason = "no DS";
if(sub_qstate) {
char* err = errinf_to_str_misc(sub_qstate);
if(err) {
char buf[1024];
snprintf(buf, sizeof(buf), "[%s]", err);
errinf(qstate, buf);
}
}
reason_bogus = LDNS_EDE_NETWORK_ERROR;
errinf_ede(qstate, reason, reason_bogus);
goto return_bogus;
}
subtype = val_classify_response(BIT_RD, qinfo, qinfo, msg->rep, 0);
if(subtype == VAL_CLASS_POSITIVE) {
struct ub_packed_rrset_key* ds;
enum sec_status sec;
ds = reply_find_answer_rrset(qinfo, msg->rep);
if(!ds) {
log_warn("internal error: POSITIVE DS response was "
"missing DS.");
reason = "no DS record";
errinf_ede(qstate, reason, reason_bogus);
goto return_bogus;
}
sec = val_verify_rrset_entry(qstate->env, ve, ds,
vq->key_entry, &reason, &reason_bogus,
LDNS_SECTION_ANSWER, qstate, &verified, reasonbuf,
sizeof(reasonbuf));
if(sec != sec_status_secure) {
verbose(VERB_DETAIL, "DS rrset in DS response did "
"not verify");
errinf_ede(qstate, reason, reason_bogus);
goto return_bogus;
}
if(!val_dsset_isusable(ds)) {
*ke = key_entry_create_null(qstate->region,
qinfo->qname, qinfo->qname_len, qinfo->qclass,
ub_packed_rrset_ttl(ds),
LDNS_EDE_UNSUPPORTED_DS_DIGEST, NULL,
*qstate->env->now);
return (*ke) == NULL;
}
log_query_info(VERB_DETAIL, "validated DS", qinfo);
*ke = key_entry_create_rrset(qstate->region,
qinfo->qname, qinfo->qname_len, qinfo->qclass, ds,
NULL, LDNS_EDE_NONE, NULL, *qstate->env->now);
return (*ke) == NULL;
} else if(subtype == VAL_CLASS_NODATA ||
subtype == VAL_CLASS_NAMEERROR) {
time_t proof_ttl = 0;
enum sec_status sec;
if(!val_has_signed_nsecs(msg->rep, &reason)) {
verbose(VERB_ALGO, "no NSECs: %s", reason);
reason_bogus = LDNS_EDE_NSEC_MISSING;
errinf_ede(qstate, reason, reason_bogus);
goto return_bogus;
}
sec = val_nsec_prove_nodata_dsreply(
qstate->env, ve, qinfo, msg->rep, vq->key_entry,
&proof_ttl, &reason, &reason_bogus, qstate,
reasonbuf, sizeof(reasonbuf));
switch(sec) {
case sec_status_secure:
verbose(VERB_DETAIL, "NSEC RRset for the "
"referral proved no DS.");
*ke = key_entry_create_null(qstate->region,
qinfo->qname, qinfo->qname_len,
qinfo->qclass, proof_ttl,
LDNS_EDE_NONE, NULL,
*qstate->env->now);
return (*ke) == NULL;
case sec_status_insecure:
verbose(VERB_DETAIL, "NSEC RRset for the "
"referral proved not a delegation point");
*ke = NULL;
return 0;
case sec_status_bogus:
verbose(VERB_DETAIL, "NSEC RRset for the "
"referral did not prove no DS.");
errinf(qstate, reason);
goto return_bogus;
case sec_status_unchecked:
default:
break;
}
if(!nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) {
log_err("malloc failure in ds_response_to_ke for "
"NSEC3 cache");
reason = "malloc failure";
errinf_ede(qstate, reason, 0);
goto return_bogus;
}
sec = nsec3_prove_nods(qstate->env, ve,
msg->rep->rrsets + msg->rep->an_numrrsets,
msg->rep->ns_numrrsets, qinfo, vq->key_entry, &reason,
&reason_bogus, qstate, &vq->nsec3_cache_table,
reasonbuf, sizeof(reasonbuf));
switch(sec) {
case sec_status_insecure:
case sec_status_secure:
verbose(VERB_DETAIL, "NSEC3s for the "
"referral proved no DS.");
*ke = key_entry_create_null(qstate->region,
qinfo->qname, qinfo->qname_len,
qinfo->qclass, proof_ttl,
LDNS_EDE_NONE, NULL,
*qstate->env->now);
return (*ke) == NULL;
case sec_status_indeterminate:
verbose(VERB_DETAIL, "NSEC3s for the "
"referral proved no delegation");
*ke = NULL;
return 0;
case sec_status_bogus:
verbose(VERB_DETAIL, "NSEC3s for the "
"referral did not prove no DS.");
errinf_ede(qstate, reason, reason_bogus);
goto return_bogus;
case sec_status_unchecked:
return 2;
default:
break;
}
verbose(VERB_DETAIL, "DS %s ran out of options, so return "
"bogus", val_classification_to_string(subtype));
reason = "no DS but also no proof of that";
errinf_ede(qstate, reason, reason_bogus);
goto return_bogus;
} else if(subtype == VAL_CLASS_CNAME ||
subtype == VAL_CLASS_CNAMENOANSWER) {
enum sec_status sec;
struct ub_packed_rrset_key* cname;
cname = reply_find_rrset_section_an(msg->rep, qinfo->qname,
qinfo->qname_len, LDNS_RR_TYPE_CNAME, qinfo->qclass);
if(!cname) {
reason = "validator classified CNAME but no "
"CNAME of the queried name for DS";
errinf_ede(qstate, reason, reason_bogus);
goto return_bogus;
}
if(((struct packed_rrset_data*)cname->entry.data)->rrsig_count
== 0) {
if(msg->rep->an_numrrsets != 0 && ntohs(msg->rep->
rrsets[0]->rk.type)==LDNS_RR_TYPE_DNAME) {
reason = "DS got DNAME answer";
} else {
reason = "DS got unsigned CNAME answer";
}
errinf_ede(qstate, reason, reason_bogus);
goto return_bogus;
}
sec = val_verify_rrset_entry(qstate->env, ve, cname,
vq->key_entry, &reason, &reason_bogus,
LDNS_SECTION_ANSWER, qstate, &verified, reasonbuf,
sizeof(reasonbuf));
if(sec == sec_status_secure) {
verbose(VERB_ALGO, "CNAME validated, "
"proof that DS does not exist");
*ke = NULL;
return 0;
}
errinf(qstate, "CNAME in DS response was not secure.");
errinf_ede(qstate, reason, reason_bogus);
goto return_bogus;
} else {
verbose(VERB_QUERY, "Encountered an unhandled type of "
"DS response, thus bogus.");
errinf(qstate, "no DS and");
reason = "no DS";
if(FLAGS_GET_RCODE(msg->rep->flags) != LDNS_RCODE_NOERROR) {
char rc[16];
rc[0]=0;
(void)sldns_wire2str_rcode_buf((int)FLAGS_GET_RCODE(
msg->rep->flags), rc, sizeof(rc));
errinf(qstate, rc);
} else errinf(qstate, val_classification_to_string(subtype));
errinf(qstate, "message fails to prove that");
goto return_bogus;
}
return_bogus:
*ke = key_entry_create_bad(qstate->region, qinfo->qname,
qinfo->qname_len, qinfo->qclass, BOGUS_KEY_TTL,
reason_bogus, reason, *qstate->env->now);
return (*ke) == NULL;
}
static void
process_ds_response(struct module_qstate* qstate, struct val_qstate* vq,
int id, int rcode, struct dns_msg* msg, struct query_info* qinfo,
struct sock_list* origin, int* suspend,
struct module_qstate* sub_qstate)
{
struct val_env* ve = (struct val_env*)qstate->env->modinfo[id];
struct key_entry_key* dske = NULL;
uint8_t* olds = vq->empty_DS_name;
int ret;
*suspend = 0;
vq->empty_DS_name = NULL;
if(sub_qstate && sub_qstate->rpz_applied) {
verbose(VERB_ALGO, "rpz was applied to the DS lookup, "
"make it insecure");
vq->key_entry = NULL;
vq->state = VAL_FINISHED_STATE;
vq->chase_reply->security = sec_status_insecure;
return;
}
ret = ds_response_to_ke(qstate, vq, id, rcode, msg, qinfo, &dske,
sub_qstate);
if(ret != 0) {
switch(ret) {
case 1:
log_err("malloc failure in process_ds_response");
vq->key_entry = NULL;
vq->state = VAL_VALIDATE_STATE;
return;
case 2:
*suspend = 1;
return;
default:
log_err("unhandled error value for ds_response_to_ke");
vq->key_entry = NULL;
vq->state = VAL_VALIDATE_STATE;
return;
}
}
if(dske == NULL) {
vq->empty_DS_name = regional_alloc_init(qstate->region,
qinfo->qname, qinfo->qname_len);
if(!vq->empty_DS_name) {
log_err("malloc failure in empty_DS_name");
vq->key_entry = NULL;
vq->state = VAL_VALIDATE_STATE;
return;
}
vq->empty_DS_len = qinfo->qname_len;
vq->chain_blacklist = NULL;
} else if(key_entry_isgood(dske)) {
vq->ds_rrset = key_entry_get_rrset(dske, qstate->region);
if(!vq->ds_rrset) {
log_err("malloc failure in process DS");
vq->key_entry = NULL;
vq->state = VAL_VALIDATE_STATE;
return;
}
vq->chain_blacklist = NULL;
} else if(key_entry_isbad(dske)
&& vq->restart_count < ve->max_restart) {
vq->empty_DS_name = olds;
val_blacklist(&vq->chain_blacklist, qstate->region, origin, 1);
qstate->errinf = NULL;
vq->restart_count++;
} else {
if(key_entry_isbad(dske)) {
errinf_origin(qstate, origin);
errinf_dname(qstate, "for DS", qinfo->qname);
}
vq->key_entry = dske;
vq->state = VAL_VALIDATE_STATE;
}
}
static void
process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq,
int id, int rcode, struct dns_msg* msg, struct query_info* qinfo,
struct sock_list* origin, struct module_qstate* sub_qstate)
{
struct val_env* ve = (struct val_env*)qstate->env->modinfo[id];
struct key_entry_key* old = vq->key_entry;
struct ub_packed_rrset_key* dnskey = NULL;
int downprot;
char reasonbuf[256];
char* reason = NULL;
sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
if(sub_qstate && sub_qstate->rpz_applied) {
verbose(VERB_ALGO, "rpz was applied to the DNSKEY lookup, "
"make it insecure");
vq->key_entry = NULL;
vq->state = VAL_FINISHED_STATE;
vq->chase_reply->security = sec_status_insecure;
return;
}
if(rcode == LDNS_RCODE_NOERROR)
dnskey = reply_find_answer_rrset(qinfo, msg->rep);
if(dnskey == NULL) {
char* err;
char rstr[1024];
verbose(VERB_DETAIL, "Missing DNSKEY RRset in response to "
"DNSKEY query.");
if(vq->restart_count < ve->max_restart) {
val_blacklist(&vq->chain_blacklist, qstate->region,
origin, 1);
qstate->errinf = NULL;
vq->restart_count++;
return;
}
err = errinf_to_str_misc(sub_qstate);
if(!err) {
snprintf(rstr, sizeof(rstr), "No DNSKEY record");
} else {
snprintf(rstr, sizeof(rstr), "No DNSKEY record "
"[%s]", err);
}
reason_bogus = LDNS_EDE_DNSKEY_MISSING;
vq->key_entry = key_entry_create_bad(qstate->region,
qinfo->qname, qinfo->qname_len, qinfo->qclass,
BOGUS_KEY_TTL, reason_bogus, rstr, *qstate->env->now);
if(!vq->key_entry) {
log_err("alloc failure in missing dnskey response");
}
errinf_ede(qstate, rstr, reason_bogus);
errinf_origin(qstate, origin);
errinf_dname(qstate, "for key", qinfo->qname);
vq->state = VAL_VALIDATE_STATE;
return;
}
if(!vq->ds_rrset) {
log_err("internal error: no DS rrset for new DNSKEY response");
vq->key_entry = NULL;
vq->state = VAL_VALIDATE_STATE;
return;
}
downprot = qstate->env->cfg->harden_algo_downgrade;
vq->key_entry = val_verify_new_DNSKEYs(qstate->region, qstate->env,
ve, dnskey, vq->ds_rrset, downprot, &reason, &reason_bogus,
qstate, reasonbuf, sizeof(reasonbuf));
if(!vq->key_entry) {
log_err("out of memory in verify new DNSKEYs");
vq->state = VAL_VALIDATE_STATE;
return;
}
if(!key_entry_isgood(vq->key_entry)) {
if(key_entry_isbad(vq->key_entry)) {
if(vq->restart_count < ve->max_restart) {
val_blacklist(&vq->chain_blacklist,
qstate->region, origin, 1);
qstate->errinf = NULL;
vq->restart_count++;
vq->key_entry = old;
return;
}
verbose(VERB_DETAIL, "Did not match a DS to a DNSKEY, "
"thus bogus.");
errinf_ede(qstate, reason, reason_bogus);
errinf_origin(qstate, origin);
errinf_dname(qstate, "for key", qinfo->qname);
}
vq->chain_blacklist = NULL;
vq->state = VAL_VALIDATE_STATE;
return;
}
vq->chain_blacklist = NULL;
qstate->errinf = NULL;
key_cache_insert(ve->kcache, vq->key_entry,
qstate->env->cfg->val_log_level >= 2);
log_query_info(VERB_DETAIL, "validated DNSKEY", qinfo);
}
static void
process_prime_response(struct module_qstate* qstate, struct val_qstate* vq,
int id, int rcode, struct dns_msg* msg, struct sock_list* origin,
struct module_qstate* sub_qstate)
{
struct val_env* ve = (struct val_env*)qstate->env->modinfo[id];
struct ub_packed_rrset_key* dnskey_rrset = NULL;
struct trust_anchor* ta = anchor_find(qstate->env->anchors,
vq->trust_anchor_name, vq->trust_anchor_labs,
vq->trust_anchor_len, vq->qchase.qclass);
if(!ta) {
vq->state = VAL_INIT_STATE;
if(!vq->trust_anchor_name)
vq->state = VAL_VALIDATE_STATE;
vq->trust_anchor_name = NULL;
return;
}
if(rcode == LDNS_RCODE_NOERROR) {
dnskey_rrset = reply_find_rrset_section_an(msg->rep,
ta->name, ta->namelen, LDNS_RR_TYPE_DNSKEY,
ta->dclass);
}
if(ta->autr) {
if(!autr_process_prime(qstate->env, ve, ta, dnskey_rrset,
qstate)) {
vq->state = VAL_INIT_STATE;
vq->trust_anchor_name = NULL;
return;
}
}
vq->key_entry = primeResponseToKE(dnskey_rrset, ta, qstate, id,
sub_qstate);
lock_basic_unlock(&ta->lock);
if(vq->key_entry) {
if(key_entry_isbad(vq->key_entry)
&& vq->restart_count < ve->max_restart) {
val_blacklist(&vq->chain_blacklist, qstate->region,
origin, 1);
qstate->errinf = NULL;
vq->restart_count++;
vq->key_entry = NULL;
vq->state = VAL_INIT_STATE;
return;
}
vq->chain_blacklist = NULL;
errinf_origin(qstate, origin);
errinf_dname(qstate, "for trust anchor", ta->name);
key_cache_insert(ve->kcache, vq->key_entry,
qstate->env->cfg->val_log_level >= 2);
}
if(!vq->key_entry || key_entry_isnull(vq->key_entry) ||
key_entry_isbad(vq->key_entry)) {
vq->state = VAL_VALIDATE_STATE;
}
}
void
val_inform_super(struct module_qstate* qstate, int id,
struct module_qstate* super)
{
struct val_qstate* vq = (struct val_qstate*)super->minfo[id];
log_query_info(VERB_ALGO, "validator: inform_super, sub is",
&qstate->qinfo);
log_query_info(VERB_ALGO, "super is", &super->qinfo);
if(!vq) {
verbose(VERB_ALGO, "super: has no validator state");
return;
}
if(vq->wait_prime_ta) {
vq->wait_prime_ta = 0;
process_prime_response(super, vq, id, qstate->return_rcode,
qstate->return_msg, qstate->reply_origin, qstate);
return;
}
if(qstate->qinfo.qtype == LDNS_RR_TYPE_DS) {
int suspend;
process_ds_response(super, vq, id, qstate->return_rcode,
qstate->return_msg, &qstate->qinfo,
qstate->reply_origin, &suspend, qstate);
if(vq->nsec3_cache_table.ct) {
vq->nsec3_cache_table.ct = NULL;
}
if(suspend) {
vq->sub_ds_msg = dns_msg_deepcopy_region(
qstate->return_msg, super->region);
}
return;
} else if(qstate->qinfo.qtype == LDNS_RR_TYPE_DNSKEY) {
process_dnskey_response(super, vq, id, qstate->return_rcode,
qstate->return_msg, &qstate->qinfo,
qstate->reply_origin, qstate);
return;
}
log_err("internal error in validator: no inform_supers possible");
}
void
val_clear(struct module_qstate* qstate, int id)
{
struct val_qstate* vq;
if(!qstate)
return;
vq = (struct val_qstate*)qstate->minfo[id];
if(vq) {
if(vq->suspend_timer) {
comm_timer_delete(vq->suspend_timer);
}
}
qstate->minfo[id] = NULL;
}
size_t
val_get_mem(struct module_env* env, int id)
{
struct val_env* ve = (struct val_env*)env->modinfo[id];
if(!ve)
return 0;
return sizeof(*ve) + key_cache_get_mem(ve->kcache) +
val_neg_get_mem(ve->neg_cache) +
sizeof(size_t)*2*ve->nsec3_keyiter_count;
}
static struct module_func_block val_block = {
"validator",
NULL, NULL, &val_init, &val_deinit, &val_operate, &val_inform_super,
&val_clear, &val_get_mem
};
struct module_func_block*
val_get_funcblock(void)
{
return &val_block;
}
const char*
val_state_to_string(enum val_state state)
{
switch(state) {
case VAL_INIT_STATE: return "VAL_INIT_STATE";
case VAL_FINDKEY_STATE: return "VAL_FINDKEY_STATE";
case VAL_VALIDATE_STATE: return "VAL_VALIDATE_STATE";
case VAL_FINISHED_STATE: return "VAL_FINISHED_STATE";
}
return "UNKNOWN VALIDATOR STATE";
}