@@ -11,7 +11,6 @@ import (
11
11
"strconv"
12
12
"time"
13
13
14
- "github.com/Shadowsocks-NET/outline-ss-server/slicepool"
15
14
"github.com/Shadowsocks-NET/outline-ss-server/socks"
16
15
)
17
16
@@ -36,78 +35,79 @@ const (
36
35
)
37
36
38
37
var (
39
- ErrBadTimestamp = errors .New ("time diff is over 30 seconds " )
40
- ErrTypeMismatch = errors .New ("header type mismatch " )
41
- ErrPaddingLengthOutOfRange = errors .New ("padding length is less than 0 or greater than 900 " )
42
- ErrClientSaltMismatch = errors .New ("client salt in response header does not match request " )
43
- ErrSessionIDMismatch = errors .New ("unexpected session ID " )
44
-
45
- tcpReqHeaderPool = slicepool . MakePool ( TCPReqHeaderMaxLength )
38
+ ErrIncompleteHeaderInFirstChunk = errors .New ("header in first chunk is missing or incomplete " )
39
+ ErrPaddingExceedChunkBorder = errors .New ("padding in first chunk is shorter than advertised " )
40
+ ErrBadTimestamp = errors .New ("time diff is over 30 seconds " )
41
+ ErrTypeMismatch = errors .New ("header type mismatch " )
42
+ ErrPaddingLengthOutOfRange = errors .New ("padding length is less than 0 or greater than 900 " )
43
+ ErrClientSaltMismatch = errors . New ( "client salt in response header does not match request" )
44
+ ErrSessionIDMismatch = errors . New ( "unexpected session ID" )
46
45
)
47
46
48
- func ParseTCPReqHeader (r io.Reader , cipherConfig CipherConfig , htype byte ) (string , error ) {
49
- if ! cipherConfig .IsSpec2022 {
50
- a , err := socks .AddrFromReader (r )
51
- if err != nil {
52
- return "" , err
53
- }
54
- return a .String (), nil
47
+ // ParseTCPReqHeader reads the first payload chunk, validates the header,
48
+ // and returns the target address, initial payload, or an error.
49
+ //
50
+ // For Shadowsocks 2022, the first payload chunk MUST contain
51
+ // either a non-zero-length padding or initial payload, or both (not recommended client behavior but allowed).
52
+ // If the padding length is 0 and there's no initial payload, an error is returned.
53
+ func ParseTCPReqHeader (r Reader , cipherConfig CipherConfig , htype byte ) (string , []byte , error ) {
54
+ // Read first payload chunk.
55
+ if err := r .EnsureLeftover (); err != nil {
56
+ return "" , nil , err
55
57
}
58
+ b := r .LeftoverZeroCopy ()
56
59
57
- lazySlice := tcpReqHeaderPool .LazySlice ()
58
- b := lazySlice .Acquire ()
59
- defer lazySlice .Release ()
60
+ var offset int
60
61
61
- // Read type & timestamp
62
- _ , err := io .ReadFull (r , b [:1 + 8 ])
63
- if err != nil {
64
- return "" , fmt .Errorf ("failed to read type and timestamp: %w" , err )
65
- }
62
+ if cipherConfig .IsSpec2022 {
63
+ if len (b ) < 1 + 8 {
64
+ return "" , nil , ErrIncompleteHeaderInFirstChunk
65
+ }
66
66
67
- // Verify type
68
- if b [0 ] != htype {
69
- return "" , ErrTypeMismatch
70
- }
67
+ // Verify type
68
+ if b [0 ] != htype {
69
+ return "" , nil , ErrTypeMismatch
70
+ }
71
71
72
- // Verify timestamp
73
- epoch := int64 (binary .BigEndian .Uint64 (b [1 : 1 + 8 ]))
74
- nowEpoch := time .Now ().Unix ()
75
- diff := epoch - nowEpoch
76
- if diff < - 30 || diff > 30 {
77
- return "" , ErrBadTimestamp
78
- }
72
+ // Verify timestamp
73
+ epoch := int64 (binary .BigEndian .Uint64 (b [1 : 1 + 8 ]))
74
+ nowEpoch := time .Now ().Unix ()
75
+ diff := epoch - nowEpoch
76
+ if diff < - 30 || diff > 30 {
77
+ return "" , nil , ErrBadTimestamp
78
+ }
79
79
80
- offset := 1 + 8
80
+ offset = 1 + 8
81
+ }
81
82
82
83
// Read socks address
83
- n , err := socks .ReadAddr (b [offset :], r )
84
+ socksaddr , err := socks .SplitAddr (b [offset :])
84
85
if err != nil {
85
- return "" , fmt .Errorf ("failed to read socks address: %w" , err )
86
+ return "" , nil , fmt .Errorf ("failed to read socks address: %w" , err )
86
87
}
87
- socksaddr := socks .Addr (b [offset : offset + n ])
88
- offset += n
88
+ offset += len (socksaddr )
89
89
90
- // Read padding length
91
- _ , err = io . ReadFull ( r , b [ offset : offset + 2 ] )
92
- if err != nil {
93
- return "" , fmt . Errorf ( "failed to read padding length: %w" , err )
94
- }
90
+ if cipherConfig . IsSpec2022 {
91
+ // Make sure the remaining length > 2 (padding length + either padding or payload )
92
+ if len ( b ) - offset <= 2 {
93
+ return "" , nil , ErrIncompleteHeaderInFirstChunk
94
+ }
95
95
96
- // Verify padding length
97
- paddingLen := int (binary .BigEndian .Uint16 (b [offset : offset + 2 ]))
98
- if paddingLen < MinPaddingLength || paddingLen > MaxPaddingLength {
99
- return "" , ErrPaddingLengthOutOfRange
100
- }
96
+ // Verify padding length
97
+ paddingLen := int (binary .BigEndian .Uint16 (b [offset : offset + 2 ]))
98
+ if paddingLen < MinPaddingLength || paddingLen > MaxPaddingLength {
99
+ return "" , nil , ErrPaddingLengthOutOfRange
100
+ }
101
+ offset += 2
101
102
102
- // Read padding
103
- if paddingLen > 0 {
104
- _ , err := io .ReadFull (r , b [offset + 2 :offset + 2 + paddingLen ])
105
- if err != nil {
106
- return "" , fmt .Errorf ("failed to read padding: %w" , err )
103
+ // Skip padding.
104
+ offset += paddingLen
105
+ if offset >= len (b ) {
106
+ return "" , nil , ErrPaddingExceedChunkBorder
107
107
}
108
108
}
109
109
110
- return socksaddr .String (), nil
110
+ return socksaddr .String (), b [ offset :], nil
111
111
}
112
112
113
113
func WriteTCPReqHeader (dst , socksaddr []byte , addPadding bool , cipherConfig CipherConfig ) (n int ) {
0 commit comments