mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-24 19:06:49 +01:00
Core/Networking: Fixed unsafe access to _worldSession member in WorldSocket
(cherry picked from commitbed88e0dd4) Core/Networking: Fixed deadlock in HandlePing if the client is about to be kicked for overspeed pings (cherry picked from commit3da0f7e409) Core/Networking: Cleanup CloseSocket calls from read failures in WorldSocket (cherry picked from commit18343a7309) Conflicts: src/server/game/Server/WorldSocket.cpp Ref #14474
This commit is contained in:
@@ -225,6 +225,7 @@ void WorldSession::SendPacket(WorldPacket* packet)
|
|||||||
|
|
||||||
sScriptMgr->OnPacketSend(this, *packet);
|
sScriptMgr->OnPacketSend(this, *packet);
|
||||||
|
|
||||||
|
TC_LOG_TRACE("network.opcode", "S->C: %s %s", GetPlayerInfo().c_str(), GetOpcodeNameForLogging(packet->GetOpcode()).c_str());
|
||||||
m_Socket->SendPacket(*packet);
|
m_Socket->SendPacket(*packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
using boost::asio::ip::tcp;
|
using boost::asio::ip::tcp;
|
||||||
|
|
||||||
WorldSocket::WorldSocket(tcp::socket&& socket)
|
WorldSocket::WorldSocket(tcp::socket&& socket)
|
||||||
: Socket(std::move(socket)), _authSeed(rand32()), _OverSpeedPings(0), _worldSession(nullptr)
|
: Socket(std::move(socket)), _authSeed(rand32()), _OverSpeedPings(0), _worldSession(nullptr), _authed(false)
|
||||||
{
|
{
|
||||||
_headerBuffer.Resize(sizeof(ClientPktHeader));
|
_headerBuffer.Resize(sizeof(ClientPktHeader));
|
||||||
}
|
}
|
||||||
@@ -53,7 +53,15 @@ void WorldSocket::HandleSendAuthSession()
|
|||||||
seed2.SetRand(16 * 8);
|
seed2.SetRand(16 * 8);
|
||||||
packet.append(seed2.AsByteArray(16).get(), 16); // new encryption seeds
|
packet.append(seed2.AsByteArray(16).get(), 16); // new encryption seeds
|
||||||
|
|
||||||
SendPacket(packet);
|
SendPacketAndLogOpcode(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldSocket::OnClose()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> sessionGuard(_worldSessionLock);
|
||||||
|
_worldSession = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldSocket::ReadHandler()
|
void WorldSocket::ReadHandler()
|
||||||
@@ -80,7 +88,10 @@ void WorldSocket::ReadHandler()
|
|||||||
|
|
||||||
// We just received nice new header
|
// We just received nice new header
|
||||||
if (!ReadHeaderHandler())
|
if (!ReadHeaderHandler())
|
||||||
|
{
|
||||||
|
CloseSocket();
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We have full read header, now check the data payload
|
// We have full read header, now check the data payload
|
||||||
@@ -101,7 +112,10 @@ void WorldSocket::ReadHandler()
|
|||||||
|
|
||||||
// just received fresh new payload
|
// just received fresh new payload
|
||||||
if (!ReadDataHandler())
|
if (!ReadDataHandler())
|
||||||
|
{
|
||||||
|
CloseSocket();
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_headerBuffer.Reset();
|
_headerBuffer.Reset();
|
||||||
}
|
}
|
||||||
@@ -121,17 +135,8 @@ bool WorldSocket::ReadHeaderHandler()
|
|||||||
|
|
||||||
if (!header->IsValidSize() || !header->IsValidOpcode())
|
if (!header->IsValidSize() || !header->IsValidOpcode())
|
||||||
{
|
{
|
||||||
if (_worldSession)
|
TC_LOG_ERROR("network", "WorldSocket::ReadHeaderHandler(): client %s sent malformed packet (size: %hu, cmd: %u)",
|
||||||
{
|
GetRemoteIpAddress().to_string().c_str(), header->size, header->cmd);
|
||||||
Player* player = _worldSession->GetPlayer();
|
|
||||||
TC_LOG_ERROR("network", "WorldSocket::ReadHeaderHandler(): client (account: %u, char [GUID: %u, name: %s]) sent malformed packet (size: %hu, cmd: %u)",
|
|
||||||
_worldSession->GetAccountId(), player ? player->GetGUIDLow() : 0, player ? player->GetName().c_str() : "<none>", header->size, header->cmd);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
TC_LOG_ERROR("network", "WorldSocket::ReadHeaderHandler(): client %s sent malformed packet (size: %hu, cmd: %u)",
|
|
||||||
GetRemoteIpAddress().to_string().c_str(), header->size, header->cmd);
|
|
||||||
|
|
||||||
CloseSocket();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,28 +156,32 @@ bool WorldSocket::ReadDataHandler()
|
|||||||
if (sPacketLog->CanLogPacket())
|
if (sPacketLog->CanLogPacket())
|
||||||
sPacketLog->LogPacket(packet, CLIENT_TO_SERVER, GetRemoteIpAddress(), GetRemotePort());
|
sPacketLog->LogPacket(packet, CLIENT_TO_SERVER, GetRemoteIpAddress(), GetRemotePort());
|
||||||
|
|
||||||
TC_LOG_TRACE("network.opcode", "C->S: %s %s", (_worldSession ? _worldSession->GetPlayerInfo() : GetRemoteIpAddress().to_string()).c_str(), GetOpcodeNameForLogging(opcode).c_str());
|
std::unique_lock<std::mutex> sessionGuard(_worldSessionLock, std::defer_lock);
|
||||||
|
|
||||||
switch (opcode)
|
switch (opcode)
|
||||||
{
|
{
|
||||||
case CMSG_PING:
|
case CMSG_PING:
|
||||||
HandlePing(packet);
|
LogOpcodeText(opcode, sessionGuard);
|
||||||
break;
|
return HandlePing(packet);
|
||||||
case CMSG_AUTH_SESSION:
|
case CMSG_AUTH_SESSION:
|
||||||
if (_worldSession)
|
LogOpcodeText(opcode, sessionGuard);
|
||||||
|
if (_authed)
|
||||||
{
|
{
|
||||||
TC_LOG_ERROR("network", "WorldSocket::ProcessIncoming: received duplicate CMSG_AUTH_SESSION from %s", _worldSession->GetPlayerInfo().c_str());
|
// locking just to safely log offending user is probably overkill but we are disconnecting him anyway
|
||||||
break;
|
if (sessionGuard.try_lock())
|
||||||
|
TC_LOG_ERROR("network", "WorldSocket::ProcessIncoming: received duplicate CMSG_AUTH_SESSION from %s", _worldSession->GetPlayerInfo().c_str());
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
HandleAuthSession(packet);
|
HandleAuthSession(packet);
|
||||||
break;
|
break;
|
||||||
case CMSG_KEEP_ALIVE:
|
case CMSG_KEEP_ALIVE:
|
||||||
TC_LOG_DEBUG("network", "%s", GetOpcodeNameForLogging(opcode).c_str());
|
LogOpcodeText(opcode, sessionGuard);
|
||||||
sScriptMgr->OnPacketReceive(_worldSession, packet);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
|
sessionGuard.lock();
|
||||||
|
LogOpcodeText(opcode, sessionGuard);
|
||||||
if (!_worldSession)
|
if (!_worldSession)
|
||||||
{
|
{
|
||||||
TC_LOG_ERROR("network.opcode", "ProcessIncoming: Client not authed opcode = %u", uint32(opcode));
|
TC_LOG_ERROR("network.opcode", "ProcessIncoming: Client not authed opcode = %u", uint32(opcode));
|
||||||
@@ -193,7 +202,26 @@ bool WorldSocket::ReadDataHandler()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldSocket::SendPacket(WorldPacket& packet)
|
void WorldSocket::LogOpcodeText(uint16 opcode, std::unique_lock<std::mutex> const& guard) const
|
||||||
|
{
|
||||||
|
if (!guard)
|
||||||
|
{
|
||||||
|
TC_LOG_TRACE("network.opcode", "C->S: %s %s", GetRemoteIpAddress().to_string().c_str(), GetOpcodeNameForLogging(opcode).c_str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TC_LOG_TRACE("network.opcode", "C->S: %s %s", (_worldSession ? _worldSession->GetPlayerInfo() : GetRemoteIpAddress().to_string()).c_str(),
|
||||||
|
GetOpcodeNameForLogging(opcode).c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldSocket::SendPacketAndLogOpcode(WorldPacket const& packet)
|
||||||
|
{
|
||||||
|
TC_LOG_TRACE("network.opcode", "S->C: %s %s", GetRemoteIpAddress().to_string().c_str(), GetOpcodeNameForLogging(packet.GetOpcode()).c_str());
|
||||||
|
SendPacket(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldSocket::SendPacket(WorldPacket const& packet)
|
||||||
{
|
{
|
||||||
if (!IsOpen())
|
if (!IsOpen())
|
||||||
return;
|
return;
|
||||||
@@ -201,8 +229,6 @@ void WorldSocket::SendPacket(WorldPacket& packet)
|
|||||||
if (sPacketLog->CanLogPacket())
|
if (sPacketLog->CanLogPacket())
|
||||||
sPacketLog->LogPacket(packet, SERVER_TO_CLIENT, GetRemoteIpAddress(), GetRemotePort());
|
sPacketLog->LogPacket(packet, SERVER_TO_CLIENT, GetRemoteIpAddress(), GetRemotePort());
|
||||||
|
|
||||||
TC_LOG_TRACE("network.opcode", "S->C: %s %s", (_worldSession ? _worldSession->GetPlayerInfo() : GetRemoteIpAddress().to_string()).c_str(), GetOpcodeNameForLogging(packet.GetOpcode()).c_str());
|
|
||||||
|
|
||||||
ServerPktHeader header(packet.size() + 2, packet.GetOpcode());
|
ServerPktHeader header(packet.size() + 2, packet.GetOpcode());
|
||||||
|
|
||||||
std::unique_lock<std::mutex> guard(_writeLock);
|
std::unique_lock<std::mutex> guard(_writeLock);
|
||||||
@@ -459,6 +485,7 @@ void WorldSocket::HandleAuthSession(WorldPacket& recvPacket)
|
|||||||
// At this point, we can safely hook a successful login
|
// At this point, we can safely hook a successful login
|
||||||
sScriptMgr->OnAccountLogin(id);
|
sScriptMgr->OnAccountLogin(id);
|
||||||
|
|
||||||
|
_authed = true;
|
||||||
_worldSession = new WorldSession(id, shared_from_this(), AccountTypes(security), expansion, mutetime, locale, recruiter, isRecruiter);
|
_worldSession = new WorldSession(id, shared_from_this(), AccountTypes(security), expansion, mutetime, locale, recruiter, isRecruiter);
|
||||||
_worldSession->LoadGlobalAccountData();
|
_worldSession->LoadGlobalAccountData();
|
||||||
_worldSession->LoadTutorialsData();
|
_worldSession->LoadTutorialsData();
|
||||||
@@ -477,10 +504,10 @@ void WorldSocket::SendAuthResponseError(uint8 code)
|
|||||||
WorldPacket packet(SMSG_AUTH_RESPONSE, 1);
|
WorldPacket packet(SMSG_AUTH_RESPONSE, 1);
|
||||||
packet << uint8(code);
|
packet << uint8(code);
|
||||||
|
|
||||||
SendPacket(packet);
|
SendPacketAndLogOpcode(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldSocket::HandlePing(WorldPacket& recvPacket)
|
bool WorldSocket::HandlePing(WorldPacket& recvPacket)
|
||||||
{
|
{
|
||||||
uint32 ping;
|
uint32 ping;
|
||||||
uint32 latency;
|
uint32 latency;
|
||||||
@@ -509,13 +536,14 @@ void WorldSocket::HandlePing(WorldPacket& recvPacket)
|
|||||||
|
|
||||||
if (maxAllowed && _OverSpeedPings > maxAllowed)
|
if (maxAllowed && _OverSpeedPings > maxAllowed)
|
||||||
{
|
{
|
||||||
|
std::unique_lock<std::mutex> sessionGuard(_worldSessionLock);
|
||||||
|
|
||||||
if (_worldSession && !_worldSession->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_OVERSPEED_PING))
|
if (_worldSession && !_worldSession->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_OVERSPEED_PING))
|
||||||
{
|
{
|
||||||
TC_LOG_ERROR("network", "WorldSocket::HandlePing: %s kicked for over-speed pings (address: %s)",
|
TC_LOG_ERROR("network", "WorldSocket::HandlePing: %s kicked for over-speed pings (address: %s)",
|
||||||
_worldSession->GetPlayerInfo().c_str(), GetRemoteIpAddress().to_string().c_str());
|
_worldSession->GetPlayerInfo().c_str(), GetRemoteIpAddress().to_string().c_str());
|
||||||
|
|
||||||
CloseSocket();
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -523,20 +551,23 @@ void WorldSocket::HandlePing(WorldPacket& recvPacket)
|
|||||||
_OverSpeedPings = 0;
|
_OverSpeedPings = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_worldSession)
|
|
||||||
{
|
{
|
||||||
_worldSession->SetLatency(latency);
|
std::lock_guard<std::mutex> sessionGuard(_worldSessionLock);
|
||||||
_worldSession->ResetClientTimeDelay();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
TC_LOG_ERROR("network", "WorldSocket::HandlePing: peer sent CMSG_PING, but is not authenticated or got recently kicked, address = %s", GetRemoteIpAddress().to_string().c_str());
|
|
||||||
|
|
||||||
CloseSocket();
|
if (_worldSession)
|
||||||
return;
|
{
|
||||||
|
_worldSession->SetLatency(latency);
|
||||||
|
_worldSession->ResetClientTimeDelay();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TC_LOG_ERROR("network", "WorldSocket::HandlePing: peer sent CMSG_PING, but is not authenticated or got recently kicked, address = %s", GetRemoteIpAddress().to_string().c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WorldPacket packet(SMSG_PONG, 4);
|
WorldPacket packet(SMSG_PONG, 4);
|
||||||
packet << ping;
|
packet << ping;
|
||||||
return SendPacket(packet);
|
SendPacketAndLogOpcode(packet);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,19 +55,25 @@ public:
|
|||||||
|
|
||||||
void Start() override;
|
void Start() override;
|
||||||
|
|
||||||
void SendPacket(WorldPacket& packet);
|
void SendPacket(WorldPacket const& packet);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void OnClose() override;
|
||||||
void ReadHandler() override;
|
void ReadHandler() override;
|
||||||
bool ReadHeaderHandler();
|
bool ReadHeaderHandler();
|
||||||
bool ReadDataHandler();
|
bool ReadDataHandler();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/// writes network.opcode log
|
||||||
|
/// accessing WorldSession is not threadsafe, only do it when holding _worldSessionLock
|
||||||
|
void LogOpcodeText(uint16 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 HandleSendAuthSession();
|
||||||
void HandleAuthSession(WorldPacket& recvPacket);
|
void HandleAuthSession(WorldPacket& recvPacket);
|
||||||
void SendAuthResponseError(uint8 code);
|
void SendAuthResponseError(uint8 code);
|
||||||
|
|
||||||
void HandlePing(WorldPacket& recvPacket);
|
bool HandlePing(WorldPacket& recvPacket);
|
||||||
|
|
||||||
uint32 _authSeed;
|
uint32 _authSeed;
|
||||||
AuthCrypt _authCrypt;
|
AuthCrypt _authCrypt;
|
||||||
@@ -75,7 +81,9 @@ private:
|
|||||||
std::chrono::steady_clock::time_point _LastPingTime;
|
std::chrono::steady_clock::time_point _LastPingTime;
|
||||||
uint32 _OverSpeedPings;
|
uint32 _OverSpeedPings;
|
||||||
|
|
||||||
|
std::mutex _worldSessionLock;
|
||||||
WorldSession* _worldSession;
|
WorldSession* _worldSession;
|
||||||
|
bool _authed;
|
||||||
|
|
||||||
MessageBuffer _headerBuffer;
|
MessageBuffer _headerBuffer;
|
||||||
MessageBuffer _packetBuffer;
|
MessageBuffer _packetBuffer;
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
#ifndef TC_SOCKET_USE_IOCP
|
#ifndef TC_SOCKET_USE_IOCP
|
||||||
std::unique_lock<std::mutex> guard(_writeLock, std::try_to_lock);
|
std::unique_lock<std::mutex> guard(_writeLock);
|
||||||
if (!guard)
|
if (!guard)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@@ -140,6 +140,8 @@ public:
|
|||||||
if (shutdownError)
|
if (shutdownError)
|
||||||
TC_LOG_DEBUG("network", "Socket::CloseSocket: %s errored when shutting down socket: %i (%s)", GetRemoteIpAddress().to_string().c_str(),
|
TC_LOG_DEBUG("network", "Socket::CloseSocket: %s errored when shutting down socket: %i (%s)", GetRemoteIpAddress().to_string().c_str(),
|
||||||
shutdownError.value(), shutdownError.message().c_str());
|
shutdownError.value(), shutdownError.message().c_str());
|
||||||
|
|
||||||
|
OnClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Marks the socket for closing after write buffer becomes empty
|
/// Marks the socket for closing after write buffer becomes empty
|
||||||
@@ -148,6 +150,8 @@ public:
|
|||||||
MessageBuffer& GetReadBuffer() { return _readBuffer; }
|
MessageBuffer& GetReadBuffer() { return _readBuffer; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
virtual void OnClose() { }
|
||||||
|
|
||||||
virtual void ReadHandler() = 0;
|
virtual void ReadHandler() = 0;
|
||||||
|
|
||||||
bool AsyncProcessQueue(std::unique_lock<std::mutex>&)
|
bool AsyncProcessQueue(std::unique_lock<std::mutex>&)
|
||||||
|
|||||||
Reference in New Issue
Block a user