Feature: Add Go TCP server framework

This commit is contained in:
Shaun
2025-12-19 14:12:29 +01:00
committed by yuanyuanxiang
parent 7d2cf647ec
commit 5a33628b92
16 changed files with 2807 additions and 0 deletions

161
server/go/protocol/codec.go Normal file
View File

@@ -0,0 +1,161 @@
package protocol
import (
"github.com/klauspost/compress/zstd"
"github.com/yuanyuanxiang/SimpleRemoter/server/go/connection"
)
// Codec handles encoding/decoding and compression
type Codec struct {
encoder *zstd.Encoder
decoder *zstd.Decoder
}
// NewCodec creates a new codec
func NewCodec() *Codec {
encoder, err := zstd.NewWriter(nil, zstd.WithEncoderLevel(zstd.SpeedDefault))
if err != nil {
panic("failed to create zstd encoder: " + err.Error())
}
decoder, err := zstd.NewReader(nil)
if err != nil {
panic("failed to create zstd decoder: " + err.Error())
}
return &Codec{
encoder: encoder,
decoder: decoder,
}
}
// Compress compresses data using the appropriate method
func (c *Codec) Compress(ctx *connection.Context, data []byte) ([]byte, error) {
switch ctx.CompressMethod {
case connection.CompressNone:
return data, nil
case connection.CompressZstd:
return c.encoder.EncodeAll(data, nil), nil
default:
// Default to zstd
return c.encoder.EncodeAll(data, nil), nil
}
}
// Decompress decompresses data using the appropriate method
func (c *Codec) Decompress(ctx *connection.Context, data []byte, origLen uint32) ([]byte, error) {
switch ctx.CompressMethod {
case connection.CompressNone:
// No compression, return as-is
result := make([]byte, len(data))
copy(result, data)
return result, nil
case connection.CompressZstd:
result := make([]byte, 0, origLen)
return c.decoder.DecodeAll(data, result)
default:
// Try zstd by default
result := make([]byte, 0, origLen)
return c.decoder.DecodeAll(data, result)
}
}
// Encode encodes data after compression (before sending) - Encoder2
func (c *Codec) Encode(ctx *connection.Context, data []byte) {
// This is Encoder2 - applied after compression
switch ctx.FlagType {
case connection.FlagHello, connection.FlagHell:
// XOREncoder16 - needs param from header
// For now, skip encoding on send since we need the header params
case connection.FlagFuck:
// No encoding after compression for FUCK
}
}
// Decode decodes data before decompression (after receiving) - Encoder2
func (c *Codec) Decode(ctx *connection.Context, data []byte) {
// This is Encoder2 - applied before decompression
// XOREncoder16 for HELL/HELLO protocols
if ctx.FlagType == connection.FlagHell || ctx.FlagType == connection.FlagHello {
// Get k1, k2 from stored header params
if len(ctx.HeaderParams) >= 8 {
k1 := ctx.HeaderParams[6]
k2 := ctx.HeaderParams[7]
if k1 != 0 || k2 != 0 {
xorDecoder16(data, k1, k2)
}
}
}
}
// EncodeData encodes data before compression - Encoder
func (c *Codec) EncodeData(ctx *connection.Context, data []byte) {
// This is Encoder - applied before compression
// Default encoder does nothing
}
// DecodeData decodes data after decompression - Encoder
func (c *Codec) DecodeData(ctx *connection.Context, data []byte) {
// This is Encoder - applied after decompression
// Default encoder does nothing
}
// xorDecoder16 implements XOREncoder16.decrypt_internal
func xorDecoder16(data []byte, k1, k2 byte) {
if len(data) == 0 {
return
}
key := (uint16(k1) << 8) | uint16(k2)
dataLen := len(data)
// Reverse two rounds of pseudo-random swaps
for round := 1; round >= 0; round-- {
for i := dataLen - 1; i >= 0; i-- {
j := int(pseudoRandom(key, i+round*100)) % dataLen
data[i], data[j] = data[j], data[i]
}
}
// XOR decode
for i := 0; i < dataLen; i++ {
data[i] ^= (k1 + byte(i*13)) ^ (k2 ^ byte(i<<1))
}
}
// xorEncoder16 implements XOREncoder16.encrypt_internal
func xorEncoder16(data []byte, k1, k2 byte) {
if len(data) == 0 {
return
}
key := (uint16(k1) << 8) | uint16(k2)
dataLen := len(data)
// XOR encode
for i := 0; i < dataLen; i++ {
data[i] ^= (k1 + byte(i*13)) ^ (k2 ^ byte(i<<1))
}
// Two rounds of pseudo-random swaps
for round := 0; round < 2; round++ {
for i := 0; i < dataLen; i++ {
j := int(pseudoRandom(key, i+round*100)) % dataLen
data[i], data[j] = data[j], data[i]
}
}
}
// pseudoRandom matches the C++ pseudo_random function
func pseudoRandom(seed uint16, index int) uint16 {
return ((seed ^ uint16(index*251+97)) * 733) ^ (seed >> 3)
}
// Close releases resources
func (c *Codec) Close() {
if c.encoder != nil {
c.encoder.Close()
}
if c.decoder != nil {
c.decoder.Close()
}
}

View File

@@ -0,0 +1,202 @@
package protocol
import (
"bytes"
"encoding/binary"
"strings"
"golang.org/x/text/encoding/simplifiedchinese"
"golang.org/x/text/transform"
)
// gbkToUTF8 converts GBK encoded bytes to UTF-8 string
func gbkToUTF8(data []byte) string {
// Find the first null byte and truncate there
if idx := bytes.IndexByte(data, 0); idx >= 0 {
data = data[:idx]
}
if len(data) == 0 {
return ""
}
// Try to decode as GBK
reader := transform.NewReader(bytes.NewReader(data), simplifiedchinese.GBK.NewDecoder())
buf := new(bytes.Buffer)
_, err := buf.ReadFrom(reader)
if err != nil {
// If GBK decoding fails, try treating as UTF-8 or ASCII
return cleanString(string(data))
}
return cleanString(buf.String())
}
// cleanString removes non-printable characters except common whitespace
func cleanString(s string) string {
var result strings.Builder
for _, r := range s {
if r >= 32 || r == '\t' || r == '\n' || r == '\r' {
result.WriteRune(r)
}
}
return strings.TrimSpace(result.String())
}
// Command tokens - matching the C++ definitions
const (
// Server -> Client commands
CommandActived byte = 0 // COMMAND_ACTIVED
CommandBye byte = 204 // COMMAND_BYE - disconnect
CommandHeartbeat byte = 216 // CMD_HEARTBEAT_ACK
// Client -> Server tokens
TokenAuth byte = 100 // TOKEN_AUTH - authorization required
TokenHeartbeat byte = 101 // TOKEN_HEARTBEAT
TokenLogin byte = 102 // TOKEN_LOGIN - login packet
)
// LOGIN_INFOR structure size and offsets (matching C++ struct with default alignment)
// Note: C++ struct uses default alignment (4-byte for uint32/int)
const (
LoginInfoSize = 980 // Total size of LOGIN_INFOR struct (with alignment padding)
// Field offsets (with alignment padding)
OffsetToken = 0 // 1 byte (unsigned char)
OffsetOsVerInfoEx = 1 // 156 bytes (char[156])
// 3 bytes padding here to align dwCPUMHz to 4-byte boundary
OffsetCPUMHz = 160 // 4 bytes (unsigned int) - aligned to 4
OffsetModuleVersion = 164 // 24 bytes (char[24])
OffsetPCName = 188 // 240 bytes (char[240])
OffsetMasterID = 428 // 20 bytes (char[20])
OffsetWebCamExist = 448 // 4 bytes (int) - aligned to 4
OffsetSpeed = 452 // 4 bytes (unsigned int)
OffsetStartTime = 456 // 20 bytes (char[20])
OffsetReserved = 476 // 512 bytes (char[512])
)
// LoginInfo represents client login information
type LoginInfo struct {
Token byte
OsVerInfo string // OS version info
CPUMHz uint32
ModuleVersion string
PCName string // Computer name
MasterID string
WebCamExist bool
Speed uint32
StartTime string
Reserved string // Contains additional info separated by |
}
// ParseLoginInfo parses LOGIN_INFOR from data
func ParseLoginInfo(data []byte) (*LoginInfo, error) {
if len(data) < 100 { // Minimum size check
return nil, ErrInvalidData
}
info := &LoginInfo{
Token: data[0],
}
// Parse OS version info (offset 1, 156 bytes)
// The C++ client fills this with a readable string like "Windows 10" via getSystemName()
if len(data) >= OffsetOsVerInfoEx+156 {
info.OsVerInfo = parseOsVersionInfo(data[OffsetOsVerInfoEx : OffsetOsVerInfoEx+156])
}
// Parse CPU MHz (offset 160, 4 bytes)
if len(data) >= OffsetCPUMHz+4 {
info.CPUMHz = binary.LittleEndian.Uint32(data[OffsetCPUMHz:])
}
// Parse module version (offset 164, 24 bytes)
// This contains date string like "Dec 19 2025"
if len(data) >= OffsetModuleVersion+24 {
info.ModuleVersion = gbkToUTF8(data[OffsetModuleVersion : OffsetModuleVersion+24])
}
// Parse PC name (offset 188, 240 bytes)
if len(data) >= OffsetPCName+240 {
info.PCName = gbkToUTF8(data[OffsetPCName : OffsetPCName+240])
}
// Parse Master ID (offset 428, 20 bytes)
if len(data) >= OffsetMasterID+20 {
info.MasterID = gbkToUTF8(data[OffsetMasterID : OffsetMasterID+20])
}
// Parse WebCam exist (offset 448, 4 bytes)
if len(data) >= OffsetWebCamExist+4 {
info.WebCamExist = binary.LittleEndian.Uint32(data[OffsetWebCamExist:]) != 0
}
// Parse Speed (offset 452, 4 bytes)
if len(data) >= OffsetSpeed+4 {
info.Speed = binary.LittleEndian.Uint32(data[OffsetSpeed:])
}
// Parse Start time (offset 456, 20 bytes)
if len(data) >= OffsetStartTime+20 {
info.StartTime = gbkToUTF8(data[OffsetStartTime : OffsetStartTime+20])
}
// Parse Reserved (offset 476, 512 bytes) - contains additional info
if len(data) >= OffsetReserved+512 {
info.Reserved = gbkToUTF8(data[OffsetReserved : OffsetReserved+512])
} else if len(data) > OffsetReserved {
info.Reserved = gbkToUTF8(data[OffsetReserved:])
}
return info, nil
}
// parseOsVersionInfo parses the OS version info field
// The C++ client fills this with a readable string like "Windows 10" via getSystemName()
func parseOsVersionInfo(data []byte) string {
return gbkToUTF8(data)
}
// ParseReserved parses the reserved field into a slice of strings
func (info *LoginInfo) ParseReserved() []string {
if info.Reserved == "" {
return nil
}
return strings.Split(info.Reserved, "|")
}
// GetReservedField returns a specific field from reserved data by index
// Fields: ClientType(0), SystemBits(1), CPU(2), Memory(3), FilePath(4),
// Reserved(5), InstallTime(6), InstallInfo(7), ProgramBits(8), ExpiredDate(9),
// ClientLoc(10), ClientPubIP(11), ExeVersion(12), Username(13), IsAdmin(14)
func (info *LoginInfo) GetReservedField(index int) string {
fields := info.ParseReserved()
if index >= 0 && index < len(fields) {
return fields[index]
}
return ""
}
// Validation structure for TOKEN_AUTH
type Validation struct {
From string // Start date
To string // End date
Admin string // Admin address
Port int // Admin port
Checksum string // Reserved field
}
// BuildValidation creates a validation response
func BuildValidation(days float64, admin string, port int) []byte {
// This would build the validation structure
// For now, return a simple structure
data := make([]byte, 160) // Size of Validation struct
data[0] = TokenAuth
// Fill in fields...
// From: 20 bytes
// To: 20 bytes
// Admin: 100 bytes
// Port: 4 bytes
// Checksum: 16 bytes
return data
}

View File

@@ -0,0 +1,267 @@
package protocol
// Header encoding/decoding functions
// Ported from common/header.h and common/encfuncs.h
const (
MsgHeader = "HELL"
FlagCompLen = 4
FlagLength = 8
HdrLength = FlagLength + 8 // FLAG_LENGTH + 2 * sizeof(uint32)
MinComLen = 12
)
// HeaderEncType represents the encryption method used for header
type HeaderEncType int
const (
HeaderEncUnknown HeaderEncType = -1
HeaderEncNone HeaderEncType = 0
HeaderEncV0 HeaderEncType = 1
HeaderEncV1 HeaderEncType = 2
HeaderEncV2 HeaderEncType = 3
HeaderEncV3 HeaderEncType = 4
HeaderEncV4 HeaderEncType = 5
HeaderEncV5 HeaderEncType = 6
HeaderEncV6 HeaderEncType = 7
)
// DecryptFunc is the function signature for header decryption
type DecryptFunc func(data []byte, key byte)
// defaultDecrypt does nothing (no encryption)
func defaultDecrypt(data []byte, key byte) {
// No-op
}
// decrypt is the default encryption method (V0)
func decrypt(data []byte, key byte) {
if key == 0 {
return
}
for i := 0; i < len(data); i++ {
k := key ^ byte(i*31)
value := int(data[i])
switch i % 4 {
case 0:
value -= int(k)
case 1:
value = value ^ int(k)
case 2:
value += int(k)
case 3:
value = ^value ^ int(k)
}
data[i] = byte(value & 0xFF)
}
}
// decryptV1 - alternating add/subtract
func decryptV1(data []byte, key byte) {
for i := 0; i < len(data); i++ {
if i%2 == 0 {
data[i] = data[i] - key
} else {
data[i] = data[i] + key
}
}
}
// decryptV2 - XOR with rotation
func decryptV2(data []byte, key byte) {
for i := 0; i < len(data); i++ {
if i%2 == 0 {
data[i] = (data[i] >> 1) | (data[i] << 7) // rotate right
} else {
data[i] = (data[i] << 1) | (data[i] >> 7) // rotate left
}
data[i] ^= key
}
}
// decryptV3 - dynamic key with position
func decryptV3(data []byte, key byte) {
for i := 0; i < len(data); i++ {
dynamicKey := key + byte(i%8)
switch i % 3 {
case 0:
data[i] = (data[i] ^ dynamicKey) - dynamicKey
case 1:
data[i] = (data[i] + dynamicKey) ^ dynamicKey
case 2:
data[i] = ^data[i] - dynamicKey
}
}
}
// decryptV4 - pseudo-random XOR (symmetric)
func decryptV4(data []byte, key byte) {
rand := uint16(key)
for i := 0; i < len(data); i++ {
rand = (rand*13 + 17) % 256
data[i] ^= byte(rand)
}
}
// decryptV5 - dynamic key with bit shift
func decryptV5(data []byte, key byte) {
for i := 0; i < len(data); i++ {
dynamicKey := (key + byte(i)) ^ 0x55
data[i] = ((data[i] - byte(i%7)) ^ (dynamicKey << 3)) - dynamicKey
}
}
// decryptV6 - pseudo-random with position (symmetric)
func decryptV6(data []byte, key byte) {
rand := uint16(key)
for i := 0; i < len(data); i++ {
rand = (rand*31 + 17) % 256
data[i] ^= byte(rand) + byte(i)
}
}
// All decrypt methods
var decryptMethods = []DecryptFunc{
defaultDecrypt,
decrypt,
decryptV1,
decryptV2,
decryptV3,
decryptV4,
decryptV5,
decryptV6,
}
// compare decrypts and compares the flag with magic
func compare(flag []byte, magic string, length int, dec DecryptFunc, key byte) bool {
if len(flag) < MinComLen {
return false
}
buf := make([]byte, MinComLen)
copy(buf, flag[:MinComLen])
dec(buf[:length], key)
return string(buf[:length]) == magic
}
// CheckHead tries all decryption methods to identify the protocol
func CheckHead(flag []byte) (flagType FlagType, encType HeaderEncType, decrypted []byte) {
if len(flag) < MinComLen {
return FlagUnknown, HeaderEncUnknown, nil
}
for i, method := range decryptMethods {
buf := make([]byte, MinComLen)
copy(buf, flag[:MinComLen])
ft := checkHeadWithMethod(buf, method)
if ft != FlagUnknown {
return ft, HeaderEncType(i), buf
}
}
return FlagUnknown, HeaderEncUnknown, nil
}
// checkHeadWithMethod checks the flag with a specific decrypt method
func checkHeadWithMethod(flag []byte, dec DecryptFunc) FlagType {
// Try HELL (FLAG_HELL)
if len(flag) >= FlagLength {
buf := make([]byte, MinComLen)
copy(buf, flag)
key := buf[6]
dec(buf[:FlagCompLen], key)
if string(buf[:4]) == MsgHeader {
copy(flag, buf)
return FlagHell
}
}
// Try Shine (FLAG_SHINE)
if len(flag) >= 5 {
buf := make([]byte, MinComLen)
copy(buf, flag)
dec(buf[:5], 0)
if string(buf[:5]) == "Shine" {
copy(flag, buf)
return FlagShine
}
}
// Try <<FUCK>> (FLAG_FUCK)
if len(flag) >= 10 {
buf := make([]byte, MinComLen)
copy(buf, flag)
key := buf[9]
dec(buf[:8], key)
if string(buf[:8]) == "<<FUCK>>" {
copy(flag, buf)
return FlagFuck
}
}
// Try Hello? (FLAG_HELLO)
if len(flag) >= 7 {
buf := make([]byte, MinComLen)
copy(buf, flag)
key := buf[6]
dec(buf[:6], key)
if string(buf[:6]) == "Hello?" {
copy(flag, buf)
return FlagHello
}
}
return FlagUnknown
}
// FlagType represents the protocol type
type FlagType int
const (
FlagWinOS FlagType = -1
FlagUnknown FlagType = 0
FlagShine FlagType = 1
FlagFuck FlagType = 2
FlagHello FlagType = 3
FlagHell FlagType = 4
)
func (f FlagType) String() string {
switch f {
case FlagWinOS:
return "WinOS"
case FlagShine:
return "Shine"
case FlagFuck:
return "Fuck"
case FlagHello:
return "Hello"
case FlagHell:
return "Hell"
default:
return "Unknown"
}
}
// GetFlagLength returns the flag length for a given flag type
func GetFlagLength(ft FlagType) int {
switch ft {
case FlagShine:
return 5
case FlagFuck:
return 11 // 8 + 3
case FlagHello:
return 8
case FlagHell:
return FlagLength
default:
return 0
}
}
// GetHeaderLength returns the full header length for a given flag type
func GetHeaderLength(ft FlagType) int {
return GetFlagLength(ft) + 8
}

View File

@@ -0,0 +1,261 @@
package protocol
import (
"encoding/binary"
"errors"
"github.com/yuanyuanxiang/SimpleRemoter/server/go/connection"
)
// Errors
var (
ErrNeedMore = errors.New("need more data")
ErrInvalidData = errors.New("invalid data")
ErrUnsupported = errors.New("unsupported protocol")
ErrDecompress = errors.New("decompression failed")
)
// Parser handles protocol parsing
type Parser struct {
codec *Codec
}
// NewParser creates a new parser
func NewParser() *Parser {
return &Parser{
codec: NewCodec(),
}
}
// Close releases resources held by the parser
func (p *Parser) Close() {
if p.codec != nil {
p.codec.Close()
}
}
// Parse attempts to parse a complete packet from the buffer
func (p *Parser) Parse(ctx *connection.Context) ([]byte, error) {
buf := ctx.InBuffer
// Need at least minimum bytes to determine protocol
if buf.Len() < MinComLen {
return nil, ErrNeedMore
}
// Check if header is already parsed
if ctx.FlagType == connection.FlagUnknown {
// Try to parse header
if err := p.parseHeader(ctx); err != nil {
return nil, err
}
}
// Now parse the packet
return p.parsePacket(ctx)
}
// parseHeader parses the protocol header with obfuscation handling
func (p *Parser) parseHeader(ctx *connection.Context) error {
buf := ctx.InBuffer
header := buf.Peek(MinComLen)
if header == nil || len(header) < MinComLen {
return ErrNeedMore
}
// Try to decode the header using all encryption methods
flagType, encType, decrypted := CheckHead(header)
if flagType == FlagUnknown {
return ErrUnsupported
}
// Store decrypted header params for later use (for XOREncoder16)
ctx.HeaderParams = make([]byte, MinComLen)
if decrypted != nil {
copy(ctx.HeaderParams, decrypted)
} else {
copy(ctx.HeaderParams, header)
}
// Map protocol FlagType to connection FlagType and set compression method
switch flagType {
case FlagHell:
ctx.FlagType = connection.FlagHell
ctx.FlagLen = FlagLength
ctx.HeaderLen = ctx.FlagLen + 8
ctx.CompressMethod = connection.CompressZstd // HELL uses ZSTD
case FlagHello:
ctx.FlagType = connection.FlagHello
ctx.FlagLen = 8
ctx.HeaderLen = ctx.FlagLen + 8
ctx.CompressMethod = connection.CompressNone // HELLO uses no compression
case FlagShine:
ctx.FlagType = connection.FlagShine
ctx.FlagLen = 5
ctx.HeaderLen = ctx.FlagLen + 8
ctx.CompressMethod = connection.CompressZstd // SHINE uses ZSTD
case FlagFuck:
ctx.FlagType = connection.FlagFuck
ctx.FlagLen = 11
ctx.HeaderLen = ctx.FlagLen + 8
ctx.CompressMethod = connection.CompressZstd // FUCK uses ZSTD
default:
return ErrUnsupported
}
// Store encryption type for later use
ctx.HeaderEncType = int(encType)
return nil
}
// parsePacket parses a complete packet
func (p *Parser) parsePacket(ctx *connection.Context) ([]byte, error) {
buf := ctx.InBuffer
// Check if we have enough data for header
if buf.Len() < ctx.HeaderLen {
return nil, ErrNeedMore
}
// Peek the header to get total length
headerData := buf.Peek(ctx.HeaderLen)
if headerData == nil {
return nil, ErrNeedMore
}
// Decrypt the header first
decryptedHeader := make([]byte, len(headerData))
copy(decryptedHeader, headerData)
// Get the encryption key (usually at position 6)
var key byte
if len(headerData) > 6 {
key = headerData[6] // Use original key before decryption
}
// Decrypt flag portion
if ctx.HeaderEncType >= 0 && ctx.HeaderEncType < len(decryptMethods) {
decryptMethods[ctx.HeaderEncType](decryptedHeader[:FlagCompLen], key)
}
// Read the total length field (after flag)
totalLen := binary.LittleEndian.Uint32(decryptedHeader[ctx.FlagLen:])
// Validate length
if totalLen < uint32(ctx.HeaderLen) || totalLen > 10*1024*1024 {
return nil, ErrInvalidData
}
// Check if we have the complete packet
if buf.Len() < int(totalLen) {
return nil, ErrNeedMore
}
// Read the complete packet
packet := buf.Read(int(totalLen))
if packet == nil {
return nil, ErrInvalidData
}
// Decrypt header portion of packet
if ctx.HeaderEncType >= 0 && ctx.HeaderEncType < len(decryptMethods) {
decryptMethods[ctx.HeaderEncType](packet[:FlagCompLen], key)
}
// Update HeaderParams with this packet's header (for XOREncoder16 k1, k2)
if len(packet) >= ctx.FlagLen {
ctx.HeaderParams = make([]byte, ctx.HeaderLen)
copy(ctx.HeaderParams, packet[:ctx.HeaderLen])
}
// Extract data after header
dataStart := ctx.HeaderLen
data := packet[dataStart:]
// Get original length (before compression)
var origLen uint32
if ctx.FlagType != connection.FlagWinOS {
origLen = binary.LittleEndian.Uint32(packet[ctx.FlagLen+4 : ctx.FlagLen+8])
}
// Decode (XOR, etc.) before decompression - this is Encoder2
p.codec.Decode(ctx, data)
// Decompress
decompressed, err := p.codec.Decompress(ctx, data, origLen)
if err != nil {
return nil, err
}
// Decode after decompression - this is Encoder
p.codec.DecodeData(ctx, decompressed)
return decompressed, nil
}
// Encode encodes data for sending
func (p *Parser) Encode(ctx *connection.Context, data []byte) ([]byte, error) {
// Encode before compression
encoded := make([]byte, len(data))
copy(encoded, data)
p.codec.EncodeData(ctx, encoded)
// Compress
compressed, err := p.codec.Compress(ctx, encoded)
if err != nil {
return nil, err
}
// Build packet
packet := p.buildPacket(ctx, compressed, uint32(len(data)))
return packet, nil
}
// buildPacket builds a complete packet with header
func (p *Parser) buildPacket(ctx *connection.Context, data []byte, origLen uint32) []byte {
totalLen := ctx.HeaderLen + len(data)
packet := make([]byte, totalLen)
// Write flag
flag := p.getFlag(ctx)
copy(packet[:ctx.FlagLen], flag)
// Write total length
binary.LittleEndian.PutUint32(packet[ctx.FlagLen:], uint32(totalLen))
// Write original length
binary.LittleEndian.PutUint32(packet[ctx.FlagLen+4:], origLen)
// Write data
copy(packet[ctx.HeaderLen:], data)
// Encode after building
p.codec.Encode(ctx, packet[ctx.HeaderLen:])
return packet
}
// getFlag returns the protocol flag bytes
func (p *Parser) getFlag(ctx *connection.Context) []byte {
switch ctx.FlagType {
case connection.FlagHell:
flag := make([]byte, FlagLength)
copy(flag, []byte(MsgHeader))
return flag
case connection.FlagHello:
flag := make([]byte, 8)
copy(flag, []byte("Hello?"))
return flag
case connection.FlagShine:
return []byte("Shine")
case connection.FlagFuck:
flag := make([]byte, 11)
copy(flag, []byte("<<FUCK>>"))
return flag
default:
return make([]byte, ctx.FlagLen)
}
}