@@ -35,6 +35,11 @@ type MTUResult struct {
3535 DownloadBytes int
3636}
3737
38+ type mtuProbeTransport struct {
39+ conn * net.UDPConn
40+ buffer []byte
41+ }
42+
3843func (c * Client ) RunInitialMTUTests () error {
3944 if len (c .connections ) == 0 {
4045 return ErrNoValidConnections
@@ -81,6 +86,7 @@ func (c *Client) RunInitialMTUTests() error {
8186 c .successMTUChecks = true
8287 c .syncedUploadMTU = minConnectionMTU (c .connections , true )
8388 c .syncedDownloadMTU = minConnectionMTU (c .connections , false )
89+ c .syncedUploadChars = minConnectionUploadChars (c .connections , c )
8490 return nil
8591}
8692
@@ -89,13 +95,20 @@ func (c *Client) runConnectionMTUTest(conn *Connection, maxUploadPayload int) {
8995 return
9096 }
9197
92- upOK , upBytes , err := c .testUploadMTU (conn , maxUploadPayload )
98+ probeTransport , err := c .newMTUProbeTransport (conn )
99+ if err != nil {
100+ conn .IsValid = false
101+ return
102+ }
103+ defer probeTransport .conn .Close ()
104+
105+ upOK , upBytes , err := c .testUploadMTU (conn , probeTransport , maxUploadPayload )
93106 if err != nil || ! upOK {
94107 conn .IsValid = false
95108 return
96109 }
97110
98- downOK , downBytes , err := c .testDownloadMTU (conn , upBytes )
111+ downOK , downBytes , err := c .testDownloadMTU (conn , probeTransport , upBytes )
99112 if err != nil || ! downOK {
100113 conn .IsValid = false
101114 return
@@ -116,7 +129,7 @@ func (c *Client) precomputeUploadCaps() map[string]int {
116129 return caps
117130}
118131
119- func (c * Client ) testUploadMTU (conn * Connection , maxPayload int ) (bool , int , error ) {
132+ func (c * Client ) testUploadMTU (conn * Connection , probeTransport * mtuProbeTransport , maxPayload int ) (bool , int , error ) {
120133 if maxPayload <= 0 {
121134 return false , 0 , nil
122135 }
@@ -133,7 +146,7 @@ func (c *Client) testUploadMTU(conn *Connection, maxPayload int) (bool, int, err
133146 c .cfg .MinUploadMTU ,
134147 maxPayload ,
135148 func (candidate int ) (bool , error ) {
136- return c .sendUploadMTUProbe (conn , candidate )
149+ return c .sendUploadMTUProbe (conn , probeTransport , candidate )
137150 },
138151 )
139152 if best < max (defaultMTUMinFloor , c .cfg .MinUploadMTU ) {
@@ -142,12 +155,12 @@ func (c *Client) testUploadMTU(conn *Connection, maxPayload int) (bool, int, err
142155 return true , best , nil
143156}
144157
145- func (c * Client ) testDownloadMTU (conn * Connection , uploadMTU int ) (bool , int , error ) {
158+ func (c * Client ) testDownloadMTU (conn * Connection , probeTransport * mtuProbeTransport , uploadMTU int ) (bool , int , error ) {
146159 best := c .binarySearchMTU (
147160 c .cfg .MinDownloadMTU ,
148161 c .cfg .MaxDownloadMTU ,
149162 func (candidate int ) (bool , error ) {
150- return c .sendDownloadMTUProbe (conn , candidate , uploadMTU )
163+ return c .sendDownloadMTUProbe (conn , probeTransport , candidate , uploadMTU )
151164 },
152165 )
153166 if best < max (defaultMTUMinFloor , c .cfg .MinDownloadMTU ) {
@@ -210,7 +223,7 @@ func (c *Client) binarySearchMTU(minValue, maxValue int, testFn func(int) (bool,
210223 return best
211224}
212225
213- func (c * Client ) sendUploadMTUProbe (conn * Connection , mtuSize int ) (bool , error ) {
226+ func (c * Client ) sendUploadMTUProbe (conn * Connection , probeTransport * mtuProbeTransport , mtuSize int ) (bool , error ) {
214227 if mtuSize < 1 {
215228 return false , nil
216229 }
@@ -230,7 +243,7 @@ func (c *Client) sendUploadMTUProbe(conn *Connection, mtuSize int) (bool, error)
230243 return false , nil
231244 }
232245
233- response , err := c .sendDNSQuery (conn , query )
246+ response , err := c .sendDNSQuery (probeTransport , query )
234247 if err != nil {
235248 return false , nil
236249 }
@@ -245,7 +258,7 @@ func (c *Client) sendUploadMTUProbe(conn *Connection, mtuSize int) (bool, error)
245258 return bytes .Equal (packet .Payload , key ), nil
246259}
247260
248- func (c * Client ) sendDownloadMTUProbe (conn * Connection , mtuSize int , uploadMTU int ) (bool , error ) {
261+ func (c * Client ) sendDownloadMTUProbe (conn * Connection , probeTransport * mtuProbeTransport , mtuSize int , uploadMTU int ) (bool , error ) {
249262 if mtuSize < defaultMTUMinFloor {
250263 return false , nil
251264 }
@@ -267,7 +280,7 @@ func (c *Client) sendDownloadMTUProbe(conn *Connection, mtuSize int, uploadMTU i
267280 return false , nil
268281 }
269282
270- response , err := c .sendDNSQuery (conn , query )
283+ response , err := c .sendDNSQuery (probeTransport , query )
271284 if err != nil {
272285 return false , nil
273286 }
@@ -309,7 +322,7 @@ func (c *Client) buildMTUProbeQuery(domain string, packetType uint8, payload []b
309322 return dnsparser .BuildTXTQuestionPacket (name , enums .DNSRecordTypeTXT , ednsSafeUDPSize )
310323}
311324
312- func (c * Client ) sendDNSQuery (conn * Connection , packet [] byte ) ([] byte , error ) {
325+ func (c * Client ) newMTUProbeTransport (conn * Connection ) (* mtuProbeTransport , error ) {
313326 addr , err := net .ResolveUDPAddr ("udp" , conn .ResolverLabel )
314327 if err != nil {
315328 return nil , err
@@ -319,25 +332,32 @@ func (c *Client) sendDNSQuery(conn *Connection, packet []byte) ([]byte, error) {
319332 if err != nil {
320333 return nil , err
321334 }
322- defer udpConn .Close ()
335+ return & mtuProbeTransport {
336+ conn : udpConn ,
337+ buffer : make ([]byte , ednsSafeUDPSize ),
338+ }, nil
339+ }
323340
341+ func (c * Client ) sendDNSQuery (probeTransport * mtuProbeTransport , packet []byte ) ([]byte , error ) {
342+ if probeTransport == nil || probeTransport .conn == nil {
343+ return nil , net .ErrClosed
344+ }
324345 timeout := time .Duration (c .cfg .MTUTestTimeout * float64 (time .Second ))
325346 if timeout <= 0 {
326347 timeout = time .Second
327348 }
328- if err := udpConn .SetDeadline (time .Now ().Add (timeout )); err != nil {
349+ if err := probeTransport . conn .SetDeadline (time .Now ().Add (timeout )); err != nil {
329350 return nil , err
330351 }
331- if _ , err := udpConn .Write (packet ); err != nil {
352+ if _ , err := probeTransport . conn .Write (packet ); err != nil {
332353 return nil , err
333354 }
334355
335- buffer := make ([]byte , ednsSafeUDPSize )
336- n , err := udpConn .Read (buffer )
356+ n , err := probeTransport .conn .Read (probeTransport .buffer )
337357 if err != nil {
338358 return nil , err
339359 }
340- return append ([]byte (nil ), buffer [:n ]... ), nil
360+ return append ([]byte (nil ), probeTransport . buffer [:n ]... ), nil
341361}
342362
343363func (c * Client ) maxUploadMTUPayload (domain string ) int {
@@ -416,6 +436,48 @@ func minConnectionMTU(connections []Connection, upload bool) int {
416436 return best
417437}
418438
439+ func minConnectionUploadChars (connections []Connection , c * Client ) int {
440+ best := 0
441+ for _ , conn := range connections {
442+ if ! conn .IsValid || conn .UploadMTUBytes <= 0 {
443+ continue
444+ }
445+ value := c .encodedCharsForPayload (conn .UploadMTUBytes )
446+ if value <= 0 {
447+ continue
448+ }
449+ if best == 0 || value < best {
450+ best = value
451+ }
452+ }
453+ return best
454+ }
455+
456+ func (c * Client ) encodedCharsForPayload (payloadLen int ) int {
457+ if payloadLen <= 0 {
458+ return 0
459+ }
460+ payload := make ([]byte , payloadLen )
461+ for i := range payload {
462+ payload [i ] = 0xAB
463+ }
464+ encoded , err := vpnproto .BuildEncoded (vpnproto.BuildOptions {
465+ SessionID : 255 ,
466+ PacketType : enums .PacketStreamData ,
467+ SessionCookie : 255 ,
468+ StreamID : 0xFFFF ,
469+ SequenceNum : 0xFFFF ,
470+ FragmentID : 0xFF ,
471+ TotalFragments : 0xFF ,
472+ CompressionType : 0xFF ,
473+ Payload : payload ,
474+ }, c .codec )
475+ if err != nil {
476+ return 0
477+ }
478+ return len (encoded )
479+ }
480+
419481func max (a , b int ) int {
420482 if a > b {
421483 return a
0 commit comments