Skip to content

Release v0.6.1

Choose a tag to compare

@github-actions github-actions released this 21 Mar 03:50
· 6 commits to android-app since this release

SafeDNS v0.6.1

πŸ› Bug Fixes

  • Fixed stats always showing zero β€” getStats, clearStats, and clearCache
    were deadlocking the tokio runtime by calling block_on from a JNI thread while
    the runtime was already executing the proxy. Switched GLOBAL_STATS and
    GLOBAL_CACHE from tokio::sync::RwLock to std::sync::RwLock so JNI threads
    can read stats without touching the async runtime.

  • Fixed Android system DNS cache bypassing the proxy β€” Android was caching DNS
    responses with their original TTLs and serving repeat queries itself, so the proxy
    cache never got a second chance to hit. All responses now have their TTLs zeroed
    before being handed back to Android, forcing every lookup through the proxy while
    the proxy's own cache (with real TTLs) serves them instantly.

  • Fixed hardcoded DNS transaction ID in heartbeat β€” heartbeat queries always
    used 0x1234, causing response collisions when multiple packets were in-flight.

  • Fixed rustls-webpki vulnerability (RUSTSEC-2026-0049) β€” CRLs not considered
    authoritative by Distribution Point due to faulty matching logic. Pinned
    rustls-webpki to >=0.103.10 across the entire dependency tree (reqwest,
    rustls, rustls-platform-verifier, quinn).

  • Replaced unmaintained daemonize crate (RUSTSEC-2025-0069) β€” reimplemented
    as a POSIX double-fork using nix which is already a required CLI dependency.
    Behaviour is identical: double-fork + setsid + chdir /tmp + umask 0o022.


⚑ Performance

  • Single persistent DNS socket β€” replaced the fixed 8-thread executor and
    per-query DatagramSocket creation with one shared protected socket,
    demultiplexed by transaction ID via ConcurrentHashMap<Short, CompletableDeferred>.
    Eliminates socket creation overhead on every DNS query.

  • 16-shard DNS cache β€” the cache HashMap is now split into 16 independent
    shards using FNV-1a hashing. Concurrent queries hashing to different shards never
    block each other, reducing lock contention ~16x under load.

  • In-flight deduplication β€” concurrent queries for the same domain (the classic
    simultaneous A + AAAA lookup) now share a single DoH fetch. The first query
    fetches and broadcasts the result; waiters receive it instantly with zero extra
    upstream requests.

  • Negative caching β€” NXDOMAIN and NODATA responses (e.g. "does this domain have
    AAAA records?") are now cached using the SOA minimum TTL from the authority
    section. Previously these always went upstream.


✨ Improvements

  • Suffix-based cache exclusion β€” exclude_domain (single exact match) replaced
    with exclude_suffixes (comma-separated suffix list). "every1dns.net" now
    automatically excludes all UUID-prefixed subdomains like
    8f3a…b4e3.is-doh.help.every1dns.net without listing each one.

  • Heartbeat no longer pollutes logs and stats β€” the heartbeat now sends a direct
    HTTPS HEAD request to the DoH resolver URL instead of fake DNS wire packets. This
    means heartbeat activity never appears in the query log or inflates the stats
    counters. constructDnsQuery removed.

  • Network change recovery β€” the proxy backend now automatically restarts when
    Android reports a new network (WiFi β†’ mobile, reconnect after drop), triggering a
    fresh bootstrap DNS resolution against the new interface.

  • Cache miss counter β€” stats now track cache misses separately alongside hits.
    The StatsScreen shows a live Cache Hit Rate progress bar
    (hits / (hits + misses)), colour-coded green β‰₯60%, yellow β‰₯30%, red below.
    Cache stored entry count also displayed.

  • Clean log thread shutdown β€” stopProxy() now sends a Shutdown sentinel to
    the native log thread, allowing it to exit cleanly instead of leaking on service
    stop.

  • Flattened log sender code β€” the 8-level nested if let Ok pyramid in the JNI
    log forwarder replaced with a flat || -> Option<()> closure.

  • Play Store compliance β€” QUERY_ALL_PACKAGES permission removed. Replaced with
    a targeted <queries> intent filter (CATEGORY_LAUNCHER) which achieves the same
    result for the app exclusion screen without requiring broad package visibility
    justification.


Full Changelog: v0.6.0...v0.6.1