// EQ2Emulator: Everquest II Server Emulator // Copyright (C) 2007 EQ2EMulator Development Team // Licensed under GPL v3 - see #ifndef _EQPROTOCOL_H #define _EQPROTOCOL_H #include #include #include #include #include #include #include #include // Unix networking headers #include // Project headers #include "EQPacket.h" #include "Mutex.h" #include "opcodemgr.h" #include "misc.h" #include "crypto/crypto.h" #include "zlib.h" #include "timer.h" #ifdef WRITE_PACKETS #include #endif using namespace std; // Forward declarations class OpcodeManager; class EQStreamFactory; /** * EverQuest stream connection states. * Represents the current state of a network stream connection. */ typedef enum { ESTABLISHED, // Active connection ready for data WAIT_CLOSE, // Waiting for graceful close CLOSING, // In process of closing DISCONNECTING, // Actively disconnecting CLOSED // Connection fully closed } EQStreamState; // Packet flags #define FLAG_COMPRESSED 0x01 // Packet is compressed #define FLAG_ENCODED 0x04 // Packet is encoded/encrypted // Rate limiting and bandwidth constants #define RATEBASE 1048576 // Base rate: 1 MB #define DECAYBASE 78642 // Decay rate: RATEBASE/10 // Retransmission timing constants #ifndef RETRANSMIT_TIMEOUT_MULT #define RETRANSMIT_TIMEOUT_MULT 3.0 // Timeout multiplier #endif #ifndef RETRANSMIT_TIMEOUT_MAX #define RETRANSMIT_TIMEOUT_MAX 5000 // Maximum retransmit timeout (ms) #endif #ifndef AVERAGE_DELTA_MAX #define AVERAGE_DELTA_MAX 2500 // Maximum average delta (ms) #endif #pragma pack(1) /** * Session request packet structure. * Sent by client to initiate a new session. */ struct SessionRequest { uint32 UnknownA; // Unknown field A uint32 Session; // Requested session ID uint32 MaxLength; // Maximum packet length }; /** * Session response packet structure. * Sent by server in response to session request. */ struct SessionResponse { uint32 Session; // Assigned session ID uint32 Key; // Encryption/authentication key uint8 UnknownA; // Unknown field A uint8 Format; // Packet format version uint8 UnknownB; // Unknown field B uint32 MaxLength; // Maximum packet length uint32 UnknownD; // Unknown field D }; /** * Client-side session statistics. * Deltas are in milliseconds, representing round trip times. */ struct ClientSessionStats { /*000*/ uint16 RequestID; // Statistics request ID /*002*/ uint32 last_local_delta; // Last local round trip time /*006*/ uint32 average_delta; // Average round trip time /*010*/ uint32 low_delta; // Lowest recorded round trip time /*014*/ uint32 high_delta; // Highest recorded round trip time /*018*/ uint32 last_remote_delta; // Last remote round trip time /*022*/ uint64 packets_sent; // Total packets sent /*030*/ uint64 packets_recieved; // Total packets received /*038*/ }; /** * Server-side session statistics. * Provides server perspective on connection quality. */ struct ServerSessionStats { uint16 RequestID; // Statistics request ID uint32 current_time; // Current server time uint32 unknown1; // Unknown field 1 uint32 received_packets; // Packets received by server uint32 unknown2; // Unknown field 2 uint32 sent_packets; // Packets sent by server uint32 unknown3; // Unknown field 3 uint32 sent_packets2; // Duplicate sent packets count uint32 unknown4; // Unknown field 4 uint32 received_packets2; // Duplicate received packets count }; #pragma pack() // External opcode manager extern OpcodeManager* EQNetworkOpcodeManager; /** * Types of EverQuest network streams. * Each type handles different aspects of the game protocol. */ typedef enum { UnknownStream = 0, // Unidentified stream type LoginStream, // Login server authentication WorldStream, // World server communication ZoneStream, // Zone server gameplay ChatOrMailStream, // Combined chat/mail (legacy) ChatStream, // Chat system only MailStream, // Mail system only EQ2Stream, // EverQuest 2 specific stream } EQStreamType; /** * EverQuest network stream class. * Handles reliable UDP communication with sequence numbers, acknowledgments, * compression, encryption, and packet combining for EverQuest protocols. */ class EQStream { protected: /** * Sequence number ordering enumeration. * Used to determine packet sequence relationships. */ typedef enum { SeqPast, // Sequence is from the past (duplicate/old) SeqInOrder, // Sequence is the expected next sequence SeqFuture // Sequence is from the future (out of order) } SeqOrder; // Packet statistics uint32 received_packets; // Total packets received uint32 sent_packets; // Total packets sent // Remote endpoint information uint32 remote_ip; // Remote IP address uint16 remote_port; // Remote port number // Packet buffers uint8 buffer[8192]; // Main packet buffer unsigned char* oversize_buffer; // Buffer for oversized packets uint32 oversize_offset; // Offset in oversize buffer uint32 oversize_length; // Length of oversize buffer data unsigned char* rogue_buffer; // Buffer for rogue/malformed packets uint32 roguebuf_offset; // Offset in rogue buffer uint32 roguebuf_size; // Size of rogue buffer // Protocol configuration uint8 app_opcode_size; // Size of application opcodes EQStreamType StreamType; // Type of this stream bool compressed; // Stream supports compression bool encoded; // Stream supports encoding/encryption // Write buffer for outgoing packets unsigned char write_buffer[2048]; // Retransmission timing uint32 retransmittimer; // Current retransmit timer uint32 retransmittimeout; // Retransmit timeout value // Session management uint16 sessionAttempts; // Number of session attempts uint16 reconnectAttempt; // Number of reconnect attempts bool streamactive; // Stream is actively connected // Session state uint32 Session; // Session ID uint32 Key; // Session encryption key uint16 NextInSeq; // Next expected incoming sequence uint16 NextOutSeq; // Next outgoing sequence number uint16 SequencedBase; // Base sequence for SequencedQueue[0] uint32 MaxLen; // Maximum packet length uint16 MaxSends; // Maximum send attempts int8 timeout_delays; // Number of timeout delays accumulated // Thread safety for stream usage uint8 active_users; // Number of active users of this stream Mutex MInUse; // Mutex for usage tracking #ifdef WRITE_PACKETS // Packet logging for debugging FILE* write_packets = nullptr; // File handle for packet dumps char GetChar(uchar in); // Convert byte to printable character void WriteToFile(char* pFormat, ...); // Write formatted data to file void WritePackets(const char* opcodeName, uchar* data, int32 size, bool outgoing); void WritePackets(EQ2Packet* app, bool outgoing); Mutex MWritePackets; // Mutex for packet writing #endif // Stream connection state EQStreamState State; // Current connection state Mutex MState; // Mutex for state access // Timing and general variables uint32 LastPacket; // Timestamp of last packet activity Mutex MVarlock; // General variable lock // Combined application packet handling EQApplicationPacket* CombinedAppPacket; // Current combined packet Mutex MCombinedAppPacket; // Mutex for combined packet access // Sequence number tracking long LastSeqSent; // Last sequence number sent Mutex MLastSeqSent; // Mutex for last sequence sent void SetLastSeqSent(uint32 seq); // Set last sent sequence number // Acknowledgment sequence tracking long MaxAckReceived; // Highest ack sequence received long NextAckToSend; // Next ack sequence to send long LastAckSent; // Last ack sequence sent // Acknowledgment accessor methods long GetMaxAckReceived(); long GetNextAckToSend(); long GetLastAckSent(); void SetMaxAckReceived(uint32 seq); void SetNextAckToSend(uint32 seq); void SetLastAckSent(uint32 seq); Mutex MAcks; // Mutex for acknowledgment data // Outbound packet queues queue NonSequencedQueue; // Non-sequenced packets deque SequencedQueue; // Sequenced packets map OutOfOrderpackets; // Out-of-order packets Mutex MOutboundQueue; // Mutex for outbound queues // Inbound packet queue deque InboundQueue; // Packets waiting processing Mutex MInboundQueue; // Mutex for inbound queue // Static configuration static uint16 MaxWindowSize; // Maximum window size for flow control // Rate limiting and flow control sint32 BytesWritten; // Bytes written this period Mutex MRate; // Mutex for rate limiting sint32 RateThreshold; // Rate limiting threshold sint32 DecayRate; // Rate decay per time period uint32 AverageDelta; // Average round-trip time // Factory reference EQStreamFactory* Factory; // Factory that created this stream public: // EQ2-specific packet combining Mutex MCombineQueueLock; // Mutex for combine queue operations bool CheckCombineQueue(); // Check and process combine queue deque combine_queue; // Queue of packets to combine Timer* combine_timer; // Timer for combine operations // Encryption and compression Crypto* crypto; // Cryptographic handler int8 EQ2_Compress(EQ2Packet* app, int8 offset = 3); // Compress EQ2 packet z_stream stream; // zlib compression stream uchar* stream_buffer; // Compression buffer int32 stream_buffer_size; // Size of compression buffer bool eq2_compressed; // Stream uses EQ2 compression int8 compressed_offset; // Offset for compressed data // Client version management int16 client_version; // Client protocol version int16 GetClientVersion() { return client_version; } void SetClientVersion(int16 version) { client_version = version; } // Session attempt management void ResetSessionAttempts() { reconnectAttempt = 0; } bool HasSessionAttempts() { return reconnectAttempt > 0; } // Constructors EQStream() { init(); remote_ip = 0; remote_port = 0; State = CLOSED; StreamType = UnknownStream; compressed = true; encoded = false; app_opcode_size = 2; } EQStream(sockaddr_in addr); // Destructor virtual ~EQStream() { // Close stream safely MOutboundQueue.lock(); SetState(CLOSED); MOutboundQueue.unlock(); // Clean up data structures RemoveData(); // Clean up allocated resources safe_delete(crypto); safe_delete(combine_timer); safe_delete(resend_que_timer); safe_delete_array(oversize_buffer); safe_delete_array(rogue_buffer); // Clean up combine queue MCombineQueueLock.lock(); for (auto cmb = combine_queue.begin(); cmb != combine_queue.end(); ++cmb) { safe_delete(*cmb); } MCombineQueueLock.unlock(); // Clean up compression stream deflateEnd(&stream); // Clean up out-of-order packets for (auto oop = OutOfOrderpackets.begin(); oop != OutOfOrderpackets.end(); ++oop) { safe_delete(oop->second); } #ifdef WRITE_PACKETS if (write_packets) { fclose(write_packets); } #endif } // Factory and initialization void SetFactory(EQStreamFactory* f) { Factory = f; } void init(bool resetSession = true); // Configuration void SetMaxLen(uint32 length) { MaxLen = length; } int8 getTimeoutDelays() { return timeout_delays; } void addTimeoutDelay() { timeout_delays++; } // EQ2-specific packet handling void EQ2QueuePacket(EQ2Packet* app, bool attempted_combine = false); void PreparePacket(EQ2Packet* app, int8 offset = 0); void UnPreparePacket(EQ2Packet* app); void EncryptPacket(EQ2Packet* app, int8 compress_offset, int8 offset); void FlushCombinedPacket(); // General packet transmission void SendPacket(EQApplicationPacket* p); void QueuePacket(EQProtocolPacket* p); void SendPacket(EQProtocolPacket* p); vector convert(EQApplicationPacket* p); void NonSequencedPush(EQProtocolPacket* p); void SequencedPush(EQProtocolPacket* p); // Resend queue management Mutex MResendQue; // Mutex for resend queue Mutex MCompressData; // Mutex for compression operations deque resend_que; // Queue of packets needing resend void CheckResend(int eq_fd); // Check and handle packet resends // Acknowledgment and writing void AckPackets(uint16 seq); // Acknowledge received packets void Write(int eq_fd); // Write packets to socket // Stream state management void SetActive(bool val) { streamactive = val; } // Low-level packet operations void WritePacket(int fd, EQProtocolPacket* p); void EncryptPacket(uchar* data, int16 size); // Session management uint32 GetKey() { return Key; } void SetKey(uint32 k) { Key = k; } void SetSession(uint32 s) { Session = s; } void SetLastPacketTime(uint32 t) { LastPacket = t; } // Packet processing void Process(const unsigned char* data, uint32 length); void ProcessPacket(EQProtocolPacket* p, EQProtocolPacket* lastp = nullptr); // Embedded packet handling bool ProcessEmbeddedPacket(uchar* pBuffer, uint16 length, int8 opcode = OP_Packet); bool HandleEmbeddedPacket(EQProtocolPacket* p, int16 offset = 2, int16 length = 0); // Encryption handling EQProtocolPacket* ProcessEncryptedPacket(EQProtocolPacket* p); EQProtocolPacket* ProcessEncryptedData(uchar* data, int32 size, int16 opcode); // Virtual packet dispatch (override in derived classes) virtual void DispatchPacket(EQApplicationPacket* p) { p->DumpRaw(); } // Session protocol messages void SendSessionResponse(); void SendSessionRequest(); void SendDisconnect(bool setstate = true); void SendAck(uint16 seq); void SendOutOfOrderAck(uint16 seq); // Connection health checks bool CheckTimeout(uint32 now, uint32 timeout = 30) { return (LastPacket && (now - LastPacket) > timeout); } bool Stale(uint32 now, uint32 timeout = 30) { return (LastPacket && (now - LastPacket) > timeout); } // Inbound queue management void InboundQueuePush(EQApplicationPacket* p); EQApplicationPacket* PopPacket(); // Pop packet from inbound queue void InboundQueueClear(); // Outbound queue management void OutboundQueueClear(); bool HasOutgoingData(); // Key exchange and RSA void SendKeyRequest(); int16 processRSAKey(EQProtocolPacket* p, uint16 subpacket_length = 0); // Data cleanup void RemoveData() { InboundQueueClear(); OutboundQueueClear(); if (CombinedAppPacket) { delete CombinedAppPacket; CombinedAppPacket = nullptr; } } // Usage tracking (thread-safe reference counting) bool IsInUse() { bool flag; MInUse.lock(); flag = (active_users > 0); MInUse.unlock(); return flag; } void PutInUse() { MInUse.lock(); active_users++; MInUse.unlock(); } void ReleaseFromUse() { MInUse.lock(); if (active_users > 0) { active_users--; } MInUse.unlock(); } // Sequence number utilities static SeqOrder CompareSequence(uint16 expected_seq, uint16 seq); // State management EQStreamState GetState() { return State; } void SetState(EQStreamState state) { MState.lock(); State = state; MState.unlock(); } // Remote endpoint access uint32 GetRemoteIP() { return remote_ip; } uint32 GetrIP() { return remote_ip; } // Legacy alias uint16 GetRemotePort() { return remote_port; } uint16 GetrPort() { return remote_port; } // Legacy alias // Static packet reading static EQProtocolPacket* Read(int eq_fd, sockaddr_in* from); // Connection management void Close() { SendDisconnect(); } bool CheckActive() { return (GetState() == ESTABLISHED); } bool CheckClosed() { return GetState() == CLOSED; } // Stream configuration void SetOpcodeSize(uint8 s) { app_opcode_size = s; } void SetStreamType(EQStreamType t); EQStreamType GetStreamType() const { return StreamType; } // Queue processing void ProcessQueue(); EQProtocolPacket* RemoveQueue(uint16 seq); // Rate limiting and flow control void Decay(); void AdjustRates(uint32 average_delta); // Resend timer Timer* resend_que_timer; // Timer for checking resend queue }; #endif