github.com/xtls/xray-core@v1.8.12-0.20240518155711-3168d27b0bdb/transport/internet/kcp/segment.go (about)

     1  package kcp
     2  
     3  import (
     4  	"encoding/binary"
     5  
     6  	"github.com/xtls/xray-core/common/buf"
     7  )
     8  
     9  // Command is a KCP command that indicate the purpose of a Segment.
    10  type Command byte
    11  
    12  const (
    13  	// CommandACK indicates an AckSegment.
    14  	CommandACK Command = 0
    15  	// CommandData indicates a DataSegment.
    16  	CommandData Command = 1
    17  	// CommandTerminate indicates that peer terminates the connection.
    18  	CommandTerminate Command = 2
    19  	// CommandPing indicates a ping.
    20  	CommandPing Command = 3
    21  )
    22  
    23  type SegmentOption byte
    24  
    25  const (
    26  	SegmentOptionClose SegmentOption = 1
    27  )
    28  
    29  type Segment interface {
    30  	Release()
    31  	Conversation() uint16
    32  	Command() Command
    33  	ByteSize() int32
    34  	Serialize([]byte)
    35  	parse(conv uint16, cmd Command, opt SegmentOption, buf []byte) (bool, []byte)
    36  }
    37  
    38  const (
    39  	DataSegmentOverhead = 18
    40  )
    41  
    42  type DataSegment struct {
    43  	Conv        uint16
    44  	Option      SegmentOption
    45  	Timestamp   uint32
    46  	Number      uint32
    47  	SendingNext uint32
    48  
    49  	payload  *buf.Buffer
    50  	timeout  uint32
    51  	transmit uint32
    52  }
    53  
    54  func NewDataSegment() *DataSegment {
    55  	return new(DataSegment)
    56  }
    57  
    58  func (s *DataSegment) parse(conv uint16, cmd Command, opt SegmentOption, buf []byte) (bool, []byte) {
    59  	s.Conv = conv
    60  	s.Option = opt
    61  	if len(buf) < 15 {
    62  		return false, nil
    63  	}
    64  	s.Timestamp = binary.BigEndian.Uint32(buf)
    65  	buf = buf[4:]
    66  
    67  	s.Number = binary.BigEndian.Uint32(buf)
    68  	buf = buf[4:]
    69  
    70  	s.SendingNext = binary.BigEndian.Uint32(buf)
    71  	buf = buf[4:]
    72  
    73  	dataLen := int(binary.BigEndian.Uint16(buf))
    74  	buf = buf[2:]
    75  
    76  	if len(buf) < dataLen {
    77  		return false, nil
    78  	}
    79  	s.Data().Clear()
    80  	s.Data().Write(buf[:dataLen])
    81  	buf = buf[dataLen:]
    82  
    83  	return true, buf
    84  }
    85  
    86  func (s *DataSegment) Conversation() uint16 {
    87  	return s.Conv
    88  }
    89  
    90  func (*DataSegment) Command() Command {
    91  	return CommandData
    92  }
    93  
    94  func (s *DataSegment) Detach() *buf.Buffer {
    95  	r := s.payload
    96  	s.payload = nil
    97  	return r
    98  }
    99  
   100  func (s *DataSegment) Data() *buf.Buffer {
   101  	if s.payload == nil {
   102  		s.payload = buf.New()
   103  	}
   104  	return s.payload
   105  }
   106  
   107  func (s *DataSegment) Serialize(b []byte) {
   108  	binary.BigEndian.PutUint16(b, s.Conv)
   109  	b[2] = byte(CommandData)
   110  	b[3] = byte(s.Option)
   111  	binary.BigEndian.PutUint32(b[4:], s.Timestamp)
   112  	binary.BigEndian.PutUint32(b[8:], s.Number)
   113  	binary.BigEndian.PutUint32(b[12:], s.SendingNext)
   114  	binary.BigEndian.PutUint16(b[16:], uint16(s.payload.Len()))
   115  	copy(b[18:], s.payload.Bytes())
   116  }
   117  
   118  func (s *DataSegment) ByteSize() int32 {
   119  	return 2 + 1 + 1 + 4 + 4 + 4 + 2 + s.payload.Len()
   120  }
   121  
   122  func (s *DataSegment) Release() {
   123  	s.payload.Release()
   124  	s.payload = nil
   125  }
   126  
   127  type AckSegment struct {
   128  	Conv            uint16
   129  	Option          SegmentOption
   130  	ReceivingWindow uint32
   131  	ReceivingNext   uint32
   132  	Timestamp       uint32
   133  	NumberList      []uint32
   134  }
   135  
   136  const ackNumberLimit = 128
   137  
   138  func NewAckSegment() *AckSegment {
   139  	return new(AckSegment)
   140  }
   141  
   142  func (s *AckSegment) parse(conv uint16, cmd Command, opt SegmentOption, buf []byte) (bool, []byte) {
   143  	s.Conv = conv
   144  	s.Option = opt
   145  	if len(buf) < 13 {
   146  		return false, nil
   147  	}
   148  
   149  	s.ReceivingWindow = binary.BigEndian.Uint32(buf)
   150  	buf = buf[4:]
   151  
   152  	s.ReceivingNext = binary.BigEndian.Uint32(buf)
   153  	buf = buf[4:]
   154  
   155  	s.Timestamp = binary.BigEndian.Uint32(buf)
   156  	buf = buf[4:]
   157  
   158  	count := int(buf[0])
   159  	buf = buf[1:]
   160  
   161  	if len(buf) < count*4 {
   162  		return false, nil
   163  	}
   164  	for i := 0; i < count; i++ {
   165  		s.PutNumber(binary.BigEndian.Uint32(buf))
   166  		buf = buf[4:]
   167  	}
   168  
   169  	return true, buf
   170  }
   171  
   172  func (s *AckSegment) Conversation() uint16 {
   173  	return s.Conv
   174  }
   175  
   176  func (*AckSegment) Command() Command {
   177  	return CommandACK
   178  }
   179  
   180  func (s *AckSegment) PutTimestamp(timestamp uint32) {
   181  	if timestamp-s.Timestamp < 0x7FFFFFFF {
   182  		s.Timestamp = timestamp
   183  	}
   184  }
   185  
   186  func (s *AckSegment) PutNumber(number uint32) {
   187  	s.NumberList = append(s.NumberList, number)
   188  }
   189  
   190  func (s *AckSegment) IsFull() bool {
   191  	return len(s.NumberList) == ackNumberLimit
   192  }
   193  
   194  func (s *AckSegment) IsEmpty() bool {
   195  	return len(s.NumberList) == 0
   196  }
   197  
   198  func (s *AckSegment) ByteSize() int32 {
   199  	return 2 + 1 + 1 + 4 + 4 + 4 + 1 + int32(len(s.NumberList)*4)
   200  }
   201  
   202  func (s *AckSegment) Serialize(b []byte) {
   203  	binary.BigEndian.PutUint16(b, s.Conv)
   204  	b[2] = byte(CommandACK)
   205  	b[3] = byte(s.Option)
   206  	binary.BigEndian.PutUint32(b[4:], s.ReceivingWindow)
   207  	binary.BigEndian.PutUint32(b[8:], s.ReceivingNext)
   208  	binary.BigEndian.PutUint32(b[12:], s.Timestamp)
   209  	b[16] = byte(len(s.NumberList))
   210  	n := 17
   211  	for _, number := range s.NumberList {
   212  		binary.BigEndian.PutUint32(b[n:], number)
   213  		n += 4
   214  	}
   215  }
   216  
   217  func (s *AckSegment) Release() {}
   218  
   219  type CmdOnlySegment struct {
   220  	Conv          uint16
   221  	Cmd           Command
   222  	Option        SegmentOption
   223  	SendingNext   uint32
   224  	ReceivingNext uint32
   225  	PeerRTO       uint32
   226  }
   227  
   228  func NewCmdOnlySegment() *CmdOnlySegment {
   229  	return new(CmdOnlySegment)
   230  }
   231  
   232  func (s *CmdOnlySegment) parse(conv uint16, cmd Command, opt SegmentOption, buf []byte) (bool, []byte) {
   233  	s.Conv = conv
   234  	s.Cmd = cmd
   235  	s.Option = opt
   236  
   237  	if len(buf) < 12 {
   238  		return false, nil
   239  	}
   240  
   241  	s.SendingNext = binary.BigEndian.Uint32(buf)
   242  	buf = buf[4:]
   243  
   244  	s.ReceivingNext = binary.BigEndian.Uint32(buf)
   245  	buf = buf[4:]
   246  
   247  	s.PeerRTO = binary.BigEndian.Uint32(buf)
   248  	buf = buf[4:]
   249  
   250  	return true, buf
   251  }
   252  
   253  func (s *CmdOnlySegment) Conversation() uint16 {
   254  	return s.Conv
   255  }
   256  
   257  func (s *CmdOnlySegment) Command() Command {
   258  	return s.Cmd
   259  }
   260  
   261  func (*CmdOnlySegment) ByteSize() int32 {
   262  	return 2 + 1 + 1 + 4 + 4 + 4
   263  }
   264  
   265  func (s *CmdOnlySegment) Serialize(b []byte) {
   266  	binary.BigEndian.PutUint16(b, s.Conv)
   267  	b[2] = byte(s.Cmd)
   268  	b[3] = byte(s.Option)
   269  	binary.BigEndian.PutUint32(b[4:], s.SendingNext)
   270  	binary.BigEndian.PutUint32(b[8:], s.ReceivingNext)
   271  	binary.BigEndian.PutUint32(b[12:], s.PeerRTO)
   272  }
   273  
   274  func (*CmdOnlySegment) Release() {}
   275  
   276  func ReadSegment(buf []byte) (Segment, []byte) {
   277  	if len(buf) < 4 {
   278  		return nil, nil
   279  	}
   280  
   281  	conv := binary.BigEndian.Uint16(buf)
   282  	buf = buf[2:]
   283  
   284  	cmd := Command(buf[0])
   285  	opt := SegmentOption(buf[1])
   286  	buf = buf[2:]
   287  
   288  	var seg Segment
   289  	switch cmd {
   290  	case CommandData:
   291  		seg = NewDataSegment()
   292  	case CommandACK:
   293  		seg = NewAckSegment()
   294  	default:
   295  		seg = NewCmdOnlySegment()
   296  	}
   297  
   298  	valid, extra := seg.parse(conv, cmd, opt, buf)
   299  	if !valid {
   300  		return nil, nil
   301  	}
   302  	return seg, extra
   303  }