package kcp import ( "encoding/binary" "github.com/xtls/xray-core/common/buf" ) // Command is a KCP command that indicate the purpose of a Segment. type Command byte const ( // CommandACK indicates an AckSegment. CommandACK Command = 0 // CommandData indicates a DataSegment. CommandData Command = 1 // CommandTerminate indicates that peer terminates the connection. CommandTerminate Command = 2 // CommandPing indicates a ping. CommandPing Command = 3 ) type SegmentOption byte const ( SegmentOptionClose SegmentOption = 1 ) type Segment interface { Release() Conversation() uint16 Command() Command ByteSize() int32 Serialize([]byte) parse(conv uint16, cmd Command, opt SegmentOption, buf []byte) (bool, []byte) } const ( DataSegmentOverhead = 18 ) type DataSegment struct { Conv uint16 Option SegmentOption Timestamp uint32 Number uint32 SendingNext uint32 payload *buf.Buffer timeout uint32 transmit uint32 } func NewDataSegment() *DataSegment { return new(DataSegment) } func (s *DataSegment) parse(conv uint16, cmd Command, opt SegmentOption, buf []byte) (bool, []byte) { s.Conv = conv s.Option = opt if len(buf) < 15 { return false, nil } s.Timestamp = binary.BigEndian.Uint32(buf) buf = buf[4:] s.Number = binary.BigEndian.Uint32(buf) buf = buf[4:] s.SendingNext = binary.BigEndian.Uint32(buf) buf = buf[4:] dataLen := int(binary.BigEndian.Uint16(buf)) buf = buf[2:] if len(buf) < dataLen { return false, nil } s.Data().Clear() s.Data().Write(buf[:dataLen]) buf = buf[dataLen:] return true, buf } func (s *DataSegment) Conversation() uint16 { return s.Conv } func (*DataSegment) Command() Command { return CommandData } func (s *DataSegment) Detach() *buf.Buffer { r := s.payload s.payload = nil return r } func (s *DataSegment) Data() *buf.Buffer { if s.payload == nil { s.payload = buf.New() } return s.payload } func (s *DataSegment) Serialize(b []byte) { binary.BigEndian.PutUint16(b, s.Conv) b[2] = byte(CommandData) b[3] = byte(s.Option) binary.BigEndian.PutUint32(b[4:], s.Timestamp) binary.BigEndian.PutUint32(b[8:], s.Number) binary.BigEndian.PutUint32(b[12:], s.SendingNext) binary.BigEndian.PutUint16(b[16:], uint16(s.payload.Len())) copy(b[18:], s.payload.Bytes()) } func (s *DataSegment) ByteSize() int32 { return 2 + 1 + 1 + 4 + 4 + 4 + 2 + s.payload.Len() } func (s *DataSegment) Release() { s.payload.Release() s.payload = nil } type AckSegment struct { Conv uint16 Option SegmentOption ReceivingWindow uint32 ReceivingNext uint32 Timestamp uint32 NumberList []uint32 } const ackNumberLimit = 128 func NewAckSegment() *AckSegment { return new(AckSegment) } func (s *AckSegment) parse(conv uint16, cmd Command, opt SegmentOption, buf []byte) (bool, []byte) { s.Conv = conv s.Option = opt if len(buf) < 13 { return false, nil } s.ReceivingWindow = binary.BigEndian.Uint32(buf) buf = buf[4:] s.ReceivingNext = binary.BigEndian.Uint32(buf) buf = buf[4:] s.Timestamp = binary.BigEndian.Uint32(buf) buf = buf[4:] count := int(buf[0]) buf = buf[1:] if len(buf) < count*4 { return false, nil } for i := 0; i < count; i++ { s.PutNumber(binary.BigEndian.Uint32(buf)) buf = buf[4:] } return true, buf } func (s *AckSegment) Conversation() uint16 { return s.Conv } func (*AckSegment) Command() Command { return CommandACK } func (s *AckSegment) PutTimestamp(timestamp uint32) { if timestamp-s.Timestamp < 0x7FFFFFFF { s.Timestamp = timestamp } } func (s *AckSegment) PutNumber(number uint32) { s.NumberList = append(s.NumberList, number) } func (s *AckSegment) IsFull() bool { return len(s.NumberList) == ackNumberLimit } func (s *AckSegment) IsEmpty() bool { return len(s.NumberList) == 0 } func (s *AckSegment) ByteSize() int32 { return 2 + 1 + 1 + 4 + 4 + 4 + 1 + int32(len(s.NumberList)*4) } func (s *AckSegment) Serialize(b []byte) { binary.BigEndian.PutUint16(b, s.Conv) b[2] = byte(CommandACK) b[3] = byte(s.Option) binary.BigEndian.PutUint32(b[4:], s.ReceivingWindow) binary.BigEndian.PutUint32(b[8:], s.ReceivingNext) binary.BigEndian.PutUint32(b[12:], s.Timestamp) b[16] = byte(len(s.NumberList)) n := 17 for _, number := range s.NumberList { binary.BigEndian.PutUint32(b[n:], number) n += 4 } } func (s *AckSegment) Release() {} type CmdOnlySegment struct { Conv uint16 Cmd Command Option SegmentOption SendingNext uint32 ReceivingNext uint32 PeerRTO uint32 } func NewCmdOnlySegment() *CmdOnlySegment { return new(CmdOnlySegment) } func (s *CmdOnlySegment) parse(conv uint16, cmd Command, opt SegmentOption, buf []byte) (bool, []byte) { s.Conv = conv s.Cmd = cmd s.Option = opt if len(buf) < 12 { return false, nil } s.SendingNext = binary.BigEndian.Uint32(buf) buf = buf[4:] s.ReceivingNext = binary.BigEndian.Uint32(buf) buf = buf[4:] s.PeerRTO = binary.BigEndian.Uint32(buf) buf = buf[4:] return true, buf } func (s *CmdOnlySegment) Conversation() uint16 { return s.Conv } func (s *CmdOnlySegment) Command() Command { return s.Cmd } func (*CmdOnlySegment) ByteSize() int32 { return 2 + 1 + 1 + 4 + 4 + 4 } func (s *CmdOnlySegment) Serialize(b []byte) { binary.BigEndian.PutUint16(b, s.Conv) b[2] = byte(s.Cmd) b[3] = byte(s.Option) binary.BigEndian.PutUint32(b[4:], s.SendingNext) binary.BigEndian.PutUint32(b[8:], s.ReceivingNext) binary.BigEndian.PutUint32(b[12:], s.PeerRTO) } func (*CmdOnlySegment) Release() {} func ReadSegment(buf []byte) (Segment, []byte) { if len(buf) < 4 { return nil, nil } conv := binary.BigEndian.Uint16(buf) buf = buf[2:] cmd := Command(buf[0]) opt := SegmentOption(buf[1]) buf = buf[2:] var seg Segment switch cmd { case CommandData: seg = NewDataSegment() case CommandACK: seg = NewAckSegment() default: seg = NewCmdOnlySegment() } valid, extra := seg.parse(conv, cmd, opt, buf) if !valid { return nil, nil } return seg, extra }