EQStream abstraction layer

This commit is contained in:
KimLS
2016-09-25 15:10:34 -07:00
parent 751e61d6e5
commit 5cad3f62d0
714 changed files with 210643 additions and 18 deletions
+96
View File
@@ -0,0 +1,96 @@
#include "crc32.h"
#include <memory.h>
unsigned int CRC32EncodeTable[256] =
{
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
};
int EQ::Crc32(const void * data, int size)
{
int crc = 0xffffffff;
auto buffer = (const uint8_t *)data;
for (int i = 0; i < size; ++i) {
crc = ((crc >> 8) & 0x00FFFFFFL) ^ CRC32EncodeTable[(crc ^ *&buffer[i]) & 0x000000FFL];
}
return ~crc;
}
int EQ::Crc32(const void * data, int size, int key)
{
int crc = 0xffffffff;
for (int i = 0; i < 4; ++i) {
crc = ((crc >> 8) & 0x00FFFFFFL) ^ CRC32EncodeTable[(crc ^ ((key >> (i * 8)) & 0xff)) & 0x000000FFL];
}
auto buffer = (const uint8_t *)data;
for (int i = 0; i < size; ++i) {
crc = ((crc >> 8) & 0x00FFFFFFL) ^ CRC32EncodeTable[(crc ^ *&buffer[i]) & 0x000000FFL];
}
return ~crc;
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include <stdint.h>
namespace EQ
{
int Crc32(const void *data, int size);
int Crc32(const void *data, int size, int key);
}
File diff suppressed because it is too large Load Diff
+273
View File
@@ -0,0 +1,273 @@
#pragma once
#include "../random.h"
#include "packet.h"
#include "daybreak_structs.h"
#include <uv.h>
#include <chrono>
#include <functional>
#include <memory>
#include <map>
#include <queue>
#include <list>
namespace EQ
{
namespace Net
{
enum DaybreakProtocolOpcode
{
OP_Padding = 0x00,
OP_SessionRequest = 0x01,
OP_SessionResponse = 0x02,
OP_Combined = 0x03,
OP_SessionDisconnect = 0x05,
OP_KeepAlive = 0x06,
OP_SessionStatRequest = 0x07,
OP_SessionStatResponse = 0x08,
OP_Packet = 0x09,
OP_Packet2 = 0x0a,
OP_Packet3 = 0x0b,
OP_Packet4 = 0x0c,
OP_Fragment = 0x0d,
OP_Fragment2 = 0x0e,
OP_Fragment3 = 0x0f,
OP_Fragment4 = 0x10,
OP_OutOfOrderAck = 0x11,
OP_OutOfOrderAck2 = 0x12,
OP_OutOfOrderAck3 = 0x13,
OP_OutOfOrderAck4 = 0x14,
OP_Ack = 0x15,
OP_Ack2 = 0x16,
OP_Ack3 = 0x17,
OP_Ack4 = 0x18,
OP_AppCombined = 0x19,
OP_OutboundPing = 0x1c,
OP_OutOfSession = 0x1d
};
enum DbProtocolStatus
{
StatusConnecting,
StatusConnected,
StatusDisconnecting,
StatusDisconnected
};
enum DaybreakEncodeType
{
EncodeNone = 0,
EncodeCompression = 1,
EncodeXOR = 4,
};
enum SequenceOrder
{
SequenceCurrent,
SequenceFuture,
SequencePast
};
typedef std::chrono::system_clock::time_point Timestamp;
typedef std::chrono::system_clock Clock;
struct DaybreakConnectionStats
{
DaybreakConnectionStats() {
recv_bytes = 0;
sent_bytes = 0;
recv_packets = 0;
sent_packets = 0;
total_ping = 0;
total_acks = 0;
min_ping = 0xFFFFFFFFFFFFFFFFUL;
max_ping = 0;
created = Clock::now();
}
uint64_t recv_bytes;
uint64_t sent_bytes;
uint64_t recv_packets;
uint64_t sent_packets;
uint64_t total_ping;
uint64_t total_acks;
uint64_t min_ping;
uint64_t max_ping;
Timestamp created;
};
class DaybreakConnectionManager;
class DaybreakConnection;
class DaybreakConnection
{
public:
DaybreakConnection(DaybreakConnectionManager *owner, const DaybreakConnect &connect, const std::string &endpoint, int port);
DaybreakConnection(DaybreakConnectionManager *owner, const std::string &endpoint, int port);
~DaybreakConnection();
const std::string& RemoteEndpoint() const { return m_endpoint; }
int RemotePort() const { return m_port; }
void Close();
void QueuePacket(Packet &p);
void QueuePacket(Packet &p, int stream);
void QueuePacket(Packet &p, int stream, bool reliable);
const DaybreakConnectionStats& GetStats() const { return m_stats; }
void ResetStats();
size_t GetRollingPing() const { return m_rolling_ping; }
DbProtocolStatus GetStatus() { return m_status; }
private:
DaybreakConnectionManager *m_owner;
std::string m_endpoint;
int m_port;
uint32_t m_connect_code;
uint32_t m_encode_key;
uint32_t m_max_packet_size;
uint32_t m_crc_bytes;
DaybreakEncodeType m_encode_passes[2];
Timestamp m_last_send;
Timestamp m_last_recv;
DbProtocolStatus m_status;
Timestamp m_hold_time;
std::list<WritablePacket> m_buffered_packets;
size_t m_buffered_packets_length;
Timestamp m_last_stats;
DaybreakConnectionStats m_stats;
Timestamp m_last_session_stats;
uint64_t m_last_session_stats_ping;
size_t m_resend_delay;
size_t m_rolling_ping;
struct DaybreakSentPacket
{
WritablePacket packet;
Timestamp last_sent;
Timestamp first_sent;
size_t times_resent;
};
struct DaybreakStream
{
DaybreakStream() {
sequence_in = 0;
sequence_out = 0;
fragment_current_bytes = 0;
fragment_total_bytes = 0;
}
uint16_t sequence_in;
uint16_t sequence_out;
std::map<uint16_t, Packet*> packet_queue;
WritablePacket fragment_packet;
uint32_t fragment_current_bytes;
uint32_t fragment_total_bytes;
std::map<uint16_t, DaybreakSentPacket> sent_packets;
};
DaybreakStream m_streams[4];
std::weak_ptr<DaybreakConnection> m_self;
void Process();
void ProcessPacket(Packet &p);
void ProcessQueue();
void RemoveFromQueue(int stream, uint16_t seq);
void AddToQueue(int stream, uint16_t seq, const Packet &p);
void ProcessDecodedPacket(const Packet &p);
void ChangeStatus(DbProtocolStatus new_status);
bool ValidateCRC(Packet &p);
void AppendCRC(Packet &p);
bool PacketCanBeEncoded(Packet &p) const;
void Decode(Packet &p, size_t offset, size_t length);
void Encode(Packet &p, size_t offset, size_t length);
void Decompress(Packet &p, size_t offset, size_t length);
void Compress(Packet &p, size_t offset, size_t length);
void ProcessResend();
void ProcessResend(int stream);
void Ack(int stream, uint16_t seq);
void OutOfOrderAck(int stream, uint16_t seq);
void SendConnect();
void SendKeepAlive();
void SendAck(int stream, uint16_t seq);
void SendOutOfOrderAck(int stream, uint16_t seq);
void SendStatSync();
void InternalBufferedSend(Packet &p);
void InternalSend(Packet &p);
void InternalQueuePacket(Packet &p, int stream_id, bool reliable);
void FlushBuffer();
SequenceOrder CompareSequence(uint16_t expected, uint16_t actual) const;
friend class DaybreakConnectionManager;
};
struct DaybreakConnectionManagerOptions
{
DaybreakConnectionManagerOptions() {
max_connection_count = 0;
keepalive_delay_ms = 0;
resend_delay_ms = 1000;
stats_delay_ms = 10000;
connect_delay_ms = 1000;
stale_connection_ms = 60000;
crc_length = 2;
max_packet_size = 512;
encode_passes[0] = DaybreakEncodeType::EncodeNone;
encode_passes[1] = DaybreakEncodeType::EncodeNone;
port = 0;
hold_size = 384;
hold_length_ms = 50;
}
size_t max_packet_size;
size_t max_connection_count;
size_t keepalive_delay_ms;
size_t resend_delay_ms;
size_t stats_delay_ms;
size_t connect_delay_ms;
size_t stale_connection_ms;
size_t crc_length;
size_t hold_size;
size_t hold_length_ms;
DaybreakEncodeType encode_passes[2];
int port;
};
class DaybreakConnectionManager
{
public:
DaybreakConnectionManager();
DaybreakConnectionManager(const DaybreakConnectionManagerOptions &opts);
~DaybreakConnectionManager();
void Connect(const std::string &addr, int port);
void Process();
void ProcessResend();
void OnNewConnection(std::function<void(std::shared_ptr<DaybreakConnection>)> func) { m_on_new_connection = func; }
void OnConnectionStateChange(std::function<void(std::shared_ptr<DaybreakConnection>, DbProtocolStatus, DbProtocolStatus)> func) { m_on_connection_state_change = func; }
void OnPacketRecv(std::function<void(std::shared_ptr<DaybreakConnection>, const Packet &)> func) { m_on_packet_recv = func; }
private:
void Attach(uv_loop_t *loop);
void Detach();
EQEmu::Random m_rand;
uv_timer_t m_timer;
uv_timer_t m_resend_timer;
uv_udp_t m_socket;
uv_loop_t *m_attached;
DaybreakConnectionManagerOptions m_options;
std::function<void(std::shared_ptr<DaybreakConnection>)> m_on_new_connection;
std::function<void(std::shared_ptr<DaybreakConnection>, DbProtocolStatus, DbProtocolStatus)> m_on_connection_state_change;
std::function<void(std::shared_ptr<DaybreakConnection>, const Packet&)> m_on_packet_recv;
std::map<std::pair<std::string, int>, std::shared_ptr<DaybreakConnection>> m_connections;
void ProcessPacket(const std::string &endpoint, int port, const char *data, size_t size);
std::shared_ptr<DaybreakConnection> FindConnectionByEndpoint(std::string addr, int port);
void SendSessionLost(const std::string &addr, int port);
friend class DaybreakConnection;
};
}
}
+175
View File
@@ -0,0 +1,175 @@
#pragma once
#include <cereal/cereal.hpp>
#include <cstdint>
#include "endian.h"
namespace EQ
{
namespace Net
{
struct DaybreakHeader
{
static size_t size() { return 2; }
uint8_t zero;
uint8_t opcode;
template <class Archive>
void serialize(Archive & archive)
{
archive(CEREAL_NVP(zero),
CEREAL_NVP(opcode));
}
};
struct DaybreakConnect
{
static size_t size() { return 14; }
uint8_t zero;
uint8_t opcode;
uint32_t protocol_version;
uint32_t connect_code;
uint32_t max_packet_size;
template <class Archive>
void serialize(Archive & archive)
{
archive(CEREAL_NVP(zero),
CEREAL_NVP(opcode),
CEREAL_NVP(protocol_version),
CEREAL_NVP(connect_code),
CEREAL_NVP(max_packet_size));
}
};
struct DaybreakConnectReply
{
static size_t size() { return 17; }
uint8_t zero;
uint8_t opcode;
uint32_t connect_code;
uint32_t encode_key;
uint8_t crc_bytes;
uint8_t encode_pass1;
uint8_t encode_pass2;
uint32_t max_packet_size;
template <class Archive>
void serialize(Archive & archive)
{
archive(CEREAL_NVP(zero),
CEREAL_NVP(opcode),
CEREAL_NVP(connect_code),
CEREAL_NVP(encode_key),
CEREAL_NVP(crc_bytes),
CEREAL_NVP(encode_pass1),
CEREAL_NVP(encode_pass2),
CEREAL_NVP(max_packet_size));
}
};
struct DaybreakDisconnect
{
static size_t size() { return 8; }
uint8_t zero;
uint8_t opcode;
uint32_t connect_code;
uint16_t disconnect_code;
template <class Archive>
void serialize(Archive & archive)
{
archive(CEREAL_NVP(zero),
CEREAL_NVP(opcode),
CEREAL_NVP(connect_code),
CEREAL_NVP(disconnect_code));
}
};
struct DaybreakReliableHeader
{
static size_t size() { return 4; }
uint8_t zero;
uint8_t opcode;
uint16_t sequence;
template <class Archive>
void serialize(Archive & archive)
{
archive(CEREAL_NVP(zero),
CEREAL_NVP(opcode),
CEREAL_NVP(sequence));
}
};
struct DaybreakReliableFragmentHeader
{
static size_t size() { return 4 + DaybreakReliableHeader::size(); }
DaybreakReliableHeader reliable;
uint32_t total_size;
template <class Archive>
void serialize(Archive & archive)
{
archive(CEREAL_NVP(reliable),
CEREAL_NVP(total_size));
}
};
struct DaybreakSessionStatRequestHeader
{
static size_t size() { return 40; }
uint8_t zero;
uint8_t opcode;
uint16_t timestamp;
uint32_t stat_ping;
uint32_t avg_ping;
uint32_t min_ping;
uint32_t max_ping;
uint32_t last_ping;
uint64_t packets_sent;
uint64_t packets_recv;
template <class Archive>
void serialize(Archive & archive)
{
archive(CEREAL_NVP(zero),
CEREAL_NVP(opcode),
CEREAL_NVP(timestamp),
CEREAL_NVP(stat_ping),
CEREAL_NVP(avg_ping),
CEREAL_NVP(min_ping),
CEREAL_NVP(max_ping),
CEREAL_NVP(last_ping),
CEREAL_NVP(packets_sent),
CEREAL_NVP(packets_recv));
}
};
struct DaybreakSessionStatResponseHeader
{
static size_t size() { return 40; }
uint8_t zero;
uint8_t opcode;
uint16_t timestamp;
uint32_t our_timestamp;
uint64_t client_sent;
uint64_t client_recv;
uint64_t server_sent;
uint64_t server_recv;
template <class Archive>
void serialize(Archive & archive)
{
archive(CEREAL_NVP(zero),
CEREAL_NVP(opcode),
CEREAL_NVP(timestamp),
CEREAL_NVP(our_timestamp),
CEREAL_NVP(client_sent),
CEREAL_NVP(client_recv),
CEREAL_NVP(server_sent),
CEREAL_NVP(server_recv));
}
};
}
}
+51
View File
@@ -0,0 +1,51 @@
#pragma once
#include <cstdint>
#include <algorithm>
#include <type_traits>
namespace EQ
{
namespace Net
{
inline bool IsLittleEndian() {
static int32_t v = 1;
return 1 == *(int8_t*)&v;
}
template<typename T>
T ByteSwap(T in) {
static_assert(std::is_integral<T>::value, "Byte swap only works on integer types.");
T ret;
char *first = (char*)&in;
char *last = (char*)&in + sizeof(in);
char *d_first = (char*)&ret;
while (first != last) {
*(d_first++) = *(--last);
}
return ret;
}
template<typename T>
T HostToNetwork(T in) {
if (IsLittleEndian()) {
return ByteSwap(in);
}
else {
return in;
}
}
template<typename T>
T NetworkToHost(T in) {
if (IsLittleEndian()) {
return ByteSwap(in);
}
else {
return in;
}
}
}
}
+215
View File
@@ -0,0 +1,215 @@
#include "eqstream.h"
#include "../eqemu_logsys.h"
EQ::Net::EQStreamManager::EQStreamManager(EQStreamManagerOptions &options) : m_daybreak(options.daybreak_options)
{
m_options = options;
m_daybreak.OnNewConnection(std::bind(&EQStreamManager::DaybreakNewConnection, this, std::placeholders::_1));
m_daybreak.OnConnectionStateChange(std::bind(&EQStreamManager::DaybreakConnectionStateChange, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
m_daybreak.OnPacketRecv(std::bind(&EQStreamManager::DaybreakPacketRecv, this, std::placeholders::_1, std::placeholders::_2));
}
EQ::Net::EQStreamManager::~EQStreamManager()
{
}
void EQ::Net::EQStreamManager::DaybreakNewConnection(std::shared_ptr<DaybreakConnection> connection)
{
std::shared_ptr<EQStream> stream(new EQStream(this, connection));
m_streams.insert(std::make_pair(connection, stream));
if (m_on_new_connection) {
m_on_new_connection(stream);
}
}
void EQ::Net::EQStreamManager::DaybreakConnectionStateChange(std::shared_ptr<DaybreakConnection> connection, DbProtocolStatus from, DbProtocolStatus to)
{
auto iter = m_streams.find(connection);
if (iter != m_streams.end()) {
if (m_on_connection_state_change) {
m_on_connection_state_change(iter->second, from, to);
}
if (to == EQ::Net::StatusDisconnected) {
m_streams.erase(iter);
}
}
}
void EQ::Net::EQStreamManager::DaybreakPacketRecv(std::shared_ptr<DaybreakConnection> connection, const Packet &p)
{
auto iter = m_streams.find(connection);
if (iter != m_streams.end()) {
auto &stream = iter->second;
if (stream->m_opcode_manager && stream->m_opcode_manager) {
std::unique_ptr<EQ::Net::Packet> t(new EQ::Net::WritablePacket());
t->PutPacket(0, p);
stream->m_packet_queue.push_back(std::move(t));
if (m_on_data_avail) {
m_on_data_avail(stream);
}
}
}
}
EQ::Net::EQStream::EQStream(EQStreamManager *owner, std::shared_ptr<DaybreakConnection> connection)
{
m_owner = owner;
m_connection = connection;
}
EQ::Net::EQStream::~EQStream()
{
}
void EQ::Net::EQStream::QueuePacket(const EQApplicationPacket *p, bool ack_req) {
if (m_opcode_manager && *m_opcode_manager) {
uint16 opcode = 0;
if (p->GetOpcodeBypass() != 0) {
opcode = p->GetOpcodeBypass();
}
else {
opcode = (*m_opcode_manager)->EmuToEQ(p->GetOpcode());
}
EQ::Net::WritablePacket out;
switch (m_owner->m_options.opcode_size) {
case 1:
out.PutUInt8(0, opcode);
out.PutData(1, p->pBuffer, p->size);
break;
case 2:
out.PutUInt16(0, opcode);
out.PutData(2, p->pBuffer, p->size);
break;
}
if (!ack_req) {
m_connection->QueuePacket(out, 0, false);
}
else {
m_connection->QueuePacket(out);
}
}
}
void EQ::Net::EQStream::FastQueuePacket(EQApplicationPacket **p, bool ack_req) {
QueuePacket(*p, ack_req);
delete *p;
*p = nullptr;
}
EQApplicationPacket *EQ::Net::EQStream::PopPacket() {
if (m_packet_queue.empty()) {
return nullptr;
}
if (m_opcode_manager != nullptr && *m_opcode_manager != nullptr) {
auto &p = m_packet_queue.front();
uint16 opcode = 0;
switch (m_owner->m_options.opcode_size) {
case 1:
opcode = p->GetUInt8(0);
break;
case 2:
opcode = p->GetUInt16(0);
break;
}
EmuOpcode emu_op = (*m_opcode_manager)->EQToEmu(opcode);
EQApplicationPacket *ret = new EQApplicationPacket(emu_op, (unsigned char*)p->Data(), p->Length() - m_owner->m_options.opcode_size);
m_packet_queue.pop_front();
return ret;
}
return nullptr;
}
void EQ::Net::EQStream::Close() {
m_connection->Close();
}
uint32 EQ::Net::EQStream::GetRemoteIP() const {
return inet_addr(RemoteEndpoint().c_str());
}
bool EQ::Net::EQStream::CheckState(EQStreamState state) {
return GetState() == state;
}
EQStreamInterface::MatchState EQ::Net::EQStream::CheckSignature(const Signature *sig) {
if (!m_packet_queue.empty()) {
auto p = m_packet_queue.front().get();
uint16 opcode = 0;
size_t length = p->Length() - m_owner->m_options.opcode_size;
switch (m_owner->m_options.opcode_size) {
case 1:
opcode = p->GetUInt8(0);
break;
case 2:
opcode = p->GetUInt16(0);
break;
}
if (sig->ignore_eq_opcode != 0 && opcode == sig->ignore_eq_opcode) {
if (m_packet_queue.size() > 1) {
p = m_packet_queue[1].get();
opcode = 0;
length = p->Length() - m_owner->m_options.opcode_size;
switch (m_owner->m_options.opcode_size) {
case 1:
opcode = p->GetUInt8(0);
break;
case 2:
opcode = p->GetUInt16(0);
break;
}
}
else {
return MatchNotReady;
}
}
if (opcode == sig->first_eq_opcode) {
if (length == sig->first_length) {
Log.OutF(Logs::General, Logs::Netcode, "[IDENT_TRACE] {0}:{1}: First opcode matched {2:#x} and length matched {3}",
RemoteEndpoint(), m_connection->RemotePort(), sig->first_eq_opcode, length);
return MatchSuccessful;
}
else if(length == 0) {
Log.OutF(Logs::General, Logs::Netcode, "[IDENT_TRACE] {0}:{1}: First opcode matched {2:#x} and length is ignored.",
RemoteEndpoint(), m_connection->RemotePort(), sig->first_eq_opcode);
return MatchSuccessful;
}
else {
Log.OutF(Logs::General, Logs::Netcode, "[IDENT_TRACE] {0}:{1}: First opcode matched {2:#x} but length {3} did not match expected {4}",
RemoteEndpoint(), m_connection->RemotePort(), sig->first_eq_opcode, length, sig->first_length);
return MatchFailed;
}
}
else {
Log.OutF(Logs::General, Logs::Netcode, "[IDENT_TRACE] {0}:{1}: First opcode {1:#x} did not match expected {2:#x}",
RemoteEndpoint(), m_connection->RemotePort(), opcode, sig->first_eq_opcode);
return MatchFailed;
}
}
return MatchNotReady;
}
EQStreamState EQ::Net::EQStream::GetState() {
auto status = m_connection->GetStatus();
switch (status) {
case StatusConnecting:
return UNESTABLISHED;
case StatusConnected:
return ESTABLISHED;
case StatusDisconnecting:
return DISCONNECTING;
default:
return CLOSED;
}
}
+101
View File
@@ -0,0 +1,101 @@
#pragma once
#include "../eq_packet.h"
#include "../eq_stream_intf.h"
#include "../opcodemgr.h"
#include "daybreak_connection.h"
#include <vector>
#include <deque>
namespace EQ
{
namespace Net
{
struct EQStreamManagerOptions
{
EQStreamManagerOptions() {
opcode_size = 2;
}
EQStreamManagerOptions(bool encoded, bool compressed) {
opcode_size = 2;
if (encoded) {
daybreak_options.encode_passes[0] = EncodeXOR;
if (compressed) {
daybreak_options.encode_passes[1] = EncodeCompression;
}
}
else {
if (compressed) {
daybreak_options.encode_passes[0] = EncodeCompression;
}
}
}
int opcode_size;
DaybreakConnectionManagerOptions daybreak_options;
};
class EQStream;
class EQStreamManager
{
public:
EQStreamManager(EQStreamManagerOptions &options);
~EQStreamManager();
void OnNewConnection(std::function<void(std::shared_ptr<EQStream>)> func) { m_on_new_connection = func; }
void OnConnectionStateChange(std::function<void(std::shared_ptr<EQStream>, DbProtocolStatus, DbProtocolStatus)> func) { m_on_connection_state_change = func; }
void OnDataAvailable(std::function<void(std::shared_ptr<EQStream>)> func) { m_on_data_avail = func; }
private:
EQStreamManagerOptions m_options;
DaybreakConnectionManager m_daybreak;
std::function<void(std::shared_ptr<EQStream>)> m_on_new_connection;
std::function<void(std::shared_ptr<EQStream>, DbProtocolStatus, DbProtocolStatus)> m_on_connection_state_change;
std::function<void(std::shared_ptr<EQStream>)> m_on_data_avail;
std::map<std::shared_ptr<DaybreakConnection>, std::shared_ptr<EQStream>> m_streams;
void DaybreakNewConnection(std::shared_ptr<DaybreakConnection> connection);
void DaybreakConnectionStateChange(std::shared_ptr<DaybreakConnection> connection, DbProtocolStatus from, DbProtocolStatus to);
void DaybreakPacketRecv(std::shared_ptr<DaybreakConnection> connection, const Packet &p);
friend class EQStream;
};
class EQStream : public EQStreamInterface
{
public:
EQStream(EQStreamManager *parent, std::shared_ptr<DaybreakConnection> connection);
~EQStream();
virtual void QueuePacket(const EQApplicationPacket *p, bool ack_req = true);
virtual void FastQueuePacket(EQApplicationPacket **p, bool ack_req = true);
virtual EQApplicationPacket *PopPacket();
virtual void Close();
virtual void ReleaseFromUse() { };
virtual void RemoveData() { };
virtual uint32 GetRemoteIP() const;
//the code is dumb and assumes this is in network order...
virtual uint16 GetRemotePort() const { return EQ::Net::HostToNetwork(m_connection->RemotePort()); }
virtual bool CheckState(EQStreamState state);
virtual std::string Describe() const { return "Direct EQStream"; }
virtual void SetActive(bool val) { }
virtual MatchState CheckSignature(const Signature *sig);
virtual EQStreamState GetState();
virtual void SetOpcodeManager(OpcodeManager **opm) {
m_opcode_manager = opm;
}
const std::string& RemoteEndpoint() const { return m_connection->RemoteEndpoint(); }
const DaybreakConnectionStats& GetStats() const { return m_connection->GetStats(); }
void ResetStats() { m_connection->ResetStats(); }
size_t GetRollingPing() const { return m_connection->GetRollingPing(); }
private:
EQStreamManager *m_owner;
std::shared_ptr<DaybreakConnection> m_connection;
OpcodeManager **m_opcode_manager;
std::deque<std::unique_ptr<EQ::Net::Packet>> m_packet_queue;
friend class EQStreamManager;
};
}
}
+332
View File
@@ -0,0 +1,332 @@
#include "packet.h"
#include "endian.h"
#include <fmt/format.h>
#include <cctype>
void EQ::Net::Packet::PutInt8(size_t offset, int8_t value)
{
if (Length() < offset + 1) {
if (!Resize(offset + 1)) {
throw std::out_of_range("Packet::PutInt8(), could not resize packet and would of written past the end.");
}
}
*(int8_t*)((char*)Data() + offset) = value;
}
void EQ::Net::Packet::PutInt16(size_t offset, int16_t value)
{
if (Length() < offset + 2) {
if (!Resize(offset + 2)) {
throw std::out_of_range("Packet::PutInt16(), could not resize packet and would of written past the end.");
}
}
*(int16_t*)((char*)Data() + offset) = value;
}
void EQ::Net::Packet::PutInt32(size_t offset, int32_t value)
{
if (Length() < offset + 4) {
if (!Resize(offset + 4)) {
throw std::out_of_range("Packet::PutInt32(), could not resize packet and would of written past the end.");
}
}
*(int32_t*)((char*)Data() + offset) = value;
}
void EQ::Net::Packet::PutInt64(size_t offset, int64_t value)
{
if (Length() < offset + 8) {
if (!Resize(offset + 8)) {
throw std::out_of_range("Packet::PutInt64(), could not resize packet and would of written past the end.");
}
}
*(int64_t*)((char*)Data() + offset) = value;
}
void EQ::Net::Packet::PutUInt8(size_t offset, uint8_t value)
{
if (Length() < offset + 1) {
if (!Resize(offset + 1)) {
throw std::out_of_range("Packet::PutUInt8(), could not resize packet and would of written past the end.");
}
}
*(uint8_t*)((char*)Data() + offset) = value;
}
void EQ::Net::Packet::PutUInt16(size_t offset, uint16_t value)
{
if (Length() < offset + 2) {
if (!Resize(offset + 2)) {
throw std::out_of_range("Packet::PutUInt16(), could not resize packet and would of written past the end.");
}
}
*(uint16_t*)((char*)Data() + offset) = value;
}
void EQ::Net::Packet::PutUInt32(size_t offset, uint32_t value)
{
if (Length() < offset + 4) {
if (!Resize(offset + 4)) {
throw std::out_of_range("Packet::PutUInt32(), could not resize packet and would of written past the end.");
}
}
*(uint32_t*)((char*)Data() + offset) = value;
}
void EQ::Net::Packet::PutUInt64(size_t offset, uint64_t value)
{
if (Length() < offset + 8) {
if (!Resize(offset + 8)) {
throw std::out_of_range("Packet::PutUInt64(), could not resize packet and would of written past the end.");
}
}
*(uint64_t*)((char*)Data() + offset) = value;
}
void EQ::Net::Packet::PutFloat(size_t offset, float value)
{
if (Length() < offset + 4) {
if (!Resize(offset + 4)) {
throw std::out_of_range("Packet::PutFloat(), could not resize packet and would of written past the end.");
}
}
*(float*)((char*)Data() + offset) = value;
}
void EQ::Net::Packet::PutDouble(size_t offset, double value)
{
if (Length() < offset + 8) {
if (!Resize(offset + 8)) {
throw std::out_of_range("Packet::PutDouble(), could not resize packet and would of written past the end.");
}
}
*(double*)((char*)Data() + offset) = value;
}
void EQ::Net::Packet::PutString(size_t offset, const std::string &str)
{
if (Length() < offset + str.length()) {
if (!Resize(offset + str.length())) {
throw std::out_of_range("Packet::PutString(), could not resize packet and would of written past the end.");
}
}
memcpy(((char*)Data() + offset), str.c_str(), str.length());
}
void EQ::Net::Packet::PutCString(size_t offset, const char *str)
{
size_t sz = strlen(str);
if (Length() < offset + sz + 1) {
if (!Resize(offset + sz + 1)) {
throw std::out_of_range("Packet::PutCString(), could not resize packet and would of written past the end.");
}
}
memcpy(((char*)Data() + offset), str, sz);
*((char*)Data() + offset + sz) = 0;
}
void EQ::Net::Packet::PutPacket(size_t offset, const Packet &p)
{
if (Length() < offset + p.Length()) {
if (!Resize(offset + p.Length())) {
throw std::out_of_range("Packet::PutPacket(), could not resize packet and would of written past the end.");
}
}
memcpy(((char*)Data() + offset), p.Data(), p.Length());
}
void EQ::Net::Packet::PutData(size_t offset, void *data, size_t length)
{
if (Length() < offset + length) {
if (!Resize(offset + length)) {
throw std::out_of_range("Packet::PutData(), could not resize packet and would of written past the end.");
}
}
memcpy(((char*)Data() + offset), data, length);
}
int8_t EQ::Net::Packet::GetInt8(size_t offset) const
{
if (Length() < offset + 1) {
throw std::out_of_range("Packet read out of range.");
}
return *(int8_t*)((char*)Data() + offset);
}
int16_t EQ::Net::Packet::GetInt16(size_t offset) const
{
if (Length() < offset + 2) {
throw std::out_of_range("Packet read out of range.");
}
return *(int16_t*)((char*)Data() + offset);
}
int32_t EQ::Net::Packet::GetInt32(size_t offset) const
{
if (Length() < offset + 4) {
throw std::out_of_range("Packet read out of range.");
}
return *(int32_t*)((char*)Data() + offset);
}
int64_t EQ::Net::Packet::GetInt64(size_t offset) const
{
if (Length() < offset + 8) {
throw std::out_of_range("Packet read out of range.");
}
return *(int64_t*)((char*)Data() + offset);
}
uint8_t EQ::Net::Packet::GetUInt8(size_t offset) const
{
if (Length() < offset + 1) {
throw std::out_of_range("Packet read out of range.");
}
return *(uint8_t*)((char*)Data() + offset);
}
uint16_t EQ::Net::Packet::GetUInt16(size_t offset) const
{
if (Length() < offset + 2) {
throw std::out_of_range("Packet read out of range.");
}
return *(uint16_t*)((char*)Data() + offset);
}
uint32_t EQ::Net::Packet::GetUInt32(size_t offset) const
{
if (Length() < offset + 4) {
throw std::out_of_range("Packet read out of range.");
}
return *(uint32_t*)((char*)Data() + offset);
}
uint64_t EQ::Net::Packet::GetUInt64(size_t offset) const
{
if (Length() < offset + 8) {
throw std::out_of_range("Packet read out of range.");
}
return *(uint64_t*)((char*)Data() + offset);
}
float EQ::Net::Packet::GetFloat(size_t offset) const
{
if (Length() < offset + 4) {
throw std::out_of_range("Packet read out of range.");
}
return *(float*)((char*)Data() + offset);
}
double EQ::Net::Packet::GetDouble(size_t offset) const
{
if (Length() < offset + 8) {
throw std::out_of_range("Packet read out of range.");
}
return *(double*)((char*)Data() + offset);
}
std::string EQ::Net::Packet::GetString(size_t offset, size_t length) const
{
if (Length() < offset + length) {
throw std::out_of_range("Packet read out of range.");
}
return std::string((char*)Data(), (char*)Data() + length);
}
std::string EQ::Net::Packet::GetCString(size_t offset) const
{
if (Length() < offset + 1) {
throw std::out_of_range("Packet read out of range.");
}
char *str = ((char*)Data() + offset);
return std::string(str);
}
char ToSafePrint(unsigned char in) {
if (std::isprint(in)) {
return in;
}
return '.';
}
std::string EQ::Net::Packet::ToString() const
{
return ToString(16);
}
std::string EQ::Net::Packet::ToString(size_t line_length) const
{
std::string ret;
size_t lines = Length() / line_length;
size_t i;
char *data = (char*)Data();
for (i = 0; i < lines; ++i) {
ret += fmt::format("{:0>5x} |", i * line_length);
std::string hex;
std::string ascii;
for (size_t j = 0; j < line_length; ++j) {
hex += fmt::format(" {:0>2x}", (uint8_t)data[(i * line_length) + j]);
ascii += fmt::format("{}", ToSafePrint(data[(i * line_length) + j]));
}
ret += hex;
ret += " | ";
ret += ascii;
ret += "\n";
}
if (Length() % line_length > 0) {
ret += fmt::format("{:0>5x} |", i * line_length);
size_t non_blank_count = Length() % line_length;
size_t blank_count = line_length - non_blank_count;
std::string hex;
std::string ascii;
for (size_t j = 0; j < non_blank_count; ++j) {
hex += fmt::format(" {:0>2x}", (uint8_t)data[(i * line_length) + j]);
ascii += fmt::format("{}", ToSafePrint(data[(i * line_length) + j]));
}
for (size_t j = 0; j < blank_count; ++j) {
hex += " ";
ascii += " ";
}
ret += hex;
ret += " | ";
ret += ascii;
ret += "\n";
}
return ret;
}
+131
View File
@@ -0,0 +1,131 @@
#pragma once
#include <cstdint>
#include <string>
#include <stdexcept>
#include <cstring>
#include "../util/memory_stream.h"
#include <cereal/cereal.hpp>
#include <cereal/archives/binary.hpp>
namespace EQ {
namespace Net {
class Packet
{
public:
Packet() { }
virtual ~Packet() { }
virtual const void *Data() const = 0;
virtual void *Data() = 0;
virtual size_t Length() const = 0;
virtual size_t Length() = 0;
virtual bool Clear() = 0;
virtual bool Resize(size_t new_size) = 0;
virtual void Reserve(size_t new_size) = 0;
template<typename T>
T GetSerialize(size_t offset) const
{
if (T::size() > (Length() - offset)) {
throw std::out_of_range("Packet::GetSerialize(), packet not large enough to cast to type.");
}
T ret;
Util::MemoryStreamReader reader(((char*)Data() + offset), Length());
cereal::BinaryInputArchive input(reader);
input(ret);
return ret;
}
template<typename T>
void PutSerialize(size_t offset, const T &value) {
std::stringstream buffer(std::ios::in | std::ios::out | std::ios::binary);
cereal::BinaryOutputArchive output(buffer);
output(value);
auto str = buffer.str();
if (Length() < offset + str.length()) {
if (!Resize(offset + str.length())) {
throw std::out_of_range("Packet::PutSerialize(), could not resize packet and would of written past the end.");
}
}
memcpy((char*)Data() + offset, &str[0], str.length());
}
void PutInt8(size_t offset, int8_t value);
void PutInt16(size_t offset, int16_t value);
void PutInt32(size_t offset, int32_t value);
void PutInt64(size_t offset, int64_t value);
void PutUInt8(size_t offset, uint8_t value);
void PutUInt16(size_t offset, uint16_t value);
void PutUInt32(size_t offset, uint32_t value);
void PutUInt64(size_t offset, uint64_t value);
void PutFloat(size_t offset, float value);
void PutDouble(size_t offset, double value);
void PutString(size_t offset, const std::string &str);
void PutCString(size_t offset, const char *str);
void PutPacket(size_t offset, const Packet &p);
void PutData(size_t offset, void *data, size_t length);
int8_t GetInt8(size_t offset) const;
int16_t GetInt16(size_t offset) const;
int32_t GetInt32(size_t offset) const;
int64_t GetInt64(size_t offset) const;
uint8_t GetUInt8(size_t offset) const;
uint16_t GetUInt16(size_t offset) const;
uint32_t GetUInt32(size_t offset) const;
uint64_t GetUInt64(size_t offset) const;
float GetFloat(size_t offset) const;
double GetDouble(size_t offset) const;
std::string GetString(size_t offset, size_t length) const;
std::string GetCString(size_t offset) const;
std::string ToString() const;
std::string ToString(size_t line_length) const;
};
class ReadOnlyPacket : public Packet
{
public:
ReadOnlyPacket(void *data, size_t size) { m_data = data; m_data_length = size; }
virtual ~ReadOnlyPacket() { }
ReadOnlyPacket(const ReadOnlyPacket &o) { m_data = o.m_data; m_data_length = o.m_data_length; }
ReadOnlyPacket& operator=(const ReadOnlyPacket &o) { m_data = o.m_data; m_data_length = o.m_data_length; return *this; }
ReadOnlyPacket(ReadOnlyPacket &&o) { m_data = o.m_data; m_data_length = o.m_data_length; }
virtual const void *Data() const { return m_data; }
virtual void *Data() { return m_data; }
virtual size_t Length() const { return m_data_length; }
virtual size_t Length() { return m_data_length; }
virtual bool Clear() { return false; }
virtual bool Resize(size_t new_size) { return false; }
virtual void Reserve(size_t new_size) { }
protected:
void *m_data;
size_t m_data_length;
};
class WritablePacket : public Packet
{
public:
WritablePacket() { }
virtual ~WritablePacket() { }
WritablePacket(WritablePacket &&o) { m_data = std::move(o.m_data); }
WritablePacket(const WritablePacket &o) { m_data = o.m_data; }
WritablePacket& operator=(const WritablePacket &o) { m_data = o.m_data; return *this; }
virtual const void *Data() const { return &m_data[0]; }
virtual void *Data() { return &m_data[0]; }
virtual size_t Length() const { return m_data.size(); }
virtual size_t Length() { return m_data.size(); }
virtual bool Clear() { m_data.clear(); return true; }
virtual bool Resize(size_t new_size) { m_data.resize(new_size); return true; }
virtual void Reserve(size_t new_size) { m_data.reserve(new_size); }
protected:
std::vector<char> m_data;
};
}
}