sniproxy 0.11.1 (2026-03-17)
===============================

* Bugfix: Fix IPV6_V6ONLY not effective when binding privileged ports
  via binder child process. On Linux, setsockopt(IPV6_V6ONLY) after
  bind() is rejected with EINVAL, so the option must be set before
  bind(). The ipv6_v6only flag is now passed to the binder child which
  sets it before calling bind().

sniproxy 0.11.0 (2026-03-16)
===============================

* Feature: Add DTLS (TLS over UDP) protocol support. Proxies DTLS
  connections by extracting the SNI extension from the DTLS ClientHello
  datagram, enabling hostname-based routing for WebRTC, OpenConnect VPN,
  CoAP, and other UDP/DTLS protocols without decrypting traffic.
  Configure with `protocol dtls` in listener blocks.
* Security: Add DTLS source validation to prevent UDP reflection and
  amplification attacks. New UDP sessions require a retransmission
  from the client before the backend is contacted. Spoofed sources
  never retransmit and are silently dropped after 3 seconds. This
  eliminates ~10-20x amplification via backend ServerHello responses.
* Security: Add per-IP rate limiting and connection counting to UDP
  sessions, matching the TCP path. Previously an attacker from a single
  IP could exhaust all UDP sessions and file descriptors.
* Security: Tighten seccomp filter for the logger child process,
  restricting it to syslog and IPC-only network syscalls instead of the
  full network set.
* Security: Disable TLS session tickets for DNS-over-TLS connections
  to preserve forward secrecy.
* Security: Add missing `sendfd` and `recvfd` pledge promises on
  OpenBSD for binder fd passing during SIGHUP reload. The `sendfd`
  promise must also be in the parent's pledge set so the binder child
  can inherit it after fork.
* Security: Cap UDP pending datagram size to 4KB to limit memory when
  many sessions are in RESOLVING state simultaneously.
* Hardening: Detect explicit_bzero in configure so secure_memzero uses
  the compiler-safe variant instead of the volatile pointer loop
  fallback.
* Hardening: Wipe IPC send buffer before free to prevent ciphertext
  lingering in freed memory.
* Hardening: Set SOCK_CLOEXEC on DoT sockets in resolver child.
* Hardening: Add bounds check on Huffman tree int16_t cast.
* Hardening: Add compiler flags -Wshadow, -Wimplicit-fallthrough,
  -Wnull-dereference, -Wundef, -fno-delete-null-pointer-checks.
* Hardening: Harden systemd service with ProtectKernelTunables,
  ProtectKernelModules, ProtectKernelLogs, ProtectControlGroups,
  ProtectClock, ProtectHostname, PrivateDevices, RestrictNamespaces,
  RestrictRealtime, RestrictSUIDSGID, MemoryDenyWriteExecute, and
  LockPersonality.
* Hardening: Use memcpy for IPv6 address hashing in UDP session lookup
  for strict-alignment portability.
* Fix: Add underflow guard on conn_count_free_count in connection rate
  limiter, matching the existing guard in rate_limit_bucket_acquire.
* Fix: Check for partial writes in resolver child send, matching the
  parent-side pattern.
* Fix: Hold resolver_queries_lock across both resolver_attach_query and
  resolver_emit_query in query resubmission, matching the documented
  atomicity invariant.
* Fix: Exit resolver child on ev_loop_new failure instead of falling
  back to the parent's event loop.
* Fix: Check sock->failed early in DoT read/write callbacks to avoid
  calling SSL on known-broken connections.
* Fix: Warn when reuseport or ipv6_v6only changes on SIGHUP reload
  since these socket options cannot be changed on a live bound socket.
* Fix: Warn when resolver config changes on SIGHUP reload since the
  resolver child is not restarted.
* Fix: Annotate intentional switch fallthroughs for clang
  -Wimplicit-fallthrough compatibility.

sniproxy 0.10.0 (2026-03-14)
===============================

* Feature: Add `per_ip_max_connections` global directive to limit the
  number of simultaneously active connections from a single source IP.
  Prevents a single client from exhausting all connection slots. The
  default is 0 (disabled). Distinct from `per_ip_connection_rate` which
  limits the rate of new connections.
* Feature: Add `tcp_fastopen on` global directive to enable TCP Fast Open
  on listener sockets (server side, Linux/FreeBSD) and backend connect
  sockets (client side, Linux 4.11+). Reduces connection latency for
  clients and backends that support TFO.
* Feature: Add `backend_acl` global directive to restrict which backend
  addresses sniproxy may connect to after DNS resolution. Supports
  `allow_except` and `deny_except` policies with CIDR ranges, preventing
  abuse as an open proxy to reach internal hosts.
* Feature: Randomly select from multiple DNS results when resolving
  backend hostnames, distributing connections across DNS round-robin
  backends instead of always using the first record.
* Feature: Add `backend_affinity on` table directive for session
  persistence. When DNS returns multiple records for a backend, the
  client IP is hashed to consistently select the same backend.
* Feature: Auto-anchor literal hostname patterns in table entries.
  Bare hostnames like `example.com` are now compiled as `^example\.com$`
  so they match exactly, not as substrings of other hostnames. Patterns
  containing regex metacharacters are left as-is.
* Feature: Accept incoming PROXY protocol headers (v1 text and v2 binary
  auto-detected) on listeners via `proxy_protocol on` directive.
* Feature: Add PROXY protocol v2 (binary) output support for table backends
  and fallback addresses via `proxy_protocol_v2` keyword.
* Feature: Add Minecraft Java Edition protocol support for routing
  Minecraft connections by hostname without decrypting traffic.
* Feature: Add SIGCHLD handler to reap zombie child processes.
* Doc: Document HTTP/2 connection coalescing limitation and workarounds.
* Security: Fix broken binary search and missing host header detection
  in HPACK decoder. The static name index was sorted incorrectly for the
  binary search comparator, and literal Host headers were only detected
  by pointer comparison instead of string comparison.
* Security: Add missing `recvfd` pledge promise in logger child on
  OpenBSD. Without this, the kernel kills the logger child with SIGABRT
  when it receives file descriptors via SCM_RIGHTS.
* Security: Fix use-after-free in resolve_server_address on synchronous
  DNS failure.
* Security: Fix per-IP conn_count mismatch when PROXY protocol is used
  (connections were counted under the proxy address, not the real client).
* Fix: Enforce RFC 7541 dynamic table size update limits in HPACK decoder.
* Fix: Restart logger child on health check failure instead of fatal exit.
* Fix: Use non-blocking poll in logger health check to avoid stalling the
  event loop for up to 100ms every 30 seconds.
* Fix: Close unexpected file descriptors in logger child NEW_SINK handler.
* Fix: Handle ECHILD when killing logger child after daemonize.
* Fix: Fix stderr log messages silently lost when routed via logger child.
* Fix: Resolve relative config path before daemonize so SIGHUP reload
  works after chdir("/").
* Fix: Check pidfile write for errors.
* Fix: Fix -T 1.3 rejecting all TLS connections (legacy_version check
  blocked before supported_versions extension was examined).
* Fix: Fix fallback address losing port number on SIGHUP reload.
* Fix: Reject stray closing brace at config top level.
* Fix: Fix EAGAIN on logger IPC permanently killing logger child.
* Fix: Fix server buffer growing 4x instead of intended 2x on resize.
* Fix: Fix IPC crypto context leak in resolver init on socketpair/fork
  failure.
* Fix: Preserve errno across accept cleanup so EMFILE backoff triggers.
* Fix: Fix resolver IPC max payload length to include header.
* Fix: Use TOCTOU-safe directory validation for /var/run/sniproxy.
* Fix: Wipe all derived keys on IPC channel init failure.
* Fix: Fix resource leak on strdup failure in DoT server registration.
* Fix: Add overflow check to iovec total length in DoT send path.
* Fix: Snapshot protocol at accept time to prevent mismatch during reload.
* Fix: Fail backend init when literal pattern anchoring malloc fails.
* Fix: Fix ensure_logger_process return value after resend failure.
* Fix: Chown log files before dropping privileges so SIGHUP log rotation
  works.
* Fix: Emit PROXY TCP4 instead of TCP6 for IPv4-mapped IPv6 addresses.
* Hardening: Use reallocarray() for overflow-safe allocation throughout.
* Hardening: Replace assert() with proper runtime error handling.
* Hardening: Check setsockopt, fcntl, and inet_ntop return values.
* Hardening: Add closefrom to AC_CHECK_FUNCS for efficient fd cleanup.
* Build: Add missing OpenSSL and libbsd build dependencies.
* Build: Clean up unused functions, dead code, and redundant includes.

sniproxy 0.9.27 (2026-03-10)
===============================

* Feature: Add Minecraft Java Edition protocol support for routing
  Minecraft connections by hostname without decrypting traffic.
* Security: Fix use-after-free in resolve_server_address when DNS query
  fails synchronously (resolver down, hostname too long, alloc failure).
* Security: Fix use-after-free when c-ares calls callback synchronously
  during DNS resolution.
* Security: Fix use-after-free in resolver callback on config reload when
  the resolver child is restarted while queries are in flight.
* Security: Fix PROXY protocol header sent after client data instead of
  before, violating the PROXY protocol specification.
* Security: Fix HTTP Host header smuggling with bare "Host:" line
  containing no value.
* Security: Reject EOS symbol in HPACK Huffman decoder per RFC 7541.
* Fix: HTTP/2 empty HEADERS frame blocking CONTINUATION frames.
* Fix: HTTP parser returning wrong error for incomplete header lines.
* Fix: Dead Huffman padding validation in HPACK decoder.
* Fix: Double timestamp in syslog messages when logger process is enabled.
  Syslog entries no longer contain a redundant embedded timestamp.
* Fix: Clean up logger child sinks on abnormal parent exit, ensuring file
  handles are flushed and closed.
* Fix: Ignore SIGPIPE in logger child process to prevent silent death on
  platforms without MSG_NOSIGNAL.
* Fix: Blocking waitpid in logger that can stall the mainloop when the
  logger child is stuck on filesystem I/O.
* Fix: NULL dereference in err() during default logger initialization.
* Fix: NULL dereference on logger->sink in vlog_msg and timestamp
  formatting.
* Fix: File descriptor leak in recv_logger_message on validation failure.
* Fix: File descriptor leak in logger REOPEN handler.
* Fix: Double-close of file descriptor in obtain_file_sink.
* Fix: Recursive fork in logger child on pledge and crypto failure.
* Fix: Zombie and repeated forks in logger on crypto init failure.
* Fix: obtain_file_sink fallback ignoring logger_parent_fs_locked state.
* Fix: Connection leak when server buffer expansion fails.
* Fix: Assert and connection leak in reactivate_watchers for connections
  with empty abort message.
* Fix: Double close_server_socket regression in buffer overflow guard.
* Fix: IPC crypto state corruption on AEAD verification failure, send
  channel rekey derivation failure, and recv channel rekey derivation
  failure.
* Fix: Undefined behavior in ipc_crypto_open for zero-length payloads.
* Fix: Reading uninitialized control buffer on recvmsg error.
* Fix: Resource leak in ipc_crypto_channel_init; wipe base_key on
  partial initialization failure.
* Fix: Add defensive NULL check for CMSG_FIRSTHDR in ipc_crypto_send_msg.
* Fix: Zombie process in binder on crypto initialization failure.
* Fix: Use _exit in binder child on seccomp filter failure.
* Fix: fatal() on config file permission change during SIGHUP reload.
* Fix: Config parser now uses consistent -1 error returns, handles EOF
  without trailing newline, uses exact match for protocol names and
  bad_request action values, and rejects negative values in all numeric
  directives.
* Fix: Check fcntl return values when setting sockets nonblocking.
* Fix: Check ipc_crypto_channel_set_role return in resolver child.
* Fix: Retry resolver child send on EINTR.
* Fix: Strict < in TLS extension parsing loop conditions.
* Fix: XMPP parser returning incomplete for malformed unclosed quotes.
* Fix: Integer overflow in Minecraft parser on 32-bit platforms.
* Fix: Heap over-read in buffer_coalesce on malloc failure.
* Hardening: Tighten OpenBSD pledge promises after privilege drop.

sniproxy 0.9.26 (2026-03-08)
===============================

* Fix: Set logger IPC socket non-blocking to prevent up to 100ms mainloop
  stall during health check ping/pong exchanges.
* Performance: Combine logger IPC header and payload into a single sendmsg()
  call, eliminating a second syscall per log message.
* Performance: Cache EVP_CIPHER_CTX in IpcCryptoState so IPC encryption
  avoids EVP_CIPHER_CTX_new/free on every message.
* Performance: Use a cached send buffer in ipc_crypto_send_msg to avoid
  per-message malloc/free overhead.
* Build: Add missing limits.h include in resolv.c for INT_MAX.
* CI: Fix autorelease glob expansion of release notes containing asterisks.

sniproxy 0.9.25 (2026-03-08)
===============================

* Fix: Infinite loop in shrink_idle_buffers under memory pressure when
  buffers are already at minimum size and force-shrink is triggered.
* Fix: DNS per-client tracking bypass where IPv4-mapped IPv6 addresses
  (::ffff:x.x.x.x) were tracked separately from native IPv4, allowing
  clients to double the per-IP DNS query limit on dual-stack listeners.
* Fix: File descriptor leak in IPC crypto receive on short prefix read
  when SCM_RIGHTS ancillary data was delivered with partial message.
* Fix: HTTP/2 connection preface followed by incomplete SETTINGS frame
  incorrectly falling through to HTTP/1.1 Host header parsing.
* Fix: ACL policy validation allowing mixed allow_except and deny_except
  stanzas when the first listener used the implicit default mode.
* Fix: Logger priority name parser accepting prefix matches (e.g. "emer"
  matching "emergency"). Now requires exact match.
* Fix: Replace select()/FD_SET with poll() in logger health check to
  avoid undefined behavior when logger socket fd exceeds FD_SETSIZE.
* Fix: Handle SSL_ERROR_ZERO_RETURN (clean TLS shutdown) in DNS-over-TLS
  read path instead of treating it as an error.
* Fix: Drain OpenSSL error queue after DoT TLS error logging to prevent
  stale errors from contaminating subsequent operations.
* Fix: Clamp SSL_read and SSL_write length parameters to INT_MAX to
  prevent silent truncation on the OpenSSL int API boundary.
* Fix: Cap IPC payload length validation to INT_MAX in addition to the
  per-channel maximum, matching the OpenSSL AEAD API constraint.
* Fix: Add minimum size validation to binder AF_UNIX sockaddr comparison
  to prevent size_t underflow on truncated addresses.
* Fix: Add NULL guard to copy_address to prevent NULL dereference.
* Fix: size_t to int narrowing in config tokenizer next_word function.
* Fix: Misleading logger stanza error messages now distinguish between
  "both file and syslog" and "neither file nor syslog" configurations.
* Fix: Remove incorrect const qualifier from table_lookup_server_address
  which was cast away internally to mutate the lookup cache.
* Fix: Remove dead code in connection_cb buffer reserve failure path.
* Doc: Update mutex nesting documentation in resolver to reflect actual
  lock ordering (resolver_pending_lock -> resolver_queries_lock).

sniproxy 0.9.24 (2026-03-04)
===============================

* Feature: Add -t flag to test configuration and exit without starting the
  proxy, similar to nginx -t. Reports whether the config file is valid.
* Feature: Add -g flag to allow group-read (0640) config file permissions.
  After privilege drop the unprivileged process cannot re-read a root-owned
  0600 config file, causing SIGHUP reload to fail. The -g flag relaxes the
  permission check to accept 0640 while still rejecting group-write,
  group-exec, and all other access. A security warning is logged at startup.
* Improved config permission error messages to show the maximum allowed mode
  and hint about the -g flag when applicable.

sniproxy 0.9.23 (2026-03-02)
===============================

* Security: Normalize IPv4-mapped IPv6 addresses (::ffff:x.x.x.x) to their
  native IPv4 equivalents in the per-IP rate limiter so the same client gets a
  single rate limit bucket regardless of address family.
* Security: Log a warning to stderr when the SNIPROXY_DISABLE_SECCOMP
  environment variable bypasses the seccomp sandbox, providing an audit trail.
* Security: Verify privilege drop succeeded in the logger child process by
  checking getuid/geteuid/getgid/getegid after setuid, matching the main
  process verification.
* Reliability: Register new listener addresses with the binder allowlist
  during config reload (SIGHUP) so that listeners on privileged ports can be
  bound after hot-adding them.
* Reliability: Warn when user or group directives change on config reload
  since privilege drop via setuid is irreversible and requires a restart.

sniproxy 0.9.22 (2026-03-01)
===============================

* Resolver: Fix deadlock when resolver send fails during query emit, fix DNS
  timeout timer not rescheduled when already active, and use _exit() instead
  of exit() for seccomp failure in resolver child.
* Binder: Reject invalid sockaddr with length shorter than sa_family_t.
* Listener: Always set listener socket to nonblocking mode regardless of
  HAVE_ACCEPT4.
* Logger: Fix fd leak on allocation failure in NEW_SINK handler, use _exit()
  for seccomp failure in logger child, and fix dead retry loop where
  logger_process_failed prevented restart.
* Config: Fix connection_buffer_limit, client_buffer_limit,
  server_buffer_limit, and http_max_headers directives silently rejecting any
  config file that used them due to wrong success return value.
* HTTP/2: Fix completely broken HPACK Huffman decoder. The tree builder read
  bits from the wrong end of left-justified codes causing all symbols to
  collide, and internal nodes were indistinguishable from NUL byte leaves.
  Huffman-encoded headers from all major browsers now decode correctly.
* Testing: Fix dead round-trip test in ipc_crypto fuzz harness.

sniproxy 0.9.21 (2026-02-28)
===============================

* IPC: Fix fd loss in encrypted IPC receive where the frame length prefix read
  discarded SCM_RIGHTS ancillary data, breaking binder and logger fd passing.
  Also retry sendmsg() on EINTR to match the receive side.
* Resolver: Fix return value check after restart so pending queries are
  resubmitted; fix use-after-free when realloc relocates the nameserver list
  and strdup fails; replace fatal() with graceful error return so restart
  failures do not kill the process.
* Listener: Re-apply SO_KEEPALIVE, SO_REUSEPORT, and IPV6_V6ONLY on sockets
  obtained from the privileged binder fallback path.
* Config: Reject NaN and Infinity in floating-point config values; reject
  configuration files with unclosed braces.
* Logger: Drain payload on malloc failure to prevent IPC protocol desync; add
  recursion guard in init_default_logger() to prevent a memory leak.
* HTTP/2: Return correct incomplete status (-1) when no frames have been parsed
  yet, instead of incorrectly reporting no hostname found (-2).
* Shutdown: Remove pidfile on clean exit.

sniproxy 0.9.20 (2026-02-25)
===============================

* Memory: Server buffers now start at 32KB (was 64KB) and grow on demand;
  spliced connections shrink both buffers to 4KB since the kernel handles all
  forwarding; buffer pool max cached entries halved (worst-case 6MB, was 12MB);
  rate limit free list capped at 2048 entries (576KB, was 2.3MB); table hostname
  cache reduced to 256 slots (68KB per table, was 272KB).
* SO_SPLICE: User-space buffers are reclaimed when kernel splice activates, and
  the libev idle timer is stopped since the kernel splice timeout handles idle
  detection with proper reset on data flow.
* Reliability: Suppress expected EPROTO warnings when unsplicing connections
  that the peer already terminated.

sniproxy 0.9.19 (2026-02-06)
===============================

* Performance: SO_SPLICE zero-copy forwarding on OpenBSD eliminates user-space
  copies after the initial handshake; PCRE2 JIT compilation speeds backend regex
  matching 2-10x; HPACK dynamic table uses a ring buffer for O(1) inserts;
  TCP_NODELAY is set on both client and server sockets; table hostname cache
  grows from 256 to 1024 entries; redundant ev_io stop/start cycles and buffer
  zeroing on pool acquire are eliminated; TLS extension validation is merged
  into a single pass; per-backend single-entry match cache removed in favor of
  the table-level cache.
* Security: TLS extension count limit is now enforced consistently across all
  parsing paths including supported_versions; spliced connections are explicitly
  unspliced before idle timeout close to avoid kernel-side data leaks; SO_SPLICE
  cleanup failures are logged for debuggability.
* Bug fixes: Fix data corruption in buffer_coalesce optimization path, double
  close of file descriptors in ipc_crypto_recv_msg and logger LOGGER_CMD_DROP,
  errno clobbered before restart check in resolver_ipc_cb, reload_tables
  skipping consecutive table removals, accept_listener_arg returning success on
  invalid argument, listeners_reload ignoring init_listener failure, ambiguous
  prefix matching in accept_resolver_mode and lookup_syslog_facility, NULL
  pointer dereference in hpack_add_entry on malloc failure, overlapping memcpy
  in new_address port stripping, and blocking nanosleep retry loop in connect
  path.
* API: Getter/setter functions for connection header/idle timeouts, TLS
  extension limits, HTTP/2 frame and header count limits, and XMPP max header
  length allow programmatic tuning of protocol guardrails.

sniproxy 0.9.18 (2026-01-02)
===============================

* XMPP: Added `protocol = xmpp` listener support that parses the stream `to`
  attribute to route XMPP (including STARTTLS) connections by hostname; includes
  dedicated parser tests and a fuzz harness.
* Fuzzing: libFuzzer builds disable ASan ODR indicators and stub HTTP/TLS
  protocol pointers in the listener ACL fuzzer are weak so the real
  implementations can link without multiple-definition errors.

sniproxy 0.9.17 (2025-12-19)
===============================

* Security: IPC crypto rejects frames with generation jumps greater than 16,
  preventing attacker-crafted UINT32_MAX generations from forcing billions of
  rekey loops.
* Packaging/CI: release-packages workflow downloads autoconf 2.71 from GNU and
  kernel mirrors before falling back to ftp.gnu.org, reducing timeout failures
  when building release artifacts.

sniproxy 0.9.16 (2025-12-16)
===============================

* IPC crypto: Protocol bumped to IPC2 with an authenticated generation field,
  making rekey detection deterministic, auto-rekeying receivers after missed
  epochs, rejecting stale-generation frames to block replay-driven hangs, and
  keeping time-based rekeys working even when traffic is sparse.
* Tests: ipc_crypto debug and time-based rekey harnesses exercise the IPC2
  protocol and auto-resync behavior.

sniproxy 0.9.15 (2025-12-15)
===============================

* Security: Binder helper now validates AF_INET/AF_INET6/AF_UNIX stream socket
  requests against the listener allowlist, seccomp filters are
  process-specific, and IPC replay protection enforces strictly monotonic
  counters.
* TLS/DNS: DNS-over-TLS upstreams accept a configurable minimum TLS version
  (default tls1.2) and too-old ClientHello versions are rejected rather than
  routed to fallback backends.
* Reliability: Logger child health watchdog detects stalls, backend regex cache
  initialization failures no longer crash lookups, global ACL policy state is
  reset on reload, EINTR connect retries were fixed, and rate-limit OOM paths
  reject connections with exponential backoff.
* Documentation: README/man pages were refreshed and the splice(2) mention was
  removed.

sniproxy 0.9.14 (2025-12-03)
===============================

* Breaking change: DNS-over-TLS nameserver entries using IP literals now require
  either an explicit TLS hostname (`dot://IP/hostname`) or `/insecure`; bare
  IP-only DoT entries are rejected to avoid silent certificate skipping.
* Reliability: Fatal exit paths now log the failure reason before terminating,
  covering daemonization, privilege drops, and child process handoff failures.

sniproxy 0.9.13 (2025-11-25)
===============================

* Packaging/CI: Release workflow now discovers Rocky Linux releases via mirrors
  and Docker tags, builds both latest and previous Rocky majors with consistent
  jobs, falls back to microdnf when dnf is missing, and the openSUSE autoconf
  download uses a mirrored source when ftp.gnu.org is unavailable.
* Testing: Buffer tests create/destroy a dedicated libev loop to stop leaks,
  Valgrind workflow runs from the tests directory and surfaces failures, and
  the buffer leak regression is fixed.
* Bug fixes: Resolved a use-after-free when config files have incorrect
  permissions.

sniproxy 0.9.12 (2025-11-24)
===============================

* Packaging: rpmbuild now preserves distribution `%{optflags}` while appending
  the libev include path, drops the unused perl dependency, ships the missing
  `hostname_sanitize.h` in release tarballs, and the release-packages workflow
  can be triggered manually to build RPM/DEB artifacts on demand.
* Installation: Remove the `sniproxy` wrapper so only the real daemon is
  installed under sbin, avoiding duplicate or stale binaries on PATH.
* Tests: Added a resolver response fuzz harness with fuzz-only resolver helpers,
  expanded the libev stub to cover timers/signals/loop lifecycle, and fixed a
  resolver fuzz leak to keep fuzz runs stable.

sniproxy 0.9.11 (2025-11-23)
===============================

* Security: HTTP parsing now enforces a configurable `http_max_headers` guard
  (default 100), TLS ClientHello parsing counts extensions before iterating
  through them, and IPC crypto rejects frames via constant-time dummy decrypts
  that use dedicated zero_tag buffers so failure timing and nonce contents stay
  hidden.
* Reliability: All absolute-path directives are canonicalized, the config
  parser wires typed cleanup hooks so repeated resolver/logger/listener ACL
  blocks free their previous allocations, resolver restart paths serialize the
  pending list, and assertions in address/table helpers were replaced with
  runtime guardrails to avoid crashes on malformed input.
* Tooling/Tests: The deprecated `sniproxy-cfg` binary/man page were removed, a
  hardened `scripts/sniproxy.service` unit now ships while the wrapper script
  is gone so the daemon only installs under sbin, GitHub releases build
  Debian/RPM packages, and the fuzzing suite gained address/table/listener
  ACL/ipc harnesses with quieter logging by default.

sniproxy 0.9.10 (2025-11-22)
===============================

* Security: Temporary-file directories now use lstat() checks for /var/run and
  /tmp fallbacks plus the existing O_NOFOLLOW open, closing the last symlink
  attack window before dump generation.
* Robustness: Unix-domain listener/target parsing forcibly null-terminates
  sun_path after strncpy(), and cfg_tokenizer always null-terminates buffers
  even when hitting EOF or buffer limits.
* DNS: Configuration reloads and startup both apply the per-client DNS query
  cap together with the global limit so resolver throttles stay in sync.

sniproxy 0.9.9 (2025-11-21)
==============================

* Security: PROXY header emission now checks buffer capacity, logs the client
  when the header cannot be appended, and aborts routing instead of silently
  continuing; sockaddr parsing validates sa_len bounds, copy_sockaddr_to_storage
  clamps copies to the destination size, and backend match caching rejects
  lengths that would overflow allocations.
* Networking: Per-client DNS concurrency limits stack on the existing global cap
  so abusive clients cannot starve other lookups, the defaults rise to 16 per
  client and 512 overall with resolver max_concurrent_queries(_per_client)
  options, and address parsing handles optional trailing ports iteratively with
  centralized apply_port_if_needed logic while enforcing a maximum parser
  recursion depth.
* Crypto: ipc_crypto_seal verifies header/tag overhead, halts once the send
  counter reaches UINT64_MAX, and refuses SIZE_MAX-topping frames; derive_key now
  rejects HKDF labels longer than 1024 bytes before allocating.
* Reliability: Buffer helpers assert read/write offsets never exceed capacity and
  setup_write_iov bails out when a buffer reports a larger length than it
  allocated, preventing corruption before data hits the socket.

sniproxy 0.9.8 (2025-11-20)
==============================

* Security: libpcre2 is now the sole supported regex backend across runtime,
  fuzzers, and packaging; all builds fail fast if the legacy PCRE1 headers or
  libraries are missing.
* Security: HKDF info buffers are wiped before free, oversized labels are
  rejected, DNS resolver cancellation uses an explicit memory fence, and
  temporary connection dumps rely on `mkostemp()` with CLOEXEC/NOFOLLOW.
* Networking: Resolver blocks can now use DNS-over-TLS upstreams via
  `dot://address/hostname` entries, verifying upstream certificates with the
  system trust store.
* Hardening: configuration reloads re-check file permissions, path directives
  require absolute paths, and resolver search domains are treated as literal
  suffixes instead of being parsed as hostnames.
* Tooling/Docs: README, ARCHITECTURE.md, Debian/RPM metadata, and tests now
  describe the libpcre2 dependency and other behavioral refinements for 0.9.8.

sniproxy 0.9.7 (2025-11-19)
==============================

* DNS: resolver blocks now default to `dnssec_validation relaxed`, enabling AD
  bit enforcement whenever upstream supports DNSSEC without requiring manual
  configuration tweaks.
* Security: sniproxy refuses to load configuration files that
  are group/world accessible by validating permissions on the opened descriptor,
  ensuring both initial startup and reloads uphold the guardrail.
* Documentation: README, architecture notes, and man pages were refreshed to
  describe the DNSSEC default, resolver requirements, and stricter configuration
  permission enforcement.

sniproxy 0.9.6 (2025-11-18)
==============================

* Security: Harden per-IP rate limiting and parser guardrails with FNV-1a hashes,
  collision cutoffs, HTTP header caps, TLS extension limits, and IPC payload clamps
  to block CPU or memory exhaustion attempts.
* DNS: arc4random()-seeded query IDs, leak-resistant handle tracking, and
  mutex-protected restarts prevent counter drift, leaks, or use-after-free in
  resolver lifecycle transitions.
* Reliability: shrink candidate queues now cap at 4096 entries with active
  trimming, buffer growth failures close connections explicitly, and log
  duration math clamps wraparound to 0.0.
* Hardening: secure memory wiping, PID file sanity checks, and buffer pool magic
  numbers detect corruption and ensure sensitive state never lingers in RAM.

sniproxy 0.9.5 (2025-11-15)
==============================

* Performance: cached ev_now() readings and idle hysteresis reduce needless wakeups and buffer thrash.
* Reliability: resolver crash logging avoids noisy write/writev warnings on Linux builds.
* CI: fuzz workflow auto-selects a libFuzzer-capable clang and surfaces compiler diagnostics.

sniproxy 0.9.4 (2025-11-14)
==============================

* Resource: connection_buffer_limit/client_buffer_limit/server_buffer_limit cap per-connection buffering.
* Security: configs with group/world permissions now abort startup instead of warning.
* IPC: helper children close all inherited descriptors other than their IPC socket.

sniproxy 0.9.3 (2025-11-12)
==============================

* Security: privilege dropping now verifies real/effective UID and aborts if root.
* Security: sniproxy warns when config files are readable or executable by group/others.
* IPC: binder/logger/resolver channels now encrypt control traffic and tighten validation/error reporting.
* Performance: idle connection buffers shrink proactively once a soft memory budget is exceeded.
* Resource: new connection_buffer_limit/client_buffer_limit/server_buffer_limit directives cap per-connection buffering.

sniproxy 0.9.2 (2025-11-10)
==============================

* Reliability: resolver restarts keep pending DNS queries and
  automatically retry after the child process respawns.
* Reliability: binder helper respawns on IPC failures and uses
  length-prefixed framing to handle partial reads safely.
* Networking: outbound connects retry transient EADDRNOTAVAIL
  errors to smooth bursts of transparent proxy traffic.
