- Collapse two 4-arm read/timeout matches to let-else (lose one
defensive debug log on payload-read timeout; idle timeouts are
routine on persistent DoT connections anyway)
- Drop MIN_MSG_LEN: DnsPacket::from_buffer rejects truncated input
on its own, and BytePacketBuffer is zero-init so buf[0..2] for
sub-2-byte messages just yields a harmless FORMERR with id=0
- Inline ACCEPT_ERROR_BACKOFF (single use site)
- Drop the partial cert/key warning: missing one of cert_path/
key_path silently falls back to self-signed; users see the
self-signed cert at startup and figure it out
- Drop dot_localhost_resolution test: RFC 6761 localhost is tested
in ctx.rs; this test only verified DoT transport, which
dot_resolves_local_zone already covers
- Drop self-documenting comment in dot_multiple_queries_on_persistent_connection
Net -32 lines, 125/125 tests pass, no behavior change users would notice.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Flatten 4-arm cert/key match in start_dot to 2 arms with the
partial-config warning hoisted into a one-liner above the match.
- Extract send_response() that serializes a DnsPacket and writes it
framed, used by both the FORMERR-on-parse-error and SERVFAIL-on-
resolve-error paths. Removes duplicated buffer/write/log boilerplate
and unifies the rescode logging via {:?}.
No behavior change; 126/126 tests still pass.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Address review findings on PR #25:
- Refactor resolve_query to take a pre-parsed DnsPacket. Parse-error
handling moves to the UDP caller, eliminating the double warn! line
on malformed UDP queries.
- Enforce MIN_MSG_LEN=12 (DNS header) in handle_dot_connection so
query_id extraction is always reading client-sent bytes, not the
zeroed buffer tail.
- Parse the DoT query before calling resolve_query and retain it, so
SERVFAIL responses can echo the original question section via
response_from(). Parse failures send FORMERR with the client id.
- Extract write_framed() helper for length-prefix + flush, reused by
success, SERVFAIL, and FORMERR paths.
- Back off 100ms on listener.accept() errors to avoid tight-looping
on fd exhaustion.
- Replace the hardcoded 127.0.0.1:53 upstream in dot_nxdomain_for_unknown
with a bound-but-unresponsive UDP socket owned by the test, making it
independent of the host's local resolver. Test now runs in ~220ms
(timeout lowered to 200ms) instead of 3s and asserts the question is
echoed in the SERVFAIL response.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add 10s timeout on TLS handshake — prevents clients from holding a
semaphore permit without completing the handshake
- Add IDLE_TIMEOUT on payload read_exact — prevents slowloris after
sending a valid length prefix then trickling bytes
- Extract accept_loop() shared between start_dot and tests — eliminates
duplicated accept logic that could drift
- Add 5s timeout on TCP reads in recursive test mock server
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Send SERVFAIL response (with correct query ID) when resolve_query
fails, preventing DoT clients from hanging until idle timeout
- Extract handle_dot_connection() so tests use the same logic as
production, eliminating duplicated accept/read/resolve loop
- Replace magic 4096 with named MAX_MSG_LEN constant tied to BUF_SIZE
- Add flush() after each TLS write to prevent buffered responses
- Extract fallback_tls() helper, handle partial cert/key config,
support IPv6 bind address, remove redundant crypto provider init
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Refactor handle_query into transport-agnostic resolve_query that returns
a BytePacketBuffer, keeping the UDP path zero-alloc. Add a TLS listener
on port 853 with persistent connections, idle timeout, connection limits,
and coalesced writes. Supports user-provided certs or self-signed CA
fallback. Includes 5 integration tests.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>