v1.6.7
A feature release: DNS64 (RFC 6147) lands as a first-class middleware, and the private-package layout gets tightened with an internal/ move. No security fixes in this one.
What's Changed
Features
-
DNS64 middleware (RFC 6147, #472). New
middleware/dns64synthesises AAAA records from A records for IPv6-only clients reaching IPv4-only services. Sits betweenkubernetesandcache; activates when a client's AAAA query has no usable answer and a secondary A lookup succeeds, embedding each IPv4 into a configured Pref64::/n per RFC 6052 §2.2.RFC 6147 coverage at a glance:
- §5.1.2 / §5.1.3 RCODE handling — NOERROR-NODATA / non-NXDOMAIN errors trigger synthesis; NXDOMAIN passes through.
- §5.1.4 exclusions — default AAAA exclusion
::ffff:0:0/96; default A exclusion under WKP follows the IANA Special-Purpose registry (incl.192.88.99.0/24per RFC 7526). - §5.1.5 CNAME / DNAME chains preserved on both synthesis and the no-A-records path.
- §5.1.6 — when the AAAA query yields empty/error, the A response's RCODE, Authority section, and chain become the basis for the client reply.
- §5.1.7 TTL =
min(A TTL, AAAA negative-cache TTL); 600 s ceiling when no SOA is present. - §5.2 multiple Pref64 prefixes synthesise in parallel; the well-known prefix
64:ff9b::/96is the runtime default when none is configured. - §5.3.1 PTR translation via CNAME to
in-addr.arpawith optional best-effort chase. - §5.3.2 — only Answer-section AAAAs are synthesised; Authority/Additional pass through unmodified.
- §5.5 DNSSEC safety —
CD=1requests and SERVFAILs carrying DNSSEC-validation EDE codes (1, 2, 5–12, 27) bypass synthesis entirely; on synthesised replies, AD is cleared and EDE 4 (Forged Answer) is attached when the upstream hadAD=1. - RD=0 clients skip DNS64 entirely.
Configuration:
[dns64] enabled = true prefixes = [\"64:ff9b::/96\"] client_networks = [] # empty = all clients exclude_zones = [] exclude_aaaa_networks = [\"::ffff:0:0/96\"] exclude_a_networks = [...] # IANA Special-Purpose default
Metrics: `dns64_synthesised_total`, `dns64_ptr_translated_total`, `dns64_passthrough_total{reason}`, `dns64_a_lookup_failures_total{reason}`.
Closes the last open item on the README TODO list.
Internal Refactor (#479)
Five packages that were never intended as public API move under `internal/`:
| Old path | New path |
|---|---|
| `cache` | `internal/cache` |
| `util` | `internal/dnsutil` (renamed) |
| `waitgroup` | `internal/waitgroup` |
| `mock` | `internal/mock` |
| `authority` | `internal/authority` |
`util` is renamed to `dnsutil` at the same time — the old name was the lowest-information identifier in Go and the package's actual contents (EDE, TTL, RRset construction, response classification) are entirely DNS-message helpers. The new name is self-documenting.
Pure rename + import-path updates, no behavioural changes. The module path is unchanged so the public binary and middleware-extension API (`middleware.Constructor` / `*config.Config`) keep working. External plugin authors who imported any of the five packages directly will need to either pin to an older version, vendor, or remove the dependency.
API Documentation
`api/README.md` rewritten end-to-end (139 → 105 lines). Fixes several inaccuracies in the previous draft:
- `/api/v1/block/exists/:key` returns `{"exists": }`, not the previously documented `{"success": true}`.
- `/api/v1/block/set/:key` and `/api/v1/block/remove/:key` can return `success: false` on duplicate / missing — not always `true`.
- 404 response shape for `/api/v1/block/get/:key` and 401 response shape are now documented explicitly.
- Bulk batch 400 cases (malformed JSON, unknown fields, oversized body, empty keys) documented.
- `/debug/pprof/*` routes documented with their auth-bypass caveat.
- `ReadHeaderTimeout`, `MaxBytesReader`, graceful-shutdown timeout, and async blocklist persistence semantics documented.
Conversational style with an endpoint table up top for quick scan and curl examples interleaved with prose where they help.
Dependencies
- `k8s.io/client-go`, `k8s.io/apimachinery` → v0.36.1 (#476, #477)
- `github.com/quic-go/quic-go` → v0.59.1 (#475)
- `golang.org/x/sys` → v0.44.0 (#474)
- `github.com/fsnotify/fsnotify` → v1.10.1 (#468)
Upgrade Notes
-
DNS64 is opt-in. `enabled = false` by default; existing deployments behave identically to 1.6.6 until you flip it.
-
Config compatibility: `configver` bumps to `1.6.7`. Existing configs continue to parse — you'll see a one-line "Config file is out of version" log warning until you regenerate. `contrib/linux/sdns.conf` has been refreshed and is the easiest reference for the new `[dns64]` block.
-
Plugin authors who imported moved packages need to update import paths. Migration cheat-sheet:
github.com/semihalev/sdns/cache → github.com/semihalev/sdns/internal/cache github.com/semihalev/sdns/util → github.com/semihalev/sdns/internal/dnsutil # also rename util.X → dnsutil.X github.com/semihalev/sdns/waitgroup → github.com/semihalev/sdns/internal/waitgroup github.com/semihalev/sdns/mock → github.com/semihalev/sdns/internal/mock github.com/semihalev/sdns/authority → github.com/semihalev/sdns/internal/authoritySince these are now `internal/`, the Go compiler will refuse to compile any out-of-tree code that imports them. The intended path forward for plugin authors is to depend only on the public middleware-extension surface (`config`, `middleware`, `ctx`, `server`).
-
No on-disk format changes to `trust-anchor.db` / `trust-anchor-tombstones.db` / blocklist persistence.
Full Changelog: v1.6.6...v1.6.7