Skip to content

fix(server): return SERVFAIL instead of dropping valid queries - #33

Closed
JohnsonRan wants to merge 1 commit into
olicesx:mainfrom
JohnsonRan:pr/listener-servfail
Closed

fix(server): return SERVFAIL instead of dropping valid queries#33
JohnsonRan wants to merge 1 commit into
olicesx:mainfrom
JohnsonRan:pr/listener-servfail

Conversation

@JohnsonRan

Copy link
Copy Markdown
Contributor

Summary

Return an explicit DNS SERVFAIL response when a valid request cannot be
completed by the UDP or TCP listener, instead of silently dropping the request.

This also centralizes SERVFAIL construction, checks downstream UDP send errors,
and ensures complete DNS-over-TCP frames are written.

Changes

Listener failure responses

Return SERVFAIL for valid standard queries when:

  • asynchronous request processing returns an error
  • the overall request timeout expires
  • the flow-control permit limit is exhausted
  • the fast path returns an internal processing error

Malformed packets and DNS response packets remain silent and do not trigger a
listener-generated response.

Shared SERVFAIL construction

  • Add a shared wire-format SERVFAIL response builder.
  • Preserve the request TXID, Question section, and RD flag.
  • Set RCODE = SERVFAIL and advertise recursion availability.
  • Validate UDP listener input before constructing a response to avoid reflecting
    malformed or non-query packets.
  • Reuse the same response construction logic in the DoH path.

UDP send handling

Replace ignored send_to results with a shared send helper:

  • attempt a non-blocking try_send_to first
  • fall back to asynchronous send_to on WouldBlock
  • log other socket errors instead of silently discarding them

DNS-over-TCP framing

Replace the single write_vectored call with a complete framed write:

  • construct the two-byte DNS-over-TCP length prefix and DNS payload
  • use write_all to ensure the entire frame is written

A successful write or write_vectored call may be partial, so a single call
does not guarantee that the client receives the complete DNS message.

Motivation

Previously, several listener-side failures produced no DNS response:

valid query
  -> processing error, timeout, or permit exhaustion
  -> no response
  -> client waits for its own timeout

Returning SERVFAIL provides a standard DNS failure result and allows clients to
retry another resolver or apply their failure policy immediately.

Unchecked UDP writes also made socket-level send failures invisible, while a
partial TCP write could leave clients waiting for the remainder of a DNS frame.

Behavior

Successful query handling is unchanged.

Only requests that cannot be completed by the listener receive SERVFAIL.
Packets that cannot be validated as normal DNS queries are not answered.

Tests

The added tests cover:

  • SERVFAIL response construction and preservation of request metadata
  • rejection of malformed and non-query packets by the listener response builder
  • flow-control permit exhaustion
  • UDP processing timeout
  • TCP processing timeout and complete frame delivery
  • propagation of UDP socket send errors

Verification

  • cargo fmt --all -- --check
  • cargo test --all-targets
  • cargo clippy --all-targets --all-features -- -D warnings

olicesx added a commit that referenced this pull request Aug 8, 2026
fix(listener): integrate SERVFAIL, query validation, and UDP truncation (#31 #32 #33)
@olicesx

olicesx commented Aug 8, 2026

Copy link
Copy Markdown
Owner

This change was integrated into main via #34 (merged). Closing as completed.

@olicesx olicesx closed this Aug 8, 2026
@JohnsonRan
JohnsonRan deleted the pr/listener-servfail branch August 8, 2026 08:42
JohnsonRan pushed a commit to JohnsonRan/kixdns that referenced this pull request Aug 8, 2026
…olicesx#33 SERVFAIL

- olicesx#32: is_standard_query_header ingress guard (UDP/TCP) + parse_quick full
  Additional validation (compression pointers, OPT root/unique, EDNS options)
- olicesx#31: truncate_udp_response at record boundaries with TC bit, integrated
  into send_udp_response/try_send_udp_response (all 5 UDP send paths)
- olicesx#33: strict try_build SERVFAIL for UDP/TCP + DoH reuse, write_all TCP framing
- fix: permit-exhaustion path uses non-blocking send (backpressure drop) with
  fast SERVFAIL from pre-parsed data, never blocks the receive loop
- tests: 99 lib + 6 bin + 9 doh integration, clippy -D warnings, fmt clean
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants