Skip to content

Releases: AZBrandCanada/Unified-DNS

v1.1.1

Choose a tag to compare

@github-actions github-actions released this 18 Sep 02:33

[1.1.1] — 2026-09-18

This release is a large correctness and feature expansion. It fixes a set of
DNSSEC validation bugs that caused certain well-signed zones to be rejected,
adds two new transport protocols, introduces a bounded caching engine with
single-flight coalescing and background prefetch, adds an optional
geo-aware GSLB layer with an authenticated multi-node peer mesh, and adds a
JSON metrics endpoint.

Users upgrading from v1.0.x should read the Migration Notes section at
the bottom of this entry.

Added — Transports

  • DNS-over-QUIC (DoQ), per RFC 9250. Binds to UDP/853 with ALPN doq,
    uses 2-octet length prefix framing, and returns RFC 9250 application reset
    codes (0x2 for DOQ_PROTOCOL_ERROR, 0x4 for DOQ_EXCESSIVE_LOAD).
  • DNS-over-HTTP/3 (DoH3), HTTP/3 over QUIC. Binds to UDP/443 with ALPN
    h3, and serves the same /dns-query, /health, and /metrics routes
    as DoH.
  • Reverse-proxy DoH mode via DOH_NO_TLS=1, allowing Nginx, Caddy, or
    Envoy to terminate TLS and forward plain HTTP to the resolver.

Added — Caching Engine

  • Bounded answer cache using W-TinyLFU admission and eviction. Capacity
    is configurable via CACHE_MAX_ENTRIES (default 500,000). Reads are
    lock-free and sharded; unrelated keys do not contend.
  • Single-flight coalescing on the cache-miss path. Concurrent identical
    {qname}:{qtype}:IN misses collapse into a single upstream resolution.
    Unrelated queries remain fully parallel.
  • Background prefetch for hot records nearing expiration. A dedicated
    loop considers only records with at least CACHE_PREFETCH_MIN_HITS
    hits, refreshes them in the last CACHE_PREFETCH_THRESHOLD_PCT percent
    of their TTL, applies exponential backoff after failures, and never
    invalidates a still-valid entry.
  • Cache-disabled test mode via CACHE_ENABLED=0, allowing A/B
    comparison between cached and uncached behavior. Single-flight remains
    active in this mode.
  • Negative entry tagging (EntryKind::Negative) to allow per-class
    metrics and future per-class eviction policies.
  • Hit tracker (HIT_TRACKER_MAX_ENTRIES, default 100,000) that scores
    record popularity for prefetch decisions.

Added — Root Zone Lifecycle

  • In-process root zone manager. Loads from a local JSON cache if
    present, parses the local text file if not, and downloads from IANA in
    the background if neither exists. Startup is never blocked on a network
    fetch.
  • Periodic in-process refresh (ROOT_ZONE_REFRESH_HOURS, default 168)
    that atomically replaces all root-zone-sourced delegations without
    touching dynamically-learned ones. No service restart required.
  • Root-zone-sourced delegation tagging (DelegationSource::RootZone)
    so that refreshes only replace entries the root zone manager owns.
  • Atomicity: if a refresh fails at any stage, the previous known-good
    root zone remains in use.
  • Status reporting via /metrics with explicit Valid,
    StaleButUsable, TooOld, and Missing states.

Added — Metrics and Observability

  • JSON /metrics endpoint on both DoH and DoH3 with counters for
    cache hits, misses, insertions, evictions, stale serves, negative cache,
    single-flight leaders and coalesced requests, prefetch attempts and
    successes, and root zone status.
  • /health endpoint returning a minimal liveness payload.

Added — Geo-Aware GSLB

  • Authoritative GSLB layer that can answer a configured set of names
    (e.g. dns.example.com) with region-appropriate A/AAAA records.
    Decisions are computed per-request and never enter the recursive cache.
  • Unbounded node configuration via NODE<N>_* environment variables.
    Nodes are discovered by walking NODE1, NODE2, … until an empty
    NODE<N>_NAME. No hardcoded upper bound.
  • GeoIP lookup using a local MaxMind GeoLite2-City database. The
    database path is configurable via GEOIP_DATABASE.
  • EDNS Client Subnet (ECS) support (RFC 7871). When the upstream
    resolver includes an ECS option, the subnet is used for the GeoIP lookup
    in preference to the raw source IP.
  • Weighted node scoring combining geographic distance, measured
    health-check RTT, and per-node health state. Nodes missing geo or
    latency data degrade gracefully.
  • Local UDP health checks on a configurable interval
    (GEO_HEALTH_INTERVAL), with a three-state machine
    (Unknown/Healthy/Degraded/Unhealthy) and hysteresis.
  • Multi-IP failover via GEO_IP_FAILOVER_IP. Setting it to 2 or more
    returns that many A/AAAA records, ordered best-first and excluding any
    node the health system has marked unavailable. This enables TCP-layer
    client failover without requiring a DNS re-resolution.
  • Health-filter fallback: if every node is excluded by the health
    system, the resolver returns all configured nodes rather than SERVFAIL
    or a single node.

Added — Peer Mesh (Cross-Node Health)

  • Authenticated inter-node heartbeat mesh for multi-node GSLB
    deployments. Each node periodically POSTs a signed JSON payload to every
    other node's /internal/peer-heartbeat endpoint.
  • Three-layer security: source-IP allowlist, HMAC-SHA256 signature
    over the raw body, and a 60-second timestamp replay window.
  • Graceful exclusion semantics: a peer is excluded from GSLB selection
    when its last heartbeat is older than 5 × GEO_PEER_HEARTBEAT_INTERVAL,
    or when it explicitly reports healthy=false. The local node never
    excludes itself.
  • Self-exclusion fix: the local node is not subject to peer mesh
    exclusion regardless of heartbeat state.
  • Health-fallback tolerance: when all peers are excluded, the resolver
    returns all configured nodes rather than failing closed.

Added — DNSSEC

  • Manual TBS (To-Be-Signed) construction that does not rely on the
    protocol library's built-in RRSIG serializer, which contains known
    correctness issues in the pinned dependency version.
  • RFC 4034 §6.2 canonical name lowercasing, including names embedded
    in RDATA (SOA mname/rname, CNAME, NS, MX, SRV, PTR).
  • Escaped-dot label handling for SOA rname fields like
    disa\.tinker\.ie\.list\.dci-ii-dns.mail.mil. — labels are emitted by
    iterating raw bytes rather than re-parsing the ASCII presentation form.
  • Empty non-terminal detection. Names like go.jp that exist only as
    ancestors of other names are correctly distinguished from real insecure
    delegations, based on the NS bit in the parent's NSEC/NSEC3 type bitmap.
  • NSEC3 Opt-Out handling per RFC 5155 §8.7. Opt-Out proofs are honored
    for DS queries (proving insecure delegation) but not accepted as generic
    proof that arbitrary records do not exist inside a signed zone. When an
    opt-out NSEC3 covers the next-closer name for a non-DS query, the
    response is treated as Insecure rather than Secure.
  • Wildcard RRSIG downgrade in NSEC3 zones. Wildcard answers in NSEC3
    opt-out zones are conservatively downgraded from Secure to
    InsecureUnknown because full RFC 5155 §8.8 wildcard proof validation is
    not yet implemented.
  • Case-insensitive DNS name comparison (RFC 4343) throughout the
    validator, chain builder, negative validation, and TBS construction.
    Authoritative servers that return mixed-case owner names within a single
    RRset no longer cause record-set filters to drop half the RRset.

Added — Cryptographic Backends

  • Pure-Rust RSA/SHA-1 fallback for algorithm 5 and algorithm 7 DNSKEYs
    whose modulus is below 2,048 bits. The optimized ring backend rejects
    these keys; the pure-Rust rsa crate does not. This restores validation
    for legacy zones such as uk.com, eu.com, us.com, co.com,
    de.com, uk.net, cmu.edu, and several *.mil and *.go.jp zones.
  • Algorithm 7 (RSASHA1-NSEC3-SHA1) is now correctly mapped to the same
    crypto path as algorithm 5, per RFC 5155 §2. The NSEC3 distinction only
    affects the algorithm identifier and the denial-of-existence records, not
    the RSA/SHA-1 signature computation.
  • Post-quantum ML-DSA-44 / DNSSEC Algorithm 18 verification continues
    to be supported via the RustCrypto ml-dsa backend.

Fixed — DNSSEC Validation

  • Truncated TBS construction — the previous release used the protocol
    library's TBS::from_rrsig, which under the pinned version emits only
    the RRSIG RDATA prefix and drops the RRset records entirely, producing a
    26-byte TBS instead of ~61 bytes. Every signature verification failed
    regardless of algorithm or key size. This affected all zones signed with
    algorithm 7.
  • Uppercase owner names — authoritative servers returning
    CMU.EDU. or APPS.MIL. no longer cause signature verification to fail
    because the canonical data is now lowercased per RFC 4034 §6.2.
  • SOA with escaped dots — SOA rname fields containing escaped dots
    (common in .mil) no longer produce invalid wire encoding.
  • Empty non-terminals — go.jp and similar names no longer terminate
    the trust chain walk prematurely.
  • NSEC3 opt-out — wildcard and NXDOMAIN answers in opt-out zones no
    longer produce a false Secure verdict.

Fixed — Caching

  • Thundering herd on cache miss — concurrent identical misses now
    coalesce into a single upstream resolution.
  • Unbounded cache growth — the answer cache is now bounded by
    W-TinyLFU admission rather than growing without limit.
  • Negative entries are now tagged and tracked separately, enabling
    future per-class policies and per-class metrics.

Fixed — GSLB

  • Self-exclusion bug — the local node was previously treated the same
    as peers by the mesh exclusion logic, causing it to exclude itself when
    no heartbeat had been received for its own name. It is now excluded from
    pee...
Read more

v1.1.0

Choose a tag to compare

@github-actions github-actions released this 17 Sep 06:28

Included

  • Iterative recursive resolution from the IANA root servers
  • DNSSEC validation from the root trust anchors down to leaf RRSIGs
  • RSA/SHA-256, RSA/SHA-512, ECDSA P-256, ECDSA P-384, and Ed25519
    signature verification via ring
  • ML-DSA-44 (post-quantum DNSSEC Algorithm 18) verification via RustCrypto
  • Plain DNS over UDP and TCP (port 53)
  • DNS-over-TLS (RFC 7858, port 853)
  • DNS-over-HTTPS (RFC 8484, port 443) with GET and POST support
  • NSEC and NSEC3 authenticated denial of existence
  • RFC 8767 stale-while-revalidate caching
  • RFC 1982 DNSSEC timestamp arithmetic
  • Per-subnet rate limiting (IPv4 /24, IPv6 /64)
  • UDP duplicate-domain Response Rate Limiting with a TC=1 challenge
  • SSRF protection via bailiwick and glue validation
  • Reverse-proxy mode for DoH
  • Self-signed development certificate generation
  • Unprivileged port fallback (5053/8853/8443)
  • Systemd unit example

v1.0.7

Choose a tag to compare

@github-actions github-actions released this 17 Sep 01:18

Full Changelog: v1.0.6...v1.0.7

v1.0.6

Choose a tag to compare

@github-actions github-actions released this 16 Sep 13:16

Full Changelog: v1.0.5...v1.0.6

v1.0.5

Choose a tag to compare

@github-actions github-actions released this 16 Sep 05:12

Full Changelog: v1.0.4...v1.0.5

v1.0.4

Choose a tag to compare

@github-actions github-actions released this 15 Sep 07:14

Full Changelog: v1.0.3...v1.0.4

v1.0.3

Choose a tag to compare

@github-actions github-actions released this 15 Sep 06:55

Full Changelog: v1.0.2...v1.0.3