summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--Makefile15
-rw-r--r--README.md509
-rwxr-xr-xgen_canned.sh122
-rw-r--r--static/0000-refuse-root.toml11
-rw-r--r--static/0001-refuse-iis.se.toml11
-rw-r--r--static/0002-refuse-icann.org.toml11
-rw-r--r--static/0003-refuse-ripe.net.toml11
-rw-r--r--static/0010-edns_version.toml16
-rw-r--r--static/0100-soa-ede.dn5.dk.toml24
-rw-r--r--static/0101-any-rfc8482.toml18
-rw-r--r--static/0102-ns.toml18
-rw-r--r--static/0103-txt.toml18
-rw-r--r--static/0104-mx.toml19
-rw-r--r--static/0110-a.toml18
-rw-r--r--static/0111-aaaa.toml18
-rw-r--r--static/0120-notice-a.toml18
-rw-r--r--static/0121-notice-aaaa.toml18
-rw-r--r--static/0130-ns-a.toml18
-rw-r--r--static/0131-ns-aaaa.toml18
-rw-r--r--static/9999-nxdomain.toml24
-rw-r--r--templates/ede-addr-a.toml25
-rw-r--r--templates/ede-addr-aaaa.toml25
-rw-r--r--templates/ede-cname.toml37
-rw-r--r--templates/ede-nx.toml19
-rw-r--r--templates/nodata.toml24
26 files changed, 1068 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e6214e2
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+/canned/
+/pub/
+*.sw?
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..b34c9ee
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,15 @@
+.PHONY: build publish all
+all: build
+
+pub:
+ mkdir pub
+
+pub/index.html: README.md | pub
+ pandoc -s README.md -o pub/index.html
+
+build: pub/index.html
+ ./gen_canned.sh
+
+publish:
+ rsync -a --delete-after canned/ ede.dn5.dk:canned/
+ rsync -a --delete-after pub/ ede.dn5.dk:pub/
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..1914c64
--- /dev/null
+++ b/README.md
@@ -0,0 +1,509 @@
+---
+title: RFC 8914 Extended DNS Errors testbed
+---
+
+This is a testbed, for testing propagation of [RFC8914 Extended DNS Errors][RFC8914] in the wild.
+
+```
+$ dig blocked.nx.ede.dn5.dk @1.1.1.1
+
+; <<>> DiG 9.20.7-1-Debian <<>> blocked.nx.ede.dn5.dk @1.1.1.1
+;; global options: +cmd
+;; Got answer:
+;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 28690
+;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
+
+;; OPT PSEUDOSECTION:
+; EDNS: version: 0, flags:; udp: 1232
+; EDE: 15 (Blocked): (🚧 Blocked 🚧)
+;; QUESTION SECTION:
+;blocked.nx.ede.dn5.dk. IN A
+
+;; Query time: 15 msec
+;; SERVER: 1.1.1.1#53(1.1.1.1) (UDP)
+;; WHEN: Sat Apr 19 16:34:11 UTC 2025
+;; MSG SIZE rcvd: 73
+```
+
+## What does `RFC 8914` say about propagation?
+
+In `RFC 8914` [section 3 - Extended DNS Error Processing][RFC8914sec3]:
+
+> [...]
+>
+> When a resolver or forwarder receives an EDE option, whether or not (and how) to pass along EDE information on to their original client is implementation dependent. Implementations MAY choose to not forward information, or they MAY choose to create a new EDE option(s) that conveys the information encoded in the received EDE. When doing so, the source of the error SHOULD be attributed in the EXTRA-TEXT field, since an EDNS0 option received by the original client will appear to have come from the resolver or forwarder sending it.
+>
+> [...]
+
+We probably need to write an update, at least changing it so that
+forwarders SHOULD propagate EDE's.
+
+Currently there is no formal definition of `EXTRA-TEXT`, so in this
+testbed `EXTRA-TEXT` is currently used for static Unicode messages for each error.
+
+## Adoption status
+
+### Adoption in DNSSEC
+
+The DNSSEC related EDE are probably the most widely deployed EDE's.
+The EDE concept also originated from the needs of the DNSSEC working-group,
+so it's not that surprising.
+
+### Use with DNS filtering
+
+While there are several EDE's defined for use with DNS filtering,
+they are still lacking implementations, hence the need for this testbed.
+In particular work is needed on integrating EDE's with [RPZ][Response Policy Zone],
+and ensuring that DNS forwarders, like home routers, propagate EDE's to end-user applications.
+
+#### STOP pages
+
+Traditionally DNS filtering have often hijacked the filtered domain,
+and sent visitors to a "STOP" page, served only over HTTP, sometimes
+with a HTTP status code `451 Unavailable for legal reasons`.
+As plain HTTP is being phased out, concepts like HSTS Preload and HTTPS-by-default and end-users following `https://` links, means that it is very
+unlikely that end-users will actually see these STOP-pages.
+
+Adopting the DNS filtering-related EDE's and formalising `EXTRA-TEXT`,
+could lead to end-user applications, like web-browsers, having more
+user-friendly error-messages, than simply `Connection refused`.
+
+### Improvement projects
+
+Last updated: 2025-04-18
+
+This is an incomplete list of improvement projects:
+
+* [Unbound: Add support for RPZ to reply with an EDE][unbound-1191]
+* Ongoing, use [RIPE Atlas][] to map EDE propagation. (Initial findings: to be presented at RIPE90 DNS-WG)
+* Find / file more bugs (TODO)
+
+#### Completed
+
+* [dnsdist: Add support for adding EDE codes with Lua][pdns-12572] (unfortunately, no support for `EXTRA-TEXT` yet)
+
+## Testbed
+
+This testbed was setup, to help with testing EDE propagation.
+
+### Query format
+
+The following queries are currently supported:
+
+- `<label>.nx.ede.dn5.dk` - Responds with `NXDOMAIN`.
+- `<label>.cname.ede.dn5.dk` - Responds with `NOERROR` and a `CNAME` record.
+- `<label>.addr.ede.dn5.dk` - Responds with `NOERROR` and an `A`/`AAAA` record.
+
+All of them responds with the EDE code assigned to the given `<label>`.
+
+As an alternative, `<label>` can also be substituted with `<code>`, all currently valid values are listed in the following section.
+
+#### Examples queries
+
+* `dig blocked.nx.ede.dn5.dk @1.1.1.1` (the one shown at the top of the page)
+* `dig prohibited.cname.ede.dn5.dk @1.1.1.1`
+* `dig a filtered.addr.ede.dn5.dk @1.1.1.1`
+* `dig aaaa censored.addr.ede.dn5.dk @1.1.1.1`
+* `dig 25.nx.ede.dn5.dk @1.1.1.1`
+
+`1.1.1.1` (aka. CloudFlare public DNS resolver) is used in these
+examples because it is currently the only public resolver,
+that propagates EDE's).
+
+### List of supported error codes
+
+These are the codes currently implemented in the testbed.
+
+<table>
+<thead>
+<th>Code</th>
+<th>Label</th>
+<th>Purpose</th>
+<th>Reference</th>
+</thead>
+<tbody>
+<tr>
+<td>0</td>
+<td>other-error</td>
+<td>Other Error</td>
+<td>[RFC][RFC8914sec4.1]</td>
+</tr>
+<tr>
+<td>1</td>
+<td>unsupp-dnskey-algo</td>
+<td>Unsupported DNSKEY Algorithm</td>
+<td>[RFC][RFC8914sec4.2]</td>
+</tr>
+<tr>
+<td>2</td>
+<td>unsupp-ds-digest-type</td>
+<td>Unsupported DS Digest Type</td>
+<td>[RFC][RFC8914sec4.3]</td>
+</tr>
+<tr>
+<td>3</td>
+<td>stale</td>
+<td>Stale Answer</td>
+<td>[RFC][RFC8914sec4.4]</td>
+</tr>
+<tr>
+<td>4</td>
+<td>forged</td>
+<td>Forged Answer</td>
+<td>[RFC][RFC8914sec4.5]</td>
+</tr>
+<tr>
+<td>5</td>
+<td>dnssec-indeterminate</td>
+<td>DNSSEC Indeterminate</td>
+<td>[RFC][RFC8914sec4.6]</td>
+</tr>
+<tr>
+<td>6</td>
+<td>dnssec-bogus</td>
+<td>DNSSEC Bogus</td>
+<td>[RFC][RFC8914sec4.7]</td>
+</tr>
+<tr>
+<td>7</td>
+<td>sig-expired</td>
+<td>Signature Expired</td>
+<td>[RFC][RFC8914sec4.8]</td>
+</tr>
+<tr>
+<td>8</td>
+<td>sig-in-future</td>
+<td>Signature Not Yet Valid</td>
+<td>[RFC][RFC8914sec4.9]</td>
+</tr>
+<tr>
+<td>9</td>
+<td>dnskey-missing</td>
+<td>DNSKEY Missing</td>
+<td>[RFC][RFC8914sec4.10]</td>
+</tr>
+<tr>
+<td>10</td>
+<td>rrsig-missing</td>
+<td>RRSIGs Missing</td>
+<td>[RFC][RFC8914sec4.11]</td>
+</tr>
+<tr>
+<td>11</td>
+<td>no-zone-key-bit</td>
+<td>No Zone Key Bit Set</td>
+<td>[RFC][RFC8914sec4.12]</td>
+</tr>
+<tr>
+<td>12</td>
+<td>nsec-missing</td>
+<td>NSEC Missing</td>
+<td>[RFC][RFC8914sec4.13]</td>
+</tr>
+<tr>
+<td>13</td>
+<td>cached-error</td>
+<td>Cached Error</td>
+<td>[RFC][RFC8914sec4.14]</td>
+</tr>
+<tr>
+<td>14</td>
+<td>not-ready</td>
+<td>Not Ready</td>
+<td>[RFC][RFC8914sec4.15]</td>
+</tr>
+<tr>
+<td>15</td>
+<td>blocked</td>
+<td>Blocked</td>
+<td>[RFC][RFC8914sec4.16]</td>
+</tr>
+<tr>
+<td>16</td>
+<td>censored</td>
+<td>Censored</td>
+<td>[RFC][RFC8914sec4.17]</td>
+</tr>
+<tr>
+<td>17</td>
+<td>filtered</td>
+<td>Filtered</td>
+<td>[RFC][RFC8914sec4.18]</td>
+</tr>
+<tr>
+<td>18</td>
+<td>prohibited</td>
+<td>Prohibited</td>
+<td>[RFC][RFC8914sec4.19]</td>
+</tr>
+<tr>
+<td>19</td>
+<td>stale-nxdomain</td>
+<td>Stale NXDomain Answer</td>
+<td>[RFC][RFC8914sec4.20]</td>
+</tr>
+<tr>
+<td>20</td>
+<td>not-auth</td>
+<td>Not Authoritative</td>
+<td>[RFC][RFC8914sec4.21]</td>
+</tr>
+<tr>
+<td>21</td>
+<td>not-supp</td>
+<td>Not Supported</td>
+<td>[RFC][RFC8914sec4.22]</td>
+</tr>
+<tr>
+<td>22</td>
+<td>auth-ns-out-for-lunch</td>
+<td>No Reachable Authority</td>
+<td>[RFC][RFC8914sec4.23]</td>
+</tr>
+<tr>
+<td>23</td>
+<td>network-error</td>
+<td>Network Error</td>
+<td>[RFC][RFC8914sec4.24]</td>
+</tr>
+<tr>
+<td>24</td>
+<td>invalid-data</td>
+<td>Invalid Data</td>
+<td>[RFC][RFC8914sec4.25]</td>
+</tr>
+<tr>
+<td>25</td>
+<td>sig-reversed-time</td>
+<td>Signature Expired before Valid</td>
+<td>[unbound][unbound-604]</td>
+</tr>
+<tr>
+<td>26</td>
+<td>too-early</td>
+<td>Too Early</td>
+<td>[RFC][RFC9250]</td>
+</tr>
+<tr>
+<td>27</td>
+<td>unsupp-nsec3-iter</td>
+<td>Unsupported NSEC3 Iterations Value</td>
+<td>[RFC][RFC9276]</td>
+</tr>
+<tr>
+<td>28</td>
+<td>bad-proxy-policy</td>
+<td>Unable to conform to policy</td>
+<td>[I-D][draft-homburg-dnsop-codcp-01]</td>
+</tr>
+<tr>
+<td>29</td>
+<td>synthesized</td>
+<td>Synthesized</td>
+<td>[pdns][pdns-12334]</td>
+</tr>
+<tr>
+<td>30</td>
+<td>invalid-query-type</td>
+<td>Invalid Query Type</td>
+<td>[I-D][draft-ietf-dnsop-compact-denial-of-existence]</td>
+</tr>
+</tbody>
+</table>
+
+## Changelog
+
+* **2025:**
+ * April:
+ * This page was put together.
+ * [CannedDNS][] was extended to be able to serve as authoritative DNS server for this testbed.
+ * March:
+ * [CannedDNS][] was developed during [DNS Hackathon 2025][].
+* **2023:**
+ * May:
+ * This testbed was [originally implemented][old-doc] at [DNS Hackathon 2023][], using a patched version of [dnsdist][].
+
+## Credits
+
+This testbed was put together by AsbjΓΈrn Sloth TΓΈnnesen.
+
+## See also
+
+- [IANA EDE code registry][]
+- [Response Policy Zone][]
+- [git repo for this testbed][git-repo]
+
+[RFC8914]:
+https://datatracker.ietf.org/doc/html/rfc8914
+"RFC 8914: Extended DNS Errors"
+
+[RFC8914sec3]:
+https://datatracker.ietf.org/doc/html/rfc8914#section-3
+"RFC 8914: Extended DNS Errors, section 3 - Extended DNS Error Processing"
+
+[RFC8914sec4.1]:
+https://datatracker.ietf.org/doc/html/rfc8914#section-4.1
+"RFC 8914: Extended DNS Errors, section 4.1 - Other"
+
+[RFC8914sec4.2]:
+https://datatracker.ietf.org/doc/html/rfc8914#section-4.2
+"RFC 8914: Extended DNS Errors, section 4.2 - Unsupported DNSKEY Algorithm"
+
+[RFC8914sec4.3]:
+https://datatracker.ietf.org/doc/html/rfc8914#section-4.3
+"RFC 8914: Extended DNS Errors, section 4.3 - Unsupported DS Digest Type"
+
+[RFC8914sec4.4]:
+https://datatracker.ietf.org/doc/html/rfc8914#section-4.4
+"RFC 8914: Extended DNS Errors, section 4.4 - Stale Answer"
+
+[RFC8914sec4.5]:
+https://datatracker.ietf.org/doc/html/rfc8914#section-4.5
+"RFC 8914: Extended DNS Errors, section 4.5 - Forged Answer"
+
+[RFC8914sec4.6]:
+https://datatracker.ietf.org/doc/html/rfc8914#section-4.6
+"RFC 8914: Extended DNS Errors, section 4.6 - DNSSEC Indeterminate"
+
+[RFC8914sec4.7]:
+https://datatracker.ietf.org/doc/html/rfc8914#section-4.7
+"RFC 8914: Extended DNS Errors, section 4.7 - DNSSEC Bogus"
+
+[RFC8914sec4.8]:
+https://datatracker.ietf.org/doc/html/rfc8914#section-4.8
+"RFC 8914: Extended DNS Errors, section 4.8 - Signature Expired"
+
+[RFC8914sec4.9]:
+https://datatracker.ietf.org/doc/html/rfc8914#section-4.9
+"RFC 8914: Extended DNS Errors, section 4.9 - Signature Not Yet Valid"
+
+[RFC8914sec4.10]:
+https://datatracker.ietf.org/doc/html/rfc8914#section-4.10
+"RFC 8914: Extended DNS Errors, section 4.10 - DNSKEY Missing"
+
+[RFC8914sec4.11]:
+https://datatracker.ietf.org/doc/html/rfc8914#section-4.11
+"RFC 8914: Extended DNS Errors, section 4.11 - RRSIGs Missing"
+
+[RFC8914sec4.12]:
+https://datatracker.ietf.org/doc/html/rfc8914#section-4.12
+"RFC 8914: Extended DNS Errors, section 4.12 - No Zone Key Bit Set"
+
+[RFC8914sec4.13]:
+https://datatracker.ietf.org/doc/html/rfc8914#section-4.13
+"RFC 8914: Extended DNS Errors, section 4.13 - NSEC Missing"
+
+[RFC8914sec4.14]:
+https://datatracker.ietf.org/doc/html/rfc8914#section-4.14
+"RFC 8914: Extended DNS Errors, section 4.14 - Cached Error"
+
+[RFC8914sec4.15]:
+https://datatracker.ietf.org/doc/html/rfc8914#section-4.15
+"RFC 8914: Extended DNS Errors, section 4.15 - Not Ready"
+
+[RFC8914sec4.16]:
+https://datatracker.ietf.org/doc/html/rfc8914#section-4.16
+"RFC 8914: Extended DNS Errors, section 4.16 - Blocked"
+
+[RFC8914sec4.17]:
+https://datatracker.ietf.org/doc/html/rfc8914#section-4.17
+"RFC 8914: Extended DNS Errors, section 4.17 - Censored"
+
+[RFC8914sec4.18]:
+https://datatracker.ietf.org/doc/html/rfc8914#section-4.18
+"RFC 8914: Extended DNS Errors, section 4.18 - Filtered"
+
+[RFC8914sec4.19]:
+https://datatracker.ietf.org/doc/html/rfc8914#section-4.19
+"RFC 8914: Extended DNS Errors, section 4.19 - Prohibited"
+
+[RFC8914sec4.20]:
+https://datatracker.ietf.org/doc/html/rfc8914#section-4.20
+"RFC 8914: Extended DNS Errors, section 4.20 - Stale NXDOMAIN Answer"
+
+[RFC8914sec4.21]:
+https://datatracker.ietf.org/doc/html/rfc8914#section-4.21
+"RFC 8914: Extended DNS Errors, section 4.21 - Not Authoritative"
+
+[RFC8914sec4.22]:
+https://datatracker.ietf.org/doc/html/rfc8914#section-4.22
+"RFC 8914: Extended DNS Errors, section 4.22 - Not Supported"
+
+[RFC8914sec4.23]:
+https://datatracker.ietf.org/doc/html/rfc8914#section-4.23
+"RFC 8914: Extended DNS Errors, section 4.23 - No Reachable Authority"
+
+[RFC8914sec4.24]:
+https://datatracker.ietf.org/doc/html/rfc8914#section-4.24
+"RFC 8914: Extended DNS Errors, section 4.24 - Network Error"
+
+[RFC8914sec4.25]:
+https://datatracker.ietf.org/doc/html/rfc8914#section-4.25
+"RFC 8914: Extended DNS Errors, section 4.25 - Invalid Data"
+
+[unbound-604]:
+https://github.com/NLnetLabs/unbound/pull/604#discussion_r802678343
+"unbound#604: Add the basic EDE (RFC8914) cases"
+
+[unbound-1191]:
+https://github.com/NLnetLabs/unbound/issues/1191
+"unbound#1191: [FR] RFC8914 EDE Extended DNS Errors for RPZ"
+
+[RFC9250]:
+https://www.rfc-editor.org/rfc/rfc9250.html
+"RFC 9250: DNS over Dedicated QUIC Connections"
+
+[RFC9276]:
+https://www.rfc-editor.org/rfc/rfc9276.html
+"RFC 9276: Guidance for NSEC3 Parameter Settings"
+
+[draft-homburg-dnsop-codcp-01]:
+https://datatracker.ietf.org/doc/draft-homburg-dnsop-codcp/
+"I-D: Control Options For DNS Client Proxies"
+
+[pdns-12334]:
+https://github.com/PowerDNS/pdns/pull/12334
+"pdns#12334: rec: Generate EDE in more cases, specifically on unreachable auths or sythesized results."
+
+[pdns-12572]:
+https://github.com/PowerDNS/pdns/issues/12572
+"pdns#12572: dnsdist: Having the ability to add EDNS Extended Errors to responses"
+
+[draft-ietf-dnsop-compact-denial-of-existence]:
+https://datatracker.ietf.org/doc/draft-ietf-dnsop-compact-denial-of-existence/07/
+"I-D: Compact Denial of Existence in DNSSEC"
+
+[IANA EDE code registry]:
+https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#extended-dns-error-codes
+
+[DNS Hackathon 2023]:
+https://labs.ripe.net/author/becha/connected-to-port-53-a-report-from-the-dns-hackathon-2023/
+"DNS Hackathon 2023 - Rotterdam"
+
+[DNS Hackathon 2025]:
+https://github.com/DNS-Hackathon
+"DNS Hackathon 2025 - Stockholm"
+
+[old-doc]:
+https://github.com/DNS-Hackathon-2023/DApper/blob/main/EDE-testbed.md
+"Original documentation from the DApper project"
+
+[CannedDNS]:
+https://gitlab.com/canneddns/canneddns
+"CannedDNS"
+
+[dnsdist]:
+https://www.dnsdist.org/
+"dnsdist"
+
+[Response Policy Zone]:
+https://en.wikipedia.org/wiki/Response_policy_zone
+"Wikipedia - Response Policy Zone"
+
+[RIPE Atlas]:
+https://atlas.ripe.net/
+"RIPE Atlas"
+
+[git-repo]:
+https://git.2e8.dk/dn5/ede/
+"git repo for this testbed"
diff --git a/gen_canned.sh b/gen_canned.sh
new file mode 100755
index 0000000..2d30518
--- /dev/null
+++ b/gen_canned.sh
@@ -0,0 +1,122 @@
+#!/bin/sh
+
+# testzone
+DOMAIN=ede.dn5.dk.
+SERIAL=2025041804
+
+gen_ede_zone(){
+ local seq="$1"
+ local tmpl="templates/$2"
+ local zonefmt="$3"
+ local zoneparam="$4"
+ local ede_code="$5"
+ local extra_text="$6"
+ local zone="$(printf "$zonefmt" "$zoneparam")"
+ local out="canned/${seq}_${zone}toml"
+ cat "$tmpl" |
+ sed -e "s/{qname}/$zone/g" |
+ sed -e "s/{ede_code}/$ede_code/g" |
+ sed -e "s/{ede_extra}/$extra_text/g" \
+ > "$out"
+}
+
+gen_ede_zones(){
+ local offset="$1"
+ local tmpl="$2"
+ local zonefmt="$3"
+
+ gen_ede(){
+ local ede_code="$1"
+ local long_name="$2"
+ local extra_text="$3"
+
+ gen_ede_zone "$(( offset + ede_code ))" \
+ "$tmpl" "$zonefmt" "$ede_code" \
+ "$ede_code" "$extra_text"
+ gen_ede_zone "$(( offset + 500 + ede_code ))" \
+ "$tmpl" "$zonefmt" "$long_name" \
+ "$ede_code" "$extra_text"
+ }
+
+ gen_ede 0 other-error 'Other Error aka. user error'
+ gen_ede 1 unsupp-dnskey-algo 'Try a simpler algorithm'
+ gen_ede 2 unsupp-ds-digest-type 'Try using another one'
+ gen_ede 3 stale 'Zombie reply 🧟'
+ gen_ede 4 forged 'Cloned reply'
+ gen_ede 5 dnssec-indeterminate 'maybe secure'
+ gen_ede 6 dnssec-bogus 'probably not secure'
+ gen_ede 7 sig-expired 'signature expired πŸ₯€'
+ gen_ede 8 sig-in-future \
+ '1st rule of time machines: Sharing is caring'
+ gen_ede 9 dnskey-missing 'DNSKEY missing'
+ gen_ede 10 rrsig-missing 'RRSIG missing'
+ gen_ede 11 no-zone-key-bit 'Where did the zone key bit go?'
+ gen_ede 12 nsec-missing 'No NSEC?'
+ gen_ede 13 cached-error 'AFAIR 🀞'
+ gen_ede 14 not-ready 'πŸ’€'
+ gen_ede 15 blocked '🚧 Blocked 🚧'
+ gen_ede 16 censored 'β›” Censored β›”'
+ gen_ede 17 filtered 'β˜‚ Filtered β˜‚'
+ gen_ede 18 prohibited \
+ '🚫 You have requested a prohibited domain! 🚫'
+ gen_ede 19 stale-nxdomain 'AFAIR 🀞'
+ gen_ede 20 not-auth 'Not authoritative for this domain'
+ gen_ede 21 not-supp 'Unsupported βœ‹'
+ gen_ede 22 auth-ns-out-for-lunch 'TODO βœ‹'
+ gen_ede 23 network-error 'πŸ”₯πŸ”₯πŸ”₯'
+ gen_ede 24 invalid-data 'Invalid data'
+ gen_ede 25 sig-reversed-time \
+ 'Please reverse the flow of time before use.'
+ gen_ede 26 too-early "Let's do a 🀝 first"
+ gen_ede 27 unsupp-nsec3-iter 'Try using another iterator'
+ gen_ede 28 bad-proxy-policy \
+ "πŸ›‘ This service hasn't been vetted! πŸ›‘"
+ gen_ede 29 synthesized 'This query is a part of the conspiracy'
+ gen_ede 30 invalid-query-type "You can't query this kind of RRs"
+}
+
+copy_can(){
+ local file="$1"
+ cp "static/$file" "canned/$file"
+}
+
+find_all_zones(){
+ grep -h '^QName' canned/*.toml | cut -d'"' -f2 | sort | uniq
+}
+
+gen_nodata(){
+ local all_zones="$(find_all_zones)"
+ local i=9000
+ for zone in $all_zones; do
+ cat templates/nodata.toml |
+ sed -e "s/{qname}/$zone/g" \
+ > canned/${i}_nodata.toml
+ i=$((i + 1))
+ done
+}
+
+canned_replace(){
+ local needle="$1"
+ local replacement="$2"
+
+ grep -l -R -F "$needle" canned/ |
+ xargs -r -- sed -i -e "s/$needle/$replacement/g"
+}
+
+generate_files(){
+ rm -rf canned/
+ mkdir canned/
+ cp static/* canned/
+ gen_ede_zones 1000 ede-nx.toml "%s.nx.$DOMAIN"
+ gen_ede_zones 2000 ede-cname.toml "%s.cname.$DOMAIN"
+ gen_ede_zones 3000 ede-addr-a.toml "%s.addr.$DOMAIN"
+ gen_ede_zones 4000 ede-addr-aaaa.toml "%s.addr.$DOMAIN"
+ gen_nodata
+ canned_replace '{serial}' "$SERIAL"
+}
+
+main(){
+ generate_files
+}
+
+main "$@"
diff --git a/static/0000-refuse-root.toml b/static/0000-refuse-root.toml
new file mode 100644
index 0000000..20fe65c
--- /dev/null
+++ b/static/0000-refuse-root.toml
@@ -0,0 +1,11 @@
+[Match]
+QType = ""
+QName = "."
+
+[Header]
+Rcode = 5 # REFUSED
+RecursionDesired = true
+CheckingDisabled = false
+
+[Response]
+CopyQuery = true
diff --git a/static/0001-refuse-iis.se.toml b/static/0001-refuse-iis.se.toml
new file mode 100644
index 0000000..4bf192c
--- /dev/null
+++ b/static/0001-refuse-iis.se.toml
@@ -0,0 +1,11 @@
+[Match]
+QType = ""
+QName = "xn--nameservertest.iis.se."
+
+[Header]
+Rcode = 5 # REFUSED
+RecursionDesired = true
+CheckingDisabled = false
+
+[Response]
+CopyQuery = true
diff --git a/static/0002-refuse-icann.org.toml b/static/0002-refuse-icann.org.toml
new file mode 100644
index 0000000..29f242f
--- /dev/null
+++ b/static/0002-refuse-icann.org.toml
@@ -0,0 +1,11 @@
+[Match]
+QType = ""
+QName = "xn--nameservertest.icann.org."
+
+[Header]
+Rcode = 5 # REFUSED
+RecursionDesired = true
+CheckingDisabled = false
+
+[Response]
+CopyQuery = true
diff --git a/static/0003-refuse-ripe.net.toml b/static/0003-refuse-ripe.net.toml
new file mode 100644
index 0000000..5cb6b5a
--- /dev/null
+++ b/static/0003-refuse-ripe.net.toml
@@ -0,0 +1,11 @@
+[Match]
+QType = ""
+QName = "xn--nameservertest.ripe.net."
+
+[Header]
+Rcode = 5 # REFUSED
+RecursionDesired = true
+CheckingDisabled = false
+
+[Response]
+CopyQuery = true
diff --git a/static/0010-edns_version.toml b/static/0010-edns_version.toml
new file mode 100644
index 0000000..e342fa8
--- /dev/null
+++ b/static/0010-edns_version.toml
@@ -0,0 +1,16 @@
+[Match]
+QType = ""
+QName = ""
+EDNSVer = "!0"
+
+[Header]
+RCode = 16 # BADVERS
+RecursionDesired = true
+CheckingDisabled = false
+
+[Response]
+CopyQuery = true
+
+[[Additional]]
+RType = "OPT"
+UDPSize = 1232
diff --git a/static/0100-soa-ede.dn5.dk.toml b/static/0100-soa-ede.dn5.dk.toml
new file mode 100644
index 0000000..6fcc015
--- /dev/null
+++ b/static/0100-soa-ede.dn5.dk.toml
@@ -0,0 +1,24 @@
+[Match]
+QType = "SOA"
+QName = "ede.dn5.dk."
+
+[Header]
+Rcode = 0 # NOERROR
+RecursionDesired = true
+Autoritative = true
+CheckingDisabled = false
+
+[Response]
+CopyQuery = true
+
+[[Answer]]
+Name = "ede.dn5.dk."
+RType = "SOA"
+TTL = 300
+NS = "ns.ede.dn5.dk."
+MBox = "hostmaster.ede.dn5.dk."
+Serial = {serial}
+Refresh = 14400
+Retry = 3600
+Expire = 604800
+MinTTL = 300
diff --git a/static/0101-any-rfc8482.toml b/static/0101-any-rfc8482.toml
new file mode 100644
index 0000000..5d27026
--- /dev/null
+++ b/static/0101-any-rfc8482.toml
@@ -0,0 +1,18 @@
+[Match]
+
+QType = "ANY"
+QName = ""
+
+[Header]
+Rcode = 0 # NOERROR
+RecursionDesired = true
+Autoritative = true
+CheckingDisabled = false
+
+[[Answer]]
+
+Name = "ede.dn5.dk."
+RType = "HINFO"
+TTL = 86400
+CPU = "RFC8482"
+OS = ""
diff --git a/static/0102-ns.toml b/static/0102-ns.toml
new file mode 100644
index 0000000..5345359
--- /dev/null
+++ b/static/0102-ns.toml
@@ -0,0 +1,18 @@
+[Match]
+QType = "NS"
+QName = "ede.dn5.dk."
+
+[Header]
+Rcode = 0 # NOERROR
+RecursionDesired = true
+Autoritative = true
+CheckingDisabled = false
+
+[Response]
+CopyQuery = true
+
+[[Answer]]
+Name = "ede.dn5.dk."
+RType = "NS"
+TTL = 300
+NS = "ns.ede.dn5.dk."
diff --git a/static/0103-txt.toml b/static/0103-txt.toml
new file mode 100644
index 0000000..cac42f4
--- /dev/null
+++ b/static/0103-txt.toml
@@ -0,0 +1,18 @@
+[Match]
+QType = "TXT"
+QName = "ede.dn5.dk."
+
+[Header]
+Rcode = 0 # NOERROR
+RecursionDesired = true
+Autoritative = true
+CheckingDisabled = false
+
+[Response]
+CopyQuery = true
+
+[[Answer]]
+Name = "ede.dn5.dk."
+RType = "TXT"
+TTL = 300
+TXT = "v=spf1 -all"
diff --git a/static/0104-mx.toml b/static/0104-mx.toml
new file mode 100644
index 0000000..58f6c77
--- /dev/null
+++ b/static/0104-mx.toml
@@ -0,0 +1,19 @@
+[Match]
+QType = "MX"
+QName = "ede.dn5.dk."
+
+[Header]
+Rcode = 0 # NOERROR
+RecursionDesired = true
+Autoritative = true
+CheckingDisabled = false
+
+[Response]
+CopyQuery = true
+
+[[Answer]]
+Name = "ede.dn5.dk."
+RType = "MX"
+TTL = 300
+Pref = 10
+MX = "mail.a5n.dk."
diff --git a/static/0110-a.toml b/static/0110-a.toml
new file mode 100644
index 0000000..ac79a03
--- /dev/null
+++ b/static/0110-a.toml
@@ -0,0 +1,18 @@
+[Match]
+QType = "A"
+QName = "ede.dn5.dk."
+
+[Header]
+Rcode = 0 # NOERROR
+RecursionDesired = true
+Autoritative = true
+CheckingDisabled = false
+
+[Response]
+CopyQuery = true
+
+[[Answer]]
+Name = "ede.dn5.dk."
+RType = "A"
+TTL = 300
+IP = "194.165.56.50"
diff --git a/static/0111-aaaa.toml b/static/0111-aaaa.toml
new file mode 100644
index 0000000..05a1082
--- /dev/null
+++ b/static/0111-aaaa.toml
@@ -0,0 +1,18 @@
+[Match]
+QType = "AAAA"
+QName = "ede.dn5.dk."
+
+[Header]
+Rcode = 0 # NOERROR
+RecursionDesired = true
+Autoritative = true
+CheckingDisabled = false
+
+[Response]
+CopyQuery = true
+
+[[Answer]]
+Name = "ede.dn5.dk."
+RType = "AAAA"
+TTL = 300
+IP = "2a10:2a80::50:2"
diff --git a/static/0120-notice-a.toml b/static/0120-notice-a.toml
new file mode 100644
index 0000000..2d71fd9
--- /dev/null
+++ b/static/0120-notice-a.toml
@@ -0,0 +1,18 @@
+[Match]
+QType = "A"
+QName = "notice.ede.dn5.dk."
+
+[Header]
+Rcode = 0 # NOERROR
+RecursionDesired = true
+Autoritative = true
+CheckingDisabled = false
+
+[Response]
+CopyQuery = true
+
+[[Answer]]
+Name = "notice.ede.dn5.dk."
+RType = "A"
+TTL = 300
+IP = "194.165.56.50"
diff --git a/static/0121-notice-aaaa.toml b/static/0121-notice-aaaa.toml
new file mode 100644
index 0000000..c05c989
--- /dev/null
+++ b/static/0121-notice-aaaa.toml
@@ -0,0 +1,18 @@
+[Match]
+QType = "AAAA"
+QName = "notice.ede.dn5.dk."
+
+[Header]
+Rcode = 0 # NOERROR
+RecursionDesired = true
+Autoritative = true
+CheckingDisabled = false
+
+[Response]
+CopyQuery = true
+
+[[Answer]]
+Name = "notice.ede.dn5.dk."
+RType = "AAAA"
+TTL = 300
+IP = "2a10:2a80::50:2"
diff --git a/static/0130-ns-a.toml b/static/0130-ns-a.toml
new file mode 100644
index 0000000..4082a7c
--- /dev/null
+++ b/static/0130-ns-a.toml
@@ -0,0 +1,18 @@
+[Match]
+QType = "A"
+QName = "ns.ede.dn5.dk."
+
+[Header]
+Rcode = 0 # NOERROR
+RecursionDesired = true
+Autoritative = true
+CheckingDisabled = false
+
+[Response]
+CopyQuery = true
+
+[[Answer]]
+Name = "ns.ede.dn5.dk."
+RType = "A"
+TTL = 300
+IP = "194.165.56.50"
diff --git a/static/0131-ns-aaaa.toml b/static/0131-ns-aaaa.toml
new file mode 100644
index 0000000..14b98dd
--- /dev/null
+++ b/static/0131-ns-aaaa.toml
@@ -0,0 +1,18 @@
+[Match]
+QType = "AAAA"
+QName = "ns.ede.dn5.dk."
+
+[Header]
+Rcode = 0 # NOERROR
+RecursionDesired = true
+Autoritative = true
+CheckingDisabled = false
+
+[Response]
+CopyQuery = true
+
+[[Answer]]
+Name = "ns.ede.dn5.dk."
+RType = "AAAA"
+TTL = 300
+IP = "2a10:2a80::50:2"
diff --git a/static/9999-nxdomain.toml b/static/9999-nxdomain.toml
new file mode 100644
index 0000000..66ad6fb
--- /dev/null
+++ b/static/9999-nxdomain.toml
@@ -0,0 +1,24 @@
+[Match]
+QType = ""
+QName = ""
+
+[Header]
+Rcode = 3 # NXDOMAIN
+RecursionDesired = true
+Autoritative = true
+CheckingDisabled = false
+
+[Response]
+CopyQuery = true
+
+[[Authority]]
+Name = "ede.dn5.dk."
+RType = "SOA"
+TTL = 300
+NS = "ns.ede.dn5.dk."
+MBox = "hostmaster.ede.dn5.dk."
+Serial = {serial}
+Refresh = 14400
+Retry = 3600
+Expire = 604800
+MinTTL = 300
diff --git a/templates/ede-addr-a.toml b/templates/ede-addr-a.toml
new file mode 100644
index 0000000..c0e1efa
--- /dev/null
+++ b/templates/ede-addr-a.toml
@@ -0,0 +1,25 @@
+[Match]
+QType = "A"
+QName = "{qname}"
+
+[Header]
+Rcode = 0 # NOERROR
+RecursionDesired = true
+Autoritative = true
+CheckingDisabled = false
+
+[Response]
+CopyQuery = true
+
+[[Answer]]
+Name = "{qname}"
+RType = "A"
+TTL = 300
+IP = "194.165.56.50"
+
+[[Additional]]
+RType = "OPT"
+UDPSize = 1232
+SetEDE = true
+EDECode = {ede_code}
+EDEExtra = "{ede_extra}"
diff --git a/templates/ede-addr-aaaa.toml b/templates/ede-addr-aaaa.toml
new file mode 100644
index 0000000..a37bfd6
--- /dev/null
+++ b/templates/ede-addr-aaaa.toml
@@ -0,0 +1,25 @@
+[Match]
+QType = "AAAA"
+QName = "{qname}"
+
+[Header]
+Rcode = 0 # NOERROR
+RecursionDesired = true
+Autoritative = true
+CheckingDisabled = false
+
+[Response]
+CopyQuery = true
+
+[[Answer]]
+Name = "{qname}"
+RType = "AAAA"
+TTL = 300
+IP = "2a10:2a80::50:2"
+
+[[Additional]]
+RType = "OPT"
+UDPSize = 1232
+SetEDE = true
+EDECode = {ede_code}
+EDEExtra = "{ede_extra}"
diff --git a/templates/ede-cname.toml b/templates/ede-cname.toml
new file mode 100644
index 0000000..3a42a2d
--- /dev/null
+++ b/templates/ede-cname.toml
@@ -0,0 +1,37 @@
+[Match]
+QType = "!SOA"
+QName = "{qname}"
+
+[Header]
+Rcode = 0 # NOERROR
+RecursionDesired = true
+Autoritative = true
+CheckingDisabled = false
+
+[Response]
+CopyQuery = true
+
+[[Answer]]
+Name = "{qname}"
+RType = "CNAME"
+TTL = 300
+Target = "notice.ede.dn5.dk."
+
+[[Additional]]
+Name = "notice.ede.dn5.dk."
+RType = "A"
+TTL = 300
+IP = "194.165.56.50"
+
+[[Additional]]
+Name = "notice.ede.dn5.dk."
+RType = "AAAA"
+TTL = 300
+IP = "2a10:2a80::50:2"
+
+[[Additional]]
+RType = "OPT"
+UDPSize = 1232
+SetEDE = true
+EDECode = {ede_code}
+EDEExtra = "{ede_extra}"
diff --git a/templates/ede-nx.toml b/templates/ede-nx.toml
new file mode 100644
index 0000000..440631a
--- /dev/null
+++ b/templates/ede-nx.toml
@@ -0,0 +1,19 @@
+[Match]
+QType = "!SOA"
+QName = "{qname}"
+
+[Header]
+Rcode = 3 # NXDOMAIN
+RecursionDesired = true
+Autoritative = true
+CheckingDisabled = false
+
+[Response]
+CopyQuery = true
+
+[[Additional]]
+RType = "OPT"
+UDPSize = 1232
+SetEDE = true
+EDECode = {ede_code}
+EDEExtra = "{ede_extra}"
diff --git a/templates/nodata.toml b/templates/nodata.toml
new file mode 100644
index 0000000..a75100f
--- /dev/null
+++ b/templates/nodata.toml
@@ -0,0 +1,24 @@
+[Match]
+QType = ""
+QName = "{qname}"
+
+[Header]
+Rcode = 0 # NOERROR
+RecursionDesired = true
+Autoritative = true
+CheckingDisabled = false
+
+[Response]
+CopyQuery = true
+
+[[Authority]]
+Name = "ede.dn5.dk."
+RType = "SOA"
+TTL = 300
+NS = "ns.ede.dn5.dk."
+MBox = "hostmaster.ede.dn5.dk."
+Serial = {serial}
+Refresh = 14400
+Retry = 3600
+Expire = 604800
+MinTTL = 300