summaryrefslogtreecommitdiffstats
path: root/lib/layer/test.integr
diff options
context:
space:
mode:
Diffstat (limited to 'lib/layer/test.integr')
-rw-r--r--lib/layer/test.integr/deckard.yaml13
-rw-r--r--lib/layer/test.integr/iter_cname_length.rpl226
-rw-r--r--lib/layer/test.integr/iter_limit_bad_glueless.rpl220
-rw-r--r--lib/layer/test.integr/iter_limit_refuse.rpl150
-rw-r--r--lib/layer/test.integr/kresd_config.j2107
5 files changed, 716 insertions, 0 deletions
diff --git a/lib/layer/test.integr/deckard.yaml b/lib/layer/test.integr/deckard.yaml
new file mode 100644
index 0000000..d2d62d0
--- /dev/null
+++ b/lib/layer/test.integr/deckard.yaml
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-3.0-or-later
+programs:
+- name: kresd
+ binary: kresd
+ additional:
+ - --noninteractive
+ templates:
+ - lib/layer/test.integr/kresd_config.j2
+ - tests/integration/hints_zone.j2
+ configs:
+ - config
+ - hints
+noclean: True
diff --git a/lib/layer/test.integr/iter_cname_length.rpl b/lib/layer/test.integr/iter_cname_length.rpl
new file mode 100644
index 0000000..39f48a8
--- /dev/null
+++ b/lib/layer/test.integr/iter_cname_length.rpl
@@ -0,0 +1,226 @@
+do-ip6: no
+; config options
+; SPDX-License-Identifier: GPL-3.0-or-later
+ stub-addr: 193.0.14.129 # k.root-servers.net.
+CONFIG_END
+
+SCENARIO_BEGIN Test restriction on CNAME chain length.
+
+
+; k.root-servers.net.
+RANGE_BEGIN 0 100
+ ADDRESS 193.0.14.129
+
+ENTRY_BEGIN
+MATCH opcode qname
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+n1.tld. IN NS
+SECTION ANSWER
+n1.tld. IN CNAME n2.tld.
+n2.tld. IN CNAME n3.tld.
+n3.tld. IN CNAME n4.tld.
+n4.tld. IN CNAME n5.tld.
+n5.tld. IN CNAME n6.tld.
+n6.tld. IN CNAME n7.sub.
+SECTION AUTHORITY
+sub. IN NS ns.sub.
+SECTION ADDITIONAL
+ns.sub. IN A 194.0.14.1
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qname
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+n2.tld. IN NS
+SECTION ANSWER
+n2.tld. IN CNAME n3.tld.
+n3.tld. IN CNAME n4.tld.
+n4.tld. IN CNAME n5.tld.
+n5.tld. IN CNAME n6.tld.
+n6.tld. IN CNAME n7.sub.
+SECTION AUTHORITY
+sub. IN NS ns.sub.
+SECTION ADDITIONAL
+ns.sub. IN A 194.0.14.1
+ENTRY_END
+
+
+; empty non-terminal for query name minimization
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR AA NOERROR
+SECTION QUESTION
+tld. IN NS
+SECTION ANSWER
+ENTRY_END
+
+
+
+; sub. subdomains
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+sub. IN NS
+SECTION AUTHORITY
+sub. IN NS ns.sub.
+SECTION ADDITIONAL
+ns.sub. IN A 194.0.14.1
+ENTRY_END
+
+RANGE_END
+
+
+; ns.sub.
+RANGE_BEGIN 0 100
+ ADDRESS 194.0.14.1
+
+ENTRY_BEGIN
+MATCH opcode qname qtype
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+ns.sub. IN A
+SECTION ANSWER
+ns.sub. IN A 194.0.14.1
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qname qtype
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+ns.sub. IN AAAA
+SECTION ANSWER
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qname qtype
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+n7.sub. IN A
+SECTION ANSWER
+n7.sub. IN CNAME n8.sub.
+n8.sub. IN CNAME n9.sub.
+n9.sub. IN CNAME n10.sub.
+n10.sub. IN CNAME n11.sub.
+n11.sub. IN CNAME n12.sub.
+n12.sub. IN CNAME n13.sub.
+n13.sub. IN CNAME n14.sub.
+n14.sub. IN A 198.18.0.1
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qname qtype
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+loop7.sub. IN A
+SECTION ANSWER
+loop7.sub. IN CNAME loop8.sub.
+loop8.sub. IN CNAME loop9.sub.
+loop9.sub. IN CNAME loop10.sub.
+loop10.sub. IN CNAME loop11.sub.
+; loop11 -> loop7 -> ... -> loop11
+loop11.sub. IN CNAME loop7.sub.
+loop12.sub. IN CNAME loop13.sub.
+loop13.sub. IN CNAME loop14.sub.
+loop14.sub. IN A 198.18.0.1
+ENTRY_END
+
+RANGE_END
+
+; maximum allowed chain length
+STEP 10 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+n2.tld. IN A
+ENTRY_END
+
+STEP 11 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+n2.tld. IN A
+SECTION ANSWER
+n2.tld. IN CNAME n3.tld.
+n3.tld. IN CNAME n4.tld.
+n4.tld. IN CNAME n5.tld.
+n5.tld. IN CNAME n6.tld.
+n6.tld. IN CNAME n7.sub.
+n7.sub. IN CNAME n8.sub.
+n8.sub. IN CNAME n9.sub.
+n9.sub. IN CNAME n10.sub.
+n10.sub. IN CNAME n11.sub.
+n11.sub. IN CNAME n12.sub.
+n12.sub. IN CNAME n13.sub.
+n13.sub. IN CNAME n14.sub.
+n14.sub. IN A 198.18.0.1
+ENTRY_END
+
+
+; too long CNAME chain across two zones
+STEP 20 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+n1.tld. IN A
+ENTRY_END
+
+STEP 21 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA SERVFAIL
+SECTION QUESTION
+n1.tld. IN A
+SECTION ANSWER
+n1.tld. IN CNAME n2.tld.
+n2.tld. IN CNAME n3.tld.
+n3.tld. IN CNAME n4.tld.
+n4.tld. IN CNAME n5.tld.
+n5.tld. IN CNAME n6.tld.
+n6.tld. IN CNAME n7.sub.
+n7.sub. IN CNAME n8.sub.
+n8.sub. IN CNAME n9.sub.
+n9.sub. IN CNAME n10.sub.
+n10.sub. IN CNAME n11.sub.
+n11.sub. IN CNAME n12.sub.
+n12.sub. IN CNAME n13.sub.
+n13.sub. IN CNAME n14.sub.
+; This chain is too long (> 13):
+; n14.sub. IN A 198.18.0.1
+ENTRY_END
+
+
+; CNAME loop detection
+STEP 30 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+loop7.sub. IN A
+ENTRY_END
+
+STEP 31 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA SERVFAIL
+SECTION QUESTION
+loop7.sub. IN A
+SECTION ANSWER
+loop7.sub. IN CNAME loop8.sub.
+loop8.sub. IN CNAME loop9.sub.
+loop9.sub. IN CNAME loop10.sub.
+loop10.sub. IN CNAME loop11.sub.
+loop11.sub. IN CNAME loop7.sub.
+ENTRY_END
+
+SCENARIO_END
diff --git a/lib/layer/test.integr/iter_limit_bad_glueless.rpl b/lib/layer/test.integr/iter_limit_bad_glueless.rpl
new file mode 100644
index 0000000..73d4627
--- /dev/null
+++ b/lib/layer/test.integr/iter_limit_bad_glueless.rpl
@@ -0,0 +1,220 @@
+do-ip6: no
+; config options
+; target-fetch-policy: "0 0 0 0 0"
+; name: "."
+ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
+CONFIG_END
+
+SCENARIO_BEGIN Test resolution with lame reply looks like nodata with noSOA
+
+; K.ROOT-SERVERS.NET.
+RANGE_BEGIN 0 100
+ 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
+
+; com
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+com. IN NS
+SECTION AUTHORITY
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+
+; net
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+net. IN NS
+SECTION AUTHORITY
+net. IN NS e.gtld-servers.net.
+SECTION ADDITIONAL
+e.gtld-servers.net. IN A 192.12.94.30
+ENTRY_END
+
+RANGE_END
+
+; a.gtld-servers.net. - com
+RANGE_BEGIN 0 100
+ 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
+victim.com. IN NS
+SECTION AUTHORITY
+victim.com. IN NS ns.victim.com.
+SECTION ADDITIONAL
+ns.victim.com. IN A 1.2.3.55
+ENTRY_END
+RANGE_END
+
+; e.gtld-servers.net. - net
+RANGE_BEGIN 0 100
+ ADDRESS 192.12.94.30
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+net. IN NS
+SECTION ANSWER
+net. IN NS e.gtld-servers.net.
+SECTION ADDITIONAL
+e.gtld-servers.net. IN A 192.12.94.30
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+attacker.net. IN NS
+SECTION AUTHORITY
+attacker.net. IN NS ns.attacker.net.
+SECTION ADDITIONAL
+ns.attacker.net. IN A 1.2.3.44
+ENTRY_END
+RANGE_END
+
+; ns.attacker.net.
+RANGE_BEGIN 0 100
+ ADDRESS 1.2.3.44
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+attacker.net. IN NS
+SECTION ANSWER
+attacker.net. IN NS ns.attacker.net.
+SECTION ADDITIONAL
+ns.attacker.net. IN A 1.2.3.44
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+ns.attacker.net. IN A
+SECTION ANSWER
+ns.attacker.net. IN A 1.2.3.44
+SECTION AUTHORITY
+attacker.net. IN NS ns.attacker.net.
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+ns.attacker.net. IN AAAA
+SECTION AUTHORITY
+SECTION ADDITIONAL
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+sub.attacker.net. IN NS
+SECTION AUTHORITY
+sub.attacker.net. IN NS ns1.victim.com.
+sub.attacker.net. IN NS ns2.victim.com.
+sub.attacker.net. IN NS ns3.victim.com.
+sub.attacker.net. IN NS ns4.victim.com.
+sub.attacker.net. IN NS ns5.victim.com.
+sub.attacker.net. IN NS ns6.victim.com.
+sub.attacker.net. IN NS ns7.victim.com.
+sub.attacker.net. IN NS ns8.victim.com.
+sub.attacker.net. IN NS ns9.victim.com.
+ENTRY_END
+RANGE_END
+
+; ns.victim.com.
+; returns NXDOMAIN for all queries (attacker generated NS names are not present)
+RANGE_BEGIN 0 100
+ ADDRESS 1.2.3.55
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NXDOMAIN
+SECTION QUESTION
+victim.com. IN NS
+SECTION AUTHORITY
+victim.com. 0 IN SOA . . 1 1 1 1 1
+SECTION ADDITIONAL
+ENTRY_END
+RANGE_END
+
+
+STEP 10 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.sub.attacker.net. IN A
+ENTRY_END
+
+; in any case we must get SERVFAIL, no deleation works
+STEP 11 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA SERVFAIL
+SECTION QUESTION
+www.sub.attacker.net. IN A
+SECTION ANSWER
+ENTRY_END
+
+; recursion happens here
+STEP 20 QUERY
+ENTRY_BEGIN
+REPLY RD DO
+SECTION QUESTION
+glueless.trigger.check.max.number.of.upstream.queries. IN TXT
+ENTRY_END
+
+STEP 21 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR AA RD RA NOERROR
+SECTION QUESTION
+glueless.trigger.check.max.number.of.upstream.queries. IN TXT
+SECTION ANSWER
+glueless.trigger.check.max.number.of.upstream.queries. IN TXT "pass"
+SECTION AUTHORITY
+SECTION ADDITIONAL
+ENTRY_END
+
+
+SCENARIO_END
diff --git a/lib/layer/test.integr/iter_limit_refuse.rpl b/lib/layer/test.integr/iter_limit_refuse.rpl
new file mode 100644
index 0000000..285b5af
--- /dev/null
+++ b/lib/layer/test.integr/iter_limit_refuse.rpl
@@ -0,0 +1,150 @@
+do-ip6: no
+; config options
+;server:
+ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
+CONFIG_END
+
+SCENARIO_BEGIN Outrageous number of auth servers return REFUSED. Simulates NXNSAttack misusing wildcard which points to victim's DNS server. Lua config checks if number of outgoing queries is within limits.
+
+; K.ROOT-SERVERS.NET.
+RANGE_BEGIN 0 100
+ 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
+
+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 100
+ 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 ns10.example.com.
+example.com. IN NS ns11.example.com.
+example.com. IN NS ns12.example.com.
+example.com. IN NS ns13.example.com.
+example.com. IN NS ns14.example.com.
+example.com. IN NS ns15.example.com.
+example.com. IN NS ns16.example.com.
+example.com. IN NS ns17.example.com.
+example.com. IN NS ns18.example.com.
+example.com. IN NS ns19.example.com.
+SECTION ADDITIONAL
+ns10.example.com. IN A 1.2.3.10
+ns11.example.com. IN A 1.2.3.11
+ns12.example.com. IN A 1.2.3.12
+ns13.example.com. IN A 1.2.3.13
+ns14.example.com. IN A 1.2.3.14
+ns15.example.com. IN A 1.2.3.15
+ns16.example.com. IN A 1.2.3.16
+ns17.example.com. IN A 1.2.3.17
+ns18.example.com. IN A 1.2.3.18
+ns19.example.com. IN A 1.2.3.19
+
+ENTRY_END
+RANGE_END
+
+; ns1.example.com.
+RANGE_BEGIN 0 100
+ ADDRESS 1.2.3.10
+ ADDRESS 1.2.3.11
+ ADDRESS 1.2.3.12
+ ADDRESS 1.2.3.13
+ ADDRESS 1.2.3.14
+ ADDRESS 1.2.3.15
+ ADDRESS 1.2.3.16
+ ADDRESS 1.2.3.17
+ ADDRESS 1.2.3.18
+ ADDRESS 1.2.3.19
+ENTRY_BEGIN
+MANDATORY
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA REFUSED
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+SECTION AUTHORITY
+SECTION ADDITIONAL
+ENTRY_END
+RANGE_END
+
+
+; recursion happens here
+STEP 10 QUERY
+ENTRY_BEGIN
+REPLY RD DO
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; in any case we must get SERVFAIL, no auth works
+STEP 11 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA DO SERVFAIL
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+SECTION AUTHORITY
+SECTION ADDITIONAL
+ENTRY_END
+
+; recursion happens here
+STEP 20 QUERY
+ENTRY_BEGIN
+REPLY RD DO
+SECTION QUESTION
+refused.trigger.check.max.number.of.upstream.queries. IN TXT
+ENTRY_END
+
+STEP 21 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR AA RD RA NOERROR
+SECTION QUESTION
+refused.trigger.check.max.number.of.upstream.queries. IN TXT
+SECTION ANSWER
+refused.trigger.check.max.number.of.upstream.queries. IN TXT "pass"
+SECTION AUTHORITY
+SECTION ADDITIONAL
+ENTRY_END
+
+SCENARIO_END
diff --git a/lib/layer/test.integr/kresd_config.j2 b/lib/layer/test.integr/kresd_config.j2
new file mode 100644
index 0000000..dc18a1b
--- /dev/null
+++ b/lib/layer/test.integr/kresd_config.j2
@@ -0,0 +1,107 @@
+-- SPDX-License-Identifier: GPL-3.0-or-later
+
+local ffi = require('ffi')
+
+-- hook for iter_refuse_toomany.rpl
+local function check_max_number_of_upstream_queries(maxcnt)
+ return function (state, req)
+ local vals = worker.stats()
+ local upstream_packets = vals.ipv4 + vals.ipv6
+ log_info(ffi.C.LOG_GRP_TESTS, '%d packets sent to upstream', upstream_packets)
+ local answ_f
+ if upstream_packets > maxcnt then -- . + com. + ????
+ answ_f = policy.ANSWER(
+ { [kres.type.TXT] = { ttl=300, rdata='\4fail' } })
+
+ else
+ answ_f = policy.ANSWER(
+ { [kres.type.TXT] = { ttl=300, rdata='\4pass' } })
+ end
+ return answ_f(state, req)
+ end
+end
+
+policy.add(
+ policy.suffix(check_max_number_of_upstream_queries(8),
+ policy.todnames({'refused.trigger.check.max.number.of.upstream.queries.'})
+ )
+)
+policy.add(
+ policy.suffix(check_max_number_of_upstream_queries(16),
+ policy.todnames({'glueless.trigger.check.max.number.of.upstream.queries.'})
+ )
+)
+
+-- hook end iter_refuse_toomany.rpl
+
+
+trust_anchors.remove('.')
+{% for TAF in TRUST_ANCHOR_FILES %}
+-- trust_anchors.add_file('{{TAF}}')
+{% endfor %}
+
+{% raw %}
+-- 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
+
+_hint_root_file('hints')
+cache.size = 2*MB
+log_level('debug')
+policy.add(policy.all(policy.DEBUG_ALWAYS))
+{% endraw %}
+
+net = { '{{SELF_ADDR}}' }
+
+{% if DO_IP6 == "true" %}
+net.ipv6 = true
+{% else %}
+net.ipv6 = false
+{% endif %}
+
+{% if DO_IP4 == "true" %}
+net.ipv4 = true
+{% else %}
+net.ipv4 = false
+{% endif %}
+
+
+{% 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)