How Zone Aliasing Works¶
Zone aliasing is the core mechanism of ShadowDNS: the root domain is fully loaded into memory, a backup domain is just a pointer to the root, and at query time, in-bailiwick rewriting produces on the fly a response that "looks like a complete backup zone". This page describes the four stages of the query processing pipeline and the rewrite rules.
Query Processing Pipeline¶
Client query
|
v
[ View Matcher ]
| Evaluates match-clients rules (GeoIP country, GeoIP ASN,
| IP/CIDR, any) in declaration order. First match wins,
| returning the view name.
|
v
[ Alias Resolver ]
| Checks whether the queried zone is a backup alias. If so,
| rewrites the query name from backup.domain to root.domain
| before the lookup, and records the original backup name
| for use in the response.
|
v
[ Zone Lookup ]
| Looks up the matching owner entry in the selected view's
| in-memory zone tree (map[ownerName][]RR), O(1) per owner name.
| On no exact hit, attempts wildcard matching per RFC 4592:
| strips labels left to right, level by level, until a
| `*.<parent>` entry is found, or an existing name blocks
| it (ENT rule).
|
v
[ In-Bailiwick Rewrite ]
| Rewrites the owner name back to the backup domain. For RDATA
| fields containing DNS names (CNAME target, NS, MX, SRV,
| SOA MNAME/RNAME): if the target points inside the root zone,
| it is rewritten to the backup zone; targets pointing elsewhere
| (e.g., third-party CDN hostnames) are left unchanged.
|
v
Response sent to client
Stage Details¶
View Matcher¶
Each view's match-clients block is compiled at startup into an ordered slice of rules. Rules are evaluated left to right; the first rule matching the client's source IP determines the view, and if no view matches, the response is REFUSED. GeoIP lookups use MaxMind mmdb files read directly into memory; the mmdb files are reopened on every SIGHUP reload, so MaxMind's monthly updates take effect without restarting the process.
Alias Resolver¶
At query time, the resolver performs a longest-suffix match against the alias map (built at startup from the aliases section of shadowdns.yaml). A backup zone entry is a thin pointer — the resolver strips the backup suffix, substitutes the root suffix, and hands the rewritten name to the zone lookup. The original backup name is retained so the rewrite stage can restore it.
Zone Lookup¶
Zone data is stored as map[viewName]map[zoneName]*Zone, with each Zone holding a map[ownerName][]dns.RR. All structures are read-only after startup, so the read path requires no locking.
When an exact match yields no result, it falls back to wildcard matching per RFC 4592: DNS labels are stripped from the query name one at a time, probing the map for a *.<parent> entry, until the zone origin is reached or an existing name that blocks further traversal is hit (the empty non-terminal rule). CNAME wildcard synthesis and correct response owner name rewriting are supported.
Backup override records (TXT, MX, SRV provided by the backup zone's own zone file) are stored separately and merged into the result after the root lookup.
In-Bailiwick Rewrite¶
The rewrite rules are deliberately conservative:
| Target | Rewrite behavior |
|---|---|
| Owner name | Always rewritten (in-bailiwick by definition) |
| DNS names in RDATA (CNAME target, NS, MX, SRV, SOA MNAME/RNAME) | Rewritten only when pointing inside the root zone — ensuring the rewritten name can also be resolved correctly through the same alias mechanism |
| RDATA names pointing externally (e.g., third-party CDN hostnames) | Left unchanged |
| A / AAAA | Carry IP addresses; never rewritten |
| TXT | RDATA is treated as opaque data; never rewritten — even if the content string happens to equal the root domain name |
SOA Inheritance and Zone Transfers¶
- A backup zone's SOA is inherited from the root zone (the serial follows the root), so slaves can detect changes correctly.
- AXFR (full zone transfer over TCP) is supported for both root zones and alias zones; existing BIND slaves require no changes.
- NOTIFY is sent to each zone's NS records after startup and reload (can be disabled with
--no-notifyoroptions { notify no; };). NOTIFY target IPs are taken only from in-zone glue records; see Migrating from BIND for details.
Configuration Example¶
# shadowdns.yaml
aliases:
example.com: # root: fully loaded into memory
- backup.example.com # backup: a pointer to example.com
- mirror.example.com
An A query for www.backup.example.com returns exactly the same response as if "a complete backup.example.com zone had been loaded" — but only a single copy of the example.com authoritative data exists in memory.
For the complete rules governing aliases (uniqueness, self-alias prohibition, override record type restrictions), see shadowdns.yaml.