@@ -372,15 +372,10 @@ type DNSExchangeResponse struct {
372372 err error
373373}
374374
375- // A resolver and an Anonymized DNSCrypt relay both refuse to return a UDP
376- // response larger than the request that triggered it, so a post-quantum
377- // certificate only comes back over UDP when the certificate probe is padded past
378- // the response. A PQ certificate is ~1.3 KB, ~1.5 KB once paired with the classical
379- // certificate; during a key rotation the set doubles to two classical plus two PQ
380- // certificates, roughly 3 KB. The large probe is padded past that rollover size so
381- // PQ discovery keeps working through relays even mid-rotation. The fragments-blocked
382- // probe stays small on purpose, so a PQ resolver truncates it and we still learn of
383- // PQ support through the TC bit and fall back to TCP.
375+ // The large certificate probe covers the PQ rollover set. It is tried first;
376+ // the small probe follows later to detect paths that block fragmented UDP.
377+ // Relayed TCP retries retain the large inner query because the relay forwards
378+ // the lookup over UDP and enforces amplification limits.
384379const (
385380 certProbePaddedLen = 3200
386381 certProbeFragmentsBlockedLen = 480
@@ -422,6 +417,10 @@ func DNSExchange(
422417 }
423418 queryCopy := cloneMsg (query )
424419 queryCopy .ID += uint16 (options )
420+ delay := time .Duration (250 * tries ) * time .Millisecond
421+ if tryFragmentsSupport {
422+ delay += 250 * time .Millisecond
423+ }
425424 go func (query * dns.Msg , delay time.Duration ) {
426425 time .Sleep (delay )
427426 option := DNSExchangeResponse {err : errors .New ("Canceled" )}
@@ -431,9 +430,9 @@ func DNSExchange(
431430 option = _dnsExchange (proxy , proto , query , serverAddress , relay , certProbeFragmentsBlockedLen )
432431 }
433432 option .fragmentsBlocked = true
434- option .priority = 1
433+ option .priority = 0
435434 channel <- option
436- }(queryCopy , time . Duration ( 250 * tries ) * time . Millisecond )
435+ }(queryCopy , delay )
437436 options ++
438437 }
439438 var bestOption * DNSExchangeResponse
@@ -487,21 +486,10 @@ func _dnsExchange(
487486 var rtt time.Duration
488487
489488 if proto == "udp" {
490- qNameLen , padding := len (query .Question [0 ].Header ().Name ), 0
491- if qNameLen < paddedLen {
492- padding = paddedLen - qNameLen
493- }
494- if padding > 0 {
495- paddingRR := & dns.PADDING {Padding : strings .Repeat ("00" , padding )}
496- query .Pseudo = append (query .Pseudo , paddingRR )
497- if query .UDPSize == 0 {
498- query .UDPSize = uint16 (MaxDNSPacketSize )
499- }
500- }
501- if err := query .Pack (); err != nil {
489+ binQuery , err := packDNSExchangeQuery (query , paddedLen , relay != nil )
490+ if err != nil {
502491 return DNSExchangeResponse {err : err }
503492 }
504- binQuery := query .Data
505493 udpAddr , err := net .ResolveUDPAddr ("udp" , serverAddress )
506494 if err != nil {
507495 return DNSExchangeResponse {err : err }
@@ -531,10 +519,19 @@ func _dnsExchange(
531519 rtt = time .Since (now )
532520 packet = packet [:length ]
533521 } else {
534- if err := query .Pack (); err != nil {
522+ var binQuery []byte
523+ var err error
524+ if relay == nil {
525+ err = query .Pack ()
526+ binQuery = query .Data
527+ } else {
528+ // The TCP stream itself does not fragment. Padding gives the relay
529+ // enough request bytes for the full PQ response on its UDP hop.
530+ binQuery , err = packDNSExchangeQuery (query , certProbePaddedLen , false )
531+ }
532+ if err != nil {
535533 return DNSExchangeResponse {err : err }
536534 }
537- binQuery := query .Data
538535 tcpAddr , err := net .ResolveTCPAddr ("tcp" , serverAddress )
539536 if err != nil {
540537 return DNSExchangeResponse {err : err }
@@ -581,3 +578,34 @@ func _dnsExchange(
581578 }
582579 return DNSExchangeResponse {response : & msg , rtt : rtt , err : nil }
583580}
581+
582+ func packDNSExchangeQuery (query * dns.Msg , paddedLen int , viaRelay bool ) ([]byte , error ) {
583+ if paddedLen <= 0 {
584+ if err := query .Pack (); err != nil {
585+ return nil , err
586+ }
587+ return query .Data , nil
588+ }
589+ if viaRelay {
590+ paddedLen -= anonymizedDNSHeaderSize
591+ }
592+ if paddedLen <= 0 {
593+ return nil , errors .New ("Padded length is too small for an anonymized DNS header" )
594+ }
595+
596+ paddingRR := & dns.PADDING {}
597+ query .Pseudo = append (query .Pseudo , paddingRR )
598+ if query .UDPSize == 0 {
599+ query .UDPSize = uint16 (MaxDNSPacketSize )
600+ }
601+ if err := query .Pack (); err != nil {
602+ return nil , err
603+ }
604+ if padding := paddedLen - len (query .Data ); padding > 0 {
605+ paddingRR .Padding = strings .Repeat ("00" , padding )
606+ if err := query .Pack (); err != nil {
607+ return nil , err
608+ }
609+ }
610+ return query .Data , nil
611+ }
0 commit comments