summaryrefslogtreecommitdiffstats
path: root/modules/rebinding
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 15:26:00 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 15:26:00 +0000
commit830407e88f9d40d954356c3754f2647f91d5c06a (patch)
treed6a0ece6feea91f3c656166dbaa884ef8a29740e /modules/rebinding
parentInitial commit. (diff)
downloadknot-resolver-upstream.tar.xz
knot-resolver-upstream.zip
Adding upstream version 5.6.0.upstream/5.6.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'modules/rebinding')
-rw-r--r--modules/rebinding/.packaging/test.config4
-rw-r--r--modules/rebinding/README.rst29
-rw-r--r--modules/rebinding/rebinding.lua115
-rw-r--r--modules/rebinding/test.integr/deckard.yaml12
-rw-r--r--modules/rebinding/test.integr/kresd_config.j259
-rw-r--r--modules/rebinding/test.integr/module_rebinding.rpl834
6 files changed, 1053 insertions, 0 deletions
diff --git a/modules/rebinding/.packaging/test.config b/modules/rebinding/.packaging/test.config
new file mode 100644
index 0000000..0a84b88
--- /dev/null
+++ b/modules/rebinding/.packaging/test.config
@@ -0,0 +1,4 @@
+-- SPDX-License-Identifier: GPL-3.0-or-later
+modules.load('rebinding')
+assert(rebinding)
+quit()
diff --git a/modules/rebinding/README.rst b/modules/rebinding/README.rst
new file mode 100644
index 0000000..222d1da
--- /dev/null
+++ b/modules/rebinding/README.rst
@@ -0,0 +1,29 @@
+.. SPDX-License-Identifier: GPL-3.0-or-later
+
+.. _mod-rebinding:
+
+Rebinding protection
+====================
+
+This module provides protection from `DNS Rebinding attack`_ by blocking
+answers which contain IPv4_ or IPv6_ addresses for private use
+(or some other special-use addresses).
+
+To enable this module insert following line into your configuration file:
+
+.. code-block:: lua
+
+ modules.load('rebinding < iterate')
+
+Please note that this module does not offer stable configuration interface
+yet. For this reason it is suitable mainly for public resolver operators
+who do not need to whitelist certain subnets.
+
+.. warning:: DNS Blacklists (`RFC 5782`_) often use `127.0.0.0/8` to blacklist
+ a domain. Using the rebinding module prevents DNSBL from functioning
+ properly.
+
+.. _`DNS Rebinding attack`: https://en.wikipedia.org/wiki/DNS_rebinding
+.. _IPv4: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
+.. _IPv6: https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
+.. _`RFC 5782`: https://tools.ietf.org/html/rfc5782#section-2.1
diff --git a/modules/rebinding/rebinding.lua b/modules/rebinding/rebinding.lua
new file mode 100644
index 0000000..d5d6e74
--- /dev/null
+++ b/modules/rebinding/rebinding.lua
@@ -0,0 +1,115 @@
+-- SPDX-License-Identifier: GPL-3.0-or-later
+local ffi = require('ffi')
+
+-- Protection from DNS rebinding attacks
+local kres = require('kres')
+local renumber = require('kres_modules.renumber')
+local policy = require('kres_modules.policy')
+
+local M = {}
+M.layer = {}
+M.blacklist = {
+ -- https://www.iana.org/assignments/iana-ipv4-special-registry
+ -- + IPv4-to-IPv6 mapping
+ renumber.prefix('0.0.0.0/8', '0.0.0.0'),
+ renumber.prefix('::ffff:0.0.0.0/104', '::'),
+ renumber.prefix('10.0.0.0/8', '0.0.0.0'),
+ renumber.prefix('::ffff:10.0.0.0/104', '::'),
+ renumber.prefix('100.64.0.0/10', '0.0.0.0'),
+ renumber.prefix('::ffff:100.64.0.0/106', '::'),
+ renumber.prefix('127.0.0.0/8', '0.0.0.0'),
+ renumber.prefix('::ffff:127.0.0.0/104', '::'),
+ renumber.prefix('169.254.0.0/16', '0.0.0.0'),
+ renumber.prefix('::ffff:169.254.0.0/112', '::'),
+ renumber.prefix('172.16.0.0/12', '0.0.0.0'),
+ renumber.prefix('::ffff:172.16.0.0/108', '::'),
+ renumber.prefix('192.168.0.0/16', '0.0.0.0'),
+ renumber.prefix('::ffff:192.168.0.0/112', '::'),
+ -- https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
+ renumber.prefix('::/128', '::'),
+ renumber.prefix('::1/128', '::'),
+ renumber.prefix('fc00::/7', '::'),
+ renumber.prefix('fe80::/10', '::'),
+} -- second parameter for renumber module is ignored except for being v4 or v6
+
+local function is_rr_blacklisted(rr)
+ for i = 1, #M.blacklist do
+ local prefix = M.blacklist[i]
+ -- Match record type to address family and record address to given subnet
+ if renumber.match_subnet(prefix[1], prefix[2], prefix[4], rr) then
+ return true
+ end
+ end
+ return false
+end
+
+local function check_section(pkt, section)
+ local records = pkt:section(section)
+ local count = #records
+ if count == 0 then
+ return nil end
+ for i = 1, count do
+ local rr = records[i]
+ if rr.type == kres.type.A or rr.type == kres.type.AAAA then
+ local result = is_rr_blacklisted(rr)
+ if result then
+ return rr end
+ end
+ end
+end
+
+local function check_pkt(pkt)
+ for _, section in ipairs({kres.section.ANSWER,
+ kres.section.AUTHORITY,
+ kres.section.ADDITIONAL}) do
+ local bad_rr = check_section(pkt, section)
+ if bad_rr then
+ return bad_rr
+ end
+ end
+end
+
+local function refuse(req)
+ policy.REFUSE(nil, req)
+ local pkt = req:ensure_answer()
+ if pkt == nil then return nil end
+ pkt:aa(false)
+ pkt:begin(kres.section.ADDITIONAL)
+
+ local msg = 'blocked by DNS rebinding protection'
+ pkt:put('\11explanation\7invalid\0', 10800, pkt:qclass(), kres.type.TXT,
+ string.char(#msg) .. msg)
+ return kres.DONE
+end
+
+-- act on DNS queries which were not answered from cache
+function M.layer.consume(state, req, pkt)
+ if state == kres.FAIL then
+ return state end
+
+ local qry = req:current()
+ if qry.flags.CACHED or qry.flags.ALLOW_LOCAL then -- do not slow down cached queries
+ return state end
+
+ local bad_rr = check_pkt(pkt)
+ if not bad_rr then
+ return state end
+
+ qry.flags.RESOLVED = 1 -- stop iteration
+ qry.flags.CACHED = 1 -- do not cache
+
+ --[[ In case we're in a sub-query, we do not touch the final req answer.
+ Only this sub-query will get finished without a result - there we
+ rely on the iterator reacting to flags.RESOLVED
+ Typical example: NS address resolution -> only this NS won't be used
+ but others may still be OK (or we SERVFAIL due to no NS being usable).
+ --]]
+ if qry.parent == nil then
+ state = refuse(req)
+ end
+ log_qry(qry, ffi.C.LOG_GRP_REBIND,
+ 'blocking blacklisted IP in RR \'%s\'\n', kres.rr2str(bad_rr))
+ return state
+end
+
+return M
diff --git a/modules/rebinding/test.integr/deckard.yaml b/modules/rebinding/test.integr/deckard.yaml
new file mode 100644
index 0000000..9b1793b
--- /dev/null
+++ b/modules/rebinding/test.integr/deckard.yaml
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-3.0-or-later
+programs:
+- name: kresd
+ binary: kresd
+ additional:
+ - --noninteractive
+ templates:
+ - modules/rebinding/test.integr/kresd_config.j2
+ - tests/integration/hints_zone.j2
+ configs:
+ - config
+ - hints
diff --git a/modules/rebinding/test.integr/kresd_config.j2 b/modules/rebinding/test.integr/kresd_config.j2
new file mode 100644
index 0000000..aed3551
--- /dev/null
+++ b/modules/rebinding/test.integr/kresd_config.j2
@@ -0,0 +1,59 @@
+-- SPDX-License-Identifier: GPL-3.0-or-later
+{% raw %}
+-- make sure DNSSEC is turned off for tests
+trust_anchors.remove('.')
+
+-- Disable RFC5011 TA update
+if ta_update then
+ modules.unload('ta_update')
+end
+
+-- Disable RFC8145 signaling, scenario doesn't provide expected answers
+if ta_signal_query then
+ modules.unload('ta_signal_query')
+end
+
+-- Disable RFC8109 priming, scenario doesn't provide expected answers
+if priming then
+ modules.unload('priming')
+end
+
+-- Disable this module because it make one priming query
+if detect_time_skew then
+ modules.unload('detect_time_skew')
+end
+
+modules.load('rebinding < iterate')
+
+_hint_root_file('hints')
+cache.size = 2*MB
+log_level('debug')
+net.ipv6 = false
+{% endraw %}
+
+net = { '{{SELF_ADDR}}' }
+
+
+{% if QMIN == "false" %}
+option('NO_MINIMIZE', true)
+{% else %}
+option('NO_MINIMIZE', false)
+{% endif %}
+
+
+-- Self-checks on globals
+assert(help() ~= nil)
+assert(worker.id ~= nil)
+-- Self-checks on facilities
+assert(cache.count() == 0)
+assert(cache.stats() ~= nil)
+assert(cache.backends() ~= nil)
+assert(worker.stats() ~= nil)
+assert(net.interfaces() ~= nil)
+-- Self-checks on loaded stuff
+assert(net.list()[1].transport.ip == '{{SELF_ADDR}}')
+assert(#modules.list() > 0)
+-- Self-check timers
+ev = event.recurrent(1 * sec, function (ev) return 1 end)
+event.cancel(ev)
+ev = event.after(0, function (ev) return 1 end)
diff --git a/modules/rebinding/test.integr/module_rebinding.rpl b/modules/rebinding/test.integr/module_rebinding.rpl
new file mode 100644
index 0000000..1e344c5
--- /dev/null
+++ b/modules/rebinding/test.integr/module_rebinding.rpl
@@ -0,0 +1,834 @@
+; SPDX-License-Identifier: GPL-3.0-or-later
+; config options
+; target-fetch-policy: "0 0 0 0 0"
+; module-config: "iterator"
+; name: "."
+ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
+CONFIG_END
+
+SCENARIO_BEGIN Test protection from DNS rebinding
+
+; K.ROOT-SERVERS.NET.
+RANGE_BEGIN 0 1000
+ ADDRESS 193.0.14.129
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+. IN NS
+SECTION ANSWER
+. IN NS K.ROOT-SERVERS.NET.
+SECTION ADDITIONAL
+K.ROOT-SERVERS.NET. IN A 193.0.14.129
+ENTRY_END
+
+; net.
+ENTRY_BEGIN
+MATCH opcode qname
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+net. IN NS
+SECTION AUTHORITY
+. IN SOA . . 0 0 0 0 0
+ENTRY_END
+
+; root-servers.net.
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+root-servers.net. IN NS
+SECTION ANSWER
+root-servers.net. IN NS k.root-servers.net.
+SECTION ADDITIONAL
+k.root-servers.net. IN A 193.0.14.129
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qname
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+root-servers.net. IN A
+SECTION AUTHORITY
+root-servers.net. IN SOA . . 0 0 0 0 0
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+k.root-servers.net. IN A
+SECTION ANSWER
+k.root-servers.net. IN A 193.0.14.129
+SECTION ADDITIONAL
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+k.root-servers.net. IN AAAA
+SECTION AUTHORITY
+root-servers.net. IN SOA . . 0 0 0 0 0
+ENTRY_END
+
+; gtld-servers.net.
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+gtld-servers.net. IN NS
+SECTION ANSWER
+gtld-servers.net. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qname
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+gtld-servers.net. IN A
+SECTION AUTHORITY
+gtld-servers.net. IN SOA . . 0 0 0 0 0
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+a.gtld-servers.net. IN A
+SECTION ANSWER
+a.gtld-servers.net. IN A 192.5.6.30
+SECTION ADDITIONAL
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+a.gtld-servers.net. IN AAAA
+SECTION AUTHORITY
+gtld-servers.net. IN SOA . . 0 0 0 0 0
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+com. IN A
+SECTION AUTHORITY
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+RANGE_END
+
+; a.gtld-servers.net.
+RANGE_BEGIN 0 1000
+ ADDRESS 192.5.6.30
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+com. IN NS
+SECTION ANSWER
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN A
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+
+; NS with address pointing into a private range must not be followed
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+attacker.com. IN A
+SECTION AUTHORITY
+attacker.com. IN NS ns.attacker.com.
+SECTION ADDITIONAL
+ns.attacker.com. IN A 192.168.3.5
+ENTRY_END
+RANGE_END
+
+; ns.attacker.com.
+RANGE_BEGIN 0 1000
+ ADDRESS 19.168.3.5
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+attacker.com. IN NS
+SECTION ANSWER
+attacker.com. IN NS ns.attacker.com.
+SECTION ADDITIONAL
+ns.attacker.com. IN A 192.168.3.5
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.attacker.com. IN A
+SECTION ANSWER
+www.attacker.com. IN A 192.0.2.55
+ENTRY_END
+RANGE_END
+
+
+; ns.example.com.
+RANGE_BEGIN 0 1000
+ ADDRESS 1.2.3.4
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN NS
+SECTION ANSWER
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 192.0.2.40
+ENTRY_END
+
+; blacklisted IP addresses
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+attack-ipv4-0-0-0-0.example.com. IN A
+SECTION ANSWER
+attack-ipv4-0-0-0-0.example.com. IN A 0.0.0.0
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+attack-ipv4over6-0-0-0-0.example.com. IN AAAA
+SECTION ANSWER
+attack-ipv4over6-0-0-0-0.example.com. IN AAAA ::ffff:0.0.0.0
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+attack-ipv4-10-1-2-3.example.com. IN A
+SECTION ANSWER
+attack-ipv4-10-1-2-3.example.com. IN A 10.1.2.3
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+attack-ipv4over6-10-2-3-4.example.com. IN AAAA
+SECTION ANSWER
+attack-ipv4over6-10-2-3-4.example.com. IN AAAA ::ffff:10.2.3.4
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+attack-ipv4-100-127-255-254.example.com. IN A
+SECTION ANSWER
+attack-ipv4-100-127-255-254.example.com. IN A 100.127.255.254
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+attack-ipv4over6-100-127-255-255.example.com. IN AAAA
+SECTION ANSWER
+attack-ipv4over6-100-127-255-255.example.com. IN AAAA ::ffff:100.127.255.255
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+attack-ipv4-127-0-0-1.example.com. IN A
+SECTION ANSWER
+attack-ipv4-127-0-0-1.example.com. IN A 127.0.0.1
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+attack-ipv4over6-127-0-0-1.example.com. IN AAAA
+SECTION ANSWER
+attack-ipv4over6-127-0-0-1.example.com. IN AAAA ::ffff:127.0.0.1
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+attack-ipv4-169-254-255-255.example.com. IN A
+SECTION ANSWER
+attack-ipv4-169-254-255-255.example.com. IN A 169.254.255.255
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+attack-ipv4over6-169-254-0-0.example.com. IN AAAA
+SECTION ANSWER
+attack-ipv4over6-169-254-0-0.example.com. IN AAAA ::ffff:169.254.0.0
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+attack-ipv4-172-16-0-0.example.com. IN A
+SECTION ANSWER
+attack-ipv4-172-16-0-0.example.com. IN A 172.16.0.0
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+attack-ipv4over6-172-31-255-255.example.com. IN AAAA
+SECTION ANSWER
+attack-ipv4over6-172-31-255-255.example.com. IN AAAA ::ffff:172.31.255.255
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+attack-ipv4-192-168-3-8.example.com. IN A
+SECTION ANSWER
+attack-ipv4-192-168-3-8.example.com. IN A 192.168.3.8
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+attack-ipv4over6-192-168-254-210.example.com. IN AAAA
+SECTION ANSWER
+attack-ipv4over6-192-168-254-210.example.com. IN AAAA ::ffff:192.168.254.210
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+attack-ipv6-.example.com. IN AAAA
+SECTION ANSWER
+attack-ipv6-.example.com. IN AAAA ::
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+attack-ipv6-1.example.com. IN AAAA
+SECTION ANSWER
+attack-ipv6-1.example.com. IN AAAA ::1
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+attack-ipv6-fc00.example.com. IN AAAA
+SECTION ANSWER
+attack-ipv6-fc00.example.com. IN AAAA fc00::
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+attack-ipv6-fe80.example.com. IN AAAA
+SECTION ANSWER
+attack-ipv6-fe80.example.com. IN AAAA fe80::
+ENTRY_END
+
+RANGE_END
+
+STEP 11 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; recursion happens here, no blacklisted IP address is present
+STEP 12 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 192.0.2.40
+;SECTION AUTHORITY
+;example.com. IN NS ns.example.com.
+;SECTION ADDITIONAL
+;ns.example.com. IN A 1.2.3.4
+ENTRY_END
+
+; test that 0.0.0.0 is blacklisted
+STEP 201 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+attack-ipv4-0-0-0-0.example.com. IN A
+ENTRY_END
+
+STEP 202 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all answer authority
+REPLY QR RD RA REFUSED
+SECTION QUESTION
+attack-ipv4-0-0-0-0.example.com. IN A
+SECTION ANSWER
+SECTION AUTHORITY
+SECTION ADDITIONAL
+explanation.invalid. TXT "blocked by DNS rebinding protection"
+ENTRY_END
+
+; test that ::ffff:0.0.0.0 is blacklisted
+STEP 211 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+attack-ipv4over6-0-0-0-0.example.com. IN AAAA
+ENTRY_END
+
+STEP 212 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all answer authority
+REPLY QR RD RA REFUSED
+SECTION QUESTION
+attack-ipv4over6-0-0-0-0.example.com. IN AAAA
+SECTION ANSWER
+SECTION AUTHORITY
+SECTION ADDITIONAL
+explanation.invalid. TXT "blocked by DNS rebinding protection"
+ENTRY_END
+
+; test that 10.1.2.3 is blacklisted
+STEP 221 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+attack-ipv4-10-1-2-3.example.com. IN A
+ENTRY_END
+
+STEP 222 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all answer authority
+REPLY QR RD RA REFUSED
+SECTION QUESTION
+attack-ipv4-10-1-2-3.example.com. IN A
+SECTION ANSWER
+SECTION AUTHORITY
+SECTION ADDITIONAL
+explanation.invalid. TXT "blocked by DNS rebinding protection"
+ENTRY_END
+
+; test that ::ffff:10.2.3.4 is blacklisted
+STEP 231 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+attack-ipv4over6-10-2-3-4.example.com. IN AAAA
+ENTRY_END
+
+STEP 232 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all answer authority
+REPLY QR RD RA REFUSED
+SECTION QUESTION
+attack-ipv4over6-10-2-3-4.example.com. IN AAAA
+SECTION ANSWER
+SECTION AUTHORITY
+SECTION ADDITIONAL
+explanation.invalid. TXT "blocked by DNS rebinding protection"
+ENTRY_END
+
+; test that 100.127.255.254 is blacklisted
+STEP 241 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+attack-ipv4-100-127-255-254.example.com. IN A
+ENTRY_END
+
+STEP 242 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all answer authority
+REPLY QR RD RA REFUSED
+SECTION QUESTION
+attack-ipv4-100-127-255-254.example.com. IN A
+SECTION ANSWER
+SECTION AUTHORITY
+SECTION ADDITIONAL
+explanation.invalid. TXT "blocked by DNS rebinding protection"
+ENTRY_END
+
+; test that ::ffff:100.127.255.255 is blacklisted
+STEP 251 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+attack-ipv4over6-100-127-255-255.example.com. IN AAAA
+ENTRY_END
+
+STEP 252 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all answer authority
+REPLY QR RD RA REFUSED
+SECTION QUESTION
+attack-ipv4over6-100-127-255-255.example.com. IN AAAA
+SECTION ANSWER
+SECTION AUTHORITY
+SECTION ADDITIONAL
+explanation.invalid. TXT "blocked by DNS rebinding protection"
+ENTRY_END
+
+; test that 127.0.0.1 is blacklisted
+STEP 261 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+attack-ipv4-127-0-0-1.example.com. IN A
+ENTRY_END
+
+STEP 262 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all answer authority
+REPLY QR RD RA REFUSED
+SECTION QUESTION
+attack-ipv4-127-0-0-1.example.com. IN A
+SECTION ANSWER
+SECTION AUTHORITY
+SECTION ADDITIONAL
+explanation.invalid. TXT "blocked by DNS rebinding protection"
+ENTRY_END
+
+; test that ::ffff:127.0.0.1 is blacklisted
+STEP 271 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+attack-ipv4over6-127-0-0-1.example.com. IN AAAA
+ENTRY_END
+
+STEP 272 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all answer authority
+REPLY QR RD RA REFUSED
+SECTION QUESTION
+attack-ipv4over6-127-0-0-1.example.com. IN AAAA
+SECTION ANSWER
+SECTION AUTHORITY
+SECTION ADDITIONAL
+explanation.invalid. TXT "blocked by DNS rebinding protection"
+ENTRY_END
+
+; test that 169.254.255.255 is blacklisted
+STEP 281 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+attack-ipv4-169-254-255-255.example.com. IN A
+ENTRY_END
+
+STEP 282 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all answer authority
+REPLY QR RD RA REFUSED
+SECTION QUESTION
+attack-ipv4-169-254-255-255.example.com. IN A
+SECTION ANSWER
+SECTION AUTHORITY
+SECTION ADDITIONAL
+explanation.invalid. TXT "blocked by DNS rebinding protection"
+ENTRY_END
+
+; test that ::ffff:169.254.0.0 is blacklisted
+STEP 291 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+attack-ipv4over6-169-254-0-0.example.com. IN AAAA
+ENTRY_END
+
+STEP 292 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all answer authority
+REPLY QR RD RA REFUSED
+SECTION QUESTION
+attack-ipv4over6-169-254-0-0.example.com. IN AAAA
+SECTION ANSWER
+SECTION AUTHORITY
+SECTION ADDITIONAL
+explanation.invalid. TXT "blocked by DNS rebinding protection"
+ENTRY_END
+
+; test that 172.16.0.0 is blacklisted
+STEP 301 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+attack-ipv4-172-16-0-0.example.com. IN A
+ENTRY_END
+
+STEP 302 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all answer authority
+REPLY QR RD RA REFUSED
+SECTION QUESTION
+attack-ipv4-172-16-0-0.example.com. IN A
+SECTION ANSWER
+SECTION AUTHORITY
+SECTION ADDITIONAL
+explanation.invalid. TXT "blocked by DNS rebinding protection"
+ENTRY_END
+
+; test that ::ffff:172.31.255.255 is blacklisted
+STEP 311 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+attack-ipv4over6-172-31-255-255.example.com. IN AAAA
+ENTRY_END
+
+STEP 312 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all answer authority
+REPLY QR RD RA REFUSED
+SECTION QUESTION
+attack-ipv4over6-172-31-255-255.example.com. IN AAAA
+SECTION ANSWER
+SECTION AUTHORITY
+SECTION ADDITIONAL
+explanation.invalid. TXT "blocked by DNS rebinding protection"
+ENTRY_END
+
+; test that 192.168.3.8 is blacklisted
+STEP 321 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+attack-ipv4-192-168-3-8.example.com. IN A
+ENTRY_END
+
+STEP 322 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all answer authority
+REPLY QR RD RA REFUSED
+SECTION QUESTION
+attack-ipv4-192-168-3-8.example.com. IN A
+SECTION ANSWER
+SECTION AUTHORITY
+SECTION ADDITIONAL
+explanation.invalid. TXT "blocked by DNS rebinding protection"
+ENTRY_END
+
+; test that ::ffff:192.168.254.210 is blacklisted
+STEP 331 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+attack-ipv4over6-192-168-254-210.example.com. IN AAAA
+ENTRY_END
+
+STEP 332 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all answer authority
+REPLY QR RD RA REFUSED
+SECTION QUESTION
+attack-ipv4over6-192-168-254-210.example.com. IN AAAA
+SECTION ANSWER
+SECTION AUTHORITY
+SECTION ADDITIONAL
+explanation.invalid. TXT "blocked by DNS rebinding protection"
+ENTRY_END
+
+; test that :: is blacklisted
+STEP 341 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+attack-ipv6-.example.com. IN AAAA
+ENTRY_END
+
+STEP 342 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all answer authority
+REPLY QR RD RA REFUSED
+SECTION QUESTION
+attack-ipv6-.example.com. IN AAAA
+SECTION ANSWER
+SECTION AUTHORITY
+SECTION ADDITIONAL
+explanation.invalid. TXT "blocked by DNS rebinding protection"
+ENTRY_END
+
+; test that ::1 is blacklisted
+STEP 351 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+attack-ipv6-1.example.com. IN AAAA
+ENTRY_END
+
+STEP 352 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all answer authority
+REPLY QR RD RA REFUSED
+SECTION QUESTION
+attack-ipv6-1.example.com. IN AAAA
+SECTION ANSWER
+SECTION AUTHORITY
+SECTION ADDITIONAL
+explanation.invalid. TXT "blocked by DNS rebinding protection"
+ENTRY_END
+
+; test that fc00:: is blacklisted
+STEP 361 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+attack-ipv6-fc00.example.com. IN AAAA
+ENTRY_END
+
+STEP 362 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all answer authority
+REPLY QR RD RA REFUSED
+SECTION QUESTION
+attack-ipv6-fc00.example.com. IN AAAA
+SECTION ANSWER
+SECTION AUTHORITY
+SECTION ADDITIONAL
+explanation.invalid. TXT "blocked by DNS rebinding protection"
+ENTRY_END
+
+; test that fe80:: is blacklisted
+STEP 371 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+attack-ipv6-fe80.example.com. IN AAAA
+ENTRY_END
+
+STEP 372 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all answer authority
+REPLY QR RD RA REFUSED
+SECTION QUESTION
+attack-ipv6-fe80.example.com. IN AAAA
+SECTION ANSWER
+SECTION AUTHORITY
+SECTION ADDITIONAL
+explanation.invalid. TXT "blocked by DNS rebinding protection"
+ENTRY_END
+
+STEP 401 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; it still works if no blacklisted IP address is present
+STEP 402 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 192.0.2.40
+ENTRY_END
+
+STEP 501 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.attacker.com. IN A
+ENTRY_END
+
+; NS for attacker.com. has IP address from private range, it must fail
+STEP 502 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all answer authority
+REPLY QR RD RA REFUSED
+SECTION QUESTION
+www.attacker.com. IN A
+SECTION ANSWER
+SECTION AUTHORITY
+SECTION ADDITIONAL
+explanation.invalid. TXT "blocked by DNS rebinding protection"
+ENTRY_END
+
+SCENARIO_END