Blob Blame Raw
diff --git a/daemon/worker.c b/daemon/worker.c
index 44a989a..3acecc1 100644
--- a/daemon/worker.c
+++ b/daemon/worker.c
@@ -1193,7 +1193,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
 	if(worker->env.cfg->log_queries) {
 		char ip[128];
 		addr_to_str(&repinfo->addr, repinfo->addrlen, ip, sizeof(ip));
-		log_nametypeclass(0, ip, qinfo.qname, qinfo.qtype, qinfo.qclass);
+		log_query_in(ip, qinfo.qname, qinfo.qtype, qinfo.qclass);
 	}
 	if(qinfo.qtype == LDNS_RR_TYPE_AXFR || 
 		qinfo.qtype == LDNS_RR_TYPE_IXFR) {
diff --git a/doc/Changelog b/doc/Changelog
index 3d05ae5..bb74461 100644
--- a/doc/Changelog
+++ b/doc/Changelog
@@ -1,3 +1,15 @@
+30 November 2018: Wouter
+	- log-tag-queryreply: yes in unbound.conf tags the log-queries and
+	  log-replies in the log file for easier log filter maintenance.
+
+21 August 2018: Wouter
+	- log-local-actions: yes option for unbound.conf that logs all the
+	  local zone actions, a patch from Saksham Manchanda (Secure64).
+
+17 August 2018: Wouter
+	- log-servfail: yes prints log lines that say why queries are
+	  returning SERVFAIL to clients.
+
 19 June 2018: Wouter
 	- Fix for unbound-control on Windows and set TCP socket parameters
 	  more closely.
diff --git a/doc/example.conf.in b/doc/example.conf.in
index be83bda..aa06cee 100644
--- a/doc/example.conf.in
+++ b/doc/example.conf.in
@@ -309,6 +309,17 @@ server:
 	# timetoresolve, fromcache and responsesize.
 	# log-replies: no
 
+	# log with tag 'query' and 'reply' instead of 'info' for
+	# filtering log-queries and log-replies from the log.
+	# log-tag-queryreply: no
+
+	# log the local-zone actions, like local-zone type inform is enabled
+	# also for the other local zone types.
+	# log-local-actions: no
+
+	# print log lines that say why queries return SERVFAIL to clients.
+	# log-servfail: no
+
 	# the pid file. Can be an absolute path outside of chroot/work dir.
 	# pidfile: "@UNBOUND_PIDFILE@"
 
diff --git a/doc/unbound.conf.5.in b/doc/unbound.conf.5.in
index 9167a5a..b73bc70 100644
--- a/doc/unbound.conf.5.in
+++ b/doc/unbound.conf.5.in
@@ -618,6 +618,21 @@ Default is no.  Note that it takes time to print these
 lines which makes the server (significantly) slower.  Odd (nonprintable)
 characters in names are printed as '?'.
 .TP
+.B log\-tag\-queryreply: \fI<yes or no>
+Prints the word 'query' and 'reply' with log\-queries and log\-replies.
+This makes filtering logs easier.  The default is off (for backwards
+compatibility).
+.TP
+.B log\-local\-actions: \fI<yes or no>
+Print log lines to inform about local zone actions.  These lines are like the
+local\-zone type inform prints out, but they are also printed for the other
+types of local zones.
+.TP
+.B log\-servfail: \fI<yes or no>
+Print log lines that say why queries return SERVFAIL to clients.
+This is separate from the verbosity debug logs, much smaller, and printed
+at the error level, not the info level of debug info from verbosity.
+.TP
 .B pidfile: \fI<filename>
 The process id is written to the file. Default is "@UNBOUND_PIDFILE@".
 So,
diff --git a/services/localzone.c b/services/localzone.c
index 0f60817..a85619b 100644
--- a/services/localzone.c
+++ b/services/localzone.c
@@ -1459,7 +1459,7 @@ lz_inform_print(struct local_zone* z, struct query_info* qinfo,
 	uint16_t port = ntohs(((struct sockaddr_in*)&repinfo->addr)->sin_port);
 	dname_str(z->name, zname);
 	addr_to_str(&repinfo->addr, repinfo->addrlen, ip, sizeof(ip));
-	snprintf(txt, sizeof(txt), "%s inform %s@%u", zname, ip,
+	snprintf(txt, sizeof(txt), "%s %s %s@%u", zname, local_zone_type2str(z->type), ip,
 		(unsigned)port);
 	log_nametypeclass(0, txt, qinfo->qname, qinfo->qtype, qinfo->qclass);
 }
@@ -1576,7 +1576,8 @@ local_zones_answer(struct local_zones* zones, struct module_env* env,
 			z->override_tree, &tag, tagname, num_tags);
 		lock_rw_unlock(&zones->lock);
 	}
-	if((lzt == local_zone_inform || lzt == local_zone_inform_deny)
+	if((env->cfg->log_local_actions ||
+		lzt == local_zone_inform || lzt == local_zone_inform_deny)
 		&& repinfo)
 		lz_inform_print(z, qinfo, repinfo);
 
diff --git a/services/mesh.c b/services/mesh.c
index 41aba74..55c1947 100644
--- a/services/mesh.c
+++ b/services/mesh.c
@@ -977,7 +977,7 @@ mesh_do_callback(struct mesh_state* m, int rcode, struct reply_info* rep,
 		rcode = LDNS_RCODE_SERVFAIL;
 	if(!rcode && (rep->security == sec_status_bogus ||
 		rep->security == sec_status_secure_sentinel_fail)) {
-		if(!(reason = errinf_to_str(&m->s)))
+		if(!(reason = errinf_to_str_bogus(&m->s)))
 			rcode = LDNS_RCODE_SERVFAIL;
 	}
 	/* send the reply */
@@ -1148,6 +1148,15 @@ void mesh_query_done(struct mesh_state* mstate)
 	struct mesh_cb* c;
 	struct reply_info* rep = (mstate->s.return_msg?
 		mstate->s.return_msg->rep:NULL);
+	if((mstate->s.return_rcode == LDNS_RCODE_SERVFAIL ||
+		(rep && FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_SERVFAIL))
+		&& mstate->s.env->cfg->log_servfail
+		&& !mstate->s.env->cfg->val_log_squelch) {
+			char* err = errinf_to_str_servfail(&mstate->s);
+			if(err)
+				log_err("%s", err);
+			free(err);
+	}
 	for(r = mstate->reply_list; r; r = r->next) {
 		/* if a response-ip address block has been stored the
 		 *  information should be logged for each client. */
diff --git a/util/config_file.c b/util/config_file.c
index b061760..68a0a15 100644
--- a/util/config_file.c
+++ b/util/config_file.c
@@ -115,6 +115,9 @@ config_create(void)
 	cfg->log_time_ascii = 0;
 	cfg->log_queries = 0;
 	cfg->log_replies = 0;
+	cfg->log_tag_queryreply = 0;
+	cfg->log_local_actions = 0;
+	cfg->log_servfail = 0;
 #ifndef USE_WINSOCK
 #  ifdef USE_MINI_EVENT
 	/* select max 1024 sockets */
@@ -540,6 +543,9 @@ int config_set_option(struct config_file* cfg, const char* opt,
 	else S_YNO("val-log-squelch:", val_log_squelch)
 	else S_YNO("log-queries:", log_queries)
 	else S_YNO("log-replies:", log_replies)
+	else S_YNO("log-tag-queryreply:", log_tag_queryreply)
+	else S_YNO("log-local-actions:", log_local_actions)
+	else S_YNO("log-servfail:", log_servfail)
 	else S_YNO("val-permissive-mode:", val_permissive_mode)
 	else S_YNO("aggressive-nsec:", aggressive_nsec)
 	else S_YNO("ignore-cd-flag:", ignore_cd)
@@ -893,6 +899,9 @@ config_get_option(struct config_file* cfg, const char* opt,
 	else O_STR(opt, "logfile", logfile)
 	else O_YNO(opt, "log-queries", log_queries)
 	else O_YNO(opt, "log-replies", log_replies)
+	else O_YNO(opt, "log-tag-queryreply", log_tag_queryreply)
+	else O_YNO(opt, "log-local-actions", log_local_actions)
+	else O_YNO(opt, "log-servfail", log_servfail)
 	else O_STR(opt, "pidfile", pidfile)
 	else O_YNO(opt, "hide-identity", hide_identity)
 	else O_YNO(opt, "hide-version", hide_version)
@@ -1845,6 +1854,7 @@ config_apply(struct config_file* config)
 	EDNS_ADVERTISED_SIZE = (uint16_t)config->edns_buffer_size;
 	MINIMAL_RESPONSES = config->minimal_responses;
 	RRSET_ROUNDROBIN = config->rrset_roundrobin;
+	LOG_TAG_QUERYREPLY = config->log_tag_queryreply;
 	log_set_time_asc(config->log_time_ascii);
 	autr_permit_small_holddown = config->permit_small_holddown;
 }
@@ -2220,7 +2230,7 @@ void errinf_origin(struct module_qstate* qstate, struct sock_list *origin)
 	}
 }
 
-char* errinf_to_str(struct module_qstate* qstate)
+char* errinf_to_str_bogus(struct module_qstate* qstate)
 {
 	char buf[20480];
 	char* p = buf;
@@ -2245,6 +2255,31 @@ char* errinf_to_str(struct module_qstate* qstate)
 	return p;
 }
 
+char* errinf_to_str_servfail(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[16], c[16];
+    sldns_wire2str_type_buf(qstate->qinfo.qtype, t, sizeof(t));
+    sldns_wire2str_class_buf(qstate->qinfo.qclass, c, sizeof(c));
+    dname_str(qstate->qinfo.qname, dname);
+    snprintf(p, left, "SERVFAIL <%s %s %s>:", dname, t, 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];
diff --git a/util/config_file.h b/util/config_file.h
index 4206eb9..1e7f402 100644
--- a/util/config_file.h
+++ b/util/config_file.h
@@ -268,6 +268,12 @@ struct config_file {
 	int log_queries;
 	/** log replies with one line per reply */
 	int log_replies;
+	/** tag log_queries and log_replies for filtering */
+	int log_tag_queryreply;
+	/** log every local-zone hit **/
+	int log_local_actions;
+	/** log servfails with a reason */
+	int log_servfail;
 	/** log identity to report */
 	char* log_identity;
 
@@ -1070,7 +1076,15 @@ void errinf_dname(struct module_qstate* qstate, const char* str,
  * @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);
+char* errinf_to_str_bogus(struct module_qstate* qstate);
+
+/**
+ * Create error info in string.  For other servfails.
+ * @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_servfail(struct module_qstate* qstate);
 
 /**
  * Used during options parsing
diff --git a/util/configlexer.lex b/util/configlexer.lex
index 6124e32..9b22db1 100644
--- a/util/configlexer.lex
+++ b/util/configlexer.lex
@@ -366,6 +366,9 @@ log-identity{COLON}		{ YDVAR(1, VAR_LOG_IDENTITY) }
 log-time-ascii{COLON}		{ YDVAR(1, VAR_LOG_TIME_ASCII) }
 log-queries{COLON}		{ YDVAR(1, VAR_LOG_QUERIES) }
 log-replies{COLON}		{ YDVAR(1, VAR_LOG_REPLIES) }
+log-tag-queryreply{COLON}      { YDVAR(1, VAR_LOG_TAG_QUERYREPLY) }
+log-local-actions{COLON}       { YDVAR(1, VAR_LOG_LOCAL_ACTIONS) }
+log-servfail{COLON}            { YDVAR(1, VAR_LOG_SERVFAIL) }
 local-zone{COLON}		{ YDVAR(2, VAR_LOCAL_ZONE) }
 local-data{COLON}		{ YDVAR(1, VAR_LOCAL_DATA) }
 local-data-ptr{COLON}		{ YDVAR(1, VAR_LOCAL_DATA_PTR) }
diff --git a/util/configparser.y b/util/configparser.y
index e34665a..4b23a71 100644
--- a/util/configparser.y
+++ b/util/configparser.y
@@ -106,7 +106,7 @@ extern struct config_parser_state* cfg_parser;
 %token VAR_AUTO_TRUST_ANCHOR_FILE VAR_KEEP_MISSING VAR_ADD_HOLDDOWN 
 %token VAR_DEL_HOLDDOWN VAR_SO_RCVBUF VAR_EDNS_BUFFER_SIZE VAR_PREFETCH
 %token VAR_PREFETCH_KEY VAR_SO_SNDBUF VAR_SO_REUSEPORT VAR_HARDEN_BELOW_NXDOMAIN
-%token VAR_IGNORE_CD_FLAG VAR_LOG_QUERIES VAR_LOG_REPLIES
+%token VAR_IGNORE_CD_FLAG VAR_LOG_QUERIES VAR_LOG_REPLIES VAR_LOG_LOCAL_ACTIONS
 %token VAR_TCP_UPSTREAM VAR_SSL_UPSTREAM
 %token VAR_SSL_SERVICE_KEY VAR_SSL_SERVICE_PEM VAR_SSL_PORT VAR_FORWARD_FIRST
 %token VAR_STUB_SSL_UPSTREAM VAR_FORWARD_SSL_UPSTREAM VAR_TLS_CERT_BUNDLE
@@ -158,6 +158,8 @@ extern struct config_parser_state* cfg_parser;
 %token VAR_AUTH_ZONE VAR_ZONEFILE VAR_MASTER VAR_URL VAR_FOR_DOWNSTREAM
 %token VAR_FALLBACK_ENABLED VAR_TLS_ADDITIONAL_PORT VAR_LOW_RTT VAR_LOW_RTT_PERMIL
 %token VAR_ALLOW_NOTIFY VAR_TLS_WIN_CERT
+%token VAR_FORWARD_NO_CACHE VAR_STUB_NO_CACHE VAR_LOG_SERVFAIL
+%token VAR_UNKNOWN_SERVER_TIME_LIMIT VAR_LOG_TAG_QUERYREPLY
 
 %%
 toplevelvars: /* empty */ | toplevelvars toplevelvar ;
@@ -217,6 +219,7 @@ content_server: server_num_threads | server_verbosity | server_port |
 	server_edns_buffer_size | server_prefetch | server_prefetch_key |
 	server_so_sndbuf | server_harden_below_nxdomain | server_ignore_cd_flag |
 	server_log_queries | server_log_replies | server_tcp_upstream | server_ssl_upstream |
+	server_log_local_actions |
 	server_ssl_service_key | server_ssl_service_pem | server_ssl_port |
 	server_minimal_responses | server_rrset_roundrobin | server_max_udp_size |
 	server_so_reuseport | server_delay_close |
@@ -249,7 +252,9 @@ content_server: server_num_threads | server_verbosity | server_port |
 	server_ipsecmod_whitelist | server_ipsecmod_strict |
 	server_udp_upstream_without_downstream | server_aggressive_nsec |
 	server_tls_cert_bundle | server_tls_additional_port | server_low_rtt |
-	server_low_rtt_permil | server_tls_win_cert
+	server_low_rtt_permil | server_tls_win_cert |
+	server_tcp_connection_limit | server_log_servfail |
+	server_unknown_server_time_limit | server_log_tag_queryreply
 	;
 stubstart: VAR_STUB_ZONE
 	{
@@ -764,6 +769,33 @@ server_log_replies: VAR_LOG_REPLIES STRING_ARG
   	free($2);
   }
   ;
+server_log_tag_queryreply: VAR_LOG_TAG_QUERYREPLY STRING_ARG
+  {
+    OUTYY(("P(server_log_tag_queryreply:%s)\n", $2));
+    if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+        yyerror("expected yes or no.");
+    else cfg_parser->cfg->log_tag_queryreply = (strcmp($2, "yes")==0);
+    free($2);
+  }
+  ;
+server_log_servfail: VAR_LOG_SERVFAIL STRING_ARG
+  {
+	OUTYY(("P(server_log_servfail:%s)\n", $2));
+	if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+		yyerror("expected yes or no.");
+	else cfg_parser->cfg->log_servfail = (strcmp($2, "yes")==0);
+	free($2);
+  }
+  ;
+server_log_local_actions: VAR_LOG_LOCAL_ACTIONS STRING_ARG
+  {
+	OUTYY(("P(server_log_local_actions:%s)\n", $2));
+	if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+		yyerror("expected yes or no.");
+	else cfg_parser->cfg->log_local_actions = (strcmp($2, "yes")==0);
+	free($2);
+  }
+  ;
 server_chroot: VAR_CHROOT STRING_ARG
 	{
 		OUTYY(("P(server_chroot:%s)\n", $2));
diff --git a/util/data/msgreply.c b/util/data/msgreply.c
index 772f5d1..df2131c 100644
--- a/util/data/msgreply.c
+++ b/util/data/msgreply.c
@@ -844,7 +844,9 @@ log_reply_info(enum verbosity_value v, struct query_info *qinf,
 	addr_to_str(addr, addrlen, clientip_buf, sizeof(clientip_buf));
 	if(rcode == LDNS_RCODE_FORMERR)
 	{
-		log_info("%s - - - %s - - - ", clientip_buf, rcode_buf);
+		if(LOG_TAG_QUERYREPLY)
+			log_reply("%s - - - %s - - - ", clientip_buf, rcode_buf);
+		else log_info("%s - - - %s - - - ", clientip_buf, rcode_buf);
 	} else {
 		if(qinf->qname)
 			dname_str(qinf->qname, qname_buf);
@@ -852,7 +854,11 @@ log_reply_info(enum verbosity_value v, struct query_info *qinf,
 		pktlen = sldns_buffer_limit(rmsg);
 		sldns_wire2str_type_buf(qinf->qtype, type_buf, sizeof(type_buf));
 		sldns_wire2str_class_buf(qinf->qclass, class_buf, sizeof(class_buf));
-		log_info("%s %s %s %s %s " ARG_LL "d.%6.6d %d %d",
+		if(LOG_TAG_QUERYREPLY)
+			log_reply("%s %s %s %s %s " ARG_LL "d.%6.6d %d %d",
+				clientip_buf, qname_buf, type_buf, class_buf,
+				rcode_buf, (long long)dur.tv_sec, (int)dur.tv_usec, cached, (int)pktlen);
+		else log_info("%s %s %s %s %s " ARG_LL "d.%6.6d %d %d",
 			clientip_buf, qname_buf, type_buf, class_buf,
 			rcode_buf, (long long)dur.tv_sec, (int)dur.tv_usec, cached, (int)pktlen);
 	}
diff --git a/util/log.c b/util/log.c
index 43dd572..fff4319 100644
--- a/util/log.c
+++ b/util/log.c
@@ -391,6 +391,24 @@ log_hex(const char* msg, void* data, size_t length)
 	log_hex_f(verbosity, msg, data, length);
 }
 
+void
+log_query(const char *format, ...)
+{
+	va_list args;
+	va_start(args, format);
+	log_vmsg(LOG_INFO, "query", format, args);
+	va_end(args);
+}
+
+void
+log_reply(const char *format, ...)
+{
+	va_list args;
+	va_start(args, format);
+	log_vmsg(LOG_INFO, "reply", format, args);
+	va_end(args);
+}
+
 void log_buf(enum verbosity_value level, const char* msg, sldns_buffer* buf)
 {
 	if(verbosity < level)
diff --git a/util/log.h b/util/log.h
index 7bc3d9e..172cd01 100644
--- a/util/log.h
+++ b/util/log.h
@@ -160,6 +160,20 @@ void log_warn(const char* format, ...) ATTR_FORMAT(printf, 1, 2);
  */
 void log_hex(const char* msg, void* data, size_t length);
 
+/**
+ * Log query.
+ * Pass printf formatted arguments. No trailing newline is needed.
+ * @param format: printf-style format string. Arguments follow.
+ */
+void log_query(const char* format, ...) ATTR_FORMAT(printf, 1, 2);
+
+/**
+ * Log reply.
+ * Pass printf formatted arguments. No trailing newline is needed.
+ * @param format: printf-style format string. Arguments follow.
+ */
+void log_reply(const char* format, ...) ATTR_FORMAT(printf, 1, 2);
+
 /**
  * Easy alternative for log_hex, takes a sldns_buffer.
  * @param level: verbosity level for this message, compared to global 
diff --git a/util/net_help.c b/util/net_help.c
index a193c36..617a896 100644
--- a/util/net_help.c
+++ b/util/net_help.c
@@ -67,6 +67,9 @@ int MINIMAL_RESPONSES = 0;
 /** rrset order roundrobin: default is no */
 int RRSET_ROUNDROBIN = 0;
 
+/** log tag queries with name instead of 'info' for filtering */
+int LOG_TAG_QUERYREPLY = 0;
+
 /* returns true is string addr is an ip6 specced address */
 int
 str_is_ip6(const char* str)
@@ -335,7 +338,7 @@ log_nametypeclass(enum verbosity_value v, const char* str, uint8_t* name,
 {
 	char buf[LDNS_MAX_DOMAINLEN+1];
 	char t[12], c[12];
-	const char *ts, *cs; 
+	const char *ts, *cs;
 	if(verbosity < v)
 		return;
 	dname_str(name, buf);
@@ -361,6 +364,37 @@ log_nametypeclass(enum verbosity_value v, const char* str, uint8_t* name,
 	log_info("%s %s %s %s", str, buf, ts, cs);
 }
 
+void
+log_query_in(const char* str, uint8_t* name, uint16_t type, uint16_t dclass)
+{
+    char buf[LDNS_MAX_DOMAINLEN+1];
+    char t[12], c[12];
+    const char *ts, *cs;
+    dname_str(name, buf);
+    if(type == LDNS_RR_TYPE_TSIG) ts = "TSIG";
+    else if(type == LDNS_RR_TYPE_IXFR) ts = "IXFR";
+    else if(type == LDNS_RR_TYPE_AXFR) ts = "AXFR";
+    else if(type == LDNS_RR_TYPE_MAILB) ts = "MAILB";
+    else if(type == LDNS_RR_TYPE_MAILA) ts = "MAILA";
+    else if(type == LDNS_RR_TYPE_ANY) ts = "ANY";
+    else if(sldns_rr_descript(type) && sldns_rr_descript(type)->_name)
+        ts = sldns_rr_descript(type)->_name;
+    else {
+        snprintf(t, sizeof(t), "TYPE%d", (int)type);
+        ts = t;
+    }
+    if(sldns_lookup_by_id(sldns_rr_classes, (int)dclass) &&
+        sldns_lookup_by_id(sldns_rr_classes, (int)dclass)->name)
+        cs = sldns_lookup_by_id(sldns_rr_classes, (int)dclass)->name;
+    else {
+        snprintf(c, sizeof(c), "CLASS%d", (int)dclass);
+        cs = c;
+    }
+    if(LOG_TAG_QUERYREPLY)
+            log_query("%s %s %s %s", str, buf, ts, cs);
+    else    log_info("%s %s %s %s", str, buf, ts, cs);
+}
+
 void log_name_addr(enum verbosity_value v, const char* str, uint8_t* zone, 
 	struct sockaddr_storage* addr, socklen_t addrlen)
 {
diff --git a/util/net_help.h b/util/net_help.h
index de2e1ac..f2e0e43 100644
--- a/util/net_help.h
+++ b/util/net_help.h
@@ -99,6 +99,9 @@ extern int MINIMAL_RESPONSES;
 /** rrset order roundrobin */
 extern int RRSET_ROUNDROBIN;
 
+/** log tag queries with name instead of 'info' for filtering */
+extern int LOG_TAG_QUERYREPLY;
+
 /**
  * See if string is ip4 or ip6.
  * @param str: IP specification.
@@ -235,6 +238,12 @@ void sockaddr_store_port(struct sockaddr_storage* addr, socklen_t addrlen,
 void log_nametypeclass(enum verbosity_value v, const char* str, 
 	uint8_t* name, uint16_t type, uint16_t dclass);
 
+/**
+ * Like log_nametypeclass, but logs with log_query for query logging
+ */
+void log_query_in(const char* str, uint8_t* name, uint16_t type,
+       uint16_t dclass);
+
 /**
  * Compare two sockaddrs. Imposes an ordering on the addresses.
  * Compares address and port.
diff --git a/validator/val_kcache.c b/validator/val_kcache.c
index 22070cc..e0b88b6 100644
--- a/validator/val_kcache.c
+++ b/validator/val_kcache.c
@@ -89,7 +89,7 @@ key_cache_insert(struct key_cache* kcache, struct key_entry_key* kkey,
 	if(key_entry_isbad(k) && qstate->errinf &&
 		qstate->env->cfg->val_log_level >= 2) {
 		/* on malloc failure there is simply no reason string */
-		key_entry_set_reason(k, errinf_to_str(qstate));
+		key_entry_set_reason(k, errinf_to_str_bogus(qstate));
 	}
 	key_entry_hash(k);
 	slabhash_insert(kcache->slab, k->entry.hash, &k->entry, 
diff --git a/validator/validator.c b/validator/validator.c
index 5777b29..2d9cc17 100644
--- a/validator/validator.c
+++ b/validator/validator.c
@@ -2227,13 +2227,15 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq,
 		vq->orig_msg->rep->ttl = ve->bogus_ttl;
 		vq->orig_msg->rep->prefetch_ttl = 
 			PREFETCH_TTL_CALC(vq->orig_msg->rep->ttl);
-		if(qstate->env->cfg->val_log_level >= 1 &&
+		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)
+            if(qstate->env->cfg->val_log_level < 2 &&
+				!qstate->env->cfg->log_servfail)
 				log_query_info(0, "validation failure",
 					&qstate->qinfo);
 			else {
-				char* err = errinf_to_str(qstate);
+				char* err = errinf_to_str_bogus(qstate);
 				if(err) log_info("%s", err);
 				free(err);
 			}
@@ -2332,6 +2334,7 @@ processDLVLookup(struct module_qstate* qstate, struct val_qstate* vq,
 
 	if(vq->dlv_status == dlv_error) {
 		verbose(VERB_QUERY, "failed DLV lookup");
+		errinf(qstate, "failed DLV lookup");
 		return val_error(qstate, id);
 	} else if(vq->dlv_status == dlv_success) {
 		uint8_t* nm;