From bf3d2255bfe4fe6819e5fa230136ffa55c4b1965 Mon Sep 17 00:00:00 2001
From: Jan Kadlec <jan.kadlec@nic.cz>
Date: Fri, 29 Aug 2014 11:13:01 +0200
Subject: [PATCH] IXFR: proper handling of AXFR-style IXFR.

---
 src/knot/nameserver/ixfr.c | 57 ++++++++++++++++++++++++++++++++------
 1 file changed, 49 insertions(+), 8 deletions(-)

diff --git a/src/knot/nameserver/ixfr.c b/src/knot/nameserver/ixfr.c
index c1483f0984..6f89bbd1cf 100644
--- a/src/knot/nameserver/ixfr.c
+++ b/src/knot/nameserver/ixfr.c
@@ -1,3 +1,19 @@
+/*  Copyright (C) 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
 #include "knot/nameserver/ixfr.h"
 #include "knot/nameserver/axfr.h"
 #include "knot/nameserver/internet.h"
@@ -308,6 +324,21 @@ static int ixfr_answer_soa(knot_pkt_t *pkt, struct query_data *qdata)
 #define IXFRIN_LOG(severity, msg...) \
 	ANSWER_LOG(severity, adata, "IXFR, incoming", msg)
 
+/*! \brief Checks whether IXFR response contains enough data for processing. */
+static bool ixfr_enough_data(const knot_pkt_t *pkt)
+{
+	const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER);
+	return answer->count >= 2;
+}
+
+/*! \brief Checks whether server responded with AXFR-style IXFR. */
+static bool ixfr_is_axfr(const knot_pkt_t *pkt)
+{
+	const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER);
+	return answer->rr[0].type == KNOT_RRTYPE_SOA &&
+	       answer->rr[1].type != KNOT_RRTYPE_SOA;
+}
+
 /*! \brief Cleans up data allocated by IXFR-in processing. */
 static void ixfrin_cleanup(struct answer_data *data)
 {
@@ -351,7 +382,7 @@ static int ixfrin_finalize(struct answer_data *adata)
 	zone_contents_t *new_contents;
 	int ret = apply_changesets(ixfr->zone, &ixfr->changesets, &new_contents);
 	if (ret != KNOT_EOK) {
-		IXFRIN_LOG(LOG_ERR, "failed to apply changes to zone (%s)",
+		IXFRIN_LOG(LOG_WARNING, "failed to apply changes to zone (%s)",
 		           knot_strerror(ret));
 		return ret;
 	}
@@ -359,7 +390,7 @@ static int ixfrin_finalize(struct answer_data *adata)
 	/* Write changes to journal. */
 	ret = zone_changes_store(ixfr->zone, &ixfr->changesets);
 	if (ret != KNOT_EOK) {
-		IXFRIN_LOG(LOG_ERR, "failed to write changes to journal (%s)",
+		IXFRIN_LOG(LOG_WARNING, "failed to write changes to journal (%s)",
 		           knot_strerror(ret));
 		updates_rollback(&ixfr->changesets);
 		update_free_zone(&new_contents);
@@ -563,7 +594,7 @@ static int process_ixfrin_packet(knot_pkt_t *pkt, struct answer_data *adata)
 
 		int ret = ixfrin_step(rr, ixfr);
 		if (ret != KNOT_EOK) {
-			IXFRIN_LOG(LOG_ERR, "failed (%s)", knot_strerror(ret));
+			IXFRIN_LOG(LOG_WARNING, "failed (%s)", knot_strerror(ret));
 			return NS_PROC_FAIL;
 		}
 
@@ -633,7 +664,7 @@ int ixfr_query(knot_pkt_t *pkt, struct query_data *qdata)
 	case KNOT_EOK:    /* Last response. */
 		gettimeofday(&now, NULL);
 		IXFROUT_LOG(LOG_INFO,
-	                    "finished, %.02f seconds, %u messages, %u bytes",
+		            "finished, %.02f seconds, %u messages, %u bytes",
 		            time_diff(&ixfr->proc.tstamp, &now) / 1000.0,
 		            ixfr->proc.npkts, ixfr->proc.nbytes);
 		ret = NS_PROC_DONE;
@@ -652,17 +683,27 @@ int ixfr_process_answer(knot_pkt_t *pkt, struct answer_data *adata)
 	if (pkt == NULL || adata == NULL) {
 		return NS_PROC_FAIL;
 	}
-
+	
+	if (!ixfr_enough_data(pkt)) {
+		return NS_PROC_FAIL;
+	}
+	
+	/* Check for AXFR-style IXFR. */
+	if (ixfr_is_axfr(pkt)) {
+		IXFRIN_LOG(LOG_NOTICE, "Fallback to AXFR");
+		return axfr_answer_process(pkt, adata);
+	}
+	
 	/* Check RCODE. */
 	uint8_t rcode = knot_wire_get_rcode(pkt->wire);
 	if (rcode != KNOT_RCODE_NOERROR) {
 		knot_lookup_table_t *lut = knot_lookup_by_id(knot_rcode_names, rcode);
 		if (lut != NULL) {
-			IXFRIN_LOG(LOG_ERR, "server responded with %s", lut->name);
+			IXFRIN_LOG(LOG_WARNING, "server responded with %s", lut->name);
 		}
 		return NS_PROC_FAIL;
 	}
-
+	
 	/* Initialize processing with first packet. */
 	if (adata->ext == NULL) {
 		NS_NEED_TSIG_SIGNED(&adata->param->tsig_ctx, 0);
@@ -675,7 +716,7 @@ int ixfr_process_answer(knot_pkt_t *pkt, struct answer_data *adata)
 		// First packet with IXFR, init context
 		int ret = ixfrin_answer_init(adata);
 		if (ret != KNOT_EOK) {
-			IXFRIN_LOG(LOG_ERR, "failed (%s)", knot_strerror(ret));
+			IXFRIN_LOG(LOG_WARNING, "failed (%s)", knot_strerror(ret));
 			return NS_PROC_FAIL;
 		}
 	} else {
-- 
GitLab