github semihalev/sdns v1.6.5

18 hours ago

Patch release for 1.6.3. Major focus on RFC 5011 trust-anchor correctness, DNSSEC validation hardening, and listener lifecycle. Also closes a build-tag bug that prevented 1.6.4 from releasing on FreeBSD/NetBSD/OpenBSD/DragonFly.

Note: 1.6.4 was tagged but never published — the goreleaser pipeline failed on freebsd_amd64 because of the reuseport_* build constraint bug fixed in this release. 1.6.5 is the first available shipping point that includes the trust-anchor work below.

What's Changed

Trust Anchors (RFC 5011)

A full pass over middleware/resolver/auto_trust_anchor.go to bring the resolver into alignment with RFC 5011 §2 / §4 and to harden persistence against partial failures. Highlights:

  • verifyFetchedKeys is now correct under KSK rollover. At-least-one-trusted-anchor RRSIG semantics with a narrow revoked-bootstrap carve-out (RFC 5011 §2.1: a revoked key may authenticate the RRset that contains it, but only for the purpose of validating its own revocation). Returns a split-mode flag so a revoked-only proof can tombstone the matching key but cannot seed AddPend or mark other anchors missing.
  • Revocation requires a self-signed RRSIG and key-material match, not just key-tag arithmetic. Defends against 16-bit tag collisions where an unrelated self-signed key could otherwise be admitted as a revocation of the real anchor.
  • Tombstones moved out of kskCurrent into a material-keyed store with its own state file. Tag collisions with future legitimate KSKs can no longer suppress them.
  • Missing keys remain valid trust-point keys until the remove hold-down expires (§4.2). Missing→Valid restoration on KeyPres. AddPend reset on KeyRem (the hold-down aborts cleanly instead of drifting through Missing). Missing-aged-out simply deletes (§2.4.2 is bookkeeping); only RevBit revocations tombstone permanently.
  • Configured-merge uses an immutable cfg.RootKeys snapshot taken at startup, honours tombstones by key material, filters seeded entries on load, and refuses to resurrect a stale admin-config anchor that the root has revoked.
  • Atomic gob writes (CreateTemp + fsync + rename + parent dirsync) with tombstones-first ordering. New revocations dual-write to a StateRevoked marker so a tombstone-write failure survives across retries; selective fail-closed only when an actual contraction would otherwise be lost on disk.
  • New errTrustAnchorsUnavailable gates answer / authority / validateDelegation and the two delegation-cache Set sites, so an empty trust set fails closed with SERVFAIL instead of slipping into the "unsigned delegation" branch. AutoTA's own DNSKEY query runs CD=true so it doesn't depend on r.rootKeys.

Bug Fixes

  • Build reuseport file on all BSDs, not just darwin. The file was named reuseport_darwin.go, which Go treats as an implicit GOOS=darwin build constraint. The explicit //go:build darwin || freebsd || netbsd || openbsd || dragonfly was ANDed against that, so freebsd/netbsd/openbsd/dragonfly all failed to link with undefined: defaultUDPWorkers / kernelLoadBalances / reusePortControl. Renamed to reuseport_bsds.go so the explicit tag governs.
  • Windows compatibility for AutoTA persistence. Split the post-rename directory fsync into platform-specific helpers (POSIX does it; Windows is a no-op since FlushFileBuffers on a directory handle requires GENERIC_WRITE which os.Open doesn't grant). Tombstones-file open errors now distinguish Windows sharing violations from real corruption — only decode failures fail closed.
  • DNSSEC validation hardening + DNAME correctness. SERVFAIL when a signed zone omits RRSIG. Multiple correctness fixes around DNAME synthesis and parallel lookup paths.
  • Five correctness bugs in the parallel lookup path.
  • Listener lifecycle: explicit fail-fast binds. Listener startup now fails immediately if a bind cannot be established, instead of silently degrading.

Performance

  • Pool net.Dialer and bypass DialContext on UDP upstream. Per-query allocation cut on the recursive hot path.
  • Pre-build hostsfile answer RRs at load time instead of constructing them per query.

Refactor / Naming

  • Package authcacheauthority (split into server.go + cache.go). Type renames: AuthServer/AuthServersServer/Servers; NSCacheCache; NSDelegation; DSRRDSSet; VersionIPVersion.
  • parentDSRR/parentdsrrparentDS.
  • accesslist.AccessListaccesslist.List; accesslog.AccessLogaccesslog.Log (config field names preserved).
  • r.ncacher.delegations; nameservers map type → hostSet; nameserverInfodelegationInfo with hosts field.
  • rootservers/rootkeys (smashed lowercase) → rootServers/rootKeys.
  • ipv4cache/ipv6cacheglueV4/glueV6.
  • Internal sub-pipeline now flows through the Queryer interface; util.ExchangeInternal retired.

Dependencies

  • github.com/semihalev/zlog/v2 → v2.0.6.
  • k8s.io/apimachinery → 0.36.0.
  • k8s.io/client-go → 0.36.0.
  • codecov/codecov-action → v6.

Full Changelog: v1.6.3...v1.6.5

Don't miss a new sdns release

NewReleases is sending notifications on new releases.