From 3846cc78b474d8ecce51b4a276ddd5be9ec14606 Mon Sep 17 00:00:00 2001
From: Grigorii Demidov <grigorii.demidov@nic.cz>
Date: Wed, 10 Aug 2016 10:24:27 +0200
Subject: [PATCH] lib/resolve: empty final answer when SERVFAIL

---
 lib/resolve.c | 51 +++++++++++++++++++++++++++++++++++----------------
 lib/utils.c   | 16 ++++++++++++++++
 lib/utils.h   |  4 ++++
 3 files changed, 55 insertions(+), 16 deletions(-)

diff --git a/lib/resolve.c b/lib/resolve.c
index 56ee37b58..55ca92ce6 100644
--- a/lib/resolve.c
+++ b/lib/resolve.c
@@ -337,43 +337,62 @@ static void write_extra_records(rr_array_t *arr, knot_pkt_t *answer)
 	}
 }
 
+static int answer_fail(knot_pkt_t *answer)
+{
+	int ret = kr_pkt_clear_payload(answer);
+	knot_wire_clear_ad(answer->wire);
+	knot_wire_clear_aa(answer->wire);
+	knot_wire_set_rcode(answer->wire, KNOT_RCODE_SERVFAIL);
+	if (ret == 0 && answer->opt_rr) {
+		/* OPT in SERVFAIL response is still useful for cookies/additional info. */
+		knot_pkt_begin(answer, KNOT_ADDITIONAL);
+		ret = edns_put(answer);
+	}
+	return ret;
+}
+
 static int answer_finalize(struct kr_request *request, int state)
 {
-	/* Write authority records. */
+	struct kr_rplan *rplan = &request->rplan;
 	knot_pkt_t *answer = request->answer;
-	if (answer->current < KNOT_AUTHORITY)
+
+	/* Always set SERVFAIL for bogus answers. */
+	if (state == KNOT_STATE_FAIL && rplan->pending.len > 0) {
+		struct kr_query *last = array_tail(rplan->pending);
+		if ((last->flags & QUERY_DNSSEC_WANT) && (last->flags & QUERY_DNSSEC_BOGUS)) {
+			return answer_fail(answer);
+		}
+	}
+
+	/* Write authority records. */
+	if (answer->current < KNOT_AUTHORITY) {
 		knot_pkt_begin(answer, KNOT_AUTHORITY);
+	}
 	write_extra_records(&request->authority, answer);
 	/* Write additional records. */
 	knot_pkt_begin(answer, KNOT_ADDITIONAL);
 	write_extra_records(&request->additional, answer);
 	/* Write EDNS information */
+	int ret = 0;
 	if (answer->opt_rr) {
-		int ret = edns_put(answer);
-		if (ret != 0) {
-			return ret;
-		}
-	}
-	struct kr_rplan *rplan = &request->rplan;
-	/* Always set SERVFAIL for bogus answers. */
-	if (state == KNOT_STATE_FAIL && rplan->pending.len > 0) {
-		struct kr_query *last = array_tail(rplan->pending);
-		if ((last->flags & QUERY_DNSSEC_WANT) && (last->flags & QUERY_DNSSEC_BOGUS)) {
-			knot_wire_set_rcode(answer->wire,KNOT_RCODE_SERVFAIL);
-		}
+		knot_pkt_begin(answer, KNOT_ADDITIONAL);
+		ret = edns_put(answer);
 	}
+
 	/* Set AD=1 if succeeded and requested secured answer. */
 	const bool has_ad = knot_wire_get_ad(answer->wire);
 	knot_wire_clear_ad(answer->wire);
 	if (state == KNOT_STATE_DONE && rplan->resolved.len > 0) {
 		struct kr_query *last = array_tail(rplan->resolved);
 		/* Do not set AD for RRSIG query, as we can't validate it. */
-		const bool secure = (last->flags & QUERY_DNSSEC_WANT) && !(last->flags & QUERY_DNSSEC_INSECURE);
+		const bool secure = (last->flags & QUERY_DNSSEC_WANT) &&
+		                   !(last->flags & QUERY_DNSSEC_INSECURE);
 		if (has_ad && secure && knot_pkt_qtype(answer) != KNOT_RRTYPE_RRSIG) {
 			knot_wire_set_ad(answer->wire);
 		}
 	}
-	return kr_ok();
+
+	return ret;
 }
 
 static int query_finalize(struct kr_request *request, struct kr_query *qry, knot_pkt_t *pkt)
diff --git a/lib/utils.c b/lib/utils.c
index 216b5f1d4..d8cc96c8a 100644
--- a/lib/utils.c
+++ b/lib/utils.c
@@ -208,6 +208,22 @@ int kr_pkt_recycle(knot_pkt_t *pkt)
 	return knot_pkt_parse_question(pkt);
 }
 
+int kr_pkt_clear_payload(knot_pkt_t *pkt)
+{
+	pkt->rrset_count = 0;
+	pkt->size = KNOT_WIRE_HEADER_SIZE + pkt->qname_size +
+		    2 * sizeof(uint16_t); /* QTYPE + QCLASS */
+	pkt->parsed = KNOT_WIRE_HEADER_SIZE;
+	pkt->current = KNOT_ANSWER;
+	knot_wire_set_ancount(pkt->wire, 0);
+	knot_wire_set_nscount(pkt->wire, 0);
+	knot_wire_set_arcount(pkt->wire, 0);
+	memset(&pkt->sections[KNOT_ANSWER], 0, sizeof(knot_pktsection_t) *
+	       (KNOT_PKT_SECTIONS - (KNOT_ANSWER + 1)));
+	knot_pkt_begin(pkt, KNOT_ANSWER);
+	return knot_pkt_parse_question(pkt);
+}
+
 int kr_pkt_put(knot_pkt_t *pkt, const knot_dname_t *name, uint32_t ttl,
                uint16_t rclass, uint16_t rtype, const uint8_t *rdata, uint16_t rdlen)
 {
diff --git a/lib/utils.h b/lib/utils.h
index dfcc55f6f..b7ef1f251 100644
--- a/lib/utils.h
+++ b/lib/utils.h
@@ -103,6 +103,10 @@ int kr_memreserve(void *baton, char **mem, size_t elm_size, size_t want, size_t
 KR_EXPORT
 int kr_pkt_recycle(knot_pkt_t *pkt);
 
+/** @internal Clear packet payload. */
+KR_EXPORT
+int kr_pkt_clear_payload(knot_pkt_t *pkt);
+
 /** Construct and put record to packet. */
 KR_EXPORT
 int kr_pkt_put(knot_pkt_t *pkt, const knot_dname_t *name, uint32_t ttl,
-- 
GitLab