commit 1be69820c7a7ddfe558ecd21f88ad79d2b2a2a37
Author: Vladimír Čunát <vladimir.cunat@nic.cz>
Date:   Fri Nov 29 16:15:10 2019 +0100

    iterate: fix limit on CNAME chain length
    
    The accounting was just broken and overly messy anyway.

diff --git a/daemon/lua/kres-gen.lua b/daemon/lua/kres-gen.lua
index e59294bb..2ed35cb1 100644
--- a/daemon/lua/kres-gen.lua
+++ b/daemon/lua/kres-gen.lua
@@ -280,6 +280,7 @@ struct kr_query {
 	struct timeval timestamp;
 	struct kr_zonecut zone_cut;
 	struct kr_layer_pickle *deferred;
+	int8_t cname_depth;
 	struct kr_query *cname_parent;
 	struct kr_request *request;
 	kr_stale_cb stale_cb;
diff --git a/lib/layer/iterate.c b/lib/layer/iterate.c
index 069b34f0..9a78b23d 100644
--- a/lib/layer/iterate.c
+++ b/lib/layer/iterate.c
@@ -501,10 +501,11 @@ static int unroll_cname(knot_pkt_t *pkt, struct kr_request *req, bool referral,
 	const knot_pktsection_t *an = knot_pkt_section(pkt, KNOT_ANSWER);
 	const knot_dname_t *cname = NULL;
 	const knot_dname_t *pending_cname = query->sname;
-	unsigned cname_chain_len = 0;
 	bool is_final = (query->parent == NULL);
-	uint32_t iter_count = 0;
 	bool strict_mode = (query->flags.STRICT);
+
+	query->cname_depth = query->cname_parent ? query->cname_parent->cname_depth : 1;
+
 	do {
 		/* CNAME was found at previous iteration, but records may not follow the correct order.
 		 * Try to find records for pending_cname owner from section start. */
@@ -562,21 +563,22 @@ static int unroll_cname(knot_pkt_t *pkt, struct kr_request *req, bool referral,
 			if ((query->stype == KNOT_RRTYPE_CNAME) || (rr->type != KNOT_RRTYPE_CNAME)) {
 				continue;
 			}
-			cname_chain_len += 1;
 			pending_cname = knot_cname_name(rr->rrs.rdata);
 			if (!pending_cname) {
 				break;
 			}
-			if (cname_chain_len > an->count || cname_chain_len > KR_CNAME_CHAIN_LIMIT) {
-				VERBOSE_MSG("<= too long cname chain\n");
-				return KR_STATE_FAIL;
-			}
 			/* Don't use pending_cname immediately.
 			 * There are can be records for "old" cname. */
 		}
 		if (!pending_cname) {
 			break;
 		}
+		if (++(query->cname_depth) > KR_CNAME_CHAIN_LIMIT) {
+			VERBOSE_MSG("<= cname chain longer than %d\n",
+					(int)KR_CNAME_CHAIN_LIMIT);
+			return KR_STATE_FAIL;
+		}
+
 		if (knot_dname_is_equal(cname, pending_cname)) {
 			VERBOSE_MSG("<= cname chain loop\n");
 			return KR_STATE_FAIL;
@@ -605,11 +607,7 @@ static int unroll_cname(knot_pkt_t *pkt, struct kr_request *req, bool referral,
 			cname = pending_cname;
 			break;
 		}
-	} while (++iter_count < KR_CNAME_CHAIN_LIMIT);
-	if (iter_count >= KR_CNAME_CHAIN_LIMIT) {
-		VERBOSE_MSG("<= too long cname chain\n");
-		return KR_STATE_FAIL;
-	}
+	} while (true);
 	*cname_ret = cname;
 	return kr_ok();
 }
diff --git a/lib/rplan.h b/lib/rplan.h
index 15ca5633..0f9377dd 100644
--- a/lib/rplan.h
+++ b/lib/rplan.h
@@ -104,6 +104,10 @@ struct kr_query {
 	struct timeval timestamp; /**< Real time for TTL+DNSSEC checks (.tv_sec only). */
 	struct kr_zonecut zone_cut;
 	struct kr_layer_pickle *deferred;
+
+	/** Current xNAME depth, set by iterator.  0 = uninitialized, 1 = no CNAME, ...
+	 * See also KR_CNAME_CHAIN_LIMIT. */
+	int8_t cname_depth;
 	/** Pointer to the query that originated this one because of following a CNAME (or NULL). */
 	struct kr_query *cname_parent;
 	struct kr_request *request; /**< Parent resolution request. */

