# 1.3.2 had 1:2:0
# 1.3.3 had 1:3:0
# 1.3.4 had 1:4:0
-# 1.4.0 had 1:5:0
+# 1.4.0-snapshots had 1:5:0
+# 1.4.0 had 2:0:0 # ub_result.why_bogus
# Current -- the number of the binary API that we're implementing
# Revision -- which iteration of the implementation of the binary
else
lt_cv_nm_interface="BSD nm"
echo "int some_variable = 0;" > conftest.$ac_ext
- (eval echo "\"\$as_me:7282: $ac_compile\"" >&5)
+ (eval echo "\"\$as_me:7283: $ac_compile\"" >&5)
(eval "$ac_compile" 2>conftest.err)
cat conftest.err >&5
- (eval echo "\"\$as_me:7285: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
+ (eval echo "\"\$as_me:7286: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
(eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
cat conftest.err >&5
- (eval echo "\"\$as_me:7288: output\"" >&5)
+ (eval echo "\"\$as_me:7289: output\"" >&5)
cat conftest.out >&5
if $GREP 'External.*some_variable' conftest.out > /dev/null; then
lt_cv_nm_interface="MS dumpbin"
;;
*-*-irix6*)
# Find out which ABI we are using.
- echo '#line 8493 "configure"' > conftest.$ac_ext
+ echo '#line 8494 "configure"' > conftest.$ac_ext
if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
(eval $ac_compile) 2>&5
ac_status=$?
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:9860: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:9861: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
- echo "$as_me:9864: \$? = $ac_status" >&5
+ echo "$as_me:9865: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:10199: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:10200: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
- echo "$as_me:10203: \$? = $ac_status" >&5
+ echo "$as_me:10204: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:10304: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:10305: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
- echo "$as_me:10308: \$? = $ac_status" >&5
+ echo "$as_me:10309: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:10359: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:10360: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
- echo "$as_me:10363: \$? = $ac_status" >&5
+ echo "$as_me:10364: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
-#line 13162 "configure"
+#line 13163 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
-#line 13258 "configure"
+#line 13259 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
# 1.3.2 had 1:2:0
# 1.3.3 had 1:3:0
# 1.3.4 had 1:4:0
-# 1.4.0 had 1:5:0
+# 1.4.0-snapshots had 1:5:0
+# 1.4.0 had 2:0:0 # ub_result.why_bogus
# Current -- the number of the binary API that we're implementing
# Revision -- which iteration of the implementation of the binary
}
void libworker_fg_done_cb(void* ATTR_UNUSED(arg), int ATTR_UNUSED(rcode),
- ldns_buffer* ATTR_UNUSED(buf), enum sec_status ATTR_UNUSED(s))
+ ldns_buffer* ATTR_UNUSED(buf), enum sec_status ATTR_UNUSED(s),
+ char* ATTR_UNUSED(why_bogus))
{
log_assert(0);
}
void libworker_bg_done_cb(void* ATTR_UNUSED(arg), int ATTR_UNUSED(rcode),
- ldns_buffer* ATTR_UNUSED(buf), enum sec_status ATTR_UNUSED(s))
+ ldns_buffer* ATTR_UNUSED(buf), enum sec_status ATTR_UNUSED(s),
+ char* ATTR_UNUSED(why_bogus))
{
log_assert(0);
}
- more detail to errors from insecure delegation checks.
- Fix double time subtraction in negative cache reported by
Amanda Constant and Hugh Mahon.
+ - Made new validator error string available from libunbound for
+ applications. It is in result->why_bogus, a zero-terminated string.
+ unbound-host prints it by default if a result is bogus.
+ Also the errinf is public in module_qstate (for other modules).
7 October 2009: Wouter
- retry for validation failure in DS and prime results. Less mem use.
int nxdomain; /* true if nodata because name does not exist */
int secure; /* true if result is secure */
int bogus; /* true if a security failure happened */
+ char* why_bogus; /* string with error if bogus */
};
.fi
.P
* o uint32 id
* o uint32 error_code
* o uint32 msg_security
+ * o uint32 length of why_bogus string (+1 for eos); 0 absent.
+ * o why_bogus_string
* o the remainder is the answer msg from resolver lookup.
* remainder can be length 0.
*/
size_t pkt_len = pkt?ldns_buffer_remaining(pkt):0;
+ size_t wlen = (pkt&&q->res->why_bogus)?strlen(q->res->why_bogus)+1:0;
uint8_t* p;
- *len = sizeof(uint32_t)*4 + pkt_len;
+ *len = sizeof(uint32_t)*5 + pkt_len + wlen;
p = (uint8_t*)malloc(*len);
if(!p) return NULL;
ldns_write_uint32(p, UB_LIBCMD_ANSWER);
ldns_write_uint32(p+sizeof(uint32_t), (uint32_t)q->querynum);
ldns_write_uint32(p+2*sizeof(uint32_t), (uint32_t)err);
ldns_write_uint32(p+3*sizeof(uint32_t), (uint32_t)q->msg_security);
+ ldns_write_uint32(p+4*sizeof(uint32_t), (uint32_t)wlen);
+ if(wlen > 0)
+ memmove(p+5*sizeof(uint32_t), q->res->why_bogus, wlen);
if(pkt_len > 0)
- memmove(p+4*sizeof(uint32_t), ldns_buffer_begin(pkt), pkt_len);
+ memmove(p+5*sizeof(uint32_t)+wlen,
+ ldns_buffer_begin(pkt), pkt_len);
return p;
}
{
struct ctx_query* q = NULL ;
int id;
- if(len < 4*sizeof(uint32_t)) return NULL;
+ size_t wlen;
+ if(len < 5*sizeof(uint32_t)) return NULL;
log_assert( ldns_read_uint32(p) == UB_LIBCMD_ANSWER);
id = (int)ldns_read_uint32(p+sizeof(uint32_t));
q = (struct ctx_query*)rbtree_search(&ctx->queries, &id);
if(!q) return NULL;
*err = (int)ldns_read_uint32(p+2*sizeof(uint32_t));
q->msg_security = ldns_read_uint32(p+3*sizeof(uint32_t));
- if(len > 4*sizeof(uint32_t)) {
- q->msg_len = len - 4*sizeof(uint32_t);
- q->msg = (uint8_t*)memdup(p+4*sizeof(uint32_t), q->msg_len);
+ wlen = (size_t)ldns_read_uint32(p+4*sizeof(uint32_t));
+ if(len > 5*sizeof(uint32_t) && wlen > 0) {
+ if(len >= 5*sizeof(uint32_t)+wlen)
+ q->res->why_bogus = (char*)memdup(
+ p+5*sizeof(uint32_t), wlen);
+ if(!q->res->why_bogus) {
+ /* pass malloc failure to the user callback */
+ q->msg_len = 0;
+ *err = UB_NOMEM;
+ return q;
+ }
+ q->res->why_bogus[wlen-1] = 0; /* zero terminated for sure */
+ }
+ if(len > 5*sizeof(uint32_t)+wlen) {
+ q->msg_len = len - 5*sizeof(uint32_t) - wlen;
+ q->msg = (uint8_t*)memdup(p+5*sizeof(uint32_t)+wlen,
+ q->msg_len);
if(!q->msg) {
/* pass malloc failure to the user callback */
q->msg_len = 0;
free(result->data);
free(result->len);
free(result->answer_packet);
+ free(result->why_bogus);
free(result);
}
/** fillup fg results */
static void
libworker_fillup_fg(struct ctx_query* q, int rcode, ldns_buffer* buf,
- enum sec_status s)
+ enum sec_status s, char* why_bogus)
{
+ if(why_bogus)
+ q->res->why_bogus = strdup(why_bogus);
if(rcode != 0) {
q->res->rcode = rcode;
q->msg_security = s;
}
void
-libworker_fg_done_cb(void* arg, int rcode, ldns_buffer* buf, enum sec_status s)
+libworker_fg_done_cb(void* arg, int rcode, ldns_buffer* buf, enum sec_status s,
+ char* why_bogus)
{
struct ctx_query* q = (struct ctx_query*)arg;
/* fg query is done; exit comm base */
comm_base_exit(q->w->base);
- libworker_fillup_fg(q, rcode, buf, s);
+ libworker_fillup_fg(q, rcode, buf, s, why_bogus);
}
/** setup qinfo and edns */
w->back->udp_buff, w->env->scratch)) {
regional_free_all(w->env->scratch);
libworker_fillup_fg(q, LDNS_RCODE_NOERROR,
- w->back->udp_buff, sec_status_insecure);
+ w->back->udp_buff, sec_status_insecure, NULL);
libworker_delete(w);
free(qinfo.qname);
return UB_NOERROR;
/** add result to the bg worker result queue */
static void
add_bg_result(struct libworker* w, struct ctx_query* q, ldns_buffer* pkt,
- int err)
+ int err, char* reason)
{
uint8_t* msg = NULL;
uint32_t len = 0;
/* serialize and delete unneeded q */
if(w->is_bg_thread) {
lock_basic_lock(&w->ctx->cfglock);
+ if(reason)
+ q->res->why_bogus = strdup(reason);
if(pkt) {
q->msg_len = ldns_buffer_remaining(pkt);
q->msg = memdup(ldns_buffer_begin(pkt), q->msg_len);
} else msg = context_serialize_answer(q, err, NULL, &len);
lock_basic_unlock(&w->ctx->cfglock);
} else {
+ if(reason)
+ q->res->why_bogus = strdup(reason);
msg = context_serialize_answer(q, err, pkt, &len);
(void)rbtree_delete(&w->ctx->queries, q->node.key);
w->ctx->num_async--;
}
void
-libworker_bg_done_cb(void* arg, int rcode, ldns_buffer* buf, enum sec_status s)
+libworker_bg_done_cb(void* arg, int rcode, ldns_buffer* buf, enum sec_status s,
+ char* why_bogus)
{
struct ctx_query* q = (struct ctx_query*)arg;
if(rcode != 0) {
error_encode(buf, rcode, NULL, 0, BIT_RD, NULL);
}
- add_bg_result(q->w, q, buf, UB_NOERROR);
+ add_bg_result(q->w, q, buf, UB_NOERROR, why_bogus);
}
return;
}
if(!setup_qinfo_edns(w, q, &qinfo, &edns)) {
- add_bg_result(w, q, NULL, UB_SYNTAX);
+ add_bg_result(w, q, NULL, UB_SYNTAX, NULL);
return;
}
qid = 0;
w->back->udp_buff, w->env->scratch)) {
regional_free_all(w->env->scratch);
q->msg_security = sec_status_insecure;
- add_bg_result(w, q, w->back->udp_buff, UB_NOERROR);
+ add_bg_result(w, q, w->back->udp_buff, UB_NOERROR, NULL);
free(qinfo.qname);
return;
}
/* process new query */
if(!mesh_new_callback(w->env->mesh, &qinfo, qflags, &edns,
w->back->udp_buff, qid, libworker_bg_done_cb, q)) {
- add_bg_result(w, q, NULL, UB_NOMEM);
+ add_bg_result(w, q, NULL, UB_NOMEM, NULL);
}
free(qinfo.qname);
}
/** mesh callback with fg results */
void libworker_fg_done_cb(void* arg, int rcode, ldns_buffer* buf,
- enum sec_status s);
+ enum sec_status s, char* why_bogus);
/** mesh callback with bg results */
void libworker_bg_done_cb(void* arg, int rcode, ldns_buffer* buf,
- enum sec_status s);
+ enum sec_status s, char* why_bogus);
/**
* fill result from parsed message, on error fills servfail
* This means the data is from a domain where data is not signed.
*/
int bogus;
+
+ /**
+ * If the result is bogus this contains a string (zero terminated)
+ * that describes the failure. There may be other errors as well
+ * as the one described, the description may not be perfectly accurate.
+ * Is NULL if the result is not bogus.
+ */
+ char* why_bogus;
};
/**
for(cb=mstate->cb_list; cb; cb=cb->next) {
fptr_ok(fptr_whitelist_mesh_cb(cb->cb));
(*cb->cb)(cb->cb_arg, LDNS_RCODE_SERVFAIL, NULL,
- sec_status_unchecked);
+ sec_status_unchecked, NULL);
}
}
struct mesh_cb* r)
{
int secure;
+ char* reason = NULL;
/* bogus messages are not made into servfail, sec_status passed
* to the callback function */
if(rep && rep->security == sec_status_secure)
else secure = 0;
if(!rep && rcode == LDNS_RCODE_NOERROR)
rcode = LDNS_RCODE_SERVFAIL;
+ if(!rcode && rep->security == sec_status_bogus) {
+ if(!(reason = errinf_to_str(&m->s)))
+ rcode = LDNS_RCODE_SERVFAIL;
+ }
/* send the reply */
if(rcode) {
fptr_ok(fptr_whitelist_mesh_cb(r->cb));
- (*r->cb)(r->cb_arg, rcode, r->buf, sec_status_unchecked);
+ (*r->cb)(r->cb_arg, rcode, r->buf, sec_status_unchecked, NULL);
} else {
size_t udp_size = r->edns.udp_size;
ldns_buffer_clear(r->buf);
{
fptr_ok(fptr_whitelist_mesh_cb(r->cb));
(*r->cb)(r->cb_arg, LDNS_RCODE_SERVFAIL, r->buf,
- sec_status_unchecked);
+ sec_status_unchecked, NULL);
} else {
fptr_ok(fptr_whitelist_mesh_cb(r->cb));
(*r->cb)(r->cb_arg, LDNS_RCODE_NOERROR, r->buf,
- rep->security);
+ rep->security, reason);
}
}
+ free(reason);
m->s.env->mesh->num_reply_addrs--;
}
/**
* Mesh result callback func.
- * called as func(cb_arg, rcode, buffer_with_reply, security);
- * */
-typedef void (*mesh_cb_func_t)(void*, int, ldns_buffer*, enum sec_status);
+ * called as func(cb_arg, rcode, buffer_with_reply, security, why_bogus);
+ */
+typedef void (*mesh_cb_func_t)(void*, int, ldns_buffer*, enum sec_status,
+ char*);
/**
* Callback to result routine
if(verb > 0)
printf(" %s", secstatus);
printf("\n");
+ if(result->bogus && result->why_bogus)
+ printf("%s\n", result->why_bogus);
return;
}
if(docname && result->canonname &&
printf(" %s\n", secstatus);
}
/* else: emptiness to indicate no data */
+ if(result->bogus && result->why_bogus)
+ printf("%s\n", result->why_bogus);
return;
}
i=0;
(size_t)result->len[i]);
i++;
}
+ if(result->bogus && result->why_bogus)
+ printf("%s\n", result->why_bogus);
}
/** perform a lookup and printout return if domain existed */
}
void libworker_fg_done_cb(void* ATTR_UNUSED(arg), int ATTR_UNUSED(rcode),
- ldns_buffer* ATTR_UNUSED(buf), enum sec_status ATTR_UNUSED(s))
+ ldns_buffer* ATTR_UNUSED(buf), enum sec_status ATTR_UNUSED(s),
+ char* ATTR_UNUSED(why_bogus))
{
log_assert(0);
}
void libworker_bg_done_cb(void* ATTR_UNUSED(arg), int ATTR_UNUSED(rcode),
- ldns_buffer* ATTR_UNUSED(buf), enum sec_status ATTR_UNUSED(s))
+ ldns_buffer* ATTR_UNUSED(buf), enum sec_status ATTR_UNUSED(s),
+ char* ATTR_UNUSED(why_bogus))
{
log_assert(0);
}
#include "util/configparser.h"
#include "util/net_help.h"
#include "util/data/msgparse.h"
+#include "util/module.h"
+#include "util/regional.h"
+#include "util/data/dname.h"
/** global config during parsing */
struct config_parser_state* cfg_parser = 0;
/** lex in file */
cfg->val_sig_skew_max = 86400; /* at most timezone settings trouble */
cfg->val_clean_additional = 1;
cfg->val_log_level = 0;
+ cfg->val_log_squelch = 0;
cfg->val_permissive_mode = 0;
cfg->add_holddown = 30*24*3600;
cfg->del_holddown = 30*24*3600;
cfg->neg_cache_size = 100 * 1024;
cfg->donotquery_localhost = 0; /* allow, so that you can ask a
forward nameserver running on localhost */
+ cfg->val_log_level = 2; /* to fill why_bogus with */
+ cfg->val_log_squelch = 1;
return cfg;
}
} else if(strcmp(opt, "val-log-level:") == 0) {
IS_NUMBER_OR_ZERO;
cfg->val_log_level = atoi(val);
+ } else if(strcmp(opt, "val-log-squelch:") == 0) {
+ IS_YES_OR_NO;
+ cfg->val_log_squelch = (strcmp(val, "yes") == 0);
} else if(strcmp(opt, "val-permissive-mode:") == 0) {
IS_YES_OR_NO;
cfg->val_permissive_mode = (strcmp(val, "yes") == 0);
}
return result;
}
+
+void errinf(struct module_qstate* qstate, const char* str)
+{
+ struct config_strlist* p;
+ if(qstate->env->cfg->val_log_level < 2 || !str)
+ return;
+ p = (struct config_strlist*)regional_alloc(qstate->region, sizeof(*p));
+ if(!p) {
+ log_err("malloc failure in validator-error-info string");
+ return;
+ }
+ p->next = NULL;
+ p->str = regional_strdup(qstate->region, str);
+ if(!p->str) {
+ log_err("malloc failure in validator-error-info string");
+ return;
+ }
+ /* add at end */
+ if(qstate->errinf) {
+ struct config_strlist* q = qstate->errinf;
+ while(q->next)
+ q = q->next;
+ q->next = p;
+ } else qstate->errinf = p;
+}
+
+void errinf_origin(struct module_qstate* qstate, struct sock_list *origin)
+{
+ struct sock_list* p;
+ if(qstate->env->cfg->val_log_level < 2)
+ return;
+ for(p=origin; p; p=p->next) {
+ char buf[256];
+ if(p == origin)
+ snprintf(buf, sizeof(buf), "from ");
+ else snprintf(buf, sizeof(buf), "and from ");
+ if(p->len == 0)
+ snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf),
+ "cache");
+ else
+ addr_to_str(&p->addr, p->len, buf+strlen(buf),
+ sizeof(buf)-strlen(buf));
+ errinf(qstate, buf);
+ }
+}
+
+char* errinf_to_str(struct module_qstate* qstate)
+{
+ char buf[20480];
+ char* p = buf;
+ size_t left = sizeof(buf);
+ struct config_strlist* s;
+ char dname[LDNS_MAX_DOMAINLEN+1];
+ char* t = ldns_rr_type2str(qstate->qinfo.qtype);
+ char* c = ldns_rr_class2str(qstate->qinfo.qclass);
+ if(!t || !c) {
+ free(t);
+ free(c);
+ log_err("malloc failure in errinf_to_str");
+ return NULL;
+ }
+ dname_str(qstate->qinfo.qname, dname);
+ snprintf(p, left, "validation failure <%s %s %s>:", dname, t, c);
+ free(t);
+ free(c);
+ left -= strlen(p); p += strlen(p);
+ if(!qstate->errinf)
+ snprintf(p, left, " misc failure");
+ else for(s=qstate->errinf; s; s=s->next) {
+ snprintf(p, left, " %s", s->str);
+ left -= strlen(p); p += strlen(p);
+ }
+ p = strdup(buf);
+ if(!p)
+ log_err("malloc failure in errinf_to_str");
+ return p;
+}
+
+void errinf_rrset(struct module_qstate* qstate, struct ub_packed_rrset_key *rr)
+{
+ char buf[1024];
+ char dname[LDNS_MAX_DOMAINLEN+1];
+ char *t, *c;
+ if(qstate->env->cfg->val_log_level < 2 || !rr)
+ return;
+ t = ldns_rr_type2str(ntohs(rr->rk.type));
+ c = ldns_rr_class2str(ntohs(rr->rk.rrset_class));
+ if(!t || !c) {
+ free(t);
+ free(c);
+ log_err("malloc failure in errinf_rrset");
+ return;
+ }
+ dname_str(qstate->qinfo.qname, dname);
+ snprintf(buf, sizeof(buf), "for <%s %s %s>", dname, t, c);
+ free(t);
+ free(c);
+ errinf(qstate, buf);
+}
+
+void errinf_dname(struct module_qstate* qstate, const char* str, uint8_t* dname)
+{
+ char b[1024];
+ char buf[LDNS_MAX_DOMAINLEN+1];
+ if(qstate->env->cfg->val_log_level < 2 || !str || !dname)
+ return;
+ dname_str(dname, buf);
+ snprintf(b, sizeof(b), "%s %s", str, buf);
+ errinf(qstate, b);
+}
struct config_stub;
struct config_strlist;
struct config_str2list;
+struct module_qstate;
+struct sock_list;
+struct ub_packed_rrset_key;
/**
* The configuration options.
int val_clean_additional;
/** log bogus messages by the validator */
int val_log_level;
+ /** squelch val_log_level to log - this is library goes to callback */
+ int val_log_squelch;
/** should validator allow bogus messages to go through */
int val_permissive_mode;
/** nsec3 maximum iterations per key size, string */
*/
char* cfg_ptr_reverse(char* str);
+/**
+ * Append text to the error info for validation.
+ * @param qstate: query state.
+ * @param str: copied into query region and appended.
+ * Failures to allocate are logged.
+ */
+void errinf(struct module_qstate* qstate, const char* str);
+
+/**
+ * Append text to error info: from 1.2.3.4
+ * @param qstate: query state.
+ * @param origin: sock list with origin of trouble.
+ * Every element added.
+ * If NULL: nothing is added.
+ * if 0len element: 'from cache' is added.
+ */
+void errinf_origin(struct module_qstate* qstate, struct sock_list *origin);
+
+/**
+ * Append text to error info: for RRset name type class
+ * @param qstate: query state.
+ * @param rr: rrset_key.
+ */
+void errinf_rrset(struct module_qstate* qstate, struct ub_packed_rrset_key *rr);
+
+/**
+ * Append text to error info: str dname
+ * @param qstate: query state.
+ * @param str: explanation string
+ * @param dname: the dname.
+ */
+void errinf_dname(struct module_qstate* qstate, const char* str,
+ uint8_t* dname);
+
+/**
+ * Create error info in string
+ * @param qstate: query state.
+ * @return string or NULL on malloc failure (already logged).
+ * This string is malloced and has to be freed by caller.
+ */
+char* errinf_to_str(struct module_qstate* qstate);
+
/**
* Used during options parsing
*/
struct sock_list* blacklist;
/** region for this query. Cleared when query process finishes. */
struct regional* region;
+ /** failure reason information if val-log-level is high */
+ struct config_strlist* errinf;
/** which module is executing */
int curmod;
}
void probe_answer_cb(void* arg, int ATTR_UNUSED(rcode),
- ldns_buffer* ATTR_UNUSED(buf), enum sec_status ATTR_UNUSED(sec))
+ ldns_buffer* ATTR_UNUSED(buf), enum sec_status ATTR_UNUSED(sec),
+ char* ATTR_UNUSED(why_bogus))
{
/* retry was set before the query was done,
* re-querytime is set when query succeeded, but that may not
/** callback for query answer to 5011 probe */
void probe_answer_cb(void* arg, int rcode, ldns_buffer* buf,
- enum sec_status sec);
+ enum sec_status sec, char* errinf);
#endif /* VALIDATOR_AUTOTRUST_H */
else sock_list_merge(blacklist, region, origin);
}
-void val_errinf(struct module_qstate* qstate, struct val_qstate* vq,
- const char* str)
-{
- struct config_strlist* p;
- if(qstate->env->cfg->val_log_level < 2 || !str)
- return;
- p = (struct config_strlist*)regional_alloc(qstate->region, sizeof(*p));
- if(!p) {
- log_err("malloc failure in validator-error-info string");
- return;
- }
- p->next = NULL;
- p->str = regional_strdup(qstate->region, str);
- if(!p->str) {
- log_err("malloc failure in validator-error-info string");
- return;
- }
- /* add at end */
- if(vq->errinf) {
- struct config_strlist* q = vq->errinf;
- while(q->next)
- q = q->next;
- q->next = p;
- } else vq->errinf = p;
-}
-
-void val_errinf_origin(struct module_qstate* qstate, struct val_qstate* vq,
- struct sock_list *origin)
-{
- struct sock_list* p;
- if(qstate->env->cfg->val_log_level < 2)
- return;
- for(p=origin; p; p=p->next) {
- char buf[256];
- if(p == origin)
- snprintf(buf, sizeof(buf), "from ");
- else snprintf(buf, sizeof(buf), "and from ");
- if(p->len == 0)
- snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf),
- "cache");
- else
- addr_to_str(&p->addr, p->len, buf+strlen(buf),
- sizeof(buf)-strlen(buf));
- val_errinf(qstate, vq, buf);
- }
-}
-
-char* val_errinf_to_str(struct module_qstate* qstate, struct val_qstate* vq)
-{
- char buf[20480];
- char* p = buf;
- size_t left = sizeof(buf);
- struct config_strlist* s;
- char dname[LDNS_MAX_DOMAINLEN+1];
- char* t = ldns_rr_type2str(qstate->qinfo.qtype);
- char* c = ldns_rr_class2str(qstate->qinfo.qclass);
- if(!t || !c) {
- free(t);
- free(c);
- log_err("malloc failure in errinf_to_str");
- return NULL;
- }
- dname_str(qstate->qinfo.qname, dname);
- snprintf(p, left, "validation failure <%s %s %s>:", dname, t, c);
- free(t);
- free(c);
- left -= strlen(p); p += strlen(p);
- if(!vq->errinf)
- snprintf(p, left, " misc failure");
- else for(s=vq->errinf; s; s=s->next) {
- snprintf(p, left, " %s", s->str);
- left -= strlen(p); p += strlen(p);
- }
- p = strdup(buf);
- if(!p)
- log_err("malloc failure in errinf_to_str");
- return p;
-}
-
-void val_errinf_rrset(struct module_qstate* qstate, struct val_qstate* vq,
- struct ub_packed_rrset_key *rr)
-{
- char buf[1024];
- char dname[LDNS_MAX_DOMAINLEN+1];
- char *t, *c;
- if(qstate->env->cfg->val_log_level < 2 || !rr)
- return;
- t = ldns_rr_type2str(ntohs(rr->rk.type));
- c = ldns_rr_class2str(ntohs(rr->rk.rrset_class));
- if(!t || !c) {
- free(t);
- free(c);
- log_err("malloc failure in errinf_rrset");
- return;
- }
- dname_str(qstate->qinfo.qname, dname);
- snprintf(buf, sizeof(buf), "for <%s %s %s>", dname, t, c);
- free(t);
- free(c);
- val_errinf(qstate, vq, buf);
-}
-
-void val_errinf_dname(struct module_qstate* qstate, struct val_qstate* vq,
- const char* str, uint8_t* dname)
-{
- char b[1024];
- char buf[LDNS_MAX_DOMAINLEN+1];
- if(qstate->env->cfg->val_log_level < 2 || !str || !dname)
- return;
- dname_str(dname, buf);
- snprintf(b, sizeof(b), "%s %s", str, buf);
- val_errinf(qstate, vq, b);
-}
-
int val_has_signed_nsecs(struct reply_info* rep, char** reason)
{
size_t i, num_nsec = 0, num_nsec3 = 0;
struct val_anchors;
struct rrset_cache;
struct sock_list;
-struct module_qstate;
-struct val_qstate;
/**
* Response classifications for the validator. The different types of proofs.
void val_blacklist(struct sock_list** blacklist, struct regional* region,
struct sock_list* origin, int cross);
-/**
- * Append text to the error info for validation.
- * @param qstate: query state.
- * @param vq: validator state.
- * @param str: copied into query region and appended.
- * Failures to allocate are logged.
- */
-void val_errinf(struct module_qstate* qstate, struct val_qstate* vq,
- const char* str);
-
-/**
- * Append text to error info: from 1.2.3.4
- * @param qstate: query state.
- * @param vq: validator state.
- * @param origin: sock list with origin of trouble.
- * Every element added.
- * If NULL: nothing is added.
- * if 0len element: 'from cache' is added.
- */
-void val_errinf_origin(struct module_qstate* qstate, struct val_qstate* vq,
- struct sock_list *origin);
-
-/**
- * Append text to error info: for RRset name type class
- * @param qstate: query state.
- * @param vq: validator state.
- * @param rr: rrset_key.
- */
-void val_errinf_rrset(struct module_qstate* qstate, struct val_qstate* vq,
- struct ub_packed_rrset_key *rr);
-
-/**
- * Append text to error info: str dname
- * @param qstate: query state.
- * @param vq: validator state.
- * @param str: explanation string
- * @param dname: the dname.
- */
-void val_errinf_dname(struct module_qstate* qstate, struct val_qstate* vq,
- const char* str, uint8_t* dname);
-
-/**
- * Create error info in string
- * @param qstate: query state. (for query name)
- * @param vq: validator state.
- * @return string or NULL on malloc failure (already logged).
- * This string is malloced and has to be freed by caller.
- */
-char* val_errinf_to_str(struct module_qstate* qstate, struct val_qstate* vq);
-
/**
* check if has dnssec info, and if it has signed nsecs. gives error reason.
* @param rep: reply to check.
* completed.
*
* @param qstate: query state.
- * @param vq: validator query state.
* @param env: module env for verify.
* @param ve: validator env for verify.
* @param qchase: query that was made.
* fail to verify. The message is then set to bogus.
*/
static int
-validate_msg_signatures(struct module_qstate* qstate, struct val_qstate* vq,
- struct module_env* env, struct val_env* ve, struct query_info* qchase,
+validate_msg_signatures(struct module_qstate* qstate, struct module_env* env,
+ struct val_env* ve, struct query_info* qchase,
struct reply_info* chase_reply, struct key_entry_key* key_entry)
{
uint8_t* sname;
log_nametypeclass(VERB_QUERY, "validator: response "
"has failed ANSWER rrset:", s->rk.dname,
ntohs(s->rk.type), ntohs(s->rk.rrset_class));
- val_errinf(qstate, vq, reason);
+ errinf(qstate, reason);
if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME)
- val_errinf(qstate, vq, "for CNAME");
+ errinf(qstate, "for CNAME");
else if(ntohs(s->rk.type) == LDNS_RR_TYPE_DNAME)
- val_errinf(qstate, vq, "for DNAME");
- val_errinf_origin(qstate, vq, qstate->reply_origin);
+ errinf(qstate, "for DNAME");
+ errinf_origin(qstate, qstate->reply_origin);
chase_reply->security = sec_status_bogus;
return 0;
}
log_nametypeclass(VERB_QUERY, "validator: response "
"has failed AUTHORITY rrset:", s->rk.dname,
ntohs(s->rk.type), ntohs(s->rk.rrset_class));
- val_errinf(qstate, vq, reason);
- val_errinf_rrset(qstate, vq, s);
- val_errinf_origin(qstate, vq, qstate->reply_origin);
+ errinf(qstate, reason);
+ errinf_rrset(qstate, s);
+ errinf_origin(qstate, qstate->reply_origin);
chase_reply->security = sec_status_bogus;
return 0;
}
"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;
- val_errinf(qstate, vq, "while building chain of trust");
+ errinf(qstate, "while building chain of trust");
return 1;
}
"signer name", &vq->qchase);
verbose(VERB_DETAIL, "Could not establish validation of "
"INSECURE status of unsigned response.");
- val_errinf(qstate, vq, "no signatures");
- val_errinf_origin(qstate, vq, qstate->reply_origin);
+ errinf(qstate, "no signatures");
+ errinf_origin(qstate, qstate->reply_origin);
vq->chase_reply->security = sec_status_bogus;
return 1;
}
/* check signatures in the message;
* answer and authority must be valid, additional is only checked. */
- if(!validate_msg_signatures(qstate, vq, qstate->env, ve, &vq->qchase,
+ if(!validate_msg_signatures(qstate, qstate->env, ve, &vq->qchase,
vq->chase_reply, vq->key_entry)) {
/* workaround bad recursor out there that truncates (even
* with EDNS4k) to 512 by removing RRSIG from auth section
vq->chase_reply->ar_numrrsets = 0;
vq->chase_reply->rrset_count =
vq->chase_reply->an_numrrsets;
- vq->errinf = NULL;
+ qstate->errinf = NULL;
}
else {
verbose(VERB_DETAIL, "Validate: message contains "
}
if(vq->chase_reply->security == sec_status_bogus) {
if(subtype == VAL_CLASS_POSITIVE)
- val_errinf(qstate, vq, "wildcard");
- else val_errinf(qstate, vq,
- val_classification_to_string(subtype));
- val_errinf(qstate, vq, "proof failed");
- val_errinf_origin(qstate, vq, qstate->reply_origin);
+ errinf(qstate, "wildcard");
+ else errinf(qstate, val_classification_to_string(subtype));
+ errinf(qstate, "proof failed");
+ errinf_origin(qstate, qstate->reply_origin);
}
return 1;
}
vq->orig_msg->rep->ttl = ve->bogus_ttl;
- if(qstate->env->cfg->val_log_level >= 1) {
+ if(qstate->env->cfg->val_log_level >= 1 &&
+ !qstate->env->cfg->val_log_squelch) {
if(qstate->env->cfg->val_log_level < 2)
log_query_info(0, "validation failure",
&qstate->qinfo);
else {
- char* err = val_errinf_to_str(qstate, vq);
+ char* err = errinf_to_str(qstate);
if(err) log_info(err);
free(err);
}
"could not fetch DNSKEY rrset",
ta->name, LDNS_RR_TYPE_DNSKEY, ta->dclass);
if(qstate->env->cfg->harden_dnssec_stripped) {
- struct val_qstate* vq = (struct val_qstate*)
- qstate->minfo[id];
- val_errinf(qstate, vq, "no DNSKEY rrset");
+ errinf(qstate, "no DNSKEY rrset");
kkey = key_entry_create_bad(qstate->region, ta->name,
ta->namelen, ta->dclass);
} else kkey = key_entry_create_null(qstate->region, ta->name,
/* NOTE: in this case, we should probably reject the trust
* anchor for longer, perhaps forever. */
if(qstate->env->cfg->harden_dnssec_stripped) {
- struct val_qstate* vq = (struct val_qstate*)
- qstate->minfo[id];
- val_errinf(qstate, vq, reason);
+ errinf(qstate, reason);
kkey = key_entry_create_bad(qstate->region, ta->name,
ta->namelen, ta->dclass);
} else kkey = key_entry_create_null(qstate->region, ta->name,
char* rc = ldns_pkt_rcode2str(rcode);
/* errors here pretty much break validation */
verbose(VERB_DETAIL, "DS response was error, thus bogus");
- val_errinf(qstate, vq, rc);
- val_errinf(qstate, vq, "no DS");
+ errinf(qstate, rc);
+ errinf(qstate, "no DS");
free(rc);
goto return_bogus;
}
if(!ds) {
log_warn("internal error: POSITIVE DS response was "
"missing DS.");
- val_errinf(qstate, vq, "no DS record");
+ errinf(qstate, "no DS record");
goto return_bogus;
}
/* Verify only returns BOGUS or SECURE. If the rrset is
if(sec != sec_status_secure) {
verbose(VERB_DETAIL, "DS rrset in DS response did "
"not verify");
- val_errinf(qstate, vq, reason);
+ errinf(qstate, reason);
goto return_bogus;
}
/* make sure there are NSECs or NSEC3s with signatures */
if(!val_has_signed_nsecs(msg->rep, &reason)) {
verbose(VERB_ALGO, "no NSECs: %s", reason);
- val_errinf(qstate, vq, reason);
+ errinf(qstate, reason);
goto return_bogus;
}
case sec_status_bogus:
verbose(VERB_DETAIL, "NSEC RRset for the "
"referral did not prove no DS.");
- val_errinf(qstate, vq, reason);
+ errinf(qstate, reason);
goto return_bogus;
case sec_status_unchecked:
default:
case sec_status_bogus:
verbose(VERB_DETAIL, "NSEC3s for the "
"referral did not prove no DS.");
- val_errinf(qstate, vq, reason);
+ errinf(qstate, reason);
goto return_bogus;
case sec_status_insecure:
case sec_status_unchecked:
* this is BOGUS. */
verbose(VERB_DETAIL, "DS %s ran out of options, so return "
"bogus", val_classification_to_string(subtype));
- val_errinf(qstate, vq, "no DS but also no proof of that");
+ errinf(qstate, "no DS but also no proof of that");
goto return_bogus;
} else {
verbose(VERB_QUERY, "Encountered an unhandled type of "
"DS response, thus bogus.");
- val_errinf(qstate, vq, "no DS and ");
+ errinf(qstate, "no DS and ");
if(FLAGS_GET_RCODE(msg->rep->flags) != LDNS_RCODE_NOERROR) {
char* rc = ldns_pkt_rcode2str(
FLAGS_GET_RCODE(msg->rep->flags));
- val_errinf(qstate, vq, rc);
+ errinf(qstate, rc);
free(rc);
- } else val_errinf(qstate, vq,
- val_classification_to_string(subtype));
- val_errinf(qstate, vq, "message fails to prove that");
+ } else errinf(qstate, val_classification_to_string(subtype));
+ errinf(qstate, "message fails to prove that");
goto return_bogus;
}
return_bogus:
&& vq->restart_count < VAL_MAX_RESTART_COUNT) {
vq->empty_DS_name = olds;
val_blacklist(&vq->chain_blacklist, qstate->region, origin, 1);
- vq->errinf = NULL;
+ qstate->errinf = NULL;
vq->restart_count++;
} else {
if(key_entry_isbad(dske)) {
- val_errinf_origin(qstate, vq, origin);
- val_errinf_dname(qstate, vq, "for DS", qinfo->qname);
+ errinf_origin(qstate, origin);
+ errinf_dname(qstate, "for DS", qinfo->qname);
}
/* NOTE: the reason for the DS to be not good (that is,
* either bad or null) should have been logged by
if(vq->restart_count < VAL_MAX_RESTART_COUNT) {
val_blacklist(&vq->chain_blacklist, qstate->region,
origin, 1);
- vq->errinf = NULL;
+ qstate->errinf = NULL;
vq->restart_count++;
return;
}
log_err("alloc failure in missing dnskey response");
/* key_entry is NULL for failure in Validate */
}
- val_errinf(qstate, vq, "No DNSKEY record");
- val_errinf_origin(qstate, vq, origin);
- val_errinf_dname(qstate, vq, "for key", qinfo->qname);
+ errinf(qstate, "No DNSKEY record");
+ errinf_origin(qstate, origin);
+ errinf_dname(qstate, "for key", qinfo->qname);
vq->state = VAL_VALIDATE_STATE;
return;
}
if(vq->restart_count < VAL_MAX_RESTART_COUNT) {
val_blacklist(&vq->chain_blacklist,
qstate->region, origin, 1);
- vq->errinf = NULL;
+ qstate->errinf = NULL;
vq->restart_count++;
vq->key_entry = old;
return;
}
verbose(VERB_DETAIL, "Did not match a DS to a DNSKEY, "
"thus bogus.");
- val_errinf(qstate, vq, reason);
- val_errinf_origin(qstate, vq, origin);
- val_errinf_dname(qstate, vq, "for key", qinfo->qname);
+ errinf(qstate, reason);
+ 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;
- vq->errinf = NULL;
+ qstate->errinf = NULL;
/* The DNSKEY validated, so cache it as a trusted key rrset. */
key_cache_insert(ve->kcache, vq->key_entry);
&& vq->restart_count < VAL_MAX_RESTART_COUNT) {
val_blacklist(&vq->chain_blacklist, qstate->region,
origin, 1);
- vq->errinf = NULL;
+ qstate->errinf = NULL;
vq->restart_count++;
vq->key_entry = NULL;
vq->state = VAL_INIT_STATE;
return;
}
vq->chain_blacklist = NULL;
- val_errinf_origin(qstate, vq, origin);
- val_errinf_dname(qstate, vq, "for trust anchor", ta->name);
+ errinf_origin(qstate, origin);
+ errinf_dname(qstate, "for trust anchor", ta->name);
/* store the freshly primed entry in the cache */
key_cache_insert(ve->kcache, vq->key_entry);
}
dlv_ask_higher, /* ask again */
dlv_there_is_no_dlv /* got no DLV, sure of it */
} dlv_status;
-
- /** failure reason information if val-log-level is high */
- struct config_strlist* errinf;
};
/**