mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 16:51:29 +00:00
236 lines
6.5 KiB
C++
236 lines
6.5 KiB
C++
#include "servertalk_client_connection.h"
|
|
#include "dns.h"
|
|
#include "../eqemu_logsys.h"
|
|
|
|
EQ::Net::ServertalkClient::ServertalkClient(const std::string &addr, int port, bool ipv6, const std::string &identifier, const std::string &credentials)
|
|
: m_timer(std::unique_ptr<EQ::Timer>(new EQ::Timer(100, true, std::bind(&EQ::Net::ServertalkClient::Connect, this))))
|
|
{
|
|
m_port = port;
|
|
m_ipv6 = ipv6;
|
|
m_identifier = identifier.empty() ? "Unknown" : identifier;
|
|
m_credentials = credentials;
|
|
m_connecting = false;
|
|
DNSLookup(addr, port, false, [this](const std::string &address) {
|
|
m_addr = address;
|
|
});
|
|
}
|
|
|
|
EQ::Net::ServertalkClient::~ServertalkClient()
|
|
{
|
|
}
|
|
|
|
void EQ::Net::ServertalkClient::Connect()
|
|
{
|
|
if (m_addr.length() == 0 || m_port == 0 || m_connection || m_connecting) {
|
|
return;
|
|
}
|
|
|
|
m_connecting = true;
|
|
EQ::Net::TCPConnection::Connect(m_addr, m_port, false, [this](std::shared_ptr<EQ::Net::TCPConnection> connection) {
|
|
if (connection == nullptr) {
|
|
Log.OutF(Logs::General, Logs::TCP_Connection, "Error connecting to {0}:{1}, attempting to reconnect...", m_addr, m_port);
|
|
m_connecting = false;
|
|
return;
|
|
}
|
|
|
|
Log.OutF(Logs::General, Logs::TCP_Connection, "Connected to {0}:{1}", m_addr, m_port);
|
|
m_connection = connection;
|
|
m_connection->OnDisconnect([this](EQ::Net::TCPConnection *c) {
|
|
Log.OutF(Logs::General, Logs::TCP_Connection, "Connection lost to {0}:{1}, attempting to reconnect...", m_addr, m_port);
|
|
m_connection.reset();
|
|
m_encrypted = false;
|
|
});
|
|
|
|
m_connection->OnRead(std::bind(&EQ::Net::ServertalkClient::ProcessData, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
|
m_connection->Start();
|
|
|
|
SendHello();
|
|
m_connecting = false;
|
|
});
|
|
}
|
|
|
|
void EQ::Net::ServertalkClient::ProcessData(EQ::Net::TCPConnection *c, const unsigned char *data, size_t length)
|
|
{
|
|
m_buffer.insert(m_buffer.end(), (const char*)data, (const char*)data + length);
|
|
ProcessReadBuffer();
|
|
}
|
|
|
|
void EQ::Net::ServertalkClient::SendHello()
|
|
{
|
|
EQ::Net::WritablePacket p;
|
|
InternalSend(ServertalkClientHello, p);
|
|
}
|
|
|
|
void EQ::Net::ServertalkClient::InternalSend(ServertalkPacketType type, EQ::Net::Packet &p)
|
|
{
|
|
if (!m_connection)
|
|
return;
|
|
|
|
EQ::Net::WritablePacket out;
|
|
out.PutUInt32(0, (uint32_t)p.Length());
|
|
out.PutUInt8(4, (uint8_t)type);
|
|
if (p.Length() > 0) {
|
|
out.PutPacket(5, p);
|
|
}
|
|
|
|
m_connection->Write((const char*)out.Data(), out.Length());
|
|
}
|
|
|
|
void EQ::Net::ServertalkClient::ProcessReadBuffer()
|
|
{
|
|
size_t current = 0;
|
|
size_t total = m_buffer.size();
|
|
|
|
while (current < total) {
|
|
auto left = total - current;
|
|
|
|
/*
|
|
//header:
|
|
//uint32 length;
|
|
//uint8 type;
|
|
*/
|
|
size_t length = 0;
|
|
uint8_t type = 0;
|
|
if (left < 5) {
|
|
break;
|
|
}
|
|
|
|
length = *(uint32_t*)&m_buffer[current];
|
|
type = *(uint8_t*)&m_buffer[current + 4];
|
|
|
|
if (current + 5 + length < total) {
|
|
break;
|
|
}
|
|
|
|
if (length == 0) {
|
|
EQ::Net::WritablePacket p;
|
|
switch (type) {
|
|
case ServertalkServerHello:
|
|
ProcessHello(p);
|
|
break;
|
|
case ServertalkMessage:
|
|
ProcessMessage(p);
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
EQ::Net::ReadOnlyPacket p(&m_buffer[current + 5], length);
|
|
switch (type) {
|
|
case ServertalkServerHello:
|
|
ProcessHello(p);
|
|
break;
|
|
case ServertalkMessage:
|
|
ProcessMessage(p);
|
|
break;
|
|
}
|
|
}
|
|
|
|
current += length + 5;
|
|
}
|
|
|
|
if (current == total) {
|
|
m_buffer.clear();
|
|
}
|
|
else {
|
|
m_buffer.erase(m_buffer.begin(), m_buffer.begin() + current);
|
|
}
|
|
}
|
|
|
|
void EQ::Net::ServertalkClient::ProcessHello(EQ::Net::Packet &p)
|
|
{
|
|
#ifdef ENABLE_SECURITY
|
|
memset(m_public_key_ours, 0, crypto_box_PUBLICKEYBYTES);
|
|
memset(m_public_key_theirs, 0, crypto_box_PUBLICKEYBYTES);
|
|
memset(m_private_key_ours, 0, crypto_box_SECRETKEYBYTES);
|
|
memset(m_nonce_ours, 0, crypto_box_NONCEBYTES);
|
|
memset(m_nonce_theirs, 0, crypto_box_NONCEBYTES);
|
|
m_encrypted = false;
|
|
|
|
try {
|
|
bool enc = p.GetInt8(0) == 1 ? true : false;
|
|
|
|
if (enc) {
|
|
if (p.Length() == (1 + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES)) {
|
|
memcpy(m_public_key_theirs, (char*)p.Data() + 1, crypto_box_PUBLICKEYBYTES);
|
|
memcpy(m_nonce_theirs, (char*)p.Data() + 1 + crypto_box_PUBLICKEYBYTES, crypto_box_NONCEBYTES);
|
|
m_encrypted = true;
|
|
|
|
SendHandshake();
|
|
}
|
|
else {
|
|
Log.OutF(Logs::General, Logs::Error, "Could not process hello, size != {0}", 1 + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES);
|
|
}
|
|
}
|
|
else {
|
|
SendHandshake();
|
|
}
|
|
}
|
|
catch (std::exception &ex) {
|
|
Log.OutF(Logs::General, Logs::Error, "Error parsing hello from server: {0}", ex.what());
|
|
m_connection->Disconnect();
|
|
}
|
|
#else
|
|
try {
|
|
bool enc = p.GetInt8(0) == 1 ? true : false;
|
|
|
|
if (enc) {
|
|
Log.OutF(Logs::General, Logs::Error, "Server requested encryption but we do not support encryption.");
|
|
m_connection->Disconnect();
|
|
return;
|
|
}
|
|
else {
|
|
SendHandshake();
|
|
}
|
|
}
|
|
catch (std::exception &ex) {
|
|
Log.OutF(Logs::General, Logs::Error, "Error parsing hello from server: {0}", ex.what());
|
|
m_connection->Disconnect();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void EQ::Net::ServertalkClient::ProcessMessage(EQ::Net::Packet &p)
|
|
{
|
|
}
|
|
|
|
void EQ::Net::ServertalkClient::SendHandshake()
|
|
{
|
|
EQ::Net::WritablePacket handshake;
|
|
#ifdef ENABLE_SECURITY
|
|
if (m_encrypted) {
|
|
crypto_box_keypair(m_public_key_ours, m_private_key_ours);
|
|
randombytes_buf(m_nonce_ours, crypto_box_NONCEBYTES);
|
|
|
|
crypto_box_beforenm(m_shared_key, m_public_key_theirs, m_private_key_ours);
|
|
|
|
handshake.PutData(0, m_public_key_ours, crypto_box_PUBLICKEYBYTES);
|
|
handshake.PutData(crypto_box_PUBLICKEYBYTES, m_nonce_ours, crypto_box_NONCEBYTES);
|
|
|
|
size_t cipher_length = m_identifier.length() + 1 + m_credentials.length() + 1 + crypto_secretbox_MACBYTES;
|
|
size_t data_length = m_identifier.length() + 1 + m_credentials.length() + 1;
|
|
unsigned char *signed_buffer = new unsigned char[cipher_length];
|
|
unsigned char *data_buffer = new unsigned char[data_length];
|
|
memset(data_buffer, 0, data_length);
|
|
memcpy(&data_buffer[0], m_identifier.c_str(), m_identifier.length());
|
|
memcpy(&data_buffer[1 + m_identifier.length()], m_credentials.c_str(), m_credentials.length());
|
|
|
|
crypto_box_easy_afternm(signed_buffer, data_buffer, data_length, m_nonce_ours, m_shared_key);
|
|
|
|
(*(uint64_t*)&m_nonce_ours[0])++;
|
|
|
|
handshake.PutData(crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES, signed_buffer, cipher_length);
|
|
|
|
Log.OutF(Logs::General, Logs::Debug, "Sending {1} bytes handshake:\n{0}", handshake.ToString(), handshake.Length());
|
|
|
|
delete[] signed_buffer;
|
|
delete[] data_buffer;
|
|
}
|
|
else {
|
|
handshake.PutString(0, m_identifier);
|
|
}
|
|
#else
|
|
handshake.PutString(0, m_identifier);
|
|
#endif
|
|
InternalSend(ServertalkClientHandshake, handshake);
|
|
}
|