1
0
Protocol/protocol_packet.go
2025-09-02 12:30:36 -05:00

362 lines
8.2 KiB
Go

package eq2net
import (
"bytes"
"compress/zlib"
"encoding/binary"
"fmt"
"git.sharkk.net/EQ2/Protocol/crypto"
)
// ProtocolPacket handles low-level protocol operations including
// compression, encryption, sequencing, and packet combining
type ProtocolPacket struct {
EQPacket
Compressed bool
Encrypted bool
PacketPrepared bool
Sequence uint16
Acknowledged bool
SentTime int32
AttemptCount int8
SubPackets []*ProtocolPacket
}
// NewProtocolPacket creates a new protocol packet
func NewProtocolPacket(opcode uint16, data []byte) *ProtocolPacket {
p := &ProtocolPacket{
EQPacket: *NewEQPacket(opcode, data),
}
return p
}
// ParseProtocolPacket creates a protocol packet from raw network data
func ParseProtocolPacket(data []byte) (*ProtocolPacket, error) {
base, err := ParsePacket(data)
if err != nil {
return nil, err
}
p := &ProtocolPacket{
EQPacket: *base,
}
// Check for compression flag
if len(p.Buffer) > 0 {
switch p.Buffer[0] {
case 0x5a: // Zlib compressed
p.Compressed = true
case 0xa5: // Simple encoding
p.Compressed = true
}
}
return p, nil
}
// Serialize writes the protocol packet to a byte buffer with optional offset
func (p *ProtocolPacket) SerializeWithOffset(offset int8) []byte {
opcodeSize := 2 // Protocol packets always use 2-byte opcodes
buf := make([]byte, opcodeSize+len(p.Buffer)-int(offset))
if p.Opcode > 0xff {
binary.BigEndian.PutUint16(buf[0:2], p.Opcode)
} else {
buf[0] = 0x00
buf[1] = byte(p.Opcode)
}
if len(p.Buffer) > int(offset) {
copy(buf[opcodeSize:], p.Buffer[offset:])
}
return buf
}
// ValidateCRC checks if the packet has a valid CRC
// Returns true if CRC is valid or packet is exempt
func (p *ProtocolPacket) ValidateCRC(key uint32) bool {
// Session control packets are exempt from CRC
if p.Opcode == OP_SessionRequest ||
p.Opcode == OP_SessionResponse ||
p.Opcode == OP_OutOfSession {
return true
}
// Need full packet data including opcode for CRC check
fullData := p.Serialize()
if len(fullData) < 2 {
return false
}
// CRC is in last 2 bytes
if len(fullData) < 4 { // Minimum: 2 byte opcode + 2 byte CRC
return false
}
dataLen := len(fullData) - 2
calculatedCRC := crypto.CalculateCRC(fullData[:dataLen], key)
packetCRC := binary.BigEndian.Uint16(fullData[dataLen:])
// CRC of 0 means no CRC check required
return packetCRC == 0 || calculatedCRC == packetCRC
}
// Compress compresses the packet data using zlib or simple encoding
func (p *ProtocolPacket) Compress() error {
if p.Compressed {
return fmt.Errorf("packet already compressed")
}
// Only compress packets larger than 30 bytes
if len(p.Buffer) > 30 {
compressed, err := p.zlibCompress()
if err != nil {
return err
}
p.Buffer = compressed
p.Compressed = true
} else if len(p.Buffer) > 0 {
// Simple encoding for small packets
p.simpleEncode()
p.Compressed = true
}
return nil
}
// Decompress decompresses the packet data
func (p *ProtocolPacket) Decompress() error {
if !p.Compressed {
return fmt.Errorf("packet not compressed")
}
if len(p.Buffer) == 0 {
return fmt.Errorf("no data to decompress")
}
var decompressed []byte
var err error
switch p.Buffer[0] {
case 0x5a: // Zlib compressed
decompressed, err = p.zlibDecompress()
if err != nil {
return err
}
case 0xa5: // Simple encoding
decompressed = p.simpleDecoded()
default:
return fmt.Errorf("unknown compression flag: 0x%02x", p.Buffer[0])
}
p.Buffer = decompressed
p.Compressed = false
return nil
}
// zlibCompress performs zlib compression
func (p *ProtocolPacket) zlibCompress() ([]byte, error) {
var compressed bytes.Buffer
// Write compression flag
compressed.WriteByte(0x5a)
// Compress the data
w := zlib.NewWriter(&compressed)
_, err := w.Write(p.Buffer)
if err != nil {
return nil, err
}
err = w.Close()
if err != nil {
return nil, err
}
return compressed.Bytes(), nil
}
// zlibDecompress performs zlib decompression
func (p *ProtocolPacket) zlibDecompress() ([]byte, error) {
if len(p.Buffer) < 2 {
return nil, fmt.Errorf("compressed data too small")
}
// Skip compression flag
r, err := zlib.NewReader(bytes.NewReader(p.Buffer[1:]))
if err != nil {
return nil, err
}
defer r.Close()
var decompressed bytes.Buffer
_, err = decompressed.ReadFrom(r)
if err != nil {
return nil, err
}
return decompressed.Bytes(), nil
}
// simpleEncode adds simple encoding flag
func (p *ProtocolPacket) simpleEncode() {
// Add encoding flag at the beginning
newBuffer := make([]byte, len(p.Buffer)+1)
newBuffer[0] = 0xa5
copy(newBuffer[1:], p.Buffer)
p.Buffer = newBuffer
}
// simpleDecoded removes simple encoding flag
func (p *ProtocolPacket) simpleDecoded() []byte {
if len(p.Buffer) > 1 {
return p.Buffer[1:]
}
return []byte{}
}
// Combine bundles multiple protocol packets into a single combined packet
func (p *ProtocolPacket) Combine(other *ProtocolPacket) bool {
// Check if this is already a combined packet
if p.Opcode != OP_Combined {
// Convert to combined packet
firstPacket := p.Clone()
p.Opcode = OP_Combined
p.SubPackets = []*ProtocolPacket{
{EQPacket: *firstPacket},
}
p.Buffer = p.buildCombinedBuffer()
}
// Check size limit (max 255 bytes for combined packets)
totalSize := len(p.Buffer) + int(other.TotalSize()) + 1 // +1 for size byte
if totalSize > 255 {
return false
}
// Add the new packet
p.SubPackets = append(p.SubPackets, other)
p.Buffer = p.buildCombinedBuffer()
return true
}
// buildCombinedBuffer creates the buffer for a combined packet
func (p *ProtocolPacket) buildCombinedBuffer() []byte {
var buf bytes.Buffer
for _, subPacket := range p.SubPackets {
serialized := subPacket.Serialize()
buf.WriteByte(byte(len(serialized))) // Size byte
buf.Write(serialized)
}
return buf.Bytes()
}
// ExtractSubPackets extracts individual packets from a combined packet
func (p *ProtocolPacket) ExtractSubPackets() ([]*ProtocolPacket, error) {
if p.Opcode != OP_Combined {
return nil, fmt.Errorf("not a combined packet")
}
var packets []*ProtocolPacket
offset := 0
data := p.Buffer
for offset < len(data) {
// Read size byte
if offset >= len(data) {
break
}
size := int(data[offset])
offset++
// Extract sub-packet data
if offset+size > len(data) {
return nil, fmt.Errorf("invalid combined packet: size exceeds buffer")
}
subData := data[offset : offset+size]
offset += size
// Parse sub-packet
subPacket, err := ParseProtocolPacket(subData)
if err != nil {
return nil, fmt.Errorf("failed to parse sub-packet: %w", err)
}
packets = append(packets, subPacket)
}
return packets, nil
}
// MakeApplicationPacket converts this protocol packet to an application packet
// This is used when a protocol packet contains application-level data
func (p *ProtocolPacket) MakeApplicationPacket(opcodeSize uint8) *AppPacket {
if len(p.Buffer) < int(opcodeSize) {
return nil
}
// Extract the application opcode from the buffer
var appOpcode uint16
if opcodeSize == 1 {
appOpcode = uint16(p.Buffer[0])
} else {
appOpcode = binary.BigEndian.Uint16(p.Buffer[0:2])
}
// Create application packet with remaining data
app := &AppPacket{
EQPacket: *NewEQPacket(appOpcode, p.Buffer[opcodeSize:]),
OpcodeSize: opcodeSize,
}
// Copy network info
app.SrcIP = p.SrcIP
app.SrcPort = p.SrcPort
app.DstIP = p.DstIP
app.DstPort = p.DstPort
app.Timestamp = p.Timestamp
app.Version = p.Version
return app
}
// ChatEncode applies XOR-based chat encryption
func (p *ProtocolPacket) ChatEncode(key uint32) {
if len(p.Buffer) <= 2 {
return // Skip opcode bytes
}
data := p.Buffer[2:] // Skip first 2 bytes (opcode)
keyBytes := make([]byte, 4)
binary.LittleEndian.PutUint32(keyBytes, key)
// Process 4-byte blocks with rolling key
i := 0
for ; i+4 <= len(data); i += 4 {
// XOR with current key
block := binary.LittleEndian.Uint32(data[i : i+4])
encrypted := block ^ key
binary.LittleEndian.PutUint32(data[i:i+4], encrypted)
// Update key with encrypted data
key = encrypted
}
// Handle remaining bytes with last key byte
keyByte := byte(key & 0xFF)
for ; i < len(data); i++ {
data[i] ^= keyByte
}
}
// ChatDecode applies XOR-based chat decryption (same as encode due to XOR properties)
func (p *ProtocolPacket) ChatDecode(key uint32) {
p.ChatEncode(key) // XOR encryption is symmetric
}