1
0

add reliability and security managers to packet handler

This commit is contained in:
Sky Johnson 2025-07-02 14:40:24 -05:00
parent 9af0a372aa
commit 5970198c5a
2 changed files with 541 additions and 0 deletions

View File

@ -0,0 +1,298 @@
package packets
import (
"sync"
"time"
"eq2emu/internal/opcodes"
)
type ConnectionState uint8
const (
StateConnecting ConnectionState = iota
StateEstablished
StateClosing
StateClosed
)
type ReliablePacket struct {
Sequence uint16
Data []byte
SentTime time.Time
Attempts int
Acked bool
}
type ReliabilityManager struct {
sessionID uint32
state ConnectionState
nextSeq uint16
lastAcked uint16
// Outbound reliability
resendQueue map[uint16]*ReliablePacket
resendTimer *time.Timer
// Inbound reliability
expectedSeq uint16
outOfOrder map[uint16][]byte
// Configuration
maxRetries int
retryDelay time.Duration
ackDelay time.Duration
mu sync.RWMutex
onSend func([]byte)
onReceive func([]byte)
}
func NewReliabilityManager(sessionID uint32) *ReliabilityManager {
rm := &ReliabilityManager{
sessionID: sessionID,
state: StateConnecting,
resendQueue: make(map[uint16]*ReliablePacket),
outOfOrder: make(map[uint16][]byte),
maxRetries: 5,
retryDelay: time.Millisecond * 500,
ackDelay: time.Millisecond * 200,
}
rm.startResendTimer()
return rm
}
func (rm *ReliabilityManager) SetCallbacks(onSend, onReceive func([]byte)) {
rm.mu.Lock()
defer rm.mu.Unlock()
rm.onSend = onSend
rm.onReceive = onReceive
}
func (rm *ReliabilityManager) SetState(state ConnectionState) {
rm.mu.Lock()
defer rm.mu.Unlock()
rm.state = state
}
func (rm *ReliabilityManager) GetState() ConnectionState {
rm.mu.RLock()
defer rm.mu.RUnlock()
return rm.state
}
// Send packet with reliability
func (rm *ReliabilityManager) SendReliable(data []byte) uint16 {
rm.mu.Lock()
defer rm.mu.Unlock()
seq := rm.nextSeq
rm.nextSeq++
packet := &ReliablePacket{
Sequence: seq,
Data: make([]byte, len(data)),
SentTime: time.Now(),
Attempts: 1,
}
copy(packet.Data, data)
rm.resendQueue[seq] = packet
if rm.onSend != nil {
rm.onSend(data)
}
return seq
}
// Send unreliable packet
func (rm *ReliabilityManager) SendUnreliable(data []byte) {
rm.mu.RLock()
onSend := rm.onSend
rm.mu.RUnlock()
if onSend != nil {
onSend(data)
}
}
// Process incoming packet
func (rm *ReliabilityManager) ProcessIncoming(data []byte) {
if len(data) < 1 {
return
}
opcode := opcodes.ProtocolOpcode(data[0])
switch opcode {
case opcodes.OP_Ack:
rm.handleAck(data)
case opcodes.OP_Packet:
rm.handleSequencedPacket(data)
case opcodes.OP_Combined:
rm.handleSequencedPacket(data)
default:
// Unreliable packet
rm.deliverPacket(data)
}
}
func (rm *ReliabilityManager) handleAck(data []byte) {
if len(data) < 3 {
return
}
seq := uint16(data[1]) | (uint16(data[2]) << 8)
rm.mu.Lock()
defer rm.mu.Unlock()
if packet, exists := rm.resendQueue[seq]; exists {
packet.Acked = true
delete(rm.resendQueue, seq)
if seq > rm.lastAcked {
rm.lastAcked = seq
}
}
}
func (rm *ReliabilityManager) handleSequencedPacket(data []byte) {
if len(data) < 4 {
return
}
// Extract sequence (bytes 1-2)
seq := uint16(data[1]) | (uint16(data[2]) << 8)
rm.mu.Lock()
defer rm.mu.Unlock()
// Send ACK
rm.sendAck(seq)
if seq == rm.expectedSeq {
// In order - deliver immediately
rm.deliverPacketLocked(data)
rm.expectedSeq++
// Check for buffered packets
rm.processBufferedPackets()
} else if seq > rm.expectedSeq {
// Out of order - buffer it
buffered := make([]byte, len(data))
copy(buffered, data)
rm.outOfOrder[seq] = buffered
}
// Ignore old/duplicate packets (seq < expectedSeq)
}
func (rm *ReliabilityManager) processBufferedPackets() {
for rm.outOfOrder[rm.expectedSeq] != nil {
data := rm.outOfOrder[rm.expectedSeq]
delete(rm.outOfOrder, rm.expectedSeq)
rm.deliverPacketLocked(data)
rm.expectedSeq++
}
}
func (rm *ReliabilityManager) sendAck(seq uint16) {
ack := []byte{
byte(opcodes.OP_Ack),
byte(seq & 0xFF),
byte((seq >> 8) & 0xFF),
}
if rm.onSend != nil {
go rm.onSend(ack)
}
}
func (rm *ReliabilityManager) deliverPacket(data []byte) {
rm.mu.RLock()
onReceive := rm.onReceive
rm.mu.RUnlock()
if onReceive != nil {
onReceive(data)
}
}
func (rm *ReliabilityManager) deliverPacketLocked(data []byte) {
if rm.onReceive != nil {
// Make copy since we're calling from locked context
delivered := make([]byte, len(data))
copy(delivered, data)
go rm.onReceive(delivered)
}
}
func (rm *ReliabilityManager) startResendTimer() {
rm.resendTimer = time.AfterFunc(rm.retryDelay, func() {
rm.checkResends()
rm.startResendTimer()
})
}
func (rm *ReliabilityManager) checkResends() {
rm.mu.Lock()
defer rm.mu.Unlock()
now := time.Now()
var toResend []*ReliablePacket
for seq, packet := range rm.resendQueue {
if packet.Acked {
continue
}
if now.Sub(packet.SentTime) > rm.retryDelay {
if packet.Attempts >= rm.maxRetries {
// Give up on this packet
delete(rm.resendQueue, seq)
continue
}
packet.Attempts++
packet.SentTime = now
toResend = append(toResend, packet)
}
}
// Send outside of lock
if len(toResend) > 0 && rm.onSend != nil {
go func() {
for _, packet := range toResend {
rm.onSend(packet.Data)
}
}()
}
}
func (rm *ReliabilityManager) Close() {
rm.mu.Lock()
defer rm.mu.Unlock()
if rm.resendTimer != nil {
rm.resendTimer.Stop()
}
rm.state = StateClosed
rm.resendQueue = make(map[uint16]*ReliablePacket)
rm.outOfOrder = make(map[uint16][]byte)
}
// Get stats for monitoring
func (rm *ReliabilityManager) GetStats() (int, int, int) {
rm.mu.RLock()
defer rm.mu.RUnlock()
pending := len(rm.resendQueue)
buffered := len(rm.outOfOrder)
nextSeq := int(rm.nextSeq)
return pending, buffered, nextSeq
}

View File

@ -0,0 +1,243 @@
package packets
import (
"crypto/rand"
"crypto/rc4"
"crypto/rsa"
"crypto/x509"
"encoding/binary"
"fmt"
"eq2emu/internal/crc16"
"eq2emu/internal/opcodes"
)
type SecurityManager struct {
sessionKey uint32
rc4Cipher *rc4.Cipher
rsaKey *rsa.PrivateKey
encrypted bool
compressed bool
}
func NewSecurityManager() *SecurityManager {
// Generate RSA key for session establishment
key, _ := rsa.GenerateKey(rand.Reader, 1024)
return &SecurityManager{
rsaKey: key,
encrypted: false,
}
}
// Validate packet CRC
func (sm *SecurityManager) ValidateCRC(data []byte, key uint32) bool {
if len(data) < 2 {
return false
}
// Skip CRC validation for session packets
if len(data) >= 2 {
opcode := opcodes.ProtocolOpcode(data[0])
if opcode == opcodes.OP_SessionRequest ||
opcode == opcodes.OP_SessionResponse ||
opcode == opcodes.OP_OutOfSession {
return true
}
}
// Extract CRC from last 2 bytes
packetCRC := binary.LittleEndian.Uint16(data[len(data)-2:])
if packetCRC == 0 {
return true // No CRC
}
// Calculate CRC on data without the CRC bytes
calcCRC := uint16(crc16.Calculate(data[:len(data)-2], key))
return calcCRC == packetCRC
}
// Append CRC to packet
func (sm *SecurityManager) AppendCRC(data []byte, key uint32) []byte {
// Skip CRC for session packets
if len(data) >= 1 {
opcode := opcodes.ProtocolOpcode(data[0])
if opcode == opcodes.OP_SessionRequest ||
opcode == opcodes.OP_SessionResponse ||
opcode == opcodes.OP_OutOfSession {
return data
}
}
crc := uint16(crc16.Calculate(data, key))
result := make([]byte, len(data)+2)
copy(result, data)
binary.LittleEndian.PutUint16(result[len(data):], crc)
return result
}
// Chat encoding (custom XOR encryption)
func (sm *SecurityManager) ChatEncode(data []byte, key uint32) {
if len(data) < 2 {
return
}
// Skip encoding for certain opcodes
if data[1] == 0x01 || data[0] == 0x02 || data[0] == 0x1d {
return
}
// Skip opcode bytes
payload := data[2:]
encKey := key
// Encode 4-byte chunks
for i := 0; i+3 < len(payload); i += 4 {
chunk := binary.LittleEndian.Uint32(payload[i:])
encoded := chunk ^ encKey
encKey = encoded
binary.LittleEndian.PutUint32(payload[i:], encoded)
}
// Encode remaining bytes
keyByte := uint8(encKey & 0xFF)
for i := (len(payload) / 4) * 4; i < len(payload); i++ {
payload[i] ^= keyByte
}
}
// Chat decoding
func (sm *SecurityManager) ChatDecode(data []byte, key uint32) {
if len(data) < 2 {
return
}
// Skip decoding for certain opcodes
if data[1] == 0x01 || data[0] == 0x02 || data[0] == 0x1d {
return
}
// Skip opcode bytes
payload := data[2:]
decKey := key
// Decode 4-byte chunks
for i := 0; i+3 < len(payload); i += 4 {
chunk := binary.LittleEndian.Uint32(payload[i:])
decoded := chunk ^ decKey
decKey = chunk
binary.LittleEndian.PutUint32(payload[i:], decoded)
}
// Decode remaining bytes
keyByte := uint8(decKey & 0xFF)
for i := (len(payload) / 4) * 4; i < len(payload); i++ {
payload[i] ^= keyByte
}
}
// RC4 encryption setup
func (sm *SecurityManager) SetRC4Key(key []byte) error {
cipher, err := rc4.NewCipher(key)
if err != nil {
return err
}
sm.rc4Cipher = cipher
sm.encrypted = true
return nil
}
// RC4 encrypt/decrypt
func (sm *SecurityManager) RC4Crypt(data []byte) {
if sm.rc4Cipher != nil {
sm.rc4Cipher.XORKeyStream(data, data)
}
}
// RSA decrypt (for key exchange)
func (sm *SecurityManager) RSADecrypt(data []byte) ([]byte, error) {
if sm.rsaKey == nil {
return nil, fmt.Errorf("no RSA key")
}
return rsa.DecryptPKCS1v15(rand.Reader, sm.rsaKey, data)
}
// Get RSA public key for client
func (sm *SecurityManager) GetRSAPublicKey() ([]byte, error) {
if sm.rsaKey == nil {
return nil, fmt.Errorf("no RSA key")
}
return x509.MarshalPKIXPublicKey(&sm.rsaKey.PublicKey)
}
// Process encrypted packet
func (sm *SecurityManager) ProcessEncryptedPacket(data []byte) []byte {
if len(data) < 3 {
return data
}
// Make copy to avoid modifying original
result := make([]byte, len(data))
copy(result, data)
// Apply RC4 if enabled
if sm.encrypted && sm.rc4Cipher != nil {
sm.RC4Crypt(result)
}
// Apply chat decoding
if sm.sessionKey != 0 {
sm.ChatDecode(result, sm.sessionKey)
}
return result
}
// Prepare packet for sending
func (sm *SecurityManager) PrepareOutgoingPacket(data []byte) []byte {
// Make copy
result := make([]byte, len(data))
copy(result, data)
// Apply chat encoding
if sm.sessionKey != 0 {
sm.ChatEncode(result, sm.sessionKey)
}
// Apply RC4 if enabled
if sm.encrypted && sm.rc4Cipher != nil {
sm.RC4Crypt(result)
}
// Append CRC
result = sm.AppendCRC(result, sm.sessionKey)
return result
}
// Session management
func (sm *SecurityManager) SetSessionKey(key uint32) {
sm.sessionKey = key
}
func (sm *SecurityManager) GetSessionKey() uint32 {
return sm.sessionKey
}
func (sm *SecurityManager) SetEncrypted(encrypted bool) {
sm.encrypted = encrypted
}
func (sm *SecurityManager) IsEncrypted() bool {
return sm.encrypted
}
func (sm *SecurityManager) SetCompressed(compressed bool) {
sm.compressed = compressed
}
func (sm *SecurityManager) IsCompressed() bool {
return sm.compressed
}