@@ -188,16 +188,10 @@ func (r *retrier) retry(buf []byte) (n int, err error) {
188
188
return
189
189
}
190
190
r .conn = newConn .(* net.TCPConn )
191
- pkts , split := splitHello (r .hello )
192
- r .stats .Split = split
193
-
194
- // We did not use pkts.WriteTo(r.conn), because under the hood, the connection
195
- // will use writev system call to write buffers, and writev may combine these
196
- // buffers into one single write.
197
- for _ , pkt := range pkts {
198
- if _ , err = r .conn .Write (pkt ); err != nil {
199
- return
200
- }
191
+ _ , split , err := splitHello (r .hello , r .conn )
192
+ r .stats .Split = int16 (split )
193
+ if err != nil {
194
+ return
201
195
}
202
196
203
197
// While we were creating the new socket, the caller might have called CloseRead
@@ -225,76 +219,74 @@ func (r *retrier) CloseRead() error {
225
219
return r .conn .CloseRead ()
226
220
}
227
221
228
- func splitHello ( hello []byte ) (pkts net. Buffers , splitLen int16 ) {
229
- if len (hello ) == 0 {
230
- return net. Buffers { hello }, 0
222
+ func getTLSClientHelloRecordLen ( h []byte ) (uint16 , bool ) {
223
+ if len (h ) < 5 {
224
+ return 0 , false
231
225
}
232
- const (
233
- MIN_SPLIT int = 32
234
- MAX_SPLIT int = 64
235
- MIN_TLS_HELLO_LEN int = 6
236
226
227
+ const (
237
228
TYPE_HANDSHAKE byte = 22
238
229
VERSION_TLS10 uint16 = 0x0301
239
230
VERSION_TLS11 uint16 = 0x0302
240
231
VERSION_TLS12 uint16 = 0x0303
241
232
VERSION_TLS13 uint16 = 0x0304
242
233
)
243
234
235
+ if h [0 ] != TYPE_HANDSHAKE {
236
+ return 0 , false
237
+ }
238
+
239
+ ver := binary .BigEndian .Uint16 (h [1 :3 ])
240
+ if ver != VERSION_TLS10 && ver != VERSION_TLS11 &&
241
+ ver != VERSION_TLS12 && ver != VERSION_TLS13 {
242
+ return 0 , false
243
+ }
244
+
245
+ return binary .BigEndian .Uint16 (h [3 :5 ]), true
246
+ }
247
+
248
+ func splitHello (hello []byte , w io.Writer ) (n int , splitLen int , err error ) {
249
+ if len (hello ) <= 1 {
250
+ n , err = w .Write (hello )
251
+ return
252
+ }
253
+
254
+ const (
255
+ MIN_SPLIT int = 6
256
+ MAX_SPLIT int = 64
257
+ )
258
+
244
259
// Random number in the range [MIN_SPLIT, MAX_SPLIT]
245
- s := MIN_SPLIT + rand .Intn (MAX_SPLIT + 1 - MIN_SPLIT )
260
+ // splitLen includes 5 bytes of TLS header
261
+ splitLen = MIN_SPLIT + rand .Intn (MAX_SPLIT + 1 - MIN_SPLIT )
246
262
limit := len (hello ) / 2
247
- if s > limit {
248
- s = limit
263
+ if splitLen > limit {
264
+ splitLen = limit
249
265
}
250
- splitLen = int16 (s )
251
- pkts = net.Buffers {hello [:s ], hello [s :]}
252
-
253
- if len (pkts [0 ]) > MIN_TLS_HELLO_LEN {
254
- // todo: Replace the following TLS fragmentation logic with tlsfrag.StreamDialer
255
- //
256
- // TLS record layout from RFC 8446:
257
- // [RecordType:1B][Ver:2B][Len:2B][Data...]
258
- // RecordType := ... | handshake(22) | ...
259
- // Ver := 0x0301 ("TLS 1.0") | 0x0302 ("TLS 1.1") | 0x0303 ("TLS 1.2")
260
- //
261
- // Now we have already TCP-splitted into pkts0 (len >= 6) and pkts1.
262
- // We just need to deal with pkts0 and fragment it:
263
- //
264
- // original: pkts[0]=[Header][data0],
265
- // pkts[1]=[data1]
266
- // fragmented: pkts[0]=[Header]
267
- // pkts[1]=[data0_0],
268
- // pkts[2]=[Header],
269
- // pkts[3]=[data0_1],
270
- // pkts[4]=[data1]
271
-
272
- h1 := make ([]byte , 5 )
273
- copy (h1 , pkts [0 ][:5 ])
274
- payload := pkts [0 ][5 :] // len(payload) > 1 is guaranteed
275
-
276
- typ := h1 [0 ]
277
- ver := binary .BigEndian .Uint16 (h1 [1 :3 ])
278
- recordLen := binary .BigEndian .Uint16 (h1 [3 :5 ])
279
-
280
- if typ == TYPE_HANDSHAKE && int (recordLen ) >= len (payload ) &&
281
- (ver == VERSION_TLS10 || ver == VERSION_TLS11 ||
282
- ver == VERSION_TLS12 || ver == VERSION_TLS13 ) {
283
- rest := pkts [1 ]
284
- frag := uint16 (1 + rand .Intn (len (payload )- 1 )) // 1 <= frag <= len(payload)-1
285
-
286
- binary .BigEndian .PutUint16 (h1 [3 :5 ], frag )
287
- payload1 := payload [:frag ]
288
-
289
- h2 := make ([]byte , 5 )
290
- copy (h2 , h1 )
291
- binary .BigEndian .PutUint16 (h2 [3 :5 ], recordLen - frag ) // recordLen >= len(payload) > frag
292
- payload2 := payload [frag :]
293
-
294
- pkts = net.Buffers {h1 , payload1 , h2 , payload2 , rest }
266
+
267
+ recordLen , ok := getTLSClientHelloRecordLen (hello )
268
+ recordSplitLen := splitLen - 5
269
+ if ! ok || recordSplitLen >= int (recordLen ) {
270
+ // Do TCP split if hello is not a valid TLS Client Hello, or cannot be fragmented
271
+ n , err = w .Write (hello [:splitLen ])
272
+ if err == nil {
273
+ var m int
274
+ m , err = w .Write (hello [splitLen :])
275
+ n += m
295
276
}
277
+ return
278
+ }
279
+
280
+ binary .BigEndian .PutUint16 (hello [3 :5 ], uint16 (recordSplitLen ))
281
+ if n , err = w .Write (hello [:splitLen ]); err != nil {
282
+ return
296
283
}
297
284
285
+ copy (hello [splitLen - 5 :splitLen ], hello [:5 ])
286
+ binary .BigEndian .PutUint16 (hello [splitLen - 2 :splitLen ], recordLen - uint16 (recordSplitLen ))
287
+ var m int
288
+ m , err = w .Write (hello [splitLen - 5 :])
289
+ n += m
298
290
return
299
291
}
300
292
0 commit comments