@@ -16,6 +16,7 @@ import (
1616 clocksmith "github.com/jedisct1/go-clocksmith"
1717 stamps "github.com/jedisct1/go-dnsstamps"
1818 "golang.org/x/crypto/curve25519"
19+ netproxy "golang.org/x/net/proxy"
1920)
2021
2122type Proxy struct {
@@ -108,6 +109,7 @@ type Proxy struct {
108109 SourceODoH bool
109110 listenersMu sync.Mutex
110111 ipCryptConfig * IPCryptConfig
112+ udpConnPool * UDPConnPool
111113}
112114
113115func (proxy * Proxy ) registerUDPListener (conn * net.UDPConn ) {
@@ -587,18 +589,68 @@ func (proxy *Proxy) exchangeWithUDPServer(
587589 if serverInfo .Relay != nil && serverInfo .Relay .Dnscrypt != nil {
588590 upstreamAddr = serverInfo .Relay .Dnscrypt .RelayUDPAddr
589591 }
590- var err error
591- var pc net.Conn
592+
592593 proxyDialer := proxy .xTransport .proxyDialer
593- if proxyDialer == nil {
594- pc , err = net .DialTimeout ("udp" , upstreamAddr .String (), serverInfo .Timeout )
595- } else {
596- pc , err = (* proxyDialer ).Dial ("udp" , upstreamAddr .String ())
594+ if proxyDialer != nil {
595+ return proxy .exchangeWithUDPServerViaProxy (serverInfo , sharedKey , encryptedQuery , clientNonce , upstreamAddr , proxyDialer )
597596 }
597+
598+ pc , err := proxy .udpConnPool .Get (upstreamAddr )
599+ if err != nil {
600+ return nil , err
601+ }
602+
603+ if err := pc .SetDeadline (time .Now ().Add (serverInfo .Timeout )); err != nil {
604+ proxy .udpConnPool .Discard (pc )
605+ return nil , err
606+ }
607+
608+ query := encryptedQuery
609+ if serverInfo .Relay != nil && serverInfo .Relay .Dnscrypt != nil {
610+ proxy .prepareForRelay (serverInfo .UDPAddr .IP , serverInfo .UDPAddr .Port , & query )
611+ }
612+
613+ encryptedResponse := make ([]byte , MaxDNSPacketSize )
614+ var readErr error
615+ for tries := 2 ; tries > 0 ; tries -- {
616+ if _ , err := pc .Write (query ); err != nil {
617+ proxy .udpConnPool .Discard (pc )
618+ return nil , err
619+ }
620+ length , err := pc .Read (encryptedResponse )
621+ if err == nil {
622+ encryptedResponse = encryptedResponse [:length ]
623+ readErr = nil
624+ break
625+ }
626+ readErr = err
627+ dlog .Debugf ("[%v] Retry on timeout" , serverInfo .Name )
628+ }
629+
630+ if readErr != nil {
631+ proxy .udpConnPool .Discard (pc )
632+ return nil , readErr
633+ }
634+
635+ proxy .udpConnPool .Put (upstreamAddr , pc )
636+
637+ return proxy .Decrypt (serverInfo , sharedKey , encryptedResponse , clientNonce )
638+ }
639+
640+ func (proxy * Proxy ) exchangeWithUDPServerViaProxy (
641+ serverInfo * ServerInfo ,
642+ sharedKey * [32 ]byte ,
643+ encryptedQuery []byte ,
644+ clientNonce []byte ,
645+ upstreamAddr * net.UDPAddr ,
646+ proxyDialer * netproxy.Dialer ,
647+ ) ([]byte , error ) {
648+ pc , err := (* proxyDialer ).Dial ("udp" , upstreamAddr .String ())
598649 if err != nil {
599650 return nil , err
600651 }
601652 defer pc .Close ()
653+
602654 if err := pc .SetDeadline (time .Now ().Add (serverInfo .Timeout )); err != nil {
603655 return nil , err
604656 }
@@ -856,5 +908,6 @@ func (proxy *Proxy) processIncomingQuery(
856908func NewProxy () * Proxy {
857909 return & Proxy {
858910 serversInfo : NewServersInfo (),
911+ udpConnPool : NewUDPConnPool (),
859912 }
860913}
0 commit comments