Skip to content

Replies truncated for PQDNSCrypt queries sent through relays over TCP #3376

Closed

Description

@followtherabbit99

Diagnostic output

./dnscrypt-proxy -version
2.1.19

./dnscrypt-proxy -check
[NOTICE] Configuration successfully checked

./dnscrypt-proxy -resolve example.com
(resolves normally)
  • I read the documentation and this is not answered there
  • Initially raised as discussion #...

What's happening?

Post-quantum DNSCrypt queries sent through an Anonymized DNSCrypt relay are not padded to cover their response. A relay forwards the inner query to the resolver over UDP and drops any response larger than that inner query. A PQ query that carries a ciphertext is about 1220 bytes and a resumed PQ query is about 424 bytes, so any answer larger than that comes back truncated and the real data is lost. A classical DNSCrypt query to the same server, through the same relay, is padded to 4096 bytes and receives the full answer.

Draft-denis-dprive-dnscrypt-11 section 11.8 states that a resumed query using Anonymized DNSCrypt MUST keep enough padding for the relay's response-size check to pass.

When does this occur?

With pqdnscrypt enabled (the default) and a route to the PQ server through a relay. Every large answer fails when force_tcp is set or a SOCKS proxy is used, because the PQ query then always travels over TCP without the padding. In plain UDP mode the first large answer fails until the UDP size estimator grows.

Where does it happen?

encryptPQ in pq.go. The full-ciphertext query is padded with pqPad(packet, 64) and the resumed query with the small PQResumedPaddingFloor. Neither path grows the query when serverInfo.Relay != nil. The classical path in crypto.go does: for a relay over TCP it pads to MaxDNSPacketSize.

How do we replicate the issue?

  1. Configure a PQ-capable DNSCrypt server and route it through a relay in [anonymized_dns].
  2. Set force_tcp = true.
  3. Query a name whose answer is larger than about 700 bytes.

The answer comes back with the TC flag and no records. The same query to the same server through the same relay with pqdnscrypt = false returns the full answer.

Expected behavior

A PQ query sent through a relay is padded to leave room for its response, as the classical path already does, so large answers are not truncated.

Additional context

Reproduced on commit 71c139a. A standalone Go test with a fake PQ server and relay shows the PQ query returning 0 records with the TC flag while the classical query returns all records.

Activity

  1. jedisct1 commented on Oct 22, 2026

    @jedisct1
    Member

    Thank you!

    f8e8e1e should fix it.

    I've never used force_tcp :)

  2. added a commit that references this issue on Oct 22, 2026
    f8e8e1e
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions