Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 96 additions & 1 deletion src/server/apps/authserver/Server/AuthSession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "DatabaseEnv.h"
#include "Errors.h"
#include "IPLocation.h"
#include "IpAddress.h"
#include "Log.h"
#include "RealmList.h"
#include "SecretMgr.h"
Expand All @@ -46,7 +47,8 @@ enum eAuthCmd
XFER_DATA = 0x31,
XFER_ACCEPT = 0x32,
XFER_RESUME = 0x33,
XFER_CANCEL = 0x34
XFER_CANCEL = 0x34,
RELAY_PACKET = 0x64 // Relay packet from relay server to auth server according to the https://github.com/masterking32/WoW-Server-Relay
};

#pragma pack(push, 1)
Expand Down Expand Up @@ -111,6 +113,16 @@ typedef struct AUTH_RECONNECT_PROOF_C
} sAuthReconnectProof_C;
static_assert(sizeof(sAuthReconnectProof_C) == (1 + 16 + 20 + 20 + 1));

typedef struct RELAY_PACKET_C
{
uint8 cmd;
uint16 secret_len;
uint16 ip_len;
uint8 secret[1];
uint8 ip[1];
} sRelayPacket_C;
static_assert(sizeof(sRelayPacket_C) == (1 + 2 + 2 + 1 + 1)); // 1 byte for the cmd, 2 bytes for the secret length, 2 bytes for the IP length

#pragma pack(pop)

std::array<uint8, 16> VersionChallenge = { { 0xBA, 0xA3, 0x1E, 0x99, 0xA0, 0x0B, 0x21, 0x57, 0xFC, 0x37, 0x3F, 0xB3, 0x69, 0xCD, 0xD2, 0xF1 } };
Expand All @@ -119,6 +131,7 @@ std::array<uint8, 16> VersionChallenge = { { 0xBA, 0xA3, 0x1E, 0x99, 0xA0, 0x0B,

#define AUTH_LOGON_CHALLENGE_INITIAL_SIZE 4
#define REALM_LIST_PACKET_SIZE 5
#define MAX_RELAY_PACKET_SIZE (sizeof(RELAY_PACKET_C) + 64 + 46) // 64 bytes for the secret, 46 bytes for the IP address (Even though IPv6 is 39 bytes, we use 46 bytes to be safe)

std::unordered_map<uint8, AuthHandler> AuthSession::InitHandlers()
{
Expand All @@ -129,6 +142,7 @@ std::unordered_map<uint8, AuthHandler> AuthSession::InitHandlers()
handlers[AUTH_RECONNECT_CHALLENGE] = { STATUS_CHALLENGE, AUTH_LOGON_CHALLENGE_INITIAL_SIZE, &AuthSession::HandleReconnectChallenge };
handlers[AUTH_RECONNECT_PROOF] = { STATUS_RECONNECT_PROOF, sizeof(AUTH_RECONNECT_PROOF_C), &AuthSession::HandleReconnectProof };
handlers[REALM_LIST] = { STATUS_AUTHED, REALM_LIST_PACKET_SIZE, &AuthSession::HandleRealmList };
handlers[RELAY_PACKET] = { STATUS_CHALLENGE, sizeof(RELAY_PACKET_C), &AuthSession::HandleRelayPacket };

return handlers;
}
Expand Down Expand Up @@ -252,6 +266,16 @@ void AuthSession::ReadHandler()
return;
}
}
else if (cmd == RELAY_PACKET)
{
sRelayPacket_C* relayPacket = reinterpret_cast<sRelayPacket_C*>(packet.GetReadPointer());
size += relayPacket->secret_len + relayPacket->ip_len - 2; // -2 because the secret and IP lengths are already included in the size
if (size > MAX_RELAY_PACKET_SIZE)
{
CloseSocket();
return;
}
}

if (packet.GetActiveSize() < size)
break;
Expand Down Expand Up @@ -281,6 +305,77 @@ void AuthSession::SendPacket(ByteBuffer& packet)
}
}

bool AuthSession::HandleRelayPacket()
{
sRelayPacket_C* relayPacket = reinterpret_cast<sRelayPacket_C*>(GetReadBuffer().GetReadPointer());

if (relayPacket->secret_len > 64 || relayPacket->ip_len > 46)
{
LOG_DEBUG("server.authserver", "[RelayPacket] Relay packet is too large");
_status = STATUS_CLOSED; // Close the connection if the packet is too large
return false;
}

if (relayPacket->secret_len < 1 || relayPacket->ip_len < 1)
{
LOG_DEBUG("server.authserver", "[RelayPacket] Relay packet is too small");
_status = STATUS_CLOSED; // Close the connection if the packet is too small
return false;
}

std::string secret(reinterpret_cast<char const*>(relayPacket->secret), relayPacket->secret_len);
std::string ip(reinterpret_cast<char const*>(relayPacket->secret + relayPacket->secret_len), relayPacket->ip_len);
std::string configSecretKey = sConfigMgr->GetOption<std::string>("RelayServerSecret", "secret");

if (configSecretKey.empty() || configSecretKey == "secret")
{
LOG_ERROR("server.authserver", "[RelayPacket] Relay server secret is not set or is default. Please set a unique secret key with a maximum of 64 characters in the authserver.conf configuration file.");
_status = STATUS_CLOSED; // Close the connection if the secret is not set
return false;
}

if (secret != configSecretKey)
{
LOG_DEBUG("server.authserver", "[RelayPacket] Invalid secret");
_status = STATUS_CLOSED; // Close the connection if the secret is invalid
return false;
}

if (ip.empty())
{
LOG_DEBUG("server.authserver", "[RelayPacket] IP is empty");
_status = STATUS_CLOSED; // Close the connection if the IP is empty
return false;
}

boost::system::error_code ip_error;
boost::asio::ip::address ip_address = Acore::Net::make_address(ip, ip_error);

if (ip_error)
{
LOG_DEBUG("server.authserver", "[RelayPacket] Invalid IP '{}'", ip);
_status = STATUS_CLOSED; // Close the connection if the IP is invalid
return false;
}

std::string RelayIPAddress = GetRemoteIpAddress().to_string();

SetRemoteIpAddress(ip_address); // Change the remote IP address to the one received from the relay server

std::string UserIPAddress = GetRemoteIpAddress().to_string();

LOG_DEBUG("server.authserver", "[RelayPacket] Received relay packet from '{}' for IP '{}'", RelayIPAddress, UserIPAddress);

// We need to check the database once again to determine if the client's IP is banned or not.
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_IP_INFO);

stmt->SetData(0, UserIPAddress);

_queryProcessor.AddCallback(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&AuthSession::CheckIpCallback, this, std::placeholders::_1)));

return true;
}

bool AuthSession::HandleLogonChallenge()
{
_status = STATUS_CLOSED;
Expand Down
1 change: 1 addition & 0 deletions src/server/apps/authserver/Server/AuthSession.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ class AuthSession : public Socket<AuthSession>
bool HandleReconnectChallenge();
bool HandleReconnectProof();
bool HandleRealmList();
bool HandleRelayPacket();

void CheckIpCallback(PreparedQueryResult result);
void LogonChallengeCallback(PreparedQueryResult result);
Expand Down
13 changes: 13 additions & 0 deletions src/server/apps/authserver/authserver.conf.dist
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
# CRYPTOGRAPHY
# UPDATE SETTINGS
# LOGGING SYSTEM SETTINGS
# CONFIGURATION FOR RELAY SERVER
#
###################################################################################################

Expand Down Expand Up @@ -440,3 +441,15 @@ Logger.root=4,Console Auth

#
###################################################################################################

###################################################################################################
#
# CONFIGURATION FOR RELAY SERVER
# Secret Key for Relay Server
# Please use a secure key that is between 32 and 64 characters in length. Note that the maximum length allowed is 64 characters.
#

RelayServerSecret = "secret"

#
###################################################################################################
13 changes: 13 additions & 0 deletions src/server/apps/worldserver/worldserver.conf.dist
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
# PERFORMANCE
# LOGGING
# METRIC
# CONFIGURATION FOR RELAY SERVER
# SERVER
# PACKET SPOOF PROTECTION SETTINGS
# WARDEN
Expand Down Expand Up @@ -853,6 +854,18 @@ Metric.OverallStatusInterval = 1
#
###################################################################################################

###################################################################################################
#
# CONFIGURATION FOR RELAY SERVER
# Secret Key for Relay Server
# Please use a secure key that is between 32 and 64 characters in length. Note that the maximum length allowed is 64 characters.
#

RelayServerSecret = "secret"

#
###################################################################################################

###################################################################################################
# SERVER
#
Expand Down
1 change: 1 addition & 0 deletions src/server/game/Server/Protocol/Opcodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1439,6 +1439,7 @@ void OpcodeTable::Initialize()
/*0x51C*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_COMMENTATOR_SKIRMISH_QUEUE_RESULT1, STATUS_NEVER);
/*0x51D*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_COMMENTATOR_SKIRMISH_QUEUE_RESULT2, STATUS_NEVER);
/*0x51E*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_MULTIPLE_MOVES, STATUS_NEVER);
/*0xA32*/ DEFINE_HANDLER(RELAY_SERVER_CMD_WORLD, STATUS_NEVER, PROCESS_THREADUNSAFE, &WorldSession::Handle_EarlyProccess); // Relay server command

#undef DEFINE_HANDLER
#undef DEFINE_SERVER_OPCODE_HANDLER
Expand Down
3 changes: 2 additions & 1 deletion src/server/game/Server/Protocol/Opcodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -1338,7 +1338,8 @@ enum Opcodes : uint16
SMSG_COMMENTATOR_SKIRMISH_QUEUE_RESULT1 = 0x51C,
SMSG_COMMENTATOR_SKIRMISH_QUEUE_RESULT2 = 0x51D,
SMSG_MULTIPLE_MOVES = 0x51E, // uncompressed version of SMSG_COMPRESSED_MOVES
NUM_MSG_TYPES = 0x51F
NUM_MSG_TYPES = 0x51F,
RELAY_SERVER_CMD_WORLD = 0xA32 // Relay packet from relay server to worldserver according to the https://github.com/masterking32/WoW-Server-Relay
};

enum OpcodeMisc : uint16
Expand Down
117 changes: 117 additions & 0 deletions src/server/game/Server/WorldSocket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "DatabaseEnv.h"
#include "GameTime.h"
#include "IPLocation.h"
#include "IpAddress.h"
#include "Opcodes.h"
#include "PacketLog.h"
#include "Random.h"
Expand All @@ -32,6 +33,7 @@
#include "WorldSession.h"
#include "zlib.h"
#include <memory>
#include <Config.h>

using boost::asio::ip::tcp;

Expand Down Expand Up @@ -341,6 +343,12 @@ struct AuthSession
ByteBuffer AddonInfo;
};

struct RelayPacketInfo
{
std::string SecretKey;
std::string UserIP;
};

struct AccountInfo
{
uint32 Id;
Expand Down Expand Up @@ -421,6 +429,27 @@ WorldSocket::ReadDataHandlerResult WorldSocket::ReadDataHandler()
LOG_ERROR("network", "WorldSocket::ReadDataHandler(): client {} sent malformed CMSG_PING", GetRemoteIpAddress().to_string());
return ReadDataHandlerResult::Error;
}

case RELAY_SERVER_CMD_WORLD:
{
LogOpcodeText(opcode, sessionGuard);

if (_authed)
{
return ReadDataHandlerResult::Error;
}

try
{
HandleRelayPacket(packet);
return ReadDataHandlerResult::WaitingForQuery;
}
catch (ByteBufferException const&) {}

LOG_ERROR("network", "WorldSocket::ReadDataHandler(): client {} sent malformed RELAY_SERVER_CMD_WORLD", GetRemoteIpAddress().to_string());
return ReadDataHandlerResult::Error;
}

case CMSG_AUTH_SESSION:
{
LogOpcodeText(opcode, sessionGuard);
Expand All @@ -442,7 +471,9 @@ WorldSocket::ReadDataHandlerResult WorldSocket::ReadDataHandler()
LOG_ERROR("network", "WorldSocket::ReadDataHandler(): client {} sent malformed CMSG_AUTH_SESSION", GetRemoteIpAddress().to_string());
return ReadDataHandlerResult::Error;
}

case CMSG_KEEP_ALIVE: /// @todo: handle this packet in the same way of CMSG_TIME_SYNC_RESP
{
sessionGuard.lock();
LogOpcodeText(opcode, sessionGuard);
if (_worldSession)
Expand All @@ -452,9 +483,14 @@ WorldSocket::ReadDataHandlerResult WorldSocket::ReadDataHandler()
}
LOG_ERROR("network", "WorldSocket::ReadDataHandler: client {} sent CMSG_KEEP_ALIVE without being authenticated", GetRemoteIpAddress().to_string());
return ReadDataHandlerResult::Error;
}

case CMSG_TIME_SYNC_RESP:
{
packetToQueue = new WorldPacket(std::move(packet), GameTime::Now());
break;
}

default:
packetToQueue = new WorldPacket(std::move(packet));
break;
Expand Down Expand Up @@ -521,6 +557,87 @@ void WorldSocket::SendPacket(WorldPacket const& packet)
_bufferQueue.Enqueue(new EncryptableAndCompressiblePacket(packet, _authCrypt.IsInitialized()));
}

void WorldSocket::HandleRelayPacket(WorldPacket& recvPacket)
{
std::shared_ptr<RelayPacketInfo> relayPacketInfo = std::make_shared<RelayPacketInfo>();

// Read the content of the packet
recvPacket >> relayPacketInfo->SecretKey; // Secret key used to authenticate the relay server
recvPacket >> relayPacketInfo->UserIP; // User IP sent by the relay server

std::string ConfigSecretKey = sConfigMgr->GetOption<std::string>("RelayServerSecret", "secret"); // Get the secret key from the configuration file

if (ConfigSecretKey.empty() || ConfigSecretKey == "secret")
{
SendAuthResponseError(AUTH_REJECT);
LOG_ERROR("network", "WorldSocket::HandleRelayPacket: Relay server secret is not set or is default. Please set a unique secret key with a maximum of 64 characters in the worldserver.conf configuration file.");
DelayedCloseSocket();
return;
}

if (relayPacketInfo->SecretKey.empty() || relayPacketInfo->SecretKey != ConfigSecretKey)
{
SendAuthResponseError(AUTH_REJECT);
LOG_ERROR("network", "WorldSocket::HandleRelayPacket: Sent Auth Response (invalid secret key).");
DelayedCloseSocket();
return;
}

if (relayPacketInfo->UserIP.empty())
{
SendAuthResponseError(AUTH_REJECT);
LOG_ERROR("network", "WorldSocket::HandleRelayPacket: Sent Auth Response (invalid user IP).");
DelayedCloseSocket();
return;
}

boost::system::error_code ip_error;
boost::asio::ip::address ip_address = Acore::Net::make_address(relayPacketInfo->UserIP, ip_error);

if (ip_error)
{
SendAuthResponseError(AUTH_REJECT);
LOG_ERROR("network", "WorldSocket::HandleRelayPacket: Sent Auth Response (invalid user IP).");
DelayedCloseSocket();
return;
}

std::string RelayIPAddress = GetRemoteIpAddress().to_string();

SetRemoteIpAddress(ip_address); // Change the remote IP address to the one received from the relay server

std::string UserIPAddress = GetRemoteIpAddress().to_string();

LOG_DEBUG("network", "WorldSocket::HandleRelayPacket: Changed remote IP address from '{}' to '{}'.", RelayIPAddress, UserIPAddress);

LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_IP_INFO);

stmt->SetData(0, UserIPAddress);

_queryProcessor.AddCallback(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSocket::CheckIpCallbackRelay, this, std::placeholders::_1)));
}
void WorldSocket::CheckIpCallbackRelay(PreparedQueryResult result)
{
if (result)
{
bool banned = false;
do
{
Field* fields = result->Fetch();
if (fields[0].Get<uint64>() != 0)
banned = true;
} while (result->NextRow());
if (banned)
{
SendAuthResponseError(AUTH_REJECT);
LOG_ERROR("network", "WorldSocket::CheckIpCallbackRelay: Sent Auth Response (IP {} banned).", GetRemoteIpAddress().to_string());
DelayedCloseSocket();
return;
}
}
AsyncRead();
}

void WorldSocket::HandleAuthSession(WorldPacket & recvPacket)
{
std::shared_ptr<AuthSession> authSession = std::make_shared<AuthSession>();
Expand Down
Loading