From 1761a1343d01ec4f889ea9a2ac48583ceb4eacdd Mon Sep 17 00:00:00 2001
From: Lubos Slovak <lubos.slovak@nic.cz>
Date: Tue, 16 Sep 2014 18:50:29 +0200
Subject: [PATCH] RDATA lowercase: Converting in packet parsing + zone load.

Converting to lowercase when saving to zone proved insufficient,
because case-insensitive comparison of RDATA would be needed in some
scenarios - e.g. in IXFR (SOA replacement) and DDNS processing.

Moved the conversion to packet parsing - all dnames in RDATA
are converted to lowercase when parsed from wire. RDATA parsed
from zone file are converted there (in scanner_process()). Later
the conversion may be moved to a lower level of parsing (and with
some option to turn it on/off).

QNAME is unaffected by this change.
---
 Knot.files                      |  1 +
 src/knot/zone/contents.c        | 83 ++-------------------------------
 src/knot/zone/zonefile.c        | 42 ++++++++++++++++-
 src/libknot/packet/rrset-wire.c | 31 ++++++------
 src/libknot/rrtype/naptr.h      | 56 ++++++++++++++++++++++
 5 files changed, 116 insertions(+), 97 deletions(-)
 create mode 100644 src/libknot/rrtype/naptr.h

diff --git a/Knot.files b/Knot.files
index 57f992eb5..a973e89a4 100644
--- a/Knot.files
+++ b/Knot.files
@@ -347,3 +347,4 @@ tests/zone_events.c
 tests/zone_update.c
 tests/zonedb.c
 tests/ztree.c
+src/libknot/rrtype/naptr.h
diff --git a/src/knot/zone/contents.c b/src/knot/zone/contents.c
index f09b018a1..2aaae40c8 100644
--- a/src/knot/zone/contents.c
+++ b/src/knot/zone/contents.c
@@ -733,87 +733,14 @@ static bool rrset_is_nsec3rel(const knot_rrset_t *rr)
 	            == KNOT_RRTYPE_NSEC3));
 }
 
-static size_t naptr_header_size(uint8_t *naptr, uint8_t *maxp)
-{
-    /*! \todo Copy-pasted from rrset.c. Unify. */
-
-    size_t size = 0;
-
-    /* Fixed fields size (order, preference) */
-    size += 2 * sizeof(uint16_t);
-
-    /* Variable fields size (flags, services, regexp) */
-    for (int i = 0; i < 3; i++) {
-        uint8_t *len_ptr = naptr + size;
-        assert(len_ptr < maxp);
-        size += 1 + *len_ptr;
-    }
-
-    return size;
-}
-
-static void convert_to_lowercase(knot_rrset_t *rrset)
-{
-    int ret = knot_dname_to_lower(rrset->owner);
-    assert(ret == KNOT_EOK);
-
-    const rdata_descriptor_t *desc = knot_get_rdata_descriptor(rrset->type);
-
-    for (int j = 0; j < rrset->rrs.rr_count; ++j) {
-        knot_rdata_t *rdata = knot_rdataset_at(&rrset->rrs, j);
-
-        uint16_t rdlen = knot_rdata_rdlen(rdata);
-        uint8_t *pos = knot_rdata_data(rdata);
-
-        for (int i = 0; desc->block_types[i] != KNOT_RDATA_WF_END; ++i) {
-            int type = desc->block_types[i];
-            switch (type) {
-            case KNOT_RDATA_WF_COMPRESSIBLE_DNAME:
-            case KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME:
-            case KNOT_RDATA_WF_FIXED_DNAME:
-                ret = knot_dname_to_lower(pos);
-                assert(ret == KNOT_EOK);
-                pos += knot_dname_size(pos);
-                break;
-            case KNOT_RDATA_WF_NAPTR_HEADER:
-                pos += naptr_header_size(pos, rdata + rdlen);
-                break;
-            case KNOT_RDATA_WF_REMAINDER:
-                break;
-            default:
-                /* Fixed size block */
-                assert(type > 0);
-                pos += type;
-            }
-        }
-    }
-}
-
-static knot_rrset_t *rrset_to_lowercase(const knot_rrset_t *rrset)
-{
-    knot_rrset_t *rr_l = knot_rrset_copy(rrset, NULL);
-    if (rr_l != NULL) {
-        convert_to_lowercase(rr_l);
-    }
-
-    return rr_l;
-}
-
 int zone_contents_add_rr(zone_contents_t *z, const knot_rrset_t *rr,
                          zone_node_t **n)
 {
-    /* Convert all dnames in RRSet to lowercase. */
-    knot_rrset_t *rr_lc = rrset_to_lowercase(rr);
-
-    int ret;
-    if (rr_lc != NULL) {
-        ret = insert_rr(z, rr_lc, n, rrset_is_nsec3rel(rr));
-        knot_rrset_free(&rr_lc, NULL);
-    } else {
-        ret = KNOT_ERROR;
-    }
-
-    return ret;
+	if (z == NULL || rr == NULL) {
+		return KNOT_EINVAL;
+	}
+
+	return insert_rr(z, rr, n, rrset_is_nsec3rel(rr));
 }
 
 /*----------------------------------------------------------------------------*/
diff --git a/src/knot/zone/zonefile.c b/src/knot/zone/zonefile.c
index 70ee8e988..1275b1905 100644
--- a/src/knot/zone/zonefile.c
+++ b/src/knot/zone/zonefile.c
@@ -37,6 +37,7 @@
 #include "knot/zone/zonefile.h"
 #include "libknot/rdata.h"
 #include "knot/zone/zone-dump.h"
+#include "libknot/rrtype/naptr.h"
 
 #define ERROR(zone, fmt...) log_zone_error(zone, "zone loader, " fmt)
 #define WARNING(zone, fmt...) log_zone_warning(zone, "zone loader, " fmt)
@@ -144,6 +145,43 @@ int zcreator_step(zcreator_t *zc, const knot_rrset_t *rr)
 	return sem_fatal_error ? KNOT_ESEMCHECK : KNOT_EOK;
 }
 
+static void convert_to_lowercase(knot_rrset_t *rrset)
+{
+	int ret = knot_dname_to_lower(rrset->owner);
+	assert(ret == KNOT_EOK);
+
+	const rdata_descriptor_t *desc = knot_get_rdata_descriptor(rrset->type);
+
+	for (int j = 0; j < rrset->rrs.rr_count; ++j) {
+		knot_rdata_t *rdata = knot_rdataset_at(&rrset->rrs, j);
+
+		uint16_t rdlen = knot_rdata_rdlen(rdata);
+		uint8_t *pos = knot_rdata_data(rdata);
+
+		for (int i = 0; desc->block_types[i] != KNOT_RDATA_WF_END; ++i) {
+		    int type = desc->block_types[i];
+		    switch (type) {
+		    case KNOT_RDATA_WF_COMPRESSIBLE_DNAME:
+		    case KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME:
+		    case KNOT_RDATA_WF_FIXED_DNAME:
+			ret = knot_dname_to_lower(pos);
+			assert(ret == KNOT_EOK);
+			pos += knot_dname_size(pos);
+			break;
+		    case KNOT_RDATA_WF_NAPTR_HEADER:
+			pos += naptr_header_size(pos, rdata + rdlen);
+			break;
+		    case KNOT_RDATA_WF_REMAINDER:
+			break;
+		    default:
+			/* Fixed size block */
+			assert(type > 0);
+			pos += type;
+		    }
+		}
+	}
+}
+
 /*! \brief Creates RR from parser input, passes it to handling function. */
 static void scanner_process(zs_scanner_t *scanner)
 {
@@ -158,7 +196,6 @@ static void scanner_process(zs_scanner_t *scanner)
 		zc->ret = KNOT_ENOMEM;
 		return;
 	}
-	knot_dname_to_lower(owner);
 
 	knot_rrset_t rr;
 	knot_rrset_init(&rr, owner, scanner->r_type, scanner->r_class);
@@ -174,6 +211,9 @@ static void scanner_process(zs_scanner_t *scanner)
 		return;
 	}
 
+	/* Convert RDATA dnames to lowercase before adding to zone. */
+	convert_to_lowercase(&rr);
+
 	zc->ret = zcreator_step(zc, &rr);
 	knot_dname_free(&owner, NULL);
 	knot_rdataset_clear(&rr.rrs, NULL);
diff --git a/src/libknot/packet/rrset-wire.c b/src/libknot/packet/rrset-wire.c
index 594f55eeb..39af97af8 100644
--- a/src/libknot/packet/rrset-wire.c
+++ b/src/libknot/packet/rrset-wire.c
@@ -29,6 +29,7 @@
 #include "libknot/packet/rrset-wire.h"
 #include "libknot/packet/wire.h"
 #include "libknot/rrset.h"
+#include "libknot/rrtype/naptr.h"
 
 /*!
  * \brief Get maximal size of a domain name in a wire with given capacity.
@@ -114,26 +115,14 @@ static int write_rdata_naptr_header(const uint8_t **src, size_t *src_avail,
 	assert(dst && *dst);
 	assert(dst_avail);
 
-	size_t size = 0;
+	int ret = naptr_header_size(*src, *src + *src_avail);
 
-	/* Fixed fields size (order, preference) */
-
-	size += 2 * sizeof(uint16_t);
-
-	/* Variable fields size (flags, services, regexp) */
-
-	for (int i = 0; i < 3; i++) {
-		const uint8_t *len_ptr = *src + size;
-		if (len_ptr >= *src + *src_avail) {
-			return KNOT_EMALF;
-		}
-
-		size += 1 + *len_ptr;
+	if (ret < 0) {
+		return ret;
 	}
 
 	/* Copy the data */
-
-	return write_rdata_fixed(src, src_avail, dst, dst_avail, size);
+	return write_rdata_fixed(src, src_avail, dst, dst_avail, ret);
 }
 
 /*!
@@ -522,7 +511,7 @@ static int decompress_rdata_dname(const uint8_t **src, size_t *src_avail,
 	assert(dst && *dst);
 	assert(dst_avail);
 	assert(dname_cfg);
-	UNUSED(flags);
+	UNUSED(dname_type);
 
 	int compr_size = knot_dname_wire_check(*src, *src + *src_avail, dname_cfg->pkt_wire);
 	if (compr_size <= 0) {
@@ -533,6 +522,12 @@ static int decompress_rdata_dname(const uint8_t **src, size_t *src_avail,
 	if (decompr_size <= 0) {
 		return decompr_size;
 	}
+
+	/* Convert to lowercase. */
+	if (flags & KNOT_RRSET_WIRE_CANONICAL) {
+		int ret = knot_dname_to_lower(*dst);
+		assert(ret == KNOT_EOK);
+	}
 	
 	/* Update buffers */
 	*dst += decompr_size;
@@ -597,7 +592,7 @@ static int parse_rdata(const uint8_t *pkt_wire, size_t *pos, size_t pkt_size,
 	};
 
 	int ret = rdata_traverse(&src, &src_avail, &dst, &dst_avail,
-	                         desc, &dname_cfg, KNOT_RRSET_WIRE_NONE);
+				 desc, &dname_cfg, KNOT_RRSET_WIRE_CANONICAL);
 	if (ret != KNOT_EOK) {
 		return ret;
 	}
diff --git a/src/libknot/rrtype/naptr.h b/src/libknot/rrtype/naptr.h
new file mode 100644
index 000000000..215ce4792
--- /dev/null
+++ b/src/libknot/rrtype/naptr.h
@@ -0,0 +1,56 @@
+/*  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/>.
+ */
+/*!
+ * \file naptr.h
+ *
+ * \author Lubos Slovak <lubos.slovak@nic.cz>
+ *
+ * \brief Functions for manipulation of NAPTR RDATA.
+ *
+ * \addtogroup libknot
+ * @{
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "libknot/errcode.h"
+
+static inline int naptr_header_size(const uint8_t *naptr, const uint8_t *maxp)
+{
+	size_t size = 0;
+
+	/* Fixed fields size (order, preference) */
+	size += 2 * sizeof(uint16_t);
+
+	/* Variable fields size (flags, services, regexp) */
+	for (int i = 0; i < 3; i++) {
+		uint8_t *len_ptr = naptr + size;
+		printf("Checking len_ptr = %p, maxp = %p\n",
+		       len_ptr, maxp);
+		if (len_ptr >= maxp) {
+			return KNOT_EMALF;
+		}
+		size += 1 + *len_ptr;
+	}
+
+	return size;
+}
+
+/*! @} */
-- 
GitLab