diff --git a/modules/block/block.mk b/modules/block/block.mk
deleted file mode 100644
index e0acb7fe124c8464bce21fb20096209663a3926a..0000000000000000000000000000000000000000
--- a/modules/block/block.mk
+++ /dev/null
@@ -1,2 +0,0 @@
-block_SOURCES := block.lua aho-corasick.lua
-$(call make_lua_module,block)
diff --git a/modules/modules.mk b/modules/modules.mk
index de98b7b9e951d8fef2e962aed59a84a8427c1a89..7c628ea27d971bef839edbae20c3625023b6cba6 100644
--- a/modules/modules.mk
+++ b/modules/modules.mk
@@ -16,7 +16,7 @@ endif
 ifeq ($(HAS_lua),yes)
 modules_TARGETS += ketcd \
                    graphite \
-                   block \
+                   policy \
                    predict
 endif
 
diff --git a/modules/block/README.rst b/modules/policy/README.rst
similarity index 69%
rename from modules/block/README.rst
rename to modules/policy/README.rst
index 0802ba38a6fc14970dc77f14074bf45b879ab03b..a6eb146e592301b8ef7023559ff43727bea4e135 100644
--- a/modules/block/README.rst
+++ b/modules/policy/README.rst
@@ -1,11 +1,12 @@
-.. _mod-block:
+.. _mod-policy:
 
-Query blocking
+Query policies 
 --------------
 
-This module can block queries (and subrequests) based on user-defined policies.
+This module can block, rewrite, or alter queries based on user-defined policies.
 By default, it blocks queries to reverse lookups in private subnets as per :rfc:`1918`, :rfc:`5735` and :rfc:`5737`.
 You can however extend it to deflect `Slow drip DNS attacks <https://blog.secure64.com/?p=377>`_ for example, or gray-list resolution of misbehaving zones.
+It supports a subset of the ISC RPZ_ format.
 
 There are two policies implemented:
 
@@ -15,11 +16,12 @@ There are two policies implemented:
   - applies action if QNAME suffix matches given list of suffixes (useful for "is domain in zone" rules),
   uses `Aho-Corasick`_ string matching algorithm implemented by `@jgrahamc`_ (CloudFlare, Inc.) (BSD 3-clause)
 
-There are three action:
+There are several defined actions:
 
 * ``PASS`` - let the query pass through
 * ``DENY`` - return NXDOMAIN answer
 * ``DROP`` - terminate query resolution, returns SERVFAIL to requestor
+* ``TC`` - set TC=1 if the request came through UDP, forcing client to retry with TCP
 
 .. note:: The module (and ``kres``) treats domain names as wire, not textual representation. So each label in name is prefixed with its length, e.g. "example.com" equals to "\7example\3com".
 
@@ -28,54 +30,55 @@ Example configuration
 
 .. code-block:: lua
 
-	-- Load default block rules
-	modules = { 'block' }
+	-- Load default policies
+	modules = { 'policy' }
 	-- Whitelist 'www[0-9].badboy.cz'
-	block:add(block.pattern(block.PASS, '\4www[0-9]\6badboy\2cz'))
+	policy:add(policy.pattern(policy.PASS, '\4www[0-9]\6badboy\2cz'))
 	-- Block all names below badboy.cz
-	block:add(block.suffix(block.DENY, {'\6badboy\2cz'}))
+	policy:add(policy.suffix(policy.DENY, {'\6badboy\2cz'}))
 	-- Custom rule
-	block:add(function (req, query)
+	policy:add(function (req, query)
 		if query:qname():find('%d.%d.%d.224\7in-addr\4arpa') then
-			return block.DENY
+			return policy.DENY
 		end
 	end)
 	-- Disallow ANY queries
-	block:add(function (req, query)
+	policy:add(function (req, query)
 		if query.type == kres.type.ANY then
-			return block.DROP
+			return policy.DROP
 		end
 	end)
 
 Properties
 ^^^^^^^^^^
 
-.. envvar:: block.PASS (number)
-.. envvar:: block.DENY (number)
-.. envvar:: block.DROP (number)
+.. envvar:: policy.PASS (number)
+.. envvar:: policy.DENY (number)
+.. envvar:: policy.DROP (number)
+.. envvar:: policy.TC   (number)
 
-.. function:: block:add(rule)
+.. function:: policy:add(rule)
 
-  :param rule: added rule, i.e. ``block.pattern(block.DENY, '[0-9]+\2cz')``
+  :param rule: added rule, i.e. ``policy.pattern(policy.DENY, '[0-9]+\2cz')``
   :param pattern: regular expression
   
   Policy to block queries based on the QNAME regex matching.
 
-.. function:: block.pattern(action, pattern)
+.. function:: policy.pattern(action, pattern)
 
   :param action: action if the pattern matches QNAME
   :param pattern: regular expression
   
   Policy to block queries based on the QNAME regex matching.
 
-.. function:: block.suffix(action, suffix_table)
+.. function:: policy.suffix(action, suffix_table)
 
   :param action: action if the pattern matches QNAME
   :param suffix_table: table of valid suffixes
   
   Policy to block queries based on the QNAME suffix match.
 
-.. function:: block.suffix_common(action, suffix_table[, common_suffix])
+.. function:: policy.suffix_common(action, suffix_table[, common_suffix])
 
   :param action: action if the pattern matches QNAME
   :param suffix_table: table of valid suffixes
@@ -86,4 +89,4 @@ Properties
 
 .. _`Aho-Corasick`: https://en.wikipedia.org/wiki/Aho%E2%80%93Corasick_string_matching_algorithm
 .. _`@jgrahamc`: https://github.com/jgrahamc/aho-corasick-lua
-
+.. _RPZ: https://dnsrpz.info/
diff --git a/modules/block/aho-corasick.lua b/modules/policy/aho-corasick.lua
similarity index 100%
rename from modules/block/aho-corasick.lua
rename to modules/policy/aho-corasick.lua
diff --git a/modules/block/block.lua b/modules/policy/policy.lua
similarity index 66%
rename from modules/block/block.lua
rename to modules/policy/policy.lua
index 399b1af4d12746c8092a873b3062ab6ebf62f0c6..b4b71231aa6cbaeb0c69ff732eb23c108fa4b900 100644
--- a/modules/block/block.lua
+++ b/modules/policy/policy.lua
@@ -1,13 +1,13 @@
 local kres = require('kres')
-local block = {
+local policy = {
 	-- Policies
-	PASS = 1, DENY = 2, DROP = 3,
+	PASS = 1, DENY = 2, DROP = 3, TC = 4,
 	-- Special values
 	ANY = 0,
 }
 
--- @function Block requests which QNAME matches given zone list (i.e. suffix match)
-function block.suffix(action, zone_list)
+-- @function Requests which QNAME matches given zone list (i.e. suffix match)
+function policy.suffix(action, zone_list)
 	local AC = require('aho-corasick')
 	local tree = AC.build(zone_list)
 	return function(req, query)
@@ -20,7 +20,7 @@ function block.suffix(action, zone_list)
 end
 
 -- @function Check for common suffix first, then suffix match (specialized version of suffix match)
-function block.suffix_common(action, suffix_list, common_suffix)
+function policy.suffix_common(action, suffix_list, common_suffix)
 	local common_len = string.len(common_suffix)
 	local suffix_count = #suffix_list
 	return function(req, query)
@@ -40,8 +40,8 @@ function block.suffix_common(action, suffix_list, common_suffix)
 	end
 end
 
--- @function Block QNAME pattern
-function block.pattern(action, pattern)
+-- @function policy QNAME pattern
+function policy.pattern(action, pattern)
 	return function(req, query)
 		if string.find(query:name(), pattern) then
 			return action
@@ -50,44 +50,51 @@ function block.pattern(action, pattern)
 	end
 end
 
--- @function Evaluate packet in given rules to determine block action
-function block.evaluate(block, req, query)
-	for i = 1, #block.rules do
-		local action = block.rules[i](req, query)
+-- @function Evaluate packet in given rules to determine policy action
+function policy.evaluate(policy, req, query)
+	for i = 1, #policy.rules do
+		local action = policy.rules[i](req, query)
 		if action ~= nil then
 			return action
 		end
 	end
-	return block.PASS
+	return policy.PASS
 end
 
--- @function Block layer implementation
-block.layer = {
+-- @function policy layer implementation
+policy.layer = {
 	begin = function(state, req)
 		req = kres.request_t(req)
-		local action = block:evaluate(req, req:current())
-		if action == block.DENY then
+		local action = policy:evaluate(req, req:current())
+		if action == policy.DENY then
 			-- Write authority information
 			local answer = req.answer
 			answer:rcode(kres.rcode.NXDOMAIN)
 			answer:begin(kres.section.AUTHORITY)
-			answer:put('\5block', 900, answer:qclass(), kres.type.SOA,
-				'\5block\0\0\0\0\0\0\0\0\14\16\0\0\3\132\0\9\58\128\0\0\3\132')
+			answer:put('\7blocked', 900, answer:qclass(), kres.type.SOA,
+				'\7blocked\0\0\0\0\0\0\0\0\14\16\0\0\3\132\0\9\58\128\0\0\3\132')
 			return kres.DONE
-		elseif action == block.DROP then
+		elseif action == policy.DROP then
 			return kres.FAIL
+		elseif action == policy.TC then
+			local answer = req.answer
+			print(answer.max_size)
+			if answer.max_size ~= 65535 then
+				answer:tc(1) -- ^ Only UDP queries
+				return kres.DONE
+			end
 		end
 		return state
 	end
 }
 
--- @function Add rule to block list
-function block.add(block, rule)
-	return table.insert(block.rules, rule)
+-- @function Add rule to policy list
+function policy.add(policy, rule)
+	return table.insert(policy.rules, rule)
 end
 
 -- @function Convert list of string names to domain names
-function block.to_domains(names)
+function policy.to_domains(names)
 	for i, v in ipairs(names) do
 		names[i] = v:gsub('([^.]*%.)', function (x)
 			return string.format('%s%s', string.char(x:len()-1), x:sub(1,-2))
@@ -133,9 +140,9 @@ local private_zones = {
 	'b.e.f.ip6.arpa.',
 	'8.b.d.0.1.0.0.2.ip6.arpa',
 }
-block.to_domains(private_zones)
+policy.to_domains(private_zones)
 
 -- @var Default rules
-block.rules = { block.suffix_common(block.DENY, private_zones, '\4arpa') }
+policy.rules = { policy.suffix_common(policy.DENY, private_zones, '\4arpa') }
 
-return block
+return policy
diff --git a/modules/policy/policy.mk b/modules/policy/policy.mk
new file mode 100644
index 0000000000000000000000000000000000000000..f859ac9701d71dffc607e99c963e64a29d39d9d2
--- /dev/null
+++ b/modules/policy/policy.mk
@@ -0,0 +1,2 @@
+policy_SOURCES := policy.lua aho-corasick.lua
+$(call make_lua_module,policy)