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  }