Skip to content

Commit

Permalink
Support RelayPacket in Auth and World servers.
Browse files Browse the repository at this point in the history
  • Loading branch information
masterking32 committed Apr 24, 2024
1 parent dfbd801 commit cb5aa9e
Show file tree
Hide file tree
Showing 9 changed files with 250 additions and 4 deletions.
91 changes: 90 additions & 1 deletion src/server/authserver/Server/AuthSession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "CryptoRandom.h"
#include "DatabaseEnv.h"
#include "IPLocation.h"
#include "IpAddress.h"
#include "Log.h"
#include "RealmList.h"
#include "SecretMgr.h"
Expand All @@ -45,7 +46,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 @@ -110,6 +112,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,10 +131,13 @@ 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()
{
std::unordered_map<uint8, AuthHandler> handlers;

handlers[RELAY_PACKET] = { STATUS_CHALLENGE, sizeof(RELAY_PACKET_C), &AuthSession::HandleRelayPacket };
handlers[AUTH_LOGON_CHALLENGE] = { STATUS_CHALLENGE, AUTH_LOGON_CHALLENGE_INITIAL_SIZE, &AuthSession::HandleLogonChallenge };
handlers[AUTH_LOGON_PROOF] = { STATUS_LOGON_PROOF, sizeof(AUTH_LOGON_PROOF_C), &AuthSession::HandleLogonProof };
handlers[AUTH_RECONNECT_CHALLENGE] = { STATUS_CHALLENGE, AUTH_LOGON_CHALLENGE_INITIAL_SIZE, &AuthSession::HandleReconnectChallenge };
Expand Down Expand Up @@ -243,6 +258,15 @@ void AuthSession::ReadHandler()
CloseSocket();
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)
Expand Down Expand Up @@ -273,6 +297,71 @@ 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)
{
TC_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)
{
TC_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->GetStringDefault("RelayServerSecret", "secret");

if(configSecretKey.empty() || configSecretKey == "secret")
{
TC_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)
{
TC_LOG_DEBUG("server.authserver", "[RelayPacket] Invalid secret");
_status = STATUS_CLOSED; // Close the connection if the secret is invalid
return false;
}

if (ip.empty())
{
TC_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 = Trinity::Net::make_address(ip, ip_error);
if (ip_error)
{
TC_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();
TC_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->setString(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/authserver/Server/AuthSession.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ class AuthSession : public Socket<AuthSession>
void ReadHandler() override;

private:
bool HandleRelayPacket();
bool HandleLogonChallenge();
bool HandleLogonProof();
bool HandleReconnectChallenge();
Expand Down
13 changes: 13 additions & 0 deletions src/server/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 @@ -397,3 +398,15 @@ Logger.root=3,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"

#
###################################################################################################
2 changes: 2 additions & 0 deletions src/server/game/Server/Protocol/Opcodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1439,6 +1439,8 @@ 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

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 @@ -1337,7 +1337,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
110 changes: 110 additions & 0 deletions src/server/game/Server/WorldSocket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "CryptoHash.h"
#include "CryptoRandom.h"
#include "IPLocation.h"
#include "IpAddress.h"
#include "Opcodes.h"
#include "PacketLog.h"
#include "Random.h"
Expand All @@ -31,6 +32,7 @@
#include "World.h"
#include "WorldSession.h"
#include <memory>
#include <Config.h>

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

Expand Down Expand Up @@ -242,6 +244,12 @@ struct AuthSession
ByteBuffer AddonInfo;
};

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

struct AccountInfo
{
uint32 Id;
Expand Down Expand Up @@ -322,6 +330,26 @@ WorldSocket::ReadDataHandlerResult WorldSocket::ReadDataHandler()
TC_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&)
{
}
TC_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 Down Expand Up @@ -421,6 +449,88 @@ void WorldSocket::SendPacket(WorldPacket const& packet)
_bufferQueue.Enqueue(new EncryptablePacket(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->GetStringDefault("RelayServerSecret", "secret"); // Get the secret key from the configuration file

if(ConfigSecretKey.empty() || ConfigSecretKey == "secret")
{
SendAuthResponseError(AUTH_REJECT);
TC_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);
TC_LOG_ERROR("network", "WorldSocket::HandleRelayPacket: Sent Auth Response (invalid secret key).");
DelayedCloseSocket();
return;
}

if(relayPacketInfo->UserIP.empty())
{
SendAuthResponseError(AUTH_REJECT);
TC_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 = Trinity::Net::make_address(relayPacketInfo->UserIP, ip_error);

if (ip_error)
{
SendAuthResponseError(AUTH_REJECT);
TC_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();

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

LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_IP_INFO);
stmt->setString(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].GetUInt64() != 0)
banned = true;

} while (result->NextRow());

if (banned)
{
SendAuthResponseError(AUTH_REJECT);
TC_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
4 changes: 3 additions & 1 deletion src/server/game/Server/WorldSocket.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ struct ClientPktHeader
uint32 cmd;

bool IsValidSize() const { return size >= 4 && size < 10240; }
bool IsValidOpcode() const { return cmd < NUM_OPCODE_HANDLERS; }
bool IsValidOpcode() const { return cmd < NUM_OPCODE_HANDLERS || cmd == RELAY_SERVER_CMD_WORLD; }
};

#pragma pack(pop)
Expand Down Expand Up @@ -98,13 +98,15 @@ class TC_GAME_API WorldSocket : public Socket<WorldSocket>

private:
void CheckIpCallback(PreparedQueryResult result);
void CheckIpCallbackRelay(PreparedQueryResult result);

/// writes network.opcode log
/// accessing WorldSession is not threadsafe, only do it when holding _worldSessionLock
void LogOpcodeText(OpcodeClient opcode, std::unique_lock<std::mutex> const& guard) const;
/// sends and logs network.opcode without accessing WorldSession
void SendPacketAndLogOpcode(WorldPacket const& packet);
void HandleSendAuthSession();
void HandleRelayPacket(WorldPacket& recvPacket);
void HandleAuthSession(WorldPacket& recvPacket);
void HandleAuthSessionCallback(std::shared_ptr<AuthSession> authSession, PreparedQueryResult result);
void LoadSessionPermissionsCallback(PreparedQueryResult result);
Expand Down
17 changes: 16 additions & 1 deletion src/server/shared/Networking/Socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,21 @@ class Socket : public std::enable_shared_from_this<T>

boost::asio::ip::address GetRemoteIpAddress() const
{
return _remoteAddress;
if(_RealRemoteAddress.is_unspecified())
return _remoteAddress;

return _RealRemoteAddress;
}

bool SetRemoteIpAddress(const boost::asio::ip::address& address)
{
if(_RealRemoteAddress.is_unspecified())
{
_RealRemoteAddress = address;
return true;
}

return false;
}

uint16 GetRemotePort() const
Expand Down Expand Up @@ -258,6 +272,7 @@ class Socket : public std::enable_shared_from_this<T>
tcp::socket _socket;

boost::asio::ip::address _remoteAddress;
boost::asio::ip::address _RealRemoteAddress;
uint16 _remotePort;

MessageBuffer _readBuffer;
Expand Down
Loading

0 comments on commit cb5aa9e

Please sign in to comment.