# synthetic A [CoreDNS](https://coredns.io) plugin that generates DNS records from IP addresses embedded in hostnames. Inspired by dnsmasq's [`synth-domain`](http://www.thekelleys.org.uk/dnsmasq/docs/dnsmasq-man.html) option, `synthetic` provides automatic alignment between forward and reverse lookups — eliminating a common class of DNS misconfigurations described in [RFC 1912 Section 2.1](https://tools.ietf.org/html/rfc1912#section-2.1). ## Features - **Forward lookups** (A/AAAA) — extract an IP from a prefixed hostname and respond directly - **Reverse lookups** (PTR) — generate a synthetic hostname from a reverse query - **Static record priority** — responses from upstream plugins (e.g., `file`) always take precedence over synthetic records - **Dual-stack** — full IPv4 and IPv6 support, including compressed IPv6 notation - **Configurable** — custom prefix, TTL, and per-network scoping ## How It Works ### Forward Lookups (A / AAAA) A hostname with an embedded IP address is resolved directly. Dots (IPv4) or colons (IPv6) are replaced with dashes in the hostname label: | Hostname | Record | Address | |---|---|---| | `ip-192-0-2-1.example.com` | A | `192.0.2.1` | | `ip-2001-db8-abcd--1.example.com` | AAAA | `2001:db8:abcd::1` | | `ip-2001-0db8-0000-0000-0000-0000-0000-0001.example.com` | AAAA | `2001:db8::1` | Only addresses within a configured `net` CIDR are resolved. All other queries pass through to the next plugin. ### Reverse Lookups (PTR) When a PTR query arrives, the plugin first consults the next plugin in the chain. If that plugin provides a successful answer (e.g., from a zone file), it is used as-is. Otherwise, a synthetic PTR record is generated pointing back to the corresponding forward hostname. IPv6 reverse responses use the compressed address form per [RFC 5952](https://tools.ietf.org/html/rfc5952#section-2.2). ## Configuration ### Directives | Directive | Description | Default | |---|---|---| | `net` | CIDR network(s) for which synthetic forward responses are generated. May be specified multiple times. | *(none — required for forward lookups)* | | `forward` | Domain name appended to synthetic hostnames in PTR responses. | *(none — required for reverse lookups)* | | `ttl` | Time-to-live for synthetic responses, in seconds. | `0` | | `prefix` | Hostname label prefix identifying synthetic queries. A trailing dash is added automatically. | `ip` | ### Examples **Forward lookups** for two IPv6 prefixes and one IPv4 prefix: ``` example.com { synthetic { net 2001:db8:abcd::/48 net 2001:db8:1234::/48 net 192.0.2.0/24 prefix ip ttl 300 } file db.example.com } ``` **Reverse lookups** for an IPv6 prefix, with zone file records taking priority: ``` d.c.b.a.8.b.d.0.1.0.0.2.ip6.arpa { synthetic { forward example.com prefix ip } file d.c.b.a.8.b.d.0.1.0.0.2.ip6.arpa } ``` ## Compiling into CoreDNS Follow the standard [external plugin procedure](https://coredns.io/manual/plugins/#plugins). Add the following line to `plugin.cfg` in the CoreDNS source tree: ``` synthetic:code.fritzlab.net/dns/synthetic ``` Then rebuild CoreDNS: ```sh go generate go build ``` ## FAQ ### Why not use the `template` plugin? Two reasons: 1. The `template` plugin cannot coexist with `file` for the same zone in a way that lets file records take priority. See this [upstream discussion](https://github.com/coredns/coredns/issues/2977#issuecomment-555938144). The `synthetic` plugin is designed to work alongside `file`, deferring to it for static records. 2. Writing regex patterns to match arbitrary IPv4 and IPv6 addresses within CIDR ranges is impractical. This plugin accepts CIDR notation directly. ## Development ```sh # Run tests go test -v ./... # Lint go vet ./... ``` ## License [Apache License 2.0](LICENSE)