From 21f74de38dc4bf1c8acc8f45404eebb84303ed40 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Je=C5=BEek?= <lukas.jezek@nic.cz>
Date: Tue, 14 Apr 2020 10:33:38 +0200
Subject: [PATCH] modules/policy: use origin and domain name as binary data

---
 modules/policy/README.rst          |  2 +-
 modules/policy/policy.lua          | 27 +++++++++++++++++++--------
 modules/policy/policy.rpz.test.lua | 18 +++++++++++++-----
 modules/policy/policy.test.rpz     |  9 ++++++---
 tests/integration/deckard          |  2 +-
 5 files changed, 40 insertions(+), 18 deletions(-)

diff --git a/modules/policy/README.rst b/modules/policy/README.rst
index 031f0afe2..7d5f86903 100644
--- a/modules/policy/README.rst
+++ b/modules/policy/README.rst
@@ -152,7 +152,7 @@ Following actions stop the policy matching on the query, i.e. other rules are no
 
    .. code-block:: lua
 
-       -- this policy change IPv4 adress and TTL for `exmaple.com`
+       -- this policy changes IPv4 adress and TTL for `exmaple.com`
        policy.add(policy.suffix(policy.ANSWER({ [kres.type.A] = { ttl=300, rdata='\192\0\2\7' } }), { todname('example.com') }))
 
 More complex non-chain actions are described in their own chapters, namely:
diff --git a/modules/policy/policy.lua b/modules/policy/policy.lua
index 3448ce3dc..b9b90f5e8 100644
--- a/modules/policy/policy.lua
+++ b/modules/policy/policy.lua
@@ -378,7 +378,7 @@ end
 local function rpz_parse(action, path)
 	local rules = {}
 	local new_actions = {}
-	local origin = '.'
+	local origin = '\0'
 	local action_map = {
 		-- RPZ Policy Actions
 		['\0'] = action,
@@ -416,22 +416,33 @@ local function rpz_parse(action, path)
 		end
 		if not ok then break end
 
-		local name = ffi.string(parser.r_owner, parser.r_owner_length)
+		local full_name = ffi.gc(ffi.C.knot_dname_copy(parser.r_owner, nil), ffi.C.free)
 		local rdata = ffi.string(parser.r_data, parser.r_data_length)
+		ffi.C.knot_dname_to_lower(full_name)
 
 		if (parser.r_type == kres.type.SOA) then
-			-- parser return \0 if SOA use @ as owner
-			origin = (name == '\0') and origin or name
+			origin = ffi.gc(ffi.C.knot_dname_copy(full_name, nil), ffi.C.free)
 			goto continue
 		end
 
-		name = (name == origin) and name or name:gsub('%'..origin, '')
+		local prefix_labels = ffi.C.knot_dname_in_bailiwick(full_name, origin)
+		local name
+		if prefix_labels > 0 then
+			local bytes = 0
+			for _=1,prefix_labels do
+				bytes = bytes + 1 + full_name[bytes]
+			end
+			name = ffi.string(full_name, bytes)
+			name = name..'\0'
+		else
+			name = ffi.string(full_name, parser.r_owner_length)
+		end
 
 		if parser.r_type == kres.type.CNAME then
 			if action_map[rdata] then
 				rules[name] = action_map[rdata]
 			else
-				log('[poli] RPZ %s:%d: CNAME in RPZ is not supported', path, tonumber(parser.line_counter))
+				log('[poli] RPZ %s:%d: CNAME with custom target in RPZ is not supported', path, tonumber(parser.line_counter))
 			end
 		else
 			-- Warn when NYI
@@ -449,8 +460,8 @@ local function rpz_parse(action, path)
 		::continue::
 	end
 	collectgarbage()
-	for k, v in pairs(new_actions) do
-		rules[k] = policy.ANSWER(v, true)
+	for qname, rrsets in pairs(new_actions) do
+		rules[qname] = policy.ANSWER(rrsets, true)
 	end
 	return rules
 end
diff --git a/modules/policy/policy.rpz.test.lua b/modules/policy/policy.rpz.test.lua
index 4f219ca20..846b642b0 100644
--- a/modules/policy/policy.rpz.test.lua
+++ b/modules/policy/policy.rpz.test.lua
@@ -45,18 +45,26 @@ end
 local function test_rpz()
 	check_answer('"CNAME ." return NXDOMAIN',
 		'nxdomain.', kres.type.A, kres.rcode.NXDOMAIN)
-	check_answer('"CNAME *." return NXDOMAIN',
+	check_answer('"CNAME *." return NODATA',
 		'nodata.', kres.type.A, kres.rcode.NOERROR)
+	check_answer('"CNAME *. on wildcard" return NODATA',
+		'nodata.nxdomain.', kres.type.A, kres.rcode.NOERROR)
 	check_answer('"CNAME rpz-drop." be dropped',
 		'rpzdrop.', kres.type.A, kres.rcode.SERVFAIL)
 	check_answer('"CNAME rpz-passthru" return A rrset',
 		'rpzpassthru.', kres.type.A, kres.rcode.NOERROR, '127.0.0.9')
-	check_answer('"A 192.168.55.5" return local A rrset',
-		'rra.', kres.type.A, kres.rcode.NOERROR, '192.168.55.5')
-	check_answer('"A 192.168.66.6" with suffixed zone name in owner return local A rrset',
-		'rra-zonename-suffix.', kres.type.A, kres.rcode.NOERROR, '192.168.66.6')
+	check_answer('"A 192.168.5.5" return local A rrset',
+		'rra.', kres.type.A, kres.rcode.NOERROR, '192.168.5.5')
+	check_answer('"A 192.168.6.6" with suffixed zone name in owner return local A rrset',
+		'rra-zonename-suffix.', kres.type.A, kres.rcode.NOERROR, '192.168.6.6')
+	check_answer('"A 192.168.7.7" with suffixed zone name in owner return local A rrset',
+		'testdomain.rra.', kres.type.A, kres.rcode.NOERROR, '192.168.7.7')
 	check_answer('non existing AAAA on rra domain return NODATA',
 		'rra.', kres.type.AAAA, kres.rcode.NOERROR)
+	check_answer('"A 192.168.8.8" and domain with uppercase and lowercase letters',
+		'case.sensitive.', kres.type.A, kres.rcode.NOERROR, '192.168.8.8')
+	check_answer('"A 192.168.8.8" and domain with uppercase and lowercase letters',
+		'CASe.SENSItivE.', kres.type.A, kres.rcode.NOERROR, '192.168.8.8')
 end
 
 net.ipv4 = false
diff --git a/modules/policy/policy.test.rpz b/modules/policy/policy.test.rpz
index dd8fe3979..16c5c39a5 100644
--- a/modules/policy/policy.test.rpz
+++ b/modules/policy/policy.test.rpz
@@ -1,10 +1,13 @@
 $TTL 30
 testdomain.		SOA nonexistent.testdomain. testdomain. 1 12h 15m 3w 2h
-			NS  nonexistant.testdomain.
+			NS  nonexistent.testdomain.
 
 nxdomain.		CNAME	.
 nodata.			CNAME	*.
+*.nxdomain.		CNAME	*.
 rpzdrop.		CNAME	rpz-drop.
 rpzpassthru.		CNAME	rpz-passthru.
-rra.			A	192.168.55.5
-rra-zonename-suffix.testdomain.		A 	192.168.66.6
+rra.			A	192.168.5.5
+rra-zonename-suffix.testdomain.		A 	192.168.6.6
+testdomain.rra.testdomain.		A 	192.168.7.7
+CaSe.SeNSiTiVe.		A	192.168.8.8
diff --git a/tests/integration/deckard b/tests/integration/deckard
index a90e8deac..84d16f4bf 160000
--- a/tests/integration/deckard
+++ b/tests/integration/deckard
@@ -1 +1 @@
-Subproject commit a90e8deacafc3a66b6109e62986fb2a72ea57c49
+Subproject commit 84d16f4bf169e1365e6e2bc68efd44592a7e37a1
-- 
GitLab