fix encryption offsets
This commit is contained in:
parent
d70daa98be
commit
50d4d9d6f0
@ -160,6 +160,11 @@ func (p *ProtoPacket) PreparePacket(maxLen int16) int8 {
|
|||||||
return offset
|
return offset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsCompressed returns whether the packet is compressed
|
||||||
|
func (p *ProtoPacket) IsCompressed() bool {
|
||||||
|
return p.eq2Compressed
|
||||||
|
}
|
||||||
|
|
||||||
// Serialize writes the protocol packet to a destination buffer
|
// Serialize writes the protocol packet to a destination buffer
|
||||||
func (p *ProtoPacket) Serialize(dest []byte, offset int8) uint32 {
|
func (p *ProtoPacket) Serialize(dest []byte, offset int8) uint32 {
|
||||||
pos := 0
|
pos := 0
|
||||||
|
174
stream/stream.go
174
stream/stream.go
@ -328,9 +328,32 @@ func (s *Stream) processQueues() error {
|
|||||||
s.resendQueue = s.resendQueue[1:]
|
s.resendQueue = s.resendQueue[1:]
|
||||||
|
|
||||||
if time.Now().After(pending.nextRetry) {
|
if time.Now().After(pending.nextRetry) {
|
||||||
data := make([]byte, pending.packet.Size()+2)
|
// Resend already prepared packet
|
||||||
|
proto := pending.packet
|
||||||
|
var encryptOffset int8
|
||||||
|
if proto.LoginOp != opcodes.OP_Unknown {
|
||||||
|
encryptOffset = proto.PreparePacket(int16(s.maxLen))
|
||||||
|
}
|
||||||
|
|
||||||
|
data := make([]byte, len(proto.Buffer)+2)
|
||||||
binary.BigEndian.PutUint16(data[:2], pending.seq)
|
binary.BigEndian.PutUint16(data[:2], pending.seq)
|
||||||
pending.packet.Serialize(data[2:], 0)
|
copy(data[2:], proto.Buffer)
|
||||||
|
|
||||||
|
// Apply encryption if needed (BEFORE CRC, with proper offset)
|
||||||
|
if s.cipher != nil && len(data) > 2 {
|
||||||
|
if proto.IsCompressed() {
|
||||||
|
// Compressed packet: encrypt from offset 3
|
||||||
|
if len(data) > 3 {
|
||||||
|
s.cipher.Encrypt(data[3:])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Uncompressed packet: encrypt from 2 + offset
|
||||||
|
offset := 2 + int(encryptOffset)
|
||||||
|
if len(data) > offset {
|
||||||
|
s.cipher.Encrypt(data[offset:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pending.attempts++
|
pending.attempts++
|
||||||
pending.sentTime = time.Now()
|
pending.sentTime = time.Now()
|
||||||
@ -344,7 +367,7 @@ func (s *Stream) processQueues() error {
|
|||||||
|
|
||||||
s.mu.Unlock()
|
s.mu.Unlock()
|
||||||
atomic.AddUint64(&s.retransmits, 1)
|
atomic.AddUint64(&s.retransmits, 1)
|
||||||
s.sendRaw(opcodes.OP_Packet, data)
|
s.sendRawWithCRC(opcodes.OP_Packet, data)
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
} else {
|
} else {
|
||||||
s.resendQueue = append(s.resendQueue, pending)
|
s.resendQueue = append(s.resendQueue, pending)
|
||||||
@ -369,11 +392,32 @@ func (s *Stream) processQueues() error {
|
|||||||
proto := s.unreliableQueue[0]
|
proto := s.unreliableQueue[0]
|
||||||
s.unreliableQueue = s.unreliableQueue[1:]
|
s.unreliableQueue = s.unreliableQueue[1:]
|
||||||
|
|
||||||
data := make([]byte, proto.Size())
|
// Prepare unreliable packets too
|
||||||
proto.Serialize(data, 0)
|
var encryptOffset int8
|
||||||
|
if proto.LoginOp != opcodes.OP_Unknown {
|
||||||
|
encryptOffset = proto.PreparePacket(int16(s.maxLen))
|
||||||
|
}
|
||||||
|
|
||||||
|
data := make([]byte, len(proto.Buffer))
|
||||||
|
copy(data, proto.Buffer)
|
||||||
|
|
||||||
|
// Apply encryption if needed (BEFORE CRC)
|
||||||
|
if s.cipher != nil && len(data) > 0 {
|
||||||
|
if proto.IsCompressed() {
|
||||||
|
// Compressed: encrypt from offset 1 (skip compress flag)
|
||||||
|
if len(data) > 1 {
|
||||||
|
s.cipher.Encrypt(data[1:])
|
||||||
|
}
|
||||||
|
} else if encryptOffset > 0 {
|
||||||
|
// Uncompressed: encrypt from offset
|
||||||
|
if len(data) > int(encryptOffset) {
|
||||||
|
s.cipher.Encrypt(data[encryptOffset:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
s.mu.Unlock()
|
s.mu.Unlock()
|
||||||
s.sendRaw(proto.Opcode, data)
|
s.sendRawWithCRC(proto.Opcode, data)
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -386,12 +430,27 @@ func (s *Stream) processQueues() error {
|
|||||||
// sendFragmented sends a fragmented packet (matches EQStream::SendPacket)
|
// sendFragmented sends a fragmented packet (matches EQStream::SendPacket)
|
||||||
func (s *Stream) sendFragmented(proto *packets.ProtoPacket) error {
|
func (s *Stream) sendFragmented(proto *packets.ProtoPacket) error {
|
||||||
// Prepare packet first
|
// Prepare packet first
|
||||||
offset := proto.PreparePacket(int16(s.maxLen))
|
encryptOffset := proto.PreparePacket(int16(s.maxLen))
|
||||||
if offset < 0 {
|
if encryptOffset < 0 {
|
||||||
return fmt.Errorf("failed to prepare packet")
|
return fmt.Errorf("failed to prepare packet")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Encrypt the prepared packet data BEFORE fragmentation
|
||||||
data := proto.Buffer
|
data := proto.Buffer
|
||||||
|
if s.cipher != nil && len(data) > 0 {
|
||||||
|
if proto.IsCompressed() {
|
||||||
|
// Compressed: encrypt from offset 1 (skip compress flag)
|
||||||
|
if len(data) > 1 {
|
||||||
|
s.cipher.Encrypt(data[1:])
|
||||||
|
}
|
||||||
|
} else if encryptOffset > 0 {
|
||||||
|
// Uncompressed: encrypt from offset
|
||||||
|
if len(data) > int(encryptOffset) {
|
||||||
|
s.cipher.Encrypt(data[encryptOffset:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
length := uint32(len(data))
|
length := uint32(len(data))
|
||||||
|
|
||||||
// First fragment with total length
|
// First fragment with total length
|
||||||
@ -403,7 +462,8 @@ func (s *Stream) sendFragmented(proto *packets.ProtoPacket) error {
|
|||||||
used := copy(out.Buffer[4:], data)
|
used := copy(out.Buffer[4:], data)
|
||||||
out.Buffer = out.Buffer[:4+used]
|
out.Buffer = out.Buffer[:4+used]
|
||||||
|
|
||||||
if err := s.sendReliable(out); err != nil {
|
// Fragment packets are sent without additional encryption
|
||||||
|
if err := s.sendReliableFragment(out); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -416,7 +476,7 @@ func (s *Stream) sendFragmented(proto *packets.ProtoPacket) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
frag := packets.NewProtoPacket(opcodes.OP_Fragment, data[pos:pos+chunkSize], s.opcodeManager)
|
frag := packets.NewProtoPacket(opcodes.OP_Fragment, data[pos:pos+chunkSize], s.opcodeManager)
|
||||||
if err := s.sendReliable(frag); err != nil {
|
if err := s.sendReliableFragment(frag); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -426,16 +486,17 @@ func (s *Stream) sendFragmented(proto *packets.ProtoPacket) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendReliable sends a packet reliably with sequencing
|
// sendReliableFragment sends a fragment packet (already encrypted data)
|
||||||
func (s *Stream) sendReliable(proto *packets.ProtoPacket) error {
|
func (s *Stream) sendReliableFragment(proto *packets.ProtoPacket) error {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
seq := s.seqOut
|
seq := s.seqOut
|
||||||
s.seqOut = s.incrementSequence(s.seqOut)
|
s.seqOut = s.incrementSequence(s.seqOut)
|
||||||
s.mu.Unlock()
|
s.mu.Unlock()
|
||||||
|
|
||||||
data := make([]byte, proto.Size()+2)
|
// Build packet with sequence (data is already encrypted)
|
||||||
|
data := make([]byte, len(proto.Buffer)+2)
|
||||||
binary.BigEndian.PutUint16(data[:2], seq)
|
binary.BigEndian.PutUint16(data[:2], seq)
|
||||||
proto.Serialize(data[2:], 0)
|
copy(data[2:], proto.Buffer)
|
||||||
|
|
||||||
pending := &pendingPacket{
|
pending := &pendingPacket{
|
||||||
packet: proto.Copy(),
|
packet: proto.Copy(),
|
||||||
@ -449,7 +510,59 @@ func (s *Stream) sendReliable(proto *packets.ProtoPacket) error {
|
|||||||
s.pendingAcks[seq] = pending
|
s.pendingAcks[seq] = pending
|
||||||
s.mu.Unlock()
|
s.mu.Unlock()
|
||||||
|
|
||||||
return s.sendRaw(opcodes.OP_Packet, data)
|
return s.sendRawWithCRC(opcodes.OP_Packet, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sendReliable sends a packet reliably with sequencing
|
||||||
|
func (s *Stream) sendReliable(proto *packets.ProtoPacket) error {
|
||||||
|
s.mu.Lock()
|
||||||
|
seq := s.seqOut
|
||||||
|
s.seqOut = s.incrementSequence(s.seqOut)
|
||||||
|
s.mu.Unlock()
|
||||||
|
|
||||||
|
// Prepare packet first (adds headers, compression)
|
||||||
|
var encryptOffset int8
|
||||||
|
if proto.LoginOp != opcodes.OP_Unknown {
|
||||||
|
encryptOffset = proto.PreparePacket(int16(s.maxLen))
|
||||||
|
if encryptOffset < 0 {
|
||||||
|
return fmt.Errorf("failed to prepare packet")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build packet with sequence
|
||||||
|
data := make([]byte, len(proto.Buffer)+2)
|
||||||
|
binary.BigEndian.PutUint16(data[:2], seq)
|
||||||
|
copy(data[2:], proto.Buffer)
|
||||||
|
|
||||||
|
// Apply encryption if needed (BEFORE CRC, with proper offset)
|
||||||
|
if s.cipher != nil && len(data) > 2 {
|
||||||
|
if proto.IsCompressed() {
|
||||||
|
// Compressed packet: encrypt from compress offset (typically 3)
|
||||||
|
if len(data) > 3 {
|
||||||
|
s.cipher.Encrypt(data[3:]) // Skip seq[2] + compress_flag[1]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Uncompressed packet: encrypt from 2 + offset
|
||||||
|
offset := 2 + int(encryptOffset)
|
||||||
|
if len(data) > offset {
|
||||||
|
s.cipher.Encrypt(data[offset:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pending := &pendingPacket{
|
||||||
|
packet: proto.Copy(),
|
||||||
|
seq: seq,
|
||||||
|
sentTime: time.Now(),
|
||||||
|
attempts: 1,
|
||||||
|
nextRetry: time.Now().Add(s.rto),
|
||||||
|
}
|
||||||
|
|
||||||
|
s.mu.Lock()
|
||||||
|
s.pendingAcks[seq] = pending
|
||||||
|
s.mu.Unlock()
|
||||||
|
|
||||||
|
return s.sendRawWithCRC(opcodes.OP_Packet, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper methods
|
// Helper methods
|
||||||
@ -500,6 +613,7 @@ func (s *Stream) handleSessionRequest(data []byte) error {
|
|||||||
binary.BigEndian.PutUint32(response[11:15], version)
|
binary.BigEndian.PutUint32(response[11:15], version)
|
||||||
|
|
||||||
s.state.Store(StateEstablished)
|
s.state.Store(StateEstablished)
|
||||||
|
// Session packets don't have CRC or encryption (matches C++)
|
||||||
return s.sendRaw(opcodes.OP_SessionResponse, response)
|
return s.sendRaw(opcodes.OP_SessionResponse, response)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -860,7 +974,7 @@ func (s *Stream) sendPendingAcks() {
|
|||||||
for _, seq := range s.ackQueue {
|
for _, seq := range s.ackQueue {
|
||||||
data := make([]byte, 2)
|
data := make([]byte, 2)
|
||||||
binary.BigEndian.PutUint16(data, seq)
|
binary.BigEndian.PutUint16(data, seq)
|
||||||
s.sendRaw(opcodes.OP_Ack, data)
|
s.sendRawWithCRC(opcodes.OP_Ack, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.ackQueue = s.ackQueue[:0]
|
s.ackQueue = s.ackQueue[:0]
|
||||||
@ -871,18 +985,18 @@ func (s *Stream) sendPendingAcks() {
|
|||||||
func (s *Stream) sendAckImmediate(seq uint16) error {
|
func (s *Stream) sendAckImmediate(seq uint16) error {
|
||||||
data := make([]byte, 2)
|
data := make([]byte, 2)
|
||||||
binary.BigEndian.PutUint16(data, seq)
|
binary.BigEndian.PutUint16(data, seq)
|
||||||
return s.sendRaw(opcodes.OP_Ack, data)
|
return s.sendRawWithCRC(opcodes.OP_Ack, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stream) sendOutOfOrderAck(seq uint16) error {
|
func (s *Stream) sendOutOfOrderAck(seq uint16) error {
|
||||||
data := make([]byte, 2)
|
data := make([]byte, 2)
|
||||||
binary.BigEndian.PutUint16(data, seq)
|
binary.BigEndian.PutUint16(data, seq)
|
||||||
return s.sendRaw(opcodes.OP_OutOfOrderAck, data)
|
return s.sendRawWithCRC(opcodes.OP_OutOfOrderAck, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stream) sendKeepalive() {
|
func (s *Stream) sendKeepalive() {
|
||||||
if s.state.Load() == StateEstablished {
|
if s.state.Load() == StateEstablished {
|
||||||
s.sendRaw(opcodes.OP_KeepAlive, nil)
|
s.sendRawWithCRC(opcodes.OP_KeepAlive, nil)
|
||||||
}
|
}
|
||||||
if s.keepAliveTimer != nil {
|
if s.keepAliveTimer != nil {
|
||||||
s.keepAliveTimer.Reset(10 * time.Second)
|
s.keepAliveTimer.Reset(10 * time.Second)
|
||||||
@ -902,20 +1016,27 @@ func (s *Stream) handleTimeout() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendRaw sends raw packet with CRC16
|
// sendRaw sends raw packet WITHOUT encryption or CRC (for special packets)
|
||||||
func (s *Stream) sendRaw(opcode uint16, data []byte) error {
|
func (s *Stream) sendRaw(opcode uint16, data []byte) error {
|
||||||
packet := make([]byte, 2+len(data))
|
packet := make([]byte, 2+len(data))
|
||||||
binary.BigEndian.PutUint16(packet[:2], opcode)
|
binary.BigEndian.PutUint16(packet[:2], opcode)
|
||||||
copy(packet[2:], data)
|
copy(packet[2:], data)
|
||||||
|
|
||||||
// Add CRC16
|
atomic.AddUint64(&s.packetsOut, 1)
|
||||||
packet = packets.AppendCRC(packet, s.crcKey)
|
atomic.AddUint64(&s.bytesOut, uint64(len(packet)))
|
||||||
|
|
||||||
// Encrypt if needed
|
return s.conn.AsyncWrite(packet, nil)
|
||||||
if s.cipher != nil {
|
|
||||||
s.cipher.Encrypt(packet)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sendRawWithCRC sends raw packet with CRC16 (matches C++ WritePacket)
|
||||||
|
func (s *Stream) sendRawWithCRC(opcode uint16, data []byte) error {
|
||||||
|
packet := make([]byte, 2+len(data))
|
||||||
|
binary.BigEndian.PutUint16(packet[:2], opcode)
|
||||||
|
copy(packet[2:], data)
|
||||||
|
|
||||||
|
// Add CRC16 AFTER encryption (matches C++ order)
|
||||||
|
packet = packets.AppendCRC(packet, s.crcKey)
|
||||||
|
|
||||||
atomic.AddUint64(&s.packetsOut, 1)
|
atomic.AddUint64(&s.packetsOut, 1)
|
||||||
atomic.AddUint64(&s.bytesOut, uint64(len(packet)))
|
atomic.AddUint64(&s.bytesOut, uint64(len(packet)))
|
||||||
|
|
||||||
@ -946,6 +1067,7 @@ func (s *Stream) SendSessionRequest() error {
|
|||||||
binary.BigEndian.PutUint16(data[8:10], s.maxLen)
|
binary.BigEndian.PutUint16(data[8:10], s.maxLen)
|
||||||
|
|
||||||
s.state.Store(StateConnecting)
|
s.state.Store(StateConnecting)
|
||||||
|
// Session packets don't have CRC or encryption (matches C++)
|
||||||
return s.sendRaw(opcodes.OP_SessionRequest, data)
|
return s.sendRaw(opcodes.OP_SessionRequest, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -974,7 +1096,7 @@ func (s *Stream) Close() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
s.state.Store(StateClosed)
|
s.state.Store(StateClosed)
|
||||||
s.sendRaw(opcodes.OP_SessionDisconnect, nil)
|
s.sendRawWithCRC(opcodes.OP_SessionDisconnect, nil)
|
||||||
|
|
||||||
// Stop all timers
|
// Stop all timers
|
||||||
if s.keepAliveTimer != nil {
|
if s.keepAliveTimer != nil {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user