diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 2d9fd18bd..90efea5ff 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -1297,6 +1297,11 @@ void WorldSession::InitWarden(SessionKey const& k, std::string const& os) } } +Warden* WorldSession::GetWarden() +{ + return &(*_warden); +} + bool WorldSession::DosProtection::EvaluateOpcode(WorldPacket& p, time_t time) const { uint32 maxPacketCounterAllowed = GetMaxPacketCounterAllowed(p.GetOpcode()); diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index d4a0478b1..64ce0703f 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -374,6 +374,7 @@ public: uint32 GetTotalTime() const { return m_total_time; } void InitWarden(SessionKey const&, std::string const& os); + Warden* GetWarden(); /// Session in auth.queue currently void SetInQueue(bool state) { m_inQueue = state; } diff --git a/src/server/game/Warden/Warden.cpp b/src/server/game/Warden/Warden.cpp index 8b53a43dd..3cc6019cb 100644 --- a/src/server/game/Warden/Warden.cpp +++ b/src/server/game/Warden/Warden.cpp @@ -30,7 +30,7 @@ #include "WorldSession.h" Warden::Warden() : _session(nullptr), _checkTimer(10000/*10 sec*/), _clientResponseTimer(0), - _dataSent(false), _module(nullptr), _initialized(false) + _dataSent(false), _module(nullptr), _initialized(false), _interrupted(false), _checkInProgress(false) { memset(_inputKey, 0, sizeof(_inputKey)); memset(_outputKey, 0, sizeof(_outputKey)); @@ -305,6 +305,11 @@ bool Warden::ProcessLuaCheckResponse(std::string const& msg) return true; } +WardenPayloadMgr* Warden::GetPayloadMgr() +{ + return &_payloadMgr; +} + void WorldSession::HandleWardenDataOpcode(WorldPacket& recvData) { if (!_warden || recvData.empty()) diff --git a/src/server/game/Warden/Warden.h b/src/server/game/Warden/Warden.h index db0222c2b..0c2d04a00 100644 --- a/src/server/game/Warden/Warden.h +++ b/src/server/game/Warden/Warden.h @@ -22,6 +22,7 @@ #include "AuthDefines.h" #include "ByteBuffer.h" #include "WardenCheckMgr.h" +#include "WardenPayloadMgr.h" #include enum WardenOpcodes @@ -113,6 +114,8 @@ public: virtual void InitializeModule() = 0; virtual void RequestHash() = 0; virtual void HandleHashResult(ByteBuffer &buff) = 0; + virtual bool IsCheckInProgress() = 0; + virtual void ForceChecks() = 0; virtual void RequestChecks() = 0; virtual void HandleData(ByteBuffer &buff) = 0; bool ProcessLuaCheckResponse(std::string const& msg); @@ -129,8 +132,11 @@ public: // If no check is passed, the default action from config is executed void ApplyPenalty(uint16 checkId, std::string const& reason); + WardenPayloadMgr* GetPayloadMgr(); + private: WorldSession* _session; + WardenPayloadMgr _payloadMgr; uint8 _inputKey[16]; uint8 _outputKey[16]; uint8 _seed[16]; @@ -141,6 +147,9 @@ private: bool _dataSent; ClientWardenModule* _module; bool _initialized; + bool _interrupted; + bool _checkInProgress; + uint32 _interruptCounter = 0; }; #endif diff --git a/src/server/game/Warden/WardenPayloadMgr.cpp b/src/server/game/Warden/WardenPayloadMgr.cpp new file mode 100644 index 000000000..dcb363dba --- /dev/null +++ b/src/server/game/Warden/WardenPayloadMgr.cpp @@ -0,0 +1,150 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by the + * Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "WardenPayloadMgr.h" +#include "StringFormat.h" +#include "Errors.h" +#include "Log.h" + +WardenPayloadMgr::WardenPayloadMgr() { } + +uint16 WardenPayloadMgr::GetFreePayloadId() +{ + uint16 payloadId = WardenPayloadOffsetMin; + + while (CachedChecks.find(payloadId) != CachedChecks.end()) + { + payloadId++; + + if (payloadId > WardenPayloadMgr::WardenPayloadOffsetMax) + { + LOG_ERROR("warden", "Max warden payload id of '{}' passed!", WardenPayloadMgr::WardenPayloadOffsetMax); + return 0; + } + } + + return payloadId; +} + +uint16 WardenPayloadMgr::RegisterPayload(const std::string& payload) +{ + uint16 payloadId = GetFreePayloadId(); + + if (!payloadId || !RegisterPayload(payload, payloadId, false)) + { + LOG_ERROR("warden", "Failed to register payload."); + return 0; + } + + return payloadId; +} + +bool WardenPayloadMgr::RegisterPayload(std::string const& payload, uint16 payloadId, bool replace) +{ + //Payload id should be over or equal to the offset to prevent conflicts. + if (payloadId < WardenPayloadMgr::WardenPayloadOffsetMin) + { + LOG_ERROR("warden", "Tried to register payloadId lower than '{}'.", WardenPayloadMgr::WardenPayloadOffsetMin); + return false; + } + + auto it = CachedChecks.find(payloadId); + if (it != CachedChecks.end() && !replace) + { + LOG_ERROR("warden", "Payload Id '{}' already exists in CachedChecks.", payloadId); + return false; + } + + WardenCheck wCheck; + wCheck.Type = WardenPayloadMgr::WardenPayloadCheckType; + wCheck.Str = payload; + wCheck.CheckId = payloadId; + + std::string idStr = Acore::StringFormat("%04u", payloadId); + ASSERT(idStr.size() == 4); + std::copy(idStr.begin(), idStr.end(), wCheck.IdStr.begin()); + + if (replace) + { + CachedChecks.erase(payloadId); + } + + CachedChecks.emplace(payloadId, wCheck); + + return true; +} + +bool WardenPayloadMgr::UnregisterPayload(uint16 payloadId) +{ + return CachedChecks.erase(payloadId); +} + +WardenCheck* WardenPayloadMgr::GetPayloadById(uint16 payloadId) +{ + auto it = CachedChecks.find(payloadId); + + if (it != CachedChecks.end()) + { + return &it->second; + } + + return nullptr; +} + +void WardenPayloadMgr::QueuePayload(uint16 payloadId, bool pushToFront) +{ + auto it = CachedChecks.find(payloadId); + + //Do not queue a payload if there is no payload matching the payloadId. + if (it == CachedChecks.end()) + { + LOG_ERROR("warden", "Failed to queue payload id '{}' as it does not exist in CachedChecks.", payloadId); + return; + } + + if (pushToFront) + { + QueuedPayloads.push_front(payloadId); + } + else + { + QueuedPayloads.push_back(payloadId); + } +} + +bool WardenPayloadMgr::DequeuePayload(uint16 payloadId) +{ + size_t const queueSize = QueuedPayloads.size(); + QueuedPayloads.remove(payloadId); + + return queueSize != QueuedPayloads.size(); +} + +void WardenPayloadMgr::ClearQueuedPayloads() +{ + QueuedPayloads.clear(); +} + +uint32 WardenPayloadMgr::GetPayloadCountInQueue() +{ + return QueuedPayloads.size(); +} + +std::list* WardenPayloadMgr::GetPayloadsInQueue() +{ + return &QueuedPayloads; +} diff --git a/src/server/game/Warden/WardenPayloadMgr.h b/src/server/game/Warden/WardenPayloadMgr.h new file mode 100644 index 000000000..aa7159519 --- /dev/null +++ b/src/server/game/Warden/WardenPayloadMgr.h @@ -0,0 +1,138 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by the + * Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef _WARDEN_PAYLOAD_MGR_H +#define _WARDEN_PAYLOAD_MGR_H + +#include "WardenCheckMgr.h" +#include + + /** + * @class WardenPayloadMgr + * @brief The WardenPayloadMgr is responsible for maintaining custom payloads used by modules. + * @details This allows users to send custom lua payloads up to a size of 512 bytes to the game client. + * Some of the things you can achieve with this is: + * - Interaction with the client interface (add custom frames) + * - Access to client CVars + * - Access to protected lua functions. + * + * Opening up many possiblilties for a patch-less custom server. + */ +class WardenPayloadMgr +{ +public: + WardenPayloadMgr(); + + /** + * @brief Finds a free payload id in WardenPayloadMgr::CachedChecks. + * @return uint16 The free payload id. Returns 0 if there is no free id. + */ + uint16 GetFreePayloadId(); + + /** + * @brief Register a payload into cache and returns its payload id. + * @param payload The payload to be stored in WardenPayloadMgr::CachedChecks. + * @return uint16 The payload id for use with WardenPayloadMgr::QueuePayload. Returns 0 if it failed to register. + * @note + * - Payloads are truncated to 512 bytes on the client, you may have to register your payloads in chunks if they are larger than this. + */ + uint16 RegisterPayload(const std::string& payload); + + /** + * @brief Register a payload into cache with a custom id and returns the result. + * @param payload The payload to be stored in WardenPayloadMgr::CachedChecks. + * @param payloadId The payload id to be stored as the key in WardenPayloadMgr::CachedChecks. + * @param replace Whether the key should replace an existing entry value. + * @return bool The payload insertion result. If exists it will return false, otherwise true. + * @note + * - Payloads are truncated to 512 bytes on the client, you may have to register your payloads in chunks if they are larger than this. + * - It's a good idea to keep the value for payloadId between 9000-9999 for self defined payloads as they're the least likely occupied ids. + */ + bool RegisterPayload(std::string const& payload, uint16 payloadId, bool replace = false); + + /** + * @brief Unregister a payload from cache and return if successful. + * @param payloadId The payload to removed from WardenPayloadMgr::CachedChecks. + * @return bool If the payloadId was present. + */ + bool UnregisterPayload(uint16 payloadId); + + /** + * @brief Get a payload by id from the WardenPayloadMgr::CachedChecks. + * @param payloadId The payload to fetched from WardenPayloadMgr::CachedChecks. + * @return WardenCheck* A pointer to the WardenCheck payload. + */ + WardenCheck* GetPayloadById(uint16 payloadId); + + /** + * @brief Queue the payload into the normal warden checks. + * @param payloadId The payloadId to be queued. + * @param pushToFront If payload should be pushed to the front queue. + */ + void QueuePayload(uint16 payloadId, bool pushToFront = false); + + /** + * @brief Dequeue the payload from the WardenPayloadMgr::QueuedPayloads queue. + * @param payloadId The payloadId to be dequeued. + * @return bool If the payload was removed. + */ + bool DequeuePayload(uint16 payloadId); + + /** + * @brief Clear the payloads from the WardenPayloadMgr::QueuedPayloads queue. + */ + void ClearQueuedPayloads(); + + /** + * @brief Get the amount of payloads waiting in WardenPayloadMgr::QueuedPayloads. + * @return The amount of payloads in queue. + */ + uint32 GetPayloadCountInQueue(); + + /** + * @brief Get payloads waiting in WardenPayloadMgr::QueuedPayloads. + * @return The payloads in queue. + */ + std::list* GetPayloadsInQueue(); + + /** + * @brief The minimum id available for custom payloads. + */ + static uint16 constexpr WardenPayloadOffsetMin = 5000; + + /** + * @brief The maximum id available for custom payloads. + */ + static uint16 constexpr WardenPayloadOffsetMax = 9999; + + /** + * @brief The checktype used for warden payloads. + */ + static uint32 constexpr WardenPayloadCheckType = 139; + + /** + * @brief The list of currently queued payload ids to be sent through Warden. + */ + std::list QueuedPayloads; + + /** + * @brief The cached payloads that are accessed by payload id. + */ + std::map CachedChecks; +}; + +#endif // _WARDEN_PAYLOAD_MGR_H diff --git a/src/server/game/Warden/WardenWin.cpp b/src/server/game/Warden/WardenWin.cpp index 3aea6b795..36175755d 100644 --- a/src/server/game/Warden/WardenWin.cpp +++ b/src/server/game/Warden/WardenWin.cpp @@ -61,7 +61,18 @@ static uint16 GetCheckPacketSize(WardenCheck const* check) return 0; } - uint16 size = 1 + GetCheckPacketBaseSize(check->Type); // 1 byte check type + uint16 size = 1; + + if (check->CheckId >= WardenPayloadMgr::WardenPayloadOffsetMin && check->Type == LUA_EVAL_CHECK) + { + // Custom payload has no prefix, midfix, postfix. + size = size + (4 + 1); + } + else + { + size = size + GetCheckPacketBaseSize(check->Type); // 1 byte check type + } + if (!check->Str.empty()) { size += (static_cast(check->Str.length()) + 1); // 1 byte string length @@ -239,10 +250,35 @@ void WardenWin::HandleHashResult(ByteBuffer& buff) _initialized = true; } +/** +* @brief Gets the warden check state. +* @return The warden check state. +*/ +bool WardenWin::IsCheckInProgress() +{ + return _checkInProgress; +} + +/** +* @brief Force call RequestChecks() so they are sent immediately, this interrupts warden and breaks result. +*/ +void WardenWin::ForceChecks() +{ + if (_dataSent) + { + _interrupted = true; + _interruptCounter++; + } + + RequestChecks(); +} + void WardenWin::RequestChecks() { LOG_DEBUG("warden", "Request data"); + _checkInProgress = true; + // If all checks were done, fill the todo list again for (uint8 i = 0; i < MAX_WARDEN_CHECK_TYPES; ++i) { @@ -253,6 +289,30 @@ void WardenWin::RequestChecks() _serverTicks = GameTime::GetGameTimeMS().count(); _CurrentChecks.clear(); + // Erase any nullptrs. + Acore::Containers::EraseIf(_PendingChecks, + [this](uint16 id) + { + WardenCheck const* check = sWardenCheckMgr->GetWardenDataById(id); + + // Custom payload should be loaded in if equal to over offset. + if (!check && id >= WardenPayloadMgr::WardenPayloadOffsetMin) + { + if (_payloadMgr.CachedChecks.find(id) != _payloadMgr.CachedChecks.end()) + { + check = &_payloadMgr.CachedChecks.at(id); + } + } + + if (!check) + { + return true; + } + + return false; + } + ); + // No pending checks if (_PendingChecks.empty()) { @@ -266,6 +326,19 @@ void WardenWin::RequestChecks() break; } + // Load in any custom payloads if available. + if (checkType == WARDEN_CHECK_LUA_TYPE && !_payloadMgr.QueuedPayloads.empty()) + { + uint16 payloadId = _payloadMgr.QueuedPayloads.front(); + + LOG_DEBUG("warden", "Adding custom warden payload '{}' to CurrentChecks.", payloadId); + + _payloadMgr.QueuedPayloads.pop_front(); + _CurrentChecks.push_front(payloadId); + + continue; + } + // Get check id from the end and remove it from todo uint16 const id = _ChecksTodo[checkType].back(); _ChecksTodo[checkType].pop_back(); @@ -288,6 +361,13 @@ void WardenWin::RequestChecks() for (uint16 const checkId : _PendingChecks) { WardenCheck const* check = sWardenCheckMgr->GetWardenDataById(checkId); + + // Custom payload should be loaded in if equal to over offset. + if (!check && checkId >= WardenPayloadMgr::WardenPayloadOffsetMin) + { + check = &_payloadMgr.CachedChecks.at(checkId); + } + if (!hasLuaChecks && check->Type == LUA_EVAL_CHECK) { hasLuaChecks = true; @@ -324,7 +404,21 @@ void WardenWin::RequestChecks() Acore::Containers::EraseIf(_CurrentChecks, [this, &expectedSize](uint16 id) { - uint16 const thisSize = GetCheckPacketSize(sWardenCheckMgr->GetWardenDataById(id)); + WardenCheck const* check = sWardenCheckMgr->GetWardenDataById(id); + + // Custom payload should be loaded in if equal to over offset. + if (!check && id >= WardenPayloadMgr::WardenPayloadOffsetMin) + { + check = &_payloadMgr.CachedChecks.at(id); + } + + // Remove nullptr if it snuck in from earlier check. + if (!check) + { + return true; + } + + uint16 const thisSize = GetCheckPacketSize(check); if ((expectedSize + thisSize) > 500) // warden packets are truncated to 512 bytes clientside { _PendingChecks.push_back(id); @@ -341,6 +435,18 @@ void WardenWin::RequestChecks() for (uint16 const checkId : _CurrentChecks) { WardenCheck const* check = sWardenCheckMgr->GetWardenDataById(checkId); + + // Custom payloads do not have prefix, midfix, postfix. + if (!check && checkId >= WardenPayloadMgr::WardenPayloadOffsetMin) + { + check = &_payloadMgr.CachedChecks.at(checkId); + + buff << uint8(check->Str.size()); + buff.append(check->Str.data(), check->Str.size()); + + continue; + } + switch (check->Type) { case LUA_EVAL_CHECK: @@ -374,6 +480,13 @@ void WardenWin::RequestChecks() for (uint16 const checkId : _CurrentChecks) { WardenCheck const* check = sWardenCheckMgr->GetWardenDataById(checkId); + + // Custom payload should be loaded in if equal to over offset. + if (!check && checkId >= WardenPayloadMgr::WardenPayloadOffsetMin) + { + check = &_payloadMgr.CachedChecks.at(checkId); + } + buff << uint8(check->Type ^ xorByte); switch (check->Type) { @@ -448,63 +561,72 @@ void WardenWin::RequestChecks() void WardenWin::HandleData(ByteBuffer& buff) { - LOG_DEBUG("warden", "Handle data"); - - _dataSent = false; - _clientResponseTimer = 0; - - uint16 Length; - buff >> Length; - uint32 Checksum; - buff >> Checksum; - - if (Length != (buff.size() - buff.rpos())) + if (!_interrupted) { - buff.rfinish(); - ApplyPenalty(0, "Failed size checks in HandleData"); - return; - } + LOG_DEBUG("warden", "Handle data"); - if (!IsValidCheckSum(Checksum, buff.contents() + buff.rpos(), Length)) - { - buff.rpos(buff.wpos()); - LOG_DEBUG("warden", "CHECKSUM FAIL"); - ApplyPenalty(0, "Failed checksum in HandleData"); - return; - } + _dataSent = false; + _clientResponseTimer = 0; - // TIMING_CHECK - { - uint8 result; - buff >> result; - /// @todo: test it. - if (result == 0x00) + uint16 Length; + buff >> Length; + uint32 Checksum; + buff >> Checksum; + + if (Length != (buff.size() - buff.rpos())) { - LOG_DEBUG("warden", "TIMING CHECK FAIL result 0x00"); - // ApplyPenalty(0, "TIMING CHECK FAIL result"); Commented out because of too many false postives. Mostly caused by client stutter. + buff.rfinish(); + ApplyPenalty(0, "Failed size checks in HandleData"); return; } - uint32 newClientTicks; - buff >> newClientTicks; - - uint32 ticksNow = GameTime::GetGameTimeMS().count(); - uint32 ourTicks = newClientTicks + (ticksNow - _serverTicks); - - LOG_DEBUG("warden", "ServerTicks {}", ticksNow); // Now - LOG_DEBUG("warden", "RequestTicks {}", _serverTicks); // At request - LOG_DEBUG("warden", "Ticks {}", newClientTicks); // At response - LOG_DEBUG("warden", "Ticks diff {}", ourTicks - newClientTicks); - } - - uint16 checkFailed = 0; - - for (uint16 const checkId : _CurrentChecks) - { - WardenCheck const* rd = sWardenCheckMgr->GetWardenDataById(checkId); - uint8 const type = rd->Type; - switch (type) + if (!IsValidCheckSum(Checksum, buff.contents() + buff.rpos(), Length)) { + buff.rpos(buff.wpos()); + LOG_DEBUG("warden", "CHECKSUM FAIL"); + ApplyPenalty(0, "Failed checksum in HandleData"); + return; + } + + // TIMING_CHECK + { + uint8 result; + buff >> result; + /// @todo: test it. + if (result == 0x00) + { + LOG_DEBUG("warden", "TIMING CHECK FAIL result 0x00"); + // ApplyPenalty(0, "TIMING CHECK FAIL result"); Commented out because of too many false postives. Mostly caused by client stutter. + return; + } + + uint32 newClientTicks; + buff >> newClientTicks; + + uint32 ticksNow = GameTime::GetGameTimeMS().count(); + uint32 ourTicks = newClientTicks + (ticksNow - _serverTicks); + + LOG_DEBUG("warden", "ServerTicks {}", ticksNow); // Now + LOG_DEBUG("warden", "RequestTicks {}", _serverTicks); // At request + LOG_DEBUG("warden", "Ticks {}", newClientTicks); // At response + LOG_DEBUG("warden", "Ticks diff {}", ourTicks - newClientTicks); + } + + uint16 checkFailed = 0; + + for (uint16 const checkId : _CurrentChecks) + { + WardenCheck const* rd = sWardenCheckMgr->GetWardenDataById(checkId); + + // Custom payload should be loaded in if equal to over offset. + if (!rd && checkId >= WardenPayloadMgr::WardenPayloadOffsetMin) + { + rd = &_payloadMgr.CachedChecks.at(checkId); + } + + uint8 const type = rd->Type; + switch (type) + { case MEM_CHECK: { uint8 Mem_Result; @@ -536,78 +658,106 @@ void WardenWin::HandleData(ByteBuffer& buff) case PAGE_CHECK_B: case DRIVER_CHECK: case MODULE_CHECK: + { + uint8 const byte = 0xE9; + if (memcmp(buff.contents() + buff.rpos(), &byte, sizeof(uint8)) != 0) { - const uint8 byte = 0xE9; - if (memcmp(buff.contents() + buff.rpos(), &byte, sizeof(uint8)) != 0) + if (type == PAGE_CHECK_A || type == PAGE_CHECK_B) { - if (type == PAGE_CHECK_A || type == PAGE_CHECK_B) - LOG_DEBUG("warden", "RESULT PAGE_CHECK fail, CheckId {} account Id {}", checkId, _session->GetAccountId()); - - if (type == MODULE_CHECK) - LOG_DEBUG("warden", "RESULT MODULE_CHECK fail, CheckId {} account Id {}", checkId, _session->GetAccountId()); - - if (type == DRIVER_CHECK) - LOG_DEBUG("warden", "RESULT DRIVER_CHECK fail, CheckId {} account Id {}", checkId, _session->GetAccountId()); - checkFailed = checkId; - buff.rpos(buff.rpos() + 1); - continue; + LOG_DEBUG("warden", "RESULT PAGE_CHECK fail, CheckId {} account Id {}", checkId, _session->GetAccountId()); } - buff.rpos(buff.rpos() + 1); + if (type == MODULE_CHECK) + { + LOG_DEBUG("warden", "RESULT MODULE_CHECK fail, CheckId {} account Id {}", checkId, _session->GetAccountId()); + } - if (type == PAGE_CHECK_A || type == PAGE_CHECK_B) - LOG_DEBUG("warden", "RESULT PAGE_CHECK passed CheckId {} account Id {}", checkId, _session->GetAccountId()); - else if (type == MODULE_CHECK) - LOG_DEBUG("warden", "RESULT MODULE_CHECK passed CheckId {} account Id {}", checkId, _session->GetAccountId()); - else if (type == DRIVER_CHECK) - LOG_DEBUG("warden", "RESULT DRIVER_CHECK passed CheckId {} account Id {}", checkId, _session->GetAccountId()); + if (type == DRIVER_CHECK) + { + LOG_DEBUG("warden", "RESULT DRIVER_CHECK fail, CheckId {} account Id {}", checkId, _session->GetAccountId()); + } + + checkFailed = checkId; + buff.rpos(buff.rpos() + 1); + continue; + } + + buff.rpos(buff.rpos() + 1); + + if (type == PAGE_CHECK_A || type == PAGE_CHECK_B) + { + LOG_DEBUG("warden", "RESULT PAGE_CHECK passed CheckId {} account Id {}", checkId, _session->GetAccountId()); + } + else if (type == MODULE_CHECK) + { + LOG_DEBUG("warden", "RESULT MODULE_CHECK passed CheckId {} account Id {}", checkId, _session->GetAccountId()); + } + else if (type == DRIVER_CHECK) + { + LOG_DEBUG("warden", "RESULT DRIVER_CHECK passed CheckId {} account Id {}", checkId, _session->GetAccountId()); + } break; } case LUA_EVAL_CHECK: { uint8 const result = buff.read(); + if (result == 0) { buff.read_skip(buff.read()); // discard attached string } LOG_DEBUG("warden", "LUA_EVAL_CHECK CheckId {} account Id {} got in-warden dummy response", checkId, _session->GetAccountId()/* , result */); - break; - } + break; + } case MPQ_CHECK: + { + uint8 Mpq_Result; + buff >> Mpq_Result; + + if (Mpq_Result != 0) { - uint8 Mpq_Result; - buff >> Mpq_Result; - - if (Mpq_Result != 0) - { - LOG_DEBUG("warden", "RESULT MPQ_CHECK not 0x00 account id {}", _session->GetAccountId()); - checkFailed = checkId; - continue; - } - - WardenCheckResult const* rs = sWardenCheckMgr->GetWardenResultById(checkId); - if (memcmp(buff.contents() + buff.rpos(), rs->Result.ToByteArray<20>(false).data(), Acore::Crypto::Constants::SHA1_DIGEST_LENGTH_BYTES) != 0) // SHA1 - { - LOG_DEBUG("warden", "RESULT MPQ_CHECK fail, CheckId {} account Id {}", checkId, _session->GetAccountId()); - checkFailed = checkId; - buff.rpos(buff.rpos() + Acore::Crypto::Constants::SHA1_DIGEST_LENGTH_BYTES); // 20 bytes SHA1 - continue; - } - - buff.rpos(buff.rpos() + Acore::Crypto::Constants::SHA1_DIGEST_LENGTH_BYTES); // 20 bytes SHA1 - LOG_DEBUG("warden", "RESULT MPQ_CHECK passed, CheckId {} account Id {}", checkId, _session->GetAccountId()); - break; + LOG_DEBUG("warden", "RESULT MPQ_CHECK not 0x00 account id {}", _session->GetAccountId()); + checkFailed = checkId; + continue; } + + WardenCheckResult const* rs = sWardenCheckMgr->GetWardenResultById(checkId); + if (memcmp(buff.contents() + buff.rpos(), rs->Result.ToByteArray<20>(false).data(), Acore::Crypto::Constants::SHA1_DIGEST_LENGTH_BYTES) != 0) // SHA1 + { + LOG_DEBUG("warden", "RESULT MPQ_CHECK fail, CheckId {} account Id {}", checkId, _session->GetAccountId()); + checkFailed = checkId; + buff.rpos(buff.rpos() + Acore::Crypto::Constants::SHA1_DIGEST_LENGTH_BYTES); // 20 bytes SHA1 + continue; + } + + buff.rpos(buff.rpos() + Acore::Crypto::Constants::SHA1_DIGEST_LENGTH_BYTES); // 20 bytes SHA1 + LOG_DEBUG("warden", "RESULT MPQ_CHECK passed, CheckId {} account Id {}", checkId, _session->GetAccountId()); + break; + } + } + } + + if (checkFailed > 0) + { + ApplyPenalty(checkFailed, ""); } } - - if (checkFailed > 0) + else { - ApplyPenalty(checkFailed, ""); + LOG_DEBUG("warden", "Warden was interrupted by ForceChecks, ignoring results."); + + _interruptCounter--; + + if (_interruptCounter == 0) + { + _interrupted = false; + } } // Set hold off timer, minimum timer should at least be 1 second uint32 const holdOff = sWorld->getIntConfig(CONFIG_WARDEN_CLIENT_CHECK_HOLDOFF); _checkTimer = (holdOff < 1 ? 1 : holdOff) * IN_MILLISECONDS; + + _checkInProgress = false; } diff --git a/src/server/game/Warden/WardenWin.h b/src/server/game/Warden/WardenWin.h index a079fa56b..fa6b72b61 100644 --- a/src/server/game/Warden/WardenWin.h +++ b/src/server/game/Warden/WardenWin.h @@ -81,6 +81,8 @@ public: void RequestHash() override; void HandleHashResult(ByteBuffer& buff) override; void RequestChecks() override; + bool IsCheckInProgress() override; + void ForceChecks() override; void HandleData(ByteBuffer& buff) override; private: