github.com/database64128/shadowsocks-go@v1.7.0/ss2022/header.go (about) 1 package ss2022 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "errors" 7 "fmt" 8 "net/netip" 9 "strconv" 10 "time" 11 12 "github.com/database64128/shadowsocks-go/conn" 13 "github.com/database64128/shadowsocks-go/socks5" 14 ) 15 16 const ( 17 HeaderTypeClientStream = 0 18 HeaderTypeServerStream = 1 19 20 HeaderTypeClientPacket = 0 21 HeaderTypeServerPacket = 1 22 23 MinPaddingLength = 0 24 MaxPaddingLength = 900 25 26 IdentityHeaderLength = 16 27 28 // type + unix epoch timestamp + u16be length 29 TCPRequestFixedLengthHeaderLength = 1 + 8 + 2 30 31 // SOCKS address + padding length + padding 32 TCPRequestVariableLengthHeaderNoPayloadMaxLength = socks5.MaxAddrLen + 2 + MaxPaddingLength 33 34 // type + unix epoch timestamp + request salt + u16be length 35 TCPResponseHeaderMaxLength = 1 + 8 + 32 + 2 36 37 // session ID + packet ID 38 UDPSeparateHeaderLength = 8 + 8 39 40 // type + unix epoch timestamp + padding length 41 UDPClientMessageHeaderFixedLength = 1 + 8 + 2 42 43 // type + unix epoch timestamp + client session id + padding length 44 UDPServerMessageHeaderFixedLength = 1 + 8 + 8 + 2 45 46 // type + unix epoch timestamp + padding length + padding + SOCKS address 47 UDPClientMessageHeaderMaxLength = UDPClientMessageHeaderFixedLength + MaxPaddingLength + socks5.MaxAddrLen 48 49 // type + unix epoch timestamp + client session id + padding length + padding + SOCKS address 50 UDPServerMessageHeaderMaxLength = UDPServerMessageHeaderFixedLength + MaxPaddingLength + socks5.IPv6AddrLen 51 52 // MaxEpochDiff is the maximum allowed time difference between a received timestamp and system time. 53 MaxEpochDiff = 30 54 55 // MaxTimeDiff is the maximum allowed time difference between a received timestamp and system time. 56 MaxTimeDiff = MaxEpochDiff * time.Second 57 58 // ReplayWindowDuration defines the amount of time during which a salt check is necessary. 59 ReplayWindowDuration = MaxTimeDiff * 2 60 ) 61 62 var ( 63 ErrIncompleteHeaderInFirstChunk = errors.New("header in first chunk is missing or incomplete") 64 ErrPaddingExceedChunkBorder = errors.New("padding in first chunk is shorter than advertised") 65 ErrBadTimestamp = errors.New("time diff is over 30 seconds") 66 ErrTypeMismatch = errors.New("header type mismatch") 67 ErrClientSaltMismatch = errors.New("client salt in response header does not match request") 68 ErrClientSessionIDMismatch = errors.New("client session ID in server message header does not match current session") 69 ErrTooManyServerSessions = errors.New("server session changed more than once during the last minute") 70 ErrPacketIncompleteHeader = errors.New("packet contains incomplete header") 71 ErrReplay = errors.New("detected replay") 72 ErrIdentityHeaderUserPSKNotFound = errors.New("decrypted identity header does not match any known uPSK") 73 ) 74 75 type HeaderError[T any] struct { 76 Err error 77 Expected T 78 Got T 79 } 80 81 func (e *HeaderError[T]) Unwrap() error { 82 return e.Err 83 } 84 85 func (e *HeaderError[T]) Error() string { 86 return fmt.Sprintf("%s: expected %v, got %v", e.Err.Error(), e.Expected, e.Got) 87 } 88 89 // ValidateUnixEpochTimestamp validates the Unix Epoch timestamp in the buffer 90 // and returns an error if the timestamp exceeds the allowed time difference from system time. 91 // 92 // This function does not check buffer length. Make sure it's exactly 8 bytes long. 93 func ValidateUnixEpochTimestamp(b []byte) error { 94 tsEpoch := int64(binary.BigEndian.Uint64(b)) 95 nowEpoch := time.Now().Unix() 96 diff := tsEpoch - nowEpoch 97 if diff < -MaxEpochDiff || diff > MaxEpochDiff { 98 return &HeaderError[int64]{ErrBadTimestamp, nowEpoch, tsEpoch} 99 } 100 return nil 101 } 102 103 func intToUint16(i int) (u uint16) { 104 u = uint16(i) 105 if int(u) != i { 106 panic("int -> uint16 overflowed: " + strconv.Itoa(i)) 107 } 108 return 109 } 110 111 // ParseTCPRequestFixedLengthHeader parses a TCP request fixed-length header and returns the length 112 // of the variable-length header, or an error if header validation fails. 113 // 114 // The buffer must be exactly 11 bytes long. No buffer length checks are performed. 115 // 116 // Request fixed-length header: 117 // 118 // +------+---------------+--------+ 119 // | type | timestamp | length | 120 // +------+---------------+--------+ 121 // | 1B | 8B unix epoch | u16be | 122 // +------+---------------+--------+ 123 func ParseTCPRequestFixedLengthHeader(b []byte) (n int, err error) { 124 // Type 125 if b[0] != HeaderTypeClientStream { 126 err = &HeaderError[byte]{ErrTypeMismatch, HeaderTypeClientStream, b[0]} 127 return 128 } 129 130 // Timestamp 131 err = ValidateUnixEpochTimestamp(b[1:]) 132 if err != nil { 133 return 134 } 135 136 // Length 137 n = int(binary.BigEndian.Uint16(b[1+8:])) 138 139 return 140 } 141 142 // WriteTCPRequestFixedLengthHeader writes a TCP request fixed-length header into the buffer. 143 // 144 // The buffer must be at least 11 bytes long. No buffer length checks are performed. 145 func WriteTCPRequestFixedLengthHeader(b []byte, length uint16) { 146 // Type 147 b[0] = HeaderTypeClientStream 148 149 // Timestamp 150 binary.BigEndian.PutUint64(b[1:], uint64(time.Now().Unix())) 151 152 // Length 153 binary.BigEndian.PutUint16(b[1+8:], length) 154 } 155 156 // ParseTCPRequestVariableLengthHeader parses a TCP request variable-length header and returns 157 // the target address, the initial payload if available, or an error if header validation fails. 158 // 159 // This function does buffer length checks and returns ErrIncompleteHeaderInFirstChunk if the buffer is too short. 160 // 161 // Request variable-length header: 162 // 163 // +------+----------+-------+----------------+----------+-----------------+ 164 // | ATYP | address | port | padding length | padding | initial payload | 165 // +------+----------+-------+----------------+----------+-----------------+ 166 // | 1B | variable | u16be | u16be | variable | variable | 167 // +------+----------+-------+----------------+----------+-----------------+ 168 func ParseTCPRequestVariableLengthHeader(b []byte) (targetAddr conn.Addr, payload []byte, err error) { 169 // SOCKS address 170 targetAddr, n, err := socks5.ConnAddrFromSlice(b) 171 if err != nil { 172 return 173 } 174 b = b[n:] 175 176 // Make sure the remaining length > 2 (padding length + either padding or payload) 177 if len(b) <= 2 { 178 err = ErrIncompleteHeaderInFirstChunk 179 return 180 } 181 182 // Padding length 183 paddingLen := int(binary.BigEndian.Uint16(b)) 184 185 // Padding 186 if 2+paddingLen > len(b) { 187 err = &HeaderError[int]{ErrPaddingExceedChunkBorder, len(b), 2 + paddingLen} 188 return 189 } 190 191 // Initial payload 192 payload = b[2+paddingLen:] 193 194 return 195 } 196 197 // WriteTCPRequestVariableLengthHeader writes a TCP request variable-length header into the buffer. 198 // 199 // The header fills the whole buffer. Excess bytes are used as padding. 200 // 201 // The buffer size can be calculated with: 202 // 203 // socks5.LengthOfAddrFromConnAddr(targetAddr) + 2 + len(payload) + paddingLen 204 // 205 // The buffer size must not exceed [MaxPayloadSize]. 206 // The excess space in the buffer must not be larger than [MaxPaddingLength] bytes. 207 func WriteTCPRequestVariableLengthHeader(b []byte, targetAddr conn.Addr, payload []byte) { 208 // SOCKS address 209 n := socks5.WriteAddrFromConnAddr(b, targetAddr) 210 211 // Padding length 212 paddingLen := len(b) - n - 2 - len(payload) 213 binary.BigEndian.PutUint16(b[n:], intToUint16(paddingLen)) 214 n += 2 + paddingLen 215 216 // Initial payload 217 copy(b[n:], payload) 218 } 219 220 // ParseTCPResponseHeader parses a TCP response fixed-length header and returns the length 221 // of the next payload chunk, or an error if header validation fails. 222 // 223 // The buffer must be exactly 1 + 8 + salt length + 2 bytes long. No buffer length checks are performed. 224 // 225 // Response fixed-length header: 226 // 227 // +------+---------------+----------------+--------+ 228 // | type | timestamp | request salt | length | 229 // +------+---------------+----------------+--------+ 230 // | 1B | 8B unix epoch | 16/32B | u16be | 231 // +------+---------------+----------------+--------+ 232 func ParseTCPResponseHeader(b []byte, requestSalt []byte) (n int, err error) { 233 // Type 234 if b[0] != HeaderTypeServerStream { 235 err = &HeaderError[byte]{ErrTypeMismatch, HeaderTypeServerStream, b[0]} 236 return 237 } 238 239 // Timestamp 240 err = ValidateUnixEpochTimestamp(b[1 : 1+8]) 241 if err != nil { 242 return 243 } 244 245 // Request salt 246 rSalt := b[1+8 : 1+8+len(requestSalt)] 247 if !bytes.Equal(requestSalt, rSalt) { 248 err = &HeaderError[[]byte]{ErrClientSaltMismatch, requestSalt, rSalt} 249 return 250 } 251 252 // Length 253 n = int(binary.BigEndian.Uint16(b[1+8+len(requestSalt):])) 254 255 return 256 } 257 258 // WriteTCPResponseHeader writes a TCP response fixed-length header into the buffer. 259 // 260 // The buffer size must be exactly 1 + 8 + len(requestSalt) + 2 bytes. 261 func WriteTCPResponseHeader(b []byte, requestSalt []byte, length uint16) { 262 // Type 263 b[0] = HeaderTypeServerStream 264 265 // Timestamp 266 binary.BigEndian.PutUint64(b[1:], uint64(time.Now().Unix())) 267 268 // Request salt 269 copy(b[1+8:], requestSalt) 270 271 // Length 272 binary.BigEndian.PutUint16(b[1+8+len(requestSalt):], length) 273 } 274 275 // ParseSessionIDAndPacketID parses the session ID and packet ID segment of a decrypted UDP packet. 276 // 277 // The buffer must be exactly 16 bytes long. No buffer length checks are performed. 278 // 279 // Session ID and packet ID segment: 280 // 281 // +------------+-----------+ 282 // | session ID | packet ID | 283 // +------------+-----------+ 284 // | 8B | u64be | 285 // +------------+-----------+ 286 func ParseSessionIDAndPacketID(b []byte) (sid, pid uint64) { 287 sid = binary.BigEndian.Uint64(b) 288 pid = binary.BigEndian.Uint64(b[8:]) 289 return 290 } 291 292 // WriteSessionIDAndPacketID writes the session ID and packet ID to the buffer. 293 // 294 // The buffer must be exactly 16 bytes long. No buffer length checks are performed. 295 func WriteSessionIDAndPacketID(b []byte, sid, pid uint64) { 296 binary.BigEndian.PutUint64(b, sid) 297 binary.BigEndian.PutUint64(b[8:], pid) 298 } 299 300 // ParseUDPClientMessageHeader parses a UDP client message header and returns the target address 301 // and payload, or an error if header validation fails or no payload is in the buffer. 302 // 303 // This function accepts buffers of arbitrary lengths. 304 // 305 // The buffer is expected to contain a decrypted client message in the following format: 306 // 307 // +------+---------------+----------------+----------+------+----------+-------+----------+ 308 // | type | timestamp | padding length | padding | ATYP | address | port | payload | 309 // +------+---------------+----------------+----------+------+----------+-------+----------+ 310 // | 1B | 8B unix epoch | u16be | variable | 1B | variable | u16be | variable | 311 // +------+---------------+----------------+----------+------+----------+-------+----------+ 312 func ParseUDPClientMessageHeader(b []byte, cachedDomain string) (targetAddr conn.Addr, updatedCachedDomain string, payloadStart, payloadLen int, err error) { 313 updatedCachedDomain = cachedDomain 314 315 // Make sure buffer has type + timestamp + padding length. 316 if len(b) < UDPClientMessageHeaderFixedLength { 317 err = ErrPacketIncompleteHeader 318 return 319 } 320 321 // Type 322 if b[0] != HeaderTypeClientPacket { 323 err = &HeaderError[byte]{ErrTypeMismatch, HeaderTypeClientPacket, b[0]} 324 return 325 } 326 327 // Timestamp 328 err = ValidateUnixEpochTimestamp(b[1 : 1+8]) 329 if err != nil { 330 return 331 } 332 333 // Padding length 334 paddingLen := int(binary.BigEndian.Uint16(b[1+8:])) 335 336 // Padding 337 payloadStart = UDPClientMessageHeaderFixedLength + paddingLen 338 if payloadStart > len(b) { 339 err = ErrPacketIncompleteHeader 340 return 341 } 342 343 // SOCKS address 344 var n int 345 targetAddr, n, updatedCachedDomain, err = socks5.ConnAddrFromSliceWithDomainCache(b[payloadStart:], cachedDomain) 346 if err != nil { 347 return 348 } 349 350 // Payload 351 payloadStart += n 352 payloadLen = len(b) - payloadStart 353 return 354 } 355 356 // WriteUDPClientMessageHeader writes a UDP client message header into the buffer. 357 // 358 // The buffer size must be exactly 1 + 8 + 2 + paddingLen + socks5.LengthOfAddrFromConnAddr(targetAddr) bytes. 359 func WriteUDPClientMessageHeader(b []byte, paddingLen int, targetAddr conn.Addr) { 360 // Type 361 b[0] = HeaderTypeClientPacket 362 363 // Timestamp 364 binary.BigEndian.PutUint64(b[1:], uint64(time.Now().Unix())) 365 366 // Padding length 367 binary.BigEndian.PutUint16(b[1+8:], intToUint16(paddingLen)) 368 369 // SOCKS address 370 socks5.WriteAddrFromConnAddr(b[1+8+2+paddingLen:], targetAddr) 371 } 372 373 // ParseUDPServerMessageHeader parses a UDP server message header and returns the payload source address 374 // and payload, or an error if header validation fails or no payload is in the buffer. 375 // 376 // This function accepts buffers of arbitrary lengths. 377 // 378 // The buffer is expected to contain a decrypted server message in the following format: 379 // 380 // +------+---------------+-------------------+----------------+----------+------+----------+-------+----------+ 381 // | type | timestamp | client session ID | padding length | padding | ATYP | address | port | payload | 382 // +------+---------------+-------------------+----------------+----------+------+----------+-------+----------+ 383 // | 1B | 8B unix epoch | 8B | u16be | variable | 1B | variable | u16be | variable | 384 // +------+---------------+-------------------+----------------+----------+------+----------+-------+----------+ 385 func ParseUDPServerMessageHeader(b []byte, csid uint64) (payloadSourceAddrPort netip.AddrPort, payloadStart, payloadLen int, err error) { 386 // Make sure buffer has type + timestamp + client session ID + padding length. 387 if len(b) < UDPServerMessageHeaderFixedLength { 388 err = ErrPacketIncompleteHeader 389 return 390 } 391 392 // Type 393 if b[0] != HeaderTypeServerPacket { 394 err = &HeaderError[byte]{ErrTypeMismatch, HeaderTypeServerPacket, b[0]} 395 return 396 } 397 398 // Timestamp 399 err = ValidateUnixEpochTimestamp(b[1 : 1+8]) 400 if err != nil { 401 return 402 } 403 404 // Client session ID 405 pcsid := binary.BigEndian.Uint64(b[1+8:]) 406 if pcsid != csid { 407 err = &HeaderError[uint64]{ErrClientSessionIDMismatch, csid, pcsid} 408 return 409 } 410 411 // Padding length 412 paddingLen := int(binary.BigEndian.Uint16(b[1+8+8:])) 413 414 // Padding 415 payloadStart = UDPServerMessageHeaderFixedLength + paddingLen 416 if payloadStart > len(b) { 417 err = ErrPacketIncompleteHeader 418 return 419 } 420 421 // SOCKS address 422 payloadSourceAddrPort, n, err := socks5.AddrPortFromSlice(b[payloadStart:]) 423 if err != nil { 424 return 425 } 426 427 // Payload 428 payloadStart += n 429 payloadLen = len(b) - payloadStart 430 return 431 } 432 433 // WriteUDPServerMessageHeader writes a UDP server message header into the buffer. 434 // 435 // The buffer size must be exactly 1 + 8 + 8 + 2 + paddingLen + socks5.LengthOfAddrFromAddrPort(sourceAddrPort) bytes. 436 func WriteUDPServerMessageHeader(b []byte, csid uint64, paddingLen int, sourceAddrPort netip.AddrPort) { 437 // Type 438 b[0] = HeaderTypeServerPacket 439 440 // Timestamp 441 binary.BigEndian.PutUint64(b[1:], uint64(time.Now().Unix())) 442 443 // Client session ID 444 binary.BigEndian.PutUint64(b[1+8:], csid) 445 446 // Padding length 447 binary.BigEndian.PutUint16(b[1+8+8:], intToUint16(paddingLen)) 448 449 // SOCKS address 450 socks5.WriteAddrFromAddrPort(b[1+8+8+2+paddingLen:], sourceAddrPort) 451 }