@@ -7,7 +7,10 @@ import (
77 "net"
88 "os"
99 "runtime"
10+ "strconv"
11+ "strings"
1012 "sync"
13+ "time"
1114
1215 "github.com/sagernet/quic-go"
1316 "github.com/sagernet/sing-quic"
@@ -28,6 +31,8 @@ type ClientOptions struct {
2831 Logger logger.Logger
2932 BrutalDebug bool
3033 ServerAddress M.Socksaddr
34+ ServerPorts []string
35+ HopInterval time.Duration
3136 SendBPS uint64
3237 ReceiveBPS uint64
3338 XPlusPassword string
@@ -48,6 +53,8 @@ type Client struct {
4853 logger logger.Logger
4954 brutalDebug bool
5055 serverAddr M.Socksaddr
56+ serverPorts []uint16
57+ hopInterval time.Duration
5158 sendBPS uint64
5259 receiveBPS uint64
5360 xplusPassword string
@@ -95,12 +102,22 @@ func NewClient(options ClientOptions) (*Client, error) {
95102 } else if options .ReceiveBPS < MinSpeedBPS {
96103 return nil , E .New ("invalid download speed" )
97104 }
105+ var serverPorts []uint16
106+ if len (options .ServerPorts ) > 0 {
107+ var err error
108+ serverPorts , err = ParsePorts (options .ServerPorts )
109+ if err != nil {
110+ return nil , err
111+ }
112+ }
98113 return & Client {
99114 ctx : options .Context ,
100115 dialer : options .Dialer ,
101116 logger : options .Logger ,
102117 brutalDebug : options .BrutalDebug ,
103118 serverAddr : options .ServerAddress ,
119+ serverPorts : serverPorts ,
120+ hopInterval : options .HopInterval ,
104121 sendBPS : options .SendBPS ,
105122 receiveBPS : options .ReceiveBPS ,
106123 xplusPassword : options .XPlusPassword ,
@@ -111,6 +128,38 @@ func NewClient(options ClientOptions) (*Client, error) {
111128 }, nil
112129}
113130
131+ func ParsePorts (serverPorts []string ) ([]uint16 , error ) {
132+ var portList []uint16
133+ for _ , portRange := range serverPorts {
134+ if ! strings .Contains (portRange , ":" ) {
135+ return nil , E .New ("bad port range: " , portRange )
136+ }
137+ subIndex := strings .Index (portRange , ":" )
138+ var (
139+ start , end uint64
140+ err error
141+ )
142+ if subIndex > 0 {
143+ start , err = strconv .ParseUint (portRange [:subIndex ], 10 , 16 )
144+ if err != nil {
145+ return nil , E .Cause (err , E .Cause (err , "bad port range: " , portRange ))
146+ }
147+ }
148+ if subIndex == len (portRange )- 1 {
149+ end = math .MaxUint16
150+ } else {
151+ end , err = strconv .ParseUint (portRange [subIndex + 1 :], 10 , 16 )
152+ if err != nil {
153+ return nil , E .Cause (err , E .Cause (err , "bad port range: " , portRange ))
154+ }
155+ }
156+ for i := start ; i <= end ; i ++ {
157+ portList = append (portList , uint16 (i ))
158+ }
159+ }
160+ return portList , nil
161+ }
162+
114163func (c * Client ) offer (ctx context.Context ) (* clientQUICConnection , error ) {
115164 conn := c .conn
116165 if conn != nil && conn .active () {
@@ -130,18 +179,33 @@ func (c *Client) offer(ctx context.Context) (*clientQUICConnection, error) {
130179}
131180
132181func (c * Client ) offerNew (ctx context.Context ) (* clientQUICConnection , error ) {
133- udpConn , err := c .dialer .DialContext (c .ctx , "udp" , c .serverAddr )
182+ dialFunc := func (serverAddr M.Socksaddr ) (net.PacketConn , error ) {
183+ udpConn , err := c .dialer .DialContext (c .ctx , "udp" , serverAddr )
184+ if err != nil {
185+ return nil , err
186+ }
187+ var packetConn net.PacketConn
188+ packetConn = bufio .NewUnbindPacketConn (udpConn )
189+ if c .xplusPassword != "" {
190+ packetConn = NewXPlusPacketConn (packetConn , []byte (c .xplusPassword ))
191+ }
192+ return packetConn , nil
193+ }
194+ var (
195+ packetConn net.PacketConn
196+ err error
197+ )
198+ if len (c .serverPorts ) == 0 {
199+ packetConn , err = dialFunc (c .serverAddr )
200+ } else {
201+ packetConn , err = NewHopPacketConn (dialFunc , c .serverAddr , c .serverPorts , c .hopInterval )
202+ }
134203 if err != nil {
135204 return nil , err
136205 }
137- var packetConn net.PacketConn
138- packetConn = bufio .NewUnbindPacketConn (udpConn )
139- if c .xplusPassword != "" {
140- packetConn = NewXPlusPacketConn (packetConn , []byte (c .xplusPassword ))
141- }
142- quicConn , err := qtls .Dial (c .ctx , packetConn , udpConn .RemoteAddr (), c .tlsConfig , c .quicConfig )
206+ quicConn , err := qtls .Dial (c .ctx , packetConn , c .serverAddr , c .tlsConfig , c .quicConfig )
143207 if err != nil {
144- udpConn .Close ()
208+ packetConn .Close ()
145209 return nil , err
146210 }
147211 controlStream , err := quicConn .OpenStreamSync (ctx )
@@ -170,7 +234,7 @@ func (c *Client) offerNew(ctx context.Context) (*clientQUICConnection, error) {
170234 quicConn .SetCongestionControl (hyCC .NewBrutalSender (uint64 (math .Min (float64 (serverHello .RecvBPS ), float64 (c .sendBPS ))), c .brutalDebug , c .logger ))
171235 conn := & clientQUICConnection {
172236 quicConn : quicConn ,
173- rawConn : udpConn ,
237+ rawConn : packetConn ,
174238 connDone : make (chan struct {}),
175239 udpDisabled : ! quicConn .ConnectionState ().SupportsDatagrams ,
176240 udpConnMap : make (map [uint32 ]* udpPacketConn ),
0 commit comments