Skip to content

Commit 7e3216f

Browse files
committed
refactor to only fragment to two packets
1 parent 342c588 commit 7e3216f

File tree

2 files changed

+59
-79
lines changed

2 files changed

+59
-79
lines changed

Android/app/src/go/intra/split/direct_split.go

+2-14
Original file line numberDiff line numberDiff line change
@@ -54,20 +54,8 @@ func (s *splitter) Write(b []byte) (int, error) {
5454

5555
// Setting `used` to true ensures that this code only runs once per socket.
5656
s.used = true
57-
pkts, _ := splitHello(b)
58-
59-
// We did not use pkts.WriteTo(conn), because under the hood, the connection
60-
// will use writev system call to write buffers, and writev may combine these
61-
// buffers into one single write
62-
n := 0
63-
for _, pkt := range pkts {
64-
nn, err := conn.Write(pkt)
65-
n += nn
66-
if err != nil {
67-
return n, err
68-
}
69-
}
70-
return n, nil
57+
n, _, err := splitHello(b, conn)
58+
return n, err
7159
}
7260

7361
func (s *splitter) ReadFrom(reader io.Reader) (bytes int64, err error) {

Android/app/src/go/intra/split/retrier.go

+57-65
Original file line numberDiff line numberDiff line change
@@ -188,16 +188,10 @@ func (r *retrier) retry(buf []byte) (n int, err error) {
188188
return
189189
}
190190
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
201195
}
202196

203197
// While we were creating the new socket, the caller might have called CloseRead
@@ -225,76 +219,74 @@ func (r *retrier) CloseRead() error {
225219
return r.conn.CloseRead()
226220
}
227221

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
231225
}
232-
const (
233-
MIN_SPLIT int = 32
234-
MAX_SPLIT int = 64
235-
MIN_TLS_HELLO_LEN int = 6
236226

227+
const (
237228
TYPE_HANDSHAKE byte = 22
238229
VERSION_TLS10 uint16 = 0x0301
239230
VERSION_TLS11 uint16 = 0x0302
240231
VERSION_TLS12 uint16 = 0x0303
241232
VERSION_TLS13 uint16 = 0x0304
242233
)
243234

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+
244259
// 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)
246262
limit := len(hello) / 2
247-
if s > limit {
248-
s = limit
263+
if splitLen > limit {
264+
splitLen = limit
249265
}
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
295276
}
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
296283
}
297284

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
298290
return
299291
}
300292

0 commit comments

Comments
 (0)