180 lines
3.7 KiB
Go
180 lines
3.7 KiB
Go
package eq2net
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"net"
|
|
"time"
|
|
)
|
|
|
|
type EQPacket struct {
|
|
Buffer []byte // Raw packet data
|
|
Opcode uint16 // Packet opcode
|
|
|
|
SrcIP net.IP // Source IP address
|
|
SrcPort uint16 // Source port
|
|
DstIP net.IP // Destination IP address
|
|
DstPort uint16 // Destination port
|
|
|
|
Timestamp time.Time // When packet was created/received
|
|
Priority uint32 // Priority in processing
|
|
Version int16 // Protocol version
|
|
}
|
|
|
|
func NewEQPacket(opcode uint16, data []byte) *EQPacket {
|
|
p := &EQPacket{
|
|
Opcode: opcode,
|
|
Timestamp: time.Now(),
|
|
Priority: 0,
|
|
Version: 0,
|
|
}
|
|
|
|
if len(data) > 0 {
|
|
p.Buffer = make([]byte, len(data))
|
|
copy(p.Buffer, data)
|
|
}
|
|
|
|
return p
|
|
}
|
|
|
|
// Size returns the size of the packet data (excluding opcode)
|
|
func (p *EQPacket) Size() uint32 {
|
|
return uint32(len(p.Buffer))
|
|
}
|
|
|
|
// TotalSize returns the total size including opcode
|
|
func (p *EQPacket) TotalSize() uint32 {
|
|
opcodeSize := uint32(1)
|
|
if p.Opcode > 0xFF {
|
|
opcodeSize = 2
|
|
}
|
|
return p.Size() + opcodeSize
|
|
}
|
|
|
|
// GetRawOpcode returns the raw opcode value
|
|
func (p *EQPacket) GetRawOpcode() uint16 {
|
|
return p.Opcode
|
|
}
|
|
|
|
// GetOpcodeName returns the string name of the opcode
|
|
func (p *EQPacket) GetOpcodeName() string {
|
|
switch p.Opcode {
|
|
case OP_SessionRequest:
|
|
return "OP_SessionRequest"
|
|
case OP_SessionResponse:
|
|
return "OP_SessionResponse"
|
|
case OP_Combined:
|
|
return "OP_Combined"
|
|
case OP_SessionDisconnect:
|
|
return "OP_SessionDisconnect"
|
|
case OP_KeepAlive:
|
|
return "OP_KeepAlive"
|
|
case OP_SessionStatRequest:
|
|
return "OP_SessionStatRequest"
|
|
case OP_SessionStatResponse:
|
|
return "OP_SessionStatResponse"
|
|
case OP_Packet:
|
|
return "OP_Packet"
|
|
case OP_Fragment:
|
|
return "OP_Fragment"
|
|
case OP_OutOfOrderAck:
|
|
return "OP_OutOfOrderAck"
|
|
case OP_Ack:
|
|
return "OP_Ack"
|
|
case OP_AppCombined:
|
|
return "OP_AppCombined"
|
|
case OP_OutOfSession:
|
|
return "OP_OutOfSession"
|
|
default:
|
|
return fmt.Sprintf("Unknown(0x%04X)", p.Opcode)
|
|
}
|
|
}
|
|
|
|
// Serialize writes the packet to a byte buffer
|
|
func (p *EQPacket) Serialize() []byte {
|
|
// Determine opcode size
|
|
opcodeSize := 1
|
|
if p.Opcode > 0xFF {
|
|
opcodeSize = 2
|
|
}
|
|
|
|
// Create buffer
|
|
buf := make([]byte, opcodeSize+len(p.Buffer))
|
|
|
|
// Write opcode
|
|
if opcodeSize == 2 {
|
|
binary.BigEndian.PutUint16(buf[0:2], p.Opcode)
|
|
} else {
|
|
buf[0] = 0x00
|
|
buf[1] = byte(p.Opcode)
|
|
opcodeSize = 2 // Even 1-byte opcodes use 2 bytes on wire
|
|
}
|
|
|
|
// Copy data
|
|
if len(p.Buffer) > 0 {
|
|
copy(buf[opcodeSize:], p.Buffer)
|
|
}
|
|
|
|
return buf
|
|
}
|
|
|
|
// SetNetworkInfo sets the network address info
|
|
func (p *EQPacket) SetNetworkInfo(srcIP net.IP, srcPort uint16, dstIP net.IP, dstPort uint16) {
|
|
p.SrcIP = srcIP
|
|
p.SrcPort = srcPort
|
|
p.DstIP = dstIP
|
|
p.DstPort = dstPort
|
|
}
|
|
|
|
// Clone creates a deep copy of the packet
|
|
func (p *EQPacket) Clone() *EQPacket {
|
|
newPacket := &EQPacket{
|
|
Opcode: p.Opcode,
|
|
SrcIP: make(net.IP, len(p.SrcIP)),
|
|
SrcPort: p.SrcPort,
|
|
DstIP: make(net.IP, len(p.DstIP)),
|
|
DstPort: p.DstPort,
|
|
Timestamp: p.Timestamp,
|
|
Priority: p.Priority,
|
|
Version: p.Version,
|
|
}
|
|
|
|
if p.SrcIP != nil {
|
|
copy(newPacket.SrcIP, p.SrcIP)
|
|
}
|
|
if p.DstIP != nil {
|
|
copy(newPacket.DstIP, p.DstIP)
|
|
}
|
|
|
|
if len(p.Buffer) > 0 {
|
|
newPacket.Buffer = make([]byte, len(p.Buffer))
|
|
copy(newPacket.Buffer, p.Buffer)
|
|
}
|
|
|
|
return newPacket
|
|
}
|
|
|
|
// ParsePacket creates a packet from raw network data
|
|
func ParsePacket(data []byte) (*EQPacket, error) {
|
|
if len(data) < 2 {
|
|
return nil, fmt.Errorf("packet too small: %d bytes", len(data))
|
|
}
|
|
|
|
// Extract opcode (always 2 bytes on wire)
|
|
opcode := binary.BigEndian.Uint16(data[0:2])
|
|
|
|
// Create packet
|
|
p := &EQPacket{
|
|
Opcode: opcode,
|
|
Timestamp: time.Now(),
|
|
}
|
|
|
|
// Copy remaining data if any
|
|
if len(data) > 2 {
|
|
p.Buffer = make([]byte, len(data)-2)
|
|
copy(p.Buffer, data[2:])
|
|
}
|
|
|
|
return p, nil
|
|
}
|