diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/server/game/Server/WorldSession.cpp | 1 | ||||
-rw-r--r-- | src/server/game/Server/WorldSocket.cpp | 138 | ||||
-rw-r--r-- | src/server/game/Server/WorldSocket.h | 8 | ||||
-rw-r--r-- | src/server/shared/Networking/Socket.h | 6 |
4 files changed, 103 insertions, 50 deletions
diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 6c889b51631..b5138752e8e 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -283,6 +283,7 @@ void WorldSession::SendPacket(WorldPacket const* packet, bool forced /*= false*/ sScriptMgr->OnPacketSend(this, *packet); + TC_LOG_TRACE("network.opcode", "S->C: %s %s", GetPlayerInfo().c_str(), GetOpcodeNameForLogging(static_cast<OpcodeServer>(packet->GetOpcode())).c_str()); m_Socket[conIdx]->SendPacket(*packet); } diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp index e58037b3956..29779fe154e 100644 --- a/src/server/game/Server/WorldSocket.cpp +++ b/src/server/game/Server/WorldSocket.cpp @@ -57,7 +57,7 @@ uint32 const SizeOfServerHeader[2] = { sizeof(uint16) + sizeof(uint32), sizeof(u WorldSocket::WorldSocket(tcp::socket&& socket) : Socket(std::move(socket)), _type(CONNECTION_TYPE_REALM), _authSeed(rand32()), _OverSpeedPings(0), - _worldSession(nullptr), _compressionStream(nullptr), _initialized(false) + _worldSession(nullptr), _authed(false), _compressionStream(nullptr), _initialized(false) { _headerBuffer.Resize(SizeOfClientHeader[0][0]); } @@ -96,7 +96,15 @@ void WorldSocket::HandleSendAuthSession() memcpy(&challenge.DosChallenge[4], _decryptSeed.AsByteArray(16).get(), 16); challenge.DosZeroBits = 1; - SendPacket(*challenge.Write()); + SendPacketAndLogOpcode(*challenge.Write()); +} + +void WorldSocket::OnClose() +{ + { + std::lock_guard<std::mutex> sessionGuard(_worldSessionLock); + _worldSession = nullptr; + } } void WorldSocket::ReadHandler() @@ -182,15 +190,8 @@ bool WorldSocket::ReadHeaderHandler() if (!ClientPktHeader::IsValidSize(size) || (_initialized && !ClientPktHeader::IsValidOpcode(opcode))) { - if (_worldSession) - { - Player* player = _worldSession->GetPlayer(); - TC_LOG_ERROR("network", "WorldSocket::ReadHeaderHandler(): client (account: %u, char [%s, name: %s]) sent malformed packet (size: %u, cmd: %u)", - _worldSession->GetAccountId(), player ? player->GetGUID().ToString().c_str() : "GUID: Empty", player ? player->GetName().c_str() : "<none>", size, opcode); - } - else - TC_LOG_ERROR("network", "WorldSocket::ReadHeaderHandler(): client %s sent malformed packet (size: %u, cmd: %u)", - GetRemoteIpAddress().to_string().c_str(), size, opcode); + TC_LOG_ERROR("network", "WorldSocket::ReadHeaderHandler(): client %s sent malformed packet (size: %u, cmd: %u)", + GetRemoteIpAddress().to_string().c_str(), size, opcode); CloseSocket(); return false; @@ -217,19 +218,29 @@ bool WorldSocket::ReadDataHandler() if (sPacketLog->CanLogPacket()) sPacketLog->LogPacket(packet, CLIENT_TO_SERVER, GetRemoteIpAddress(), GetRemotePort(), GetConnectionType()); - 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) { case CMSG_PING: + LogOpcodeText(opcode, sessionGuard); HandlePing(packet); break; 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()); - break; + // locking just to safely log offending user is probably overkill but we are disconnecting him anyway + { + if (sessionGuard.try_lock()) + { + TC_LOG_ERROR("network", "WorldSocket::ProcessIncoming: received duplicate CMSG_AUTH_SESSION from %s", _worldSession->GetPlayerInfo().c_str()); + sessionGuard.unlock(); // unlock session guard to prevent deadlocking in CloseSocket + } + } + CloseSocket(); + return; } WorldPackets::Auth::AuthSession authSession(std::move(packet)); @@ -239,10 +250,19 @@ bool WorldSocket::ReadDataHandler() } case CMSG_AUTH_CONTINUED_SESSION: { - if (_worldSession) + LogOpcodeText(opcode, sessionGuard); + if (_authed) { - TC_LOG_ERROR("network", "WorldSocket::ProcessIncoming: received duplicate CMSG_AUTH_CONTINUED_SESSION from %s", _worldSession->GetPlayerInfo().c_str()); - break; + // locking just to safely log offending user is probably overkill but we are disconnecting him anyway + { + if (sessionGuard.try_lock()) + { + TC_LOG_ERROR("network", "WorldSocket::ProcessIncoming: received duplicate CMSG_AUTH_CONTINUED_SESSION from %s", _worldSession->GetPlayerInfo().c_str()); + sessionGuard.unlock(); // unlock session guard to prevent deadlocking in CloseSocket + } + } + CloseSocket(); + return; } WorldPackets::Auth::AuthContinuedSession authSession(std::move(packet)); @@ -251,23 +271,21 @@ bool WorldSocket::ReadDataHandler() break; } case CMSG_KEEP_ALIVE: - TC_LOG_DEBUG("network", "%s", GetOpcodeNameForLogging(opcode).c_str()); - sScriptMgr->OnPacketReceive(_worldSession, packet); + LogOpcodeText(opcode, sessionGuard); break; case CMSG_LOG_DISCONNECT: + LogOpcodeText(opcode, sessionGuard); packet.rfinish(); // contains uint32 disconnectReason; - TC_LOG_DEBUG("network", "%s", GetOpcodeNameForLogging(opcode).c_str()); - sScriptMgr->OnPacketReceive(_worldSession, packet); return true; case CMSG_ENABLE_NAGLE: - { - TC_LOG_DEBUG("network", "Client %s requested enabling nagle algorithm", GetRemoteIpAddress().to_string().c_str()); - sScriptMgr->OnPacketReceive(_worldSession, packet); + LogOpcodeText(opcode, sessionGuard); SetNoDelay(false); break; - } case CMSG_CONNECT_TO_FAILED: { + sessionGuard.lock(); + + LogOpcodeText(opcode, sessionGuard); WorldPackets::Auth::ConnectToFailed connectToFailed(std::move(packet)); connectToFailed.Read(); HandleConnectToFailed(connectToFailed); @@ -275,6 +293,10 @@ bool WorldSocket::ReadDataHandler() } default: { + sessionGuard.lock(); + + LogOpcodeText(opcode, sessionGuard); + if (!_worldSession) { TC_LOG_ERROR("network.opcode", "ProcessIncoming: Client not authed opcode = %u", uint32(opcode)); @@ -282,13 +304,6 @@ bool WorldSocket::ReadDataHandler() return false; } - // prevent invalid memory access/crash with custom opcodes - if (static_cast<uint32>(opcode) >= NUM_OPCODE_HANDLERS) - { - CloseSocket(); - return false; - } - OpcodeHandler const* handler = opcodeTable[opcode]; if (!handler) { @@ -338,6 +353,25 @@ bool WorldSocket::ReadDataHandler() return true; } +void WorldSocket::LogOpcodeText(OpcodeServer 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(static_cast<OpcodeServer>(packet.GetOpcode())).c_str()); + SendPacket(packet); +} + void WorldSocket::SendPacket(WorldPacket const& packet) { if (!IsOpen()) @@ -346,8 +380,6 @@ void WorldSocket::SendPacket(WorldPacket const& packet) if (sPacketLog->CanLogPacket()) sPacketLog->LogPacket(packet, SERVER_TO_CLIENT, GetRemoteIpAddress(), GetRemotePort(), GetConnectionType()); - TC_LOG_TRACE("network.opcode", "S->C: %s %s", (_worldSession ? _worldSession->GetPlayerInfo() : GetRemoteIpAddress().to_string()).c_str(), GetOpcodeNameForLogging(static_cast<OpcodeServer>(packet.GetOpcode())).c_str()); - uint32 packetSize = packet.size(); uint32 sizeOfHeader = SizeOfServerHeader[_authCrypt.IsInitialized()]; if (packetSize > 0x400) @@ -655,6 +687,7 @@ void WorldSocket::HandleAuthSession(WorldPackets::Auth::AuthSession& authSession // At this point, we can safely hook a successful login sScriptMgr->OnAccountLogin(id); + _authed = true; _worldSession = new WorldSession(id, battlenetAccountId, shared_from_this(), AccountTypes(security), expansion, mutetime, locale, recruiter, isRecruiter); _worldSession->LoadGlobalAccountData(); _worldSession->LoadTutorialsData(); @@ -714,8 +747,10 @@ void WorldSocket::HandleAuthContinuedSession(WorldPackets::Auth::AuthContinuedSe return; } + _authed = true; + WorldPackets::Auth::ResumeComms resumeComms; - SendPacket(*resumeComms.Write()); + SendPacketAndLogOpcode(*resumeComms.Write()); _worldSession->AddInstanceConnection(shared_from_this()); _worldSession->HandleContinuePlayerLogin(); @@ -754,10 +789,9 @@ void WorldSocket::HandleConnectToFailed(WorldPackets::Auth::ConnectToFailed& con //else //{ // transfer_aborted when/if we get map node redirection - // SendPacket(*WorldPackets::Auth::ResumeComms().Write()); + // SendPacketAndLogOpcode(*WorldPackets::Auth::ResumeComms().Write()); //} } - } void WorldSocket::SendAuthResponseError(uint8 code) @@ -766,7 +800,7 @@ void WorldSocket::SendAuthResponseError(uint8 code) response.SuccessInfo.HasValue = false; response.WaitInfo.HasValue = false; response.Result = code; - SendPacket(*response.Write()); + SendPacketAndLogOpcode(*response.Write()); } void WorldSocket::HandlePing(WorldPacket& recvPacket) @@ -798,6 +832,8 @@ void WorldSocket::HandlePing(WorldPacket& recvPacket) if (maxAllowed && _OverSpeedPings > maxAllowed) { + std::lock_guard<std::mutex> sessionGuard(_worldSessionLock); + 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)", @@ -812,20 +848,24 @@ void WorldSocket::HandlePing(WorldPacket& recvPacket) _OverSpeedPings = 0; } - if (_worldSession) { - _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()); + std::lock_guard<std::mutex> sessionGuard(_worldSessionLock); - CloseSocket(); - return; + if (_worldSession) + { + _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()); + + CloseSocket(); + return; + } } WorldPacket packet(SMSG_PONG, 4); packet << ping; - return SendPacket(packet); + return SendPacketAndLogOpcode(packet); } diff --git a/src/server/game/Server/WorldSocket.h b/src/server/game/Server/WorldSocket.h index a0304468682..f670e2bb9e5 100644 --- a/src/server/game/Server/WorldSocket.h +++ b/src/server/game/Server/WorldSocket.h @@ -86,11 +86,17 @@ public: ConnectionType GetConnectionType() const { return _type; } protected: + void OnClose() override; void ReadHandler() override; bool ReadHeaderHandler(); bool ReadDataHandler(); private: + /// writes network.opcode log + /// accessing WorldSession is not threadsafe, only do it when holding _worldSessionLock + void LogOpcodeText(OpcodeServer opcode, std::unique_lock<std::mutex> const& guard) const; + /// sends and logs network.opcode without accessing WorldSession + void SendPacketAndLogOpcode(WorldPacket const& packet); void WritePacketToBuffer(WorldPacket const& packet, MessageBuffer& buffer); uint32 CompressPacket(uint8* buffer, WorldPacket const& packet); @@ -114,7 +120,9 @@ private: std::chrono::steady_clock::time_point _LastPingTime; uint32 _OverSpeedPings; + std::mutex _worldSessionLock; WorldSession* _worldSession; + bool _authed; MessageBuffer _headerBuffer; MessageBuffer _packetBuffer; diff --git a/src/server/shared/Networking/Socket.h b/src/server/shared/Networking/Socket.h index 6482c0ed08a..0ac23a96224 100644 --- a/src/server/shared/Networking/Socket.h +++ b/src/server/shared/Networking/Socket.h @@ -62,7 +62,7 @@ public: return false; #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) return true; @@ -140,6 +140,8 @@ public: if (shutdownError) 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()); + + OnClose(); } /// Marks the socket for closing after write buffer becomes empty @@ -148,6 +150,8 @@ public: MessageBuffer& GetReadBuffer() { return _readBuffer; } protected: + virtual void OnClose() { } + virtual void ReadHandler() = 0; bool AsyncProcessQueue(std::unique_lock<std::mutex>&) |