mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-16 07:30:42 +01:00
Core/NetworkIO: Improve packet spam solution
Implement an improved packet Anti-DoS by counting how many times the same opcode has been sent in the last second and applying the policy specified in the configs if the amount of packets exceeds a reasonable amount. Credits to the original author who decided to share this with TrinityCore team.
This commit is contained in:
@@ -25,7 +25,7 @@
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
#include "Opcodes.h"
|
||||
#include "WorldSession.h"
|
||||
#include "WorldPacket.h"
|
||||
|
||||
class Player;
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "ScriptMgr.h"
|
||||
#include "ConditionMgr.h"
|
||||
#include "Player.h"
|
||||
#include "Opcodes.h"
|
||||
#include "WorldSession.h"
|
||||
|
||||
void AddItemsSetItem(Player* player, Item* item)
|
||||
{
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
#include "Unit.h"
|
||||
#include "Util.h"
|
||||
#include "Group.h"
|
||||
#include "Opcodes.h"
|
||||
#include "WorldSession.h"
|
||||
|
||||
#define PET_XP_FACTOR 0.05f
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#include "SocialMgr.h"
|
||||
|
||||
#include "DatabaseEnv.h"
|
||||
#include "Opcodes.h"
|
||||
#include "WorldSession.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "Player.h"
|
||||
#include "ObjectMgr.h"
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#include "World.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "Opcodes.h"
|
||||
#include "WorldSession.h"
|
||||
|
||||
const int32 ReputationMgr::PointsInRank[MAX_REPUTATION_RANK] = {36000, 3000, 3000, 3000, 6000, 12000, 21000, 1000};
|
||||
|
||||
|
||||
@@ -25,12 +25,6 @@
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
// Note: this include need for be sure have full definition of class WorldSession
|
||||
// if this class definition not complete then VS for x64 release use different size for
|
||||
// struct OpcodeHandler in this header and Opcode.cpp and get totally wrong data from
|
||||
// table opcodeTable in source when Opcode.h included but WorldSession.h not included
|
||||
#include "WorldSession.h"
|
||||
|
||||
/// List of Opcodes
|
||||
enum Opcodes
|
||||
{
|
||||
@@ -1366,8 +1360,15 @@ enum PacketProcessing
|
||||
PROCESS_THREADSAFE //packet is thread-safe - process it in Map::Update()
|
||||
};
|
||||
|
||||
class WorldSession;
|
||||
class WorldPacket;
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#pragma pack(1)
|
||||
#else
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
|
||||
struct OpcodeHandler
|
||||
{
|
||||
char const* name;
|
||||
@@ -1378,6 +1379,12 @@ struct OpcodeHandler
|
||||
|
||||
extern OpcodeHandler opcodeTable[NUM_MSG_TYPES];
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#pragma pack()
|
||||
#else
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
/// Lookup opcode name for human understandable logging
|
||||
inline const char* LookupOpcodeName(uint16 id)
|
||||
{
|
||||
|
||||
@@ -277,12 +277,13 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
|
||||
//! loop caused by re-enqueueing the same packets over and over again, we stop updating this session
|
||||
//! and continue updating others. The re-enqueued packets will be handled in the next Update call for this session.
|
||||
uint32 processedPackets = 0;
|
||||
time_t currentTime = time(NULL);
|
||||
|
||||
while (m_Socket && !m_Socket->IsClosed() &&
|
||||
!_recvQueue.empty() && _recvQueue.peek(true) != firstDelayedPacket &&
|
||||
_recvQueue.next(packet, updater))
|
||||
{
|
||||
if (!AntiDOS.EvaluateOpcode(*packet))
|
||||
if (!AntiDOS.EvaluateOpcode(*packet, currentTime))
|
||||
{
|
||||
KickPlayer();
|
||||
}
|
||||
@@ -1236,14 +1237,38 @@ void WorldSession::InvalidateRBACData()
|
||||
_RBACData = NULL;
|
||||
}
|
||||
|
||||
bool WorldSession::DosProtection::EvaluateOpcode(WorldPacket& p) const
|
||||
bool WorldSession::DosProtection::EvaluateOpcode(WorldPacket& p, time_t time) const
|
||||
{
|
||||
if (IsOpcodeAllowed(p.GetOpcode()))
|
||||
return true;
|
||||
PacketCounter& packetCounter = _PacketThrottlingMap[p.GetOpcode()];
|
||||
if (packetCounter.lastReceiveTime != time)
|
||||
{
|
||||
packetCounter.lastReceiveTime = time;
|
||||
packetCounter.amountCounter = 0;
|
||||
}
|
||||
|
||||
// Opcode not allowed, let the punishment begin
|
||||
TC_LOG_INFO("network", "AntiDOS: Account %u, IP: %s, sent unacceptable packet (opc: %u, size: %u)",
|
||||
Session->GetAccountId(), Session->GetRemoteAddress().c_str(), p.GetOpcode(), (uint32)p.size());
|
||||
uint32 maxPacketCounterAllowed = GetMaxPacketCounterAllowed(p.GetOpcode());
|
||||
|
||||
bool dosTriggered = false;
|
||||
// Check if player is flooding some packets
|
||||
if (++packetCounter.amountCounter > maxPacketCounterAllowed)
|
||||
{
|
||||
dosTriggered = true;
|
||||
TC_LOG_WARN("network", "AntiDOS: Account %u, IP: %s, flooding packet (opc: %u, size: %u)",
|
||||
Session->GetAccountId(), Session->GetRemoteAddress().c_str(), p.GetOpcode(), (uint32)p.size());
|
||||
}
|
||||
|
||||
// Then check if player is sending packets not allowed
|
||||
if (!IsOpcodeAllowed(p.GetOpcode()))
|
||||
{
|
||||
dosTriggered = true;
|
||||
// Opcode not allowed, let the punishment begin
|
||||
TC_LOG_WARN("network", "AntiDOS: Account %u, IP: %s, sent unacceptable packet (opc: %u, size: %u)",
|
||||
Session->GetAccountId(), Session->GetRemoteAddress().c_str(), p.GetOpcode(), (uint32)p.size());
|
||||
}
|
||||
|
||||
// Return true if everything is fine, otherwise apply the configured policy
|
||||
if (!dosTriggered)
|
||||
return true;
|
||||
|
||||
switch (_policy)
|
||||
{
|
||||
@@ -1272,3 +1297,194 @@ bool WorldSession::DosProtection::EvaluateOpcode(WorldPacket& p) const
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint32 WorldSession::DosProtection::GetMaxPacketCounterAllowed(uint16 opcode) const
|
||||
{
|
||||
uint32 maxPacketCounterAllowed;
|
||||
switch (opcode)
|
||||
{
|
||||
case CMSG_ITEM_QUERY_SINGLE:
|
||||
case CMSG_ITEM_NAME_QUERY:
|
||||
case CMSG_GUILD_QUERY:
|
||||
case CMSG_NAME_QUERY:
|
||||
case CMSG_PET_NAME_QUERY:
|
||||
case CMSG_GAMEOBJECT_QUERY:
|
||||
case CMSG_CREATURE_QUERY:
|
||||
case CMSG_NPC_TEXT_QUERY:
|
||||
case CMSG_ARENA_TEAM_QUERY:
|
||||
case CMSG_TAXINODE_STATUS_QUERY:
|
||||
case CMSG_TAXIQUERYAVAILABLENODES:
|
||||
case CMSG_QUESTGIVER_QUERY_QUEST:
|
||||
case CMSG_QUEST_QUERY:
|
||||
case CMSG_QUESTGIVER_STATUS_MULTIPLE_QUERY:
|
||||
case CMSG_QUERY_QUESTS_COMPLETED:
|
||||
case CMSG_QUEST_POI_QUERY:
|
||||
case CMSG_QUERY_TIME:
|
||||
case CMSG_PAGE_TEXT_QUERY:
|
||||
case CMSG_PETITION_QUERY:
|
||||
case CMSG_QUERY_INSPECT_ACHIEVEMENTS:
|
||||
case CMSG_AREA_SPIRIT_HEALER_QUERY:
|
||||
case CMSG_CORPSE_MAP_POSITION_QUERY:
|
||||
case CMSG_MOVE_TIME_SKIPPED:
|
||||
case CMSG_GUILD_BANK_QUERY_TAB:
|
||||
case MSG_GUILD_BANK_LOG_QUERY:
|
||||
case MSG_QUERY_GUILD_BANK_TEXT:
|
||||
case MSG_CORPSE_QUERY:
|
||||
case MSG_QUERY_NEXT_MAIL_TIME:
|
||||
case MSG_GUILD_EVENT_LOG_QUERY:
|
||||
case MSG_MOVE_SET_FACING:
|
||||
{
|
||||
maxPacketCounterAllowed = 200;
|
||||
break;
|
||||
}
|
||||
|
||||
case CMSG_MESSAGECHAT:
|
||||
case CMSG_WHO:
|
||||
case CMSG_GAMEOBJ_USE:
|
||||
case CMSG_GAMEOBJ_REPORT_USE:
|
||||
case CMSG_SPELLCLICK:
|
||||
case CMSG_PLAYER_LOGOUT:
|
||||
case CMSG_LOGOUT_REQUEST:
|
||||
case CMSG_LOGOUT_CANCEL:
|
||||
case CMSG_CHANGE_SEATS_ON_CONTROLLED_VEHICLE:
|
||||
case CMSG_REQUEST_VEHICLE_PREV_SEAT:
|
||||
case CMSG_REQUEST_VEHICLE_NEXT_SEAT:
|
||||
case CMSG_REQUEST_VEHICLE_SWITCH_SEAT:
|
||||
case CMSG_TOGGLE_PVP:
|
||||
case CMSG_CONTACT_LIST:
|
||||
case CMSG_ADD_FRIEND:
|
||||
case CMSG_DEL_FRIEND:
|
||||
case CMSG_SET_CONTACT_NOTES:
|
||||
case CMSG_RESET_INSTANCES:
|
||||
case CMSG_HEARTH_AND_RESURRECT:
|
||||
case CMSG_CHAR_CREATE:
|
||||
case CMSG_READY_FOR_ACCOUNT_DATA_TIMES:
|
||||
case CMSG_CHAR_ENUM:
|
||||
case CMSG_REALM_SPLIT:
|
||||
case CMSG_CHAR_DELETE:
|
||||
case CMSG_PLAYER_LOGIN:
|
||||
case CMSG_PET_ABANDON:
|
||||
case CMSG_PET_RENAME:
|
||||
case CMSG_CHAR_RENAME:
|
||||
case CMSG_CHAR_CUSTOMIZE:
|
||||
case CMSG_CHAR_RACE_CHANGE:
|
||||
case CMSG_CHAR_FACTION_CHANGE:
|
||||
case CMSG_GMTICKET_CREATE:
|
||||
case CMSG_GMTICKET_UPDATETEXT:
|
||||
case CMSG_GMTICKET_DELETETICKET:
|
||||
case CMSG_GMSURVEY_SUBMIT:
|
||||
case CMSG_GM_REPORT_LAG:
|
||||
case CMSG_BUG:
|
||||
case CMSG_GMRESPONSE_RESOLVE:
|
||||
case CMSG_ACTIVATETAXIEXPRESS:
|
||||
case CMSG_ACTIVATETAXI:
|
||||
case CMSG_SELF_RES:
|
||||
case CMSG_INITIATE_TRADE:
|
||||
case CMSG_BEGIN_TRADE:
|
||||
case CMSG_UNLEARN_SKILL:
|
||||
case CMSG_DISMISS_CONTROLLED_VEHICLE:
|
||||
case CMSG_REQUEST_VEHICLE_EXIT:
|
||||
case CMSG_LEARN_PREVIEW_TALENTS:
|
||||
case CMSG_LEARN_PREVIEW_TALENTS_PET:
|
||||
case CMSG_PLAYER_VEHICLE_ENTER:
|
||||
case CMSG_CONTROLLER_EJECT_PASSENGER:
|
||||
case CMSG_EQUIPMENT_SET_SAVE:
|
||||
case CMSG_DELETEEQUIPMENT_SET:
|
||||
case CMSG_REMOVE_GLYPH:
|
||||
case CMSG_ALTER_APPEARANCE:
|
||||
case CMSG_QUESTGIVER_ACCEPT_QUEST:
|
||||
case CMSG_QUESTGIVER_CHOOSE_REWARD:
|
||||
case CMSG_QUESTGIVER_REQUEST_REWARD:
|
||||
case CMSG_QUESTGIVER_CANCEL:
|
||||
case CMSG_QUESTLOG_REMOVE_QUEST:
|
||||
case CMSG_QUEST_CONFIRM_ACCEPT:
|
||||
case CMSG_QUESTGIVER_COMPLETE_QUEST:
|
||||
case CMSG_DISMISS_CRITTER:
|
||||
case CMSG_REPOP_REQUEST:
|
||||
case CMSG_PETITION_BUY:
|
||||
case CMSG_PETITION_SIGN:
|
||||
case CMSG_TURN_IN_PETITION:
|
||||
case CMSG_COMPLETE_CINEMATIC:
|
||||
case CMSG_ITEM_REFUND:
|
||||
case CMSG_SOCKET_GEMS:
|
||||
case CMSG_WRAP_ITEM:
|
||||
case CMSG_BUY_BANK_SLOT:
|
||||
case CMSG_GROUP_ACCEPT:
|
||||
case CMSG_GROUP_DECLINE:
|
||||
case CMSG_GROUP_UNINVITE_GUID:
|
||||
case CMSG_GROUP_UNINVITE:
|
||||
case CMSG_GROUP_SET_LEADER:
|
||||
case CMSG_GROUP_DISBAND:
|
||||
case CMSG_GROUP_RAID_CONVERT:
|
||||
case CMSG_GROUP_CHANGE_SUB_GROUP:
|
||||
case CMSG_GROUP_ASSISTANT_LEADER:
|
||||
case CMSG_REQUEST_PARTY_MEMBER_STATS:
|
||||
case CMSG_OPT_OUT_OF_LOOT:
|
||||
case CMSG_BATTLEMASTER_JOIN_ARENA:
|
||||
case CMSG_LEAVE_BATTLEFIELD:
|
||||
case CMSG_REPORT_PVP_AFK:
|
||||
case CMSG_DUEL_ACCEPTED:
|
||||
case CMSG_DUEL_CANCELLED:
|
||||
case CMSG_SETSHEATHED:
|
||||
case CMSG_CALENDAR_GET_CALENDAR:
|
||||
case CMSG_CALENDAR_ADD_EVENT:
|
||||
case CMSG_CALENDAR_UPDATE_EVENT:
|
||||
case CMSG_CALENDAR_REMOVE_EVENT:
|
||||
case CMSG_CALENDAR_COPY_EVENT:
|
||||
case CMSG_CALENDAR_EVENT_INVITE:
|
||||
case CMSG_CALENDAR_EVENT_SIGNUP:
|
||||
case CMSG_CALENDAR_EVENT_RSVP:
|
||||
case CMSG_CALENDAR_EVENT_REMOVE_INVITE:
|
||||
case CMSG_CALENDAR_EVENT_MODERATOR_STATUS:
|
||||
case CMSG_CALENDAR_COMPLAIN:
|
||||
case CMSG_ARENA_TEAM_INVITE:
|
||||
case CMSG_ARENA_TEAM_ACCEPT:
|
||||
case CMSG_ARENA_TEAM_DECLINE:
|
||||
case CMSG_ARENA_TEAM_LEAVE:
|
||||
case CMSG_ARENA_TEAM_DISBAND:
|
||||
case CMSG_ARENA_TEAM_REMOVE:
|
||||
case CMSG_ARENA_TEAM_LEADER:
|
||||
case CMSG_LOOT_METHOD:
|
||||
case CMSG_GUILD_INVITE:
|
||||
case CMSG_GUILD_ACCEPT:
|
||||
case CMSG_GUILD_DECLINE:
|
||||
case CMSG_GUILD_LEAVE:
|
||||
case CMSG_GUILD_DISBAND:
|
||||
case CMSG_GUILD_LEADER:
|
||||
case CMSG_GUILD_MOTD:
|
||||
case CMSG_GUILD_SET_PUBLIC_NOTE:
|
||||
case CMSG_GUILD_SET_OFFICER_NOTE:
|
||||
case CMSG_GUILD_RANK:
|
||||
case CMSG_GUILD_ADD_RANK:
|
||||
case CMSG_GUILD_DEL_RANK:
|
||||
case CMSG_GUILD_INFO_TEXT:
|
||||
case CMSG_GUILD_BANK_DEPOSIT_MONEY:
|
||||
case CMSG_GUILD_BANK_WITHDRAW_MONEY:
|
||||
case CMSG_GUILD_BANK_BUY_TAB:
|
||||
case CMSG_GUILD_BANK_UPDATE_TAB:
|
||||
case CMSG_SET_GUILD_BANK_TEXT:
|
||||
case MSG_SAVE_GUILD_EMBLEM:
|
||||
case MSG_PETITION_RENAME:
|
||||
case MSG_PETITION_DECLINE:
|
||||
case MSG_TALENT_WIPE_CONFIRM:
|
||||
case MSG_SET_DUNGEON_DIFFICULTY:
|
||||
case MSG_SET_RAID_DIFFICULTY:
|
||||
case MSG_RANDOM_ROLL:
|
||||
case MSG_RAID_TARGET_UPDATE:
|
||||
case MSG_PARTY_ASSIGNMENT:
|
||||
case MSG_RAID_READY_CHECK:
|
||||
{
|
||||
maxPacketCounterAllowed = 3;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
maxPacketCounterAllowed = 30;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return maxPacketCounterAllowed;
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "AddonMgr.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "World.h"
|
||||
#include "Opcodes.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "Cryptography/BigNumber.h"
|
||||
#include "AccountMgr.h"
|
||||
@@ -196,6 +197,12 @@ class CharacterCreateInfo
|
||||
uint8 CharCount;
|
||||
};
|
||||
|
||||
struct PacketCounter
|
||||
{
|
||||
time_t lastReceiveTime;
|
||||
uint32 amountCounter;
|
||||
};
|
||||
|
||||
/// Player session in the World
|
||||
class WorldSession
|
||||
{
|
||||
@@ -929,7 +936,7 @@ class WorldSession
|
||||
friend class World;
|
||||
public:
|
||||
DosProtection(WorldSession* s) : Session(s), _policy((Policy)sWorld->getIntConfig(CONFIG_PACKET_SPOOF_POLICY)) { }
|
||||
bool EvaluateOpcode(WorldPacket& p) const;
|
||||
bool EvaluateOpcode(WorldPacket& p, time_t time) const;
|
||||
void AllowOpcode(uint16 opcode, bool allow) { _isOpcodeAllowed[opcode] = allow; }
|
||||
protected:
|
||||
enum Policy
|
||||
@@ -948,12 +955,17 @@ class WorldSession
|
||||
return itr->second;
|
||||
}
|
||||
|
||||
uint32 GetMaxPacketCounterAllowed(uint16 opcode) const;
|
||||
|
||||
WorldSession* Session;
|
||||
|
||||
private:
|
||||
typedef std::unordered_map<uint16, bool> OpcodeStatusMap;
|
||||
OpcodeStatusMap _isOpcodeAllowed; // could be bool array, but wouldn't be practical for game versions with non-linear opcodes
|
||||
Policy _policy;
|
||||
typedef std::unordered_map<uint16, PacketCounter> PacketThrottlingMap;
|
||||
// mark this member as "mutable" so it can be modified even in const functions
|
||||
mutable PacketThrottlingMap _PacketThrottlingMap;
|
||||
|
||||
DosProtection(DosProtection const& right) = delete;
|
||||
DosProtection& operator=(DosProtection const& right) = delete;
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#include "ObjectMgr.h"
|
||||
#include "Util.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "Opcodes.h"
|
||||
#include "WorldSession.h"
|
||||
|
||||
/// Create the Weather object
|
||||
Weather::Weather(uint32 zone, WeatherData const* weatherChances)
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "AutoPtr.h"
|
||||
#include "Player.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "Opcodes.h"
|
||||
#include "WorldSession.h"
|
||||
|
||||
namespace WeatherMgr
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user