@@ -408,11 +408,11 @@ func (t *Tester) testDNSCrypt(stamp stamps.ServerStamp) error {
408408 // The provider name in the stamp is already the full name to query (e.g., "2.dnscrypt.default.ns1.adguard.com")
409409 certName := providerName
410410
411- ctx , cancel := context .WithTimeout (context .Background (), t .timeout )
411+ // Use 3x timeout to allow for retries with slow resolvers (some take 5s to respond)
412+ // Budget: 3 UDP attempts * timeout + 2 delays + 1 TCP attempt
413+ ctx , cancel := context .WithTimeout (context .Background (), t .timeout * 3 )
412414 defer cancel ()
413415
414- dialer := net.Dialer {Timeout : t .timeout }
415-
416416 // Create TXT query for the certificate
417417 query := new (dns.Msg )
418418 query .SetQuestion (dns .Fqdn (certName ), dns .TypeTXT )
@@ -430,15 +430,24 @@ func (t *Tester) testDNSCrypt(stamp stamps.ServerStamp) error {
430430 var lastErr error
431431 for attempt := 0 ; attempt < maxUDPRetries ; attempt ++ {
432432 if attempt > 0 {
433- time .Sleep (udpRetryDelay )
433+ select {
434+ case <- ctx .Done ():
435+ return fmt .Errorf ("timeout after %d UDP attempts: %v" , attempt , lastErr )
436+ case <- time .After (udpRetryDelay ):
437+ }
434438 }
435439
436- conn , err := dialer .DialContext (ctx , "udp" , addr )
440+ // Per-attempt timeout for dialing
441+ dialCtx , dialCancel := context .WithTimeout (ctx , t .timeout )
442+ conn , err := new (net.Dialer ).DialContext (dialCtx , "udp" , addr )
443+ dialCancel ()
437444 if err != nil {
438445 lastErr = err
439446 continue
440447 }
441448
449+ // Per-attempt timeout for the query
450+ conn .SetDeadline (time .Now ().Add (t .timeout ))
442451 response , err := t .sendDNSQuery (conn , wireFormat , false )
443452 conn .Close ()
444453 if err != nil {
@@ -450,12 +459,15 @@ func (t *Tester) testDNSCrypt(stamp stamps.ServerStamp) error {
450459 }
451460
452461 // UDP failed after retries, try TCP
453- conn , err := dialer .DialContext (ctx , "tcp" , addr )
462+ dialCtx , dialCancel := context .WithTimeout (ctx , t .timeout )
463+ conn , err := new (net.Dialer ).DialContext (dialCtx , "tcp" , addr )
464+ dialCancel ()
454465 if err != nil {
455466 return fmt .Errorf ("connection failed (UDP: %v, TCP: %v)" , lastErr , err )
456467 }
457468 defer conn .Close ()
458469
470+ conn .SetDeadline (time .Now ().Add (t .timeout ))
459471 response , err := t .sendDNSQuery (conn , wireFormat , true )
460472 if err != nil {
461473 return err
@@ -464,10 +476,9 @@ func (t *Tester) testDNSCrypt(stamp stamps.ServerStamp) error {
464476 return t .validateDNSCryptResponse (response , stamp )
465477}
466478
467- // sendDNSQuery sends a DNS query over an established connection and returns the response
479+ // sendDNSQuery sends a DNS query over an established connection and returns the response.
480+ // The caller must set the connection deadline before calling this function.
468481func (t * Tester ) sendDNSQuery (conn net.Conn , wireFormat []byte , useTCP bool ) (* dns.Msg , error ) {
469- conn .SetDeadline (time .Now ().Add (t .timeout ))
470-
471482 if useTCP {
472483 // TCP requires 2-byte length prefix
473484 length := make ([]byte , 2 )
@@ -708,11 +719,11 @@ func (t *Tester) testRelay(stamp stamps.ServerStamp) error {
708719 return fmt .Errorf ("no server address in stamp" )
709720 }
710721
711- ctx , cancel := context .WithTimeout (context .Background (), t .timeout )
722+ // Use 3x timeout to allow for retries with slow relays
723+ // Budget: 3 UDP attempts * timeout + 2 delays (1s each) + 1 TCP attempt
724+ ctx , cancel := context .WithTimeout (context .Background (), t .timeout * 3 )
712725 defer cancel ()
713726
714- dialer := net.Dialer {Timeout : t .timeout }
715-
716727 // Create a DNS TXT query for the reference server's certificate
717728 query := new (dns.Msg )
718729 query .SetQuestion (dns .Fqdn (referenceProviderName ), dns .TypeTXT )
@@ -735,15 +746,24 @@ func (t *Tester) testRelay(stamp stamps.ServerStamp) error {
735746 var lastErr error
736747 for attempt := 0 ; attempt < maxUDPRetries ; attempt ++ {
737748 if attempt > 0 {
738- time .Sleep (udpRetryDelay )
749+ select {
750+ case <- ctx .Done ():
751+ return fmt .Errorf ("timeout after %d UDP attempts: %v" , attempt , lastErr )
752+ case <- time .After (udpRetryDelay ):
753+ }
739754 }
740755
741- conn , err := dialer .DialContext (ctx , "udp" , addr )
756+ // Per-attempt timeout for dialing
757+ dialCtx , dialCancel := context .WithTimeout (ctx , t .timeout )
758+ conn , err := new (net.Dialer ).DialContext (dialCtx , "udp" , addr )
759+ dialCancel ()
742760 if err != nil {
743761 lastErr = err
744762 continue
745763 }
746764
765+ // Per-attempt timeout for the query
766+ conn .SetDeadline (time .Now ().Add (t .timeout ))
747767 response , err := t .sendRelayQuery (conn , anonPacket )
748768 conn .Close ()
749769 if err != nil {
@@ -761,12 +781,15 @@ func (t *Tester) testRelay(stamp stamps.ServerStamp) error {
761781 }
762782
763783 // UDP failed after retries, try TCP
764- conn , err := dialer .DialContext (ctx , "tcp" , addr )
784+ dialCtx , dialCancel := context .WithTimeout (ctx , t .timeout )
785+ conn , err := new (net.Dialer ).DialContext (dialCtx , "tcp" , addr )
786+ dialCancel ()
765787 if err != nil {
766788 return fmt .Errorf ("relay test failed (UDP: %v, TCP connect: %v)" , lastErr , err )
767789 }
768790 defer conn .Close ()
769791
792+ conn .SetDeadline (time .Now ().Add (t .timeout ))
770793 response , err := t .sendRelayQueryTCP (conn , anonPacket )
771794 if err != nil {
772795 return fmt .Errorf ("relay test failed (UDP: %v, TCP: %v)" , lastErr , err )
@@ -789,10 +812,9 @@ func buildAnonPacket(serverIP net.IP, serverPort int, dnsQuery []byte) []byte {
789812 return packet
790813}
791814
792- // sendRelayQuery sends a query through the relay via UDP and returns the response
815+ // sendRelayQuery sends a query through the relay via UDP and returns the response.
816+ // The caller must set the connection deadline before calling this function.
793817func (t * Tester ) sendRelayQuery (conn net.Conn , packet []byte ) (* dns.Msg , error ) {
794- conn .SetDeadline (time .Now ().Add (t .timeout ))
795-
796818 if _ , err := conn .Write (packet ); err != nil {
797819 return nil , fmt .Errorf ("failed to write query: %v" , err )
798820 }
@@ -811,10 +833,9 @@ func (t *Tester) sendRelayQuery(conn net.Conn, packet []byte) (*dns.Msg, error)
811833 return response , nil
812834}
813835
814- // sendRelayQueryTCP sends a query through the relay via TCP and returns the response
836+ // sendRelayQueryTCP sends a query through the relay via TCP and returns the response.
837+ // The caller must set the connection deadline before calling this function.
815838func (t * Tester ) sendRelayQueryTCP (conn net.Conn , packet []byte ) (* dns.Msg , error ) {
816- conn .SetDeadline (time .Now ().Add (t .timeout ))
817-
818839 // TCP requires 2-byte length prefix
819840 length := make ([]byte , 2 )
820841 length [0 ] = byte (len (packet ) >> 8 )
0 commit comments