diff --git a/src/server/game/Chat/Channels/Channel.h b/src/server/game/Chat/Channels/Channel.h index f25997c7c..e2eaa4c73 100644 --- a/src/server/game/Chat/Channels/Channel.h +++ b/src/server/game/Chat/Channels/Channel.h @@ -13,7 +13,7 @@ #include "Common.h" -#include "Opcodes.h" +#include "WorldSession.h" #include "WorldPacket.h" class Player; diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp index bbf4d76eb..c8b2d754f 100644 --- a/src/server/game/Entities/Item/Item.cpp +++ b/src/server/game/Entities/Item/Item.cpp @@ -15,7 +15,7 @@ #include "ScriptMgr.h" #include "ConditionMgr.h" #include "Player.h" -#include "Opcodes.h" +#include "WorldSession.h" void AddItemsSetItem(Player* player, Item* item) { diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index 679b8d933..e4578cc08 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -18,7 +18,7 @@ #include "Unit.h" #include "Util.h" #include "Group.h" -#include "Opcodes.h" +#include "WorldSession.h" #include "Battleground.h" #include "InstanceScript.h" #include "ArenaSpectator.h" diff --git a/src/server/game/Entities/Player/SocialMgr.cpp b/src/server/game/Entities/Player/SocialMgr.cpp index f0db9ec8c..fdf3dfc41 100644 --- a/src/server/game/Entities/Player/SocialMgr.cpp +++ b/src/server/game/Entities/Player/SocialMgr.cpp @@ -7,7 +7,7 @@ #include "SocialMgr.h" #include "DatabaseEnv.h" -#include "Opcodes.h" +#include "WorldSession.h" #include "WorldPacket.h" #include "Player.h" #include "ObjectMgr.h" diff --git a/src/server/game/Reputation/ReputationMgr.cpp b/src/server/game/Reputation/ReputationMgr.cpp index 2c68864a8..88e384e94 100644 --- a/src/server/game/Reputation/ReputationMgr.cpp +++ b/src/server/game/Reputation/ReputationMgr.cpp @@ -12,7 +12,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}; diff --git a/src/server/game/Server/Protocol/Opcodes.h b/src/server/game/Server/Protocol/Opcodes.h index 07b283c50..078d5d8a4 100644 --- a/src/server/game/Server/Protocol/Opcodes.h +++ b/src/server/game/Server/Protocol/Opcodes.h @@ -13,12 +13,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 { @@ -1353,8 +1347,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; @@ -1365,6 +1366,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) { diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index b21ec80a3..4ce058d09 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -41,7 +41,7 @@ namespace { -std::string const DefaultPlayerName = ""; + std::string const DefaultPlayerName = ""; } // namespace @@ -86,17 +86,18 @@ bool WorldSessionFilter::Process(WorldPacket* packet) } /// WorldSession constructor -WorldSession::WorldSession(uint32 id, WorldSocket* sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter, bool skipQueue, uint32 TotalTime): +WorldSession::WorldSession(uint32 id, WorldSocket* sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter, bool skipQueue, uint32 TotalTime) : m_muteTime(mute_time), m_timeOutTime(0), _lastAuctionListItemsMSTime(0), _lastAuctionListOwnerItemsMSTime(0), + AntiDOS(this), m_GUIDLow(0), _player(NULL), m_Socket(sock), _security(sec), _skipQueue(skipQueue), - _accountId(id), + _accountId(id), m_expansion(expansion), m_total_time(TotalTime), _logoutTime(0), @@ -109,7 +110,7 @@ WorldSession::WorldSession(uint32 id, WorldSocket* sock, AccountTypes sec, uint8 m_latency(0), m_clientTimeDelay(0), m_TutorialsChanged(false), - recruiterId(recruiter), + recruiterId(recruiter), isRecruiter(isARecruiter), m_currentBankerGUID(0), timeWhoCommandAllowed(0) @@ -139,7 +140,7 @@ WorldSession::~WorldSession() ///- unload player if not unloaded if (_player) - LogoutPlayer (true); + LogoutPlayer(true); /// - If have unclosed socket, close it if (m_Socket) @@ -174,8 +175,8 @@ std::string WorldSession::GetPlayerInfo() const std::ostringstream ss; ss << "[Player: " << GetPlayerName() - << " (Guid: " << (_player != NULL ? _player->GetGUID() : 0) - << ", Account: " << GetAccountId() << ")]"; + << " (Guid: " << (_player != NULL ? _player->GetGUID() : 0) + << ", Account: " << GetAccountId() << ")]"; return ss.str(); } @@ -207,20 +208,20 @@ void WorldSession::SendPacket(WorldPacket const* packet) if ((cur_time - lastTime) < 60) { - sendPacketCount+=1; - sendPacketBytes+=packet->size(); + sendPacketCount += 1; + sendPacketBytes += packet->size(); - sendLastPacketCount+=1; - sendLastPacketBytes+=packet->size(); + sendLastPacketCount += 1; + sendLastPacketBytes += packet->size(); } else { uint64 minTime = uint64(cur_time - lastTime); uint64 fullTime = uint64(lastTime - firstTime); - sLog->outDetail("Send all time packets count: " UI64FMTD " bytes: " UI64FMTD " avr.count/sec: %f avr.bytes/sec: %f time: %u", sendPacketCount, sendPacketBytes, float(sendPacketCount)/fullTime, float(sendPacketBytes)/fullTime, uint32(fullTime)); + sLog->outDetail("Send all time packets count: " UI64FMTD " bytes: " UI64FMTD " avr.count/sec: %f avr.bytes/sec: %f time: %u", sendPacketCount, sendPacketBytes, float(sendPacketCount) / fullTime, float(sendPacketBytes) / fullTime, uint32(fullTime)); - sLog->outDetail("Send last min packets count: " UI64FMTD " bytes: " UI64FMTD " avr.count/sec: %f avr.bytes/sec: %f", sendLastPacketCount, sendLastPacketBytes, float(sendLastPacketCount)/minTime, float(sendLastPacketBytes)/minTime); + sLog->outDetail("Send last min packets count: " UI64FMTD " bytes: " UI64FMTD " avr.count/sec: %f avr.bytes/sec: %f", sendLastPacketCount, sendLastPacketBytes, float(sendLastPacketCount) / minTime, float(sendLastPacketBytes) / minTime); lastTime = cur_time; sendLastPacketCount = 1; @@ -251,7 +252,7 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) if (updater.ProcessLogout()) { UpdateTimeOutTime(diff); - + /// If necessary, kick the player because the client didn't send anything for too long /// (or they've been idling in character select) if (sWorld->getBoolConfig(CONFIG_CLOSE_IDLE_CONNECTIONS) && IsConnectionIdle() && m_Socket) @@ -266,12 +267,13 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) bool deletePacket = true; WorldPacket* firstDelayedPacket = NULL; uint32 processedPackets = 0; + time_t currentTime = time(nullptr); while (m_Socket && !m_Socket->IsClosed() && !_recvQueue.empty() && _recvQueue.peek(true) != firstDelayedPacket && _recvQueue.next(packet, updater)) { if (packet->GetOpcode() >= NUM_MSG_TYPES) { - sLog->outError("WorldSession Packet filter: received non-existent opcode %s (0x%.4X)",LookupOpcodeName(packet->GetOpcode()), packet->GetOpcode()); + sLog->outError("WorldSession Packet filter: received non-existent opcode %s (0x%.4X)", LookupOpcodeName(packet->GetOpcode()), packet->GetOpcode()); } else { @@ -299,24 +301,24 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) QueuePacket(packet); }*/ } - else + else if (_player->IsInWorld() && AntiDOS.EvaluateOpcode(*packet, currentTime)) { - if (movementPacket) - { - HandleMovementOpcodes(*movementPacket); - delete movementPacket; - movementPacket = NULL; - } - sScriptMgr->OnPacketReceive(this, *packet); -#ifdef ELUNA - if (!sEluna->OnPacketReceive(this, *packet)) - break; -#endif - (this->*opHandle.handler)(*packet); + if (movementPacket) + { + HandleMovementOpcodes(*movementPacket); + delete movementPacket; + movementPacket = NULL; + } + sScriptMgr->OnPacketReceive(this, *packet); + #ifdef ELUNA + if (!sEluna->OnPacketReceive(this, *packet)) + break; + #endif + (this->*opHandle.handler)(*packet); } break; case STATUS_TRANSFER: - if (_player && !_player->IsInWorld()) + if (_player && !_player->IsInWorld() && AntiDOS.EvaluateOpcode(*packet, currentTime)) { if (movementPacket) { @@ -324,10 +326,10 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) movementPacket = NULL; } sScriptMgr->OnPacketReceive(this, *packet); -#ifdef ELUNA + #ifdef ELUNA if (!sEluna->OnPacketReceive(this, *packet)) break; -#endif + #endif (this->*opHandle.handler)(*packet); } break; @@ -335,12 +337,15 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) if (m_inQueue) // prevent cheating break; - sScriptMgr->OnPacketReceive(this, *packet); -#ifdef ELUNA - if (!sEluna->OnPacketReceive(this, *packet)) - break; -#endif - (this->*opHandle.handler)(*packet); + if (AntiDOS.EvaluateOpcode(*packet, currentTime)) + { + sScriptMgr->OnPacketReceive(this, *packet); + #ifdef ELUNA + if (!sEluna->OnPacketReceive(this, *packet)) + break; + #endif + (this->*opHandle.handler)(*packet); + } break; case STATUS_NEVER: break; @@ -348,7 +353,7 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) break; } } - catch(ByteBufferException const&) + catch (ByteBufferException const&) { sLog->outError("WorldSession::Update ByteBufferException occured while parsing a packet (opcode: %u) from client %s, accountid=%i. Skipped packet.", packet->GetOpcode(), GetRemoteAddress().c_str(), GetAccountId()); if (sLog->IsOutDebug()) @@ -361,8 +366,8 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) if (deletePacket) delete packet; - else - deletePacket = true; + + deletePacket = true; if (++processedPackets >= 150) // limit (by count) packets processed in one update, prevent DDoS break; @@ -423,13 +428,13 @@ void WorldSession::HandleTeleportTimeout(bool updateInSessions) time_t currTime = time(NULL); if (updateInSessions) // session update from World::UpdateSessions { - if (GetPlayer()->IsBeingTeleportedFar() && GetPlayer()->GetSemaphoreTeleportFar()+sWorld->getIntConfig(CONFIG_TELEPORT_TIMEOUT_FAR) < currTime) + if (GetPlayer()->IsBeingTeleportedFar() && GetPlayer()->GetSemaphoreTeleportFar() + sWorld->getIntConfig(CONFIG_TELEPORT_TIMEOUT_FAR) < currTime) while (GetPlayer() && GetPlayer()->IsBeingTeleportedFar()) HandleMoveWorldportAckOpcode(); } else // session update from Map::Update { - if (GetPlayer()->IsBeingTeleportedNear() && GetPlayer()->GetSemaphoreTeleportNear()+sWorld->getIntConfig(CONFIG_TELEPORT_TIMEOUT_NEAR) < currTime) + if (GetPlayer()->IsBeingTeleportedNear() && GetPlayer()->GetSemaphoreTeleportNear() + sWorld->getIntConfig(CONFIG_TELEPORT_TIMEOUT_NEAR) < currTime) while (GetPlayer() && GetPlayer()->IsInWorld() && GetPlayer()->IsBeingTeleportedNear()) { Player* plMover = GetPlayer()->m_mover->ToPlayer(); @@ -493,13 +498,13 @@ void WorldSession::LogoutPlayer(bool save) sOutdoorPvPMgr->HandlePlayerLeaveZone(_player, _player->GetZoneId()); // pussywizard: remove from battleground queues on logout - for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) + for (int i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) if (BattlegroundQueueTypeId bgQueueTypeId = _player->GetBattlegroundQueueTypeId(i)) { _player->RemoveBattlegroundQueueId(bgQueueTypeId); sBattlegroundMgr->GetBattlegroundQueue(bgQueueTypeId).RemovePlayer(_player->GetGUID(), false, i); // track if player logs out after invited to join BG - if(_player->IsInvitedForBattlegroundInstance() && sWorld->getBoolConfig(CONFIG_BATTLEGROUND_TRACK_DESERTERS)) + if (_player->IsInvitedForBattlegroundInstance() && sWorld->getBoolConfig(CONFIG_BATTLEGROUND_TRACK_DESERTERS)) { PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_DESERTER_TRACK); stmt->setUInt32(0, _player->GetGUIDLow()); @@ -560,7 +565,7 @@ void WorldSession::LogoutPlayer(bool save) { _player->GetGroup()->SendUpdate(); _player->GetGroup()->ResetMaxEnchantingLevel(); - + Map::PlayerList const &playerList = _player->GetMap()->GetPlayers(); if (_player->GetMap()->IsDungeon() || _player->GetMap()->IsRaidOrHeroicDungeon()) @@ -732,8 +737,7 @@ void WorldSession::LoadAccountData(PreparedQueryResult result, uint32 mask) m_accountData[type].Time = time_t(fields[1].GetUInt32()); m_accountData[type].Data = fields[2].GetString(); - } - while (result->NextRow()); + } while (result->NextRow()); } void WorldSession::SetAccountData(AccountDataType type, time_t tm, std::string const& data) @@ -757,7 +761,7 @@ void WorldSession::SetAccountData(AccountDataType type, time_t tm, std::string c PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(index); stmt->setUInt32(0, id); - stmt->setUInt8 (1, type); + stmt->setUInt8(1, type); stmt->setUInt32(2, uint32(tm)); stmt->setString(3, data); CharacterDatabase.Execute(stmt); @@ -855,8 +859,8 @@ void WorldSession::ReadMovementInfo(WorldPacket &data, MovementInfo* mi) //! Anti-cheat checks. Please keep them in seperate if() blocks to maintain a clear overview. //! Might be subject to latency, so just remove improper flags. - #ifdef ACORE_DEBUG - #define REMOVE_VIOLATING_FLAGS(check, maskToRemove) \ +#ifdef ACORE_DEBUG +#define REMOVE_VIOLATING_FLAGS(check, maskToRemove) \ { \ if (check) \ { \ @@ -866,17 +870,17 @@ void WorldSession::ReadMovementInfo(WorldPacket &data, MovementInfo* mi) mi->RemoveMovementFlag((maskToRemove)); \ } \ } - #else - #define REMOVE_VIOLATING_FLAGS(check, maskToRemove) \ +#else +#define REMOVE_VIOLATING_FLAGS(check, maskToRemove) \ if (check) \ mi->RemoveMovementFlag((maskToRemove)); - #endif +#endif - /*! This must be a packet spoofing attempt. MOVEMENTFLAG_ROOT sent from the client is not valid - in conjunction with any of the moving movement flags such as MOVEMENTFLAG_FORWARD. - It will freeze clients that receive this player's movement info. - */ +/*! This must be a packet spoofing attempt. MOVEMENTFLAG_ROOT sent from the client is not valid + in conjunction with any of the moving movement flags such as MOVEMENTFLAG_FORWARD. + It will freeze clients that receive this player's movement info. +*/ REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_ROOT), MOVEMENTFLAG_ROOT); @@ -920,7 +924,7 @@ void WorldSession::ReadMovementInfo(WorldPacket &data, MovementInfo* mi) e.g. aerial combat. */ - + REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_FLYING | MOVEMENTFLAG_CAN_FLY) && GetSecurity() == SEC_PLAYER && !GetPlayer()->m_mover->HasAuraType(SPELL_AURA_FLY) && !GetPlayer()->m_mover->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED), MOVEMENTFLAG_FLYING | MOVEMENTFLAG_CAN_FLY); @@ -932,7 +936,7 @@ void WorldSession::ReadMovementInfo(WorldPacket &data, MovementInfo* mi) REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_SPLINE_ENABLED) && (!GetPlayer()->movespline->Initialized() || GetPlayer()->movespline->Finalized()), MOVEMENTFLAG_SPLINE_ENABLED); - #undef REMOVE_VIOLATING_FLAGS +#undef REMOVE_VIOLATING_FLAGS } void WorldSession::WriteMovementInfo(WorldPacket* data, MovementInfo* mi) @@ -946,14 +950,14 @@ void WorldSession::WriteMovementInfo(WorldPacket* data, MovementInfo* mi) if (mi->HasMovementFlag(MOVEMENTFLAG_ONTRANSPORT)) { - data->appendPackGUID(mi->transport.guid); + data->appendPackGUID(mi->transport.guid); - *data << mi->transport.pos.PositionXYZOStream(); - *data << mi->transport.time; - *data << mi->transport.seat; + *data << mi->transport.pos.PositionXYZOStream(); + *data << mi->transport.time; + *data << mi->transport.seat; - if (mi->HasExtraMovementFlag(MOVEMENTFLAG2_INTERPOLATED_MOVEMENT)) - *data << mi->transport.time2; + if (mi->HasExtraMovementFlag(MOVEMENTFLAG2_INTERPOLATED_MOVEMENT)) + *data << mi->transport.time2; } if (mi->HasMovementFlag(MovementFlags(MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_FLYING)) || mi->HasExtraMovementFlag(MOVEMENTFLAG2_ALWAYS_ALLOW_PITCHING)) @@ -1126,7 +1130,7 @@ void WorldSession::SendAddonsInfo() data << uint32(bannedAddons->size()); for (AddonMgr::BannedAddonList::const_iterator itr = bannedAddons->begin(); itr != bannedAddons->end(); ++itr) { - data << uint32(itr->Id); + data << uint32(itr->Id); data.append(itr->NameMD5, sizeof(itr->NameMD5)); data.append(itr->VersionMD5, sizeof(itr->VersionMD5)); data << uint32(itr->Timestamp); @@ -1282,7 +1286,7 @@ void WorldSession::ProcessQueryCallbackPet() _loadPetFromDBSecondCallback.get(param); HandleLoadPetFromDBSecondCallback((LoadPetFromDBQueryHolder*)param); delete param; - _loadPetFromDBSecondCallback.cancel(); + _loadPetFromDBSecondCallback.cancel(); } return; } @@ -1331,3 +1335,299 @@ void WorldSession::InitWarden(BigNumber* k, std::string const& os) // _warden->Init(this, k); } } + +bool WorldSession::DosProtection::EvaluateOpcode(WorldPacket& p, time_t time) const +{ + uint32 maxPacketCounterAllowed = GetMaxPacketCounterAllowed(p.GetOpcode()); + + // Return true if there no limit for the opcode + if (!maxPacketCounterAllowed) + return true; + + PacketCounter& packetCounter = _PacketThrottlingMap[p.GetOpcode()]; + if (packetCounter.lastReceiveTime != time) + { + packetCounter.lastReceiveTime = time; + packetCounter.amountCounter = 0; + } + + // Check if player is flooding some packets + if (++packetCounter.amountCounter <= maxPacketCounterAllowed) + return true; + + sLog->outString("AntiDOS: Account %u, IP: %s, Ping: %u, Character %s, flooding packet (opc: %s (0x%X), count: %u)", + Session->GetAccountId(), Session->GetRemoteAddress().c_str(), Session->GetLatency(), + Session->GetPlayerName().c_str(), opcodeTable[p.GetOpcode()].name, p.GetOpcode(), packetCounter.amountCounter); + + switch (_policy) + { + case POLICY_LOG: + return true; + case POLICY_KICK: + { + sLog->outString("AntiDOS: Player %s kicked!", Session->GetPlayerName().c_str()); + Session->KickPlayer(); + return false; + } + case POLICY_BAN: + { + uint32 bm = sWorld->getIntConfig(CONFIG_PACKET_SPOOF_BANMODE); + uint32 duration = sWorld->getIntConfig(CONFIG_PACKET_SPOOF_BANDURATION); // in seconds + std::string nameOrIp = ""; + switch (bm) + { + case 0: // Ban account + (void)AccountMgr::GetName(Session->GetAccountId(), nameOrIp); + sBan->BanAccount(nameOrIp, std::to_string(duration), "DOS (Packet Flooding/Spoofing", "Server: AutoDOS"); + break; + case 1: // Ban ip + nameOrIp = Session->GetRemoteAddress(); + sBan->BanIP(nameOrIp, std::to_string(duration), "DOS (Packet Flooding/Spoofing", "Server: AutoDOS"); + break; + } + + sLog->outString("AntiDOS: Player automatically banned for %u seconds.", duration); + return false; + } + default: // invalid policy + return true; + } +} + +uint32 WorldSession::DosProtection::GetMaxPacketCounterAllowed(uint16 opcode) const +{ + uint32 maxPacketCounterAllowed; + switch (opcode) + { + // CPU usage sending 2000 packets/second on a 3.70 GHz 4 cores on Win x64 + // [% CPU mysqld] [%CPU worldserver RelWithDebInfo] + case CMSG_PLAYER_LOGIN: // 0 0.5 + case CMSG_NAME_QUERY: // 0 1 + case CMSG_PET_NAME_QUERY: // 0 1 + case CMSG_NPC_TEXT_QUERY: // 0 1 + case CMSG_ATTACKSTOP: // 0 1 + case CMSG_QUERY_QUESTS_COMPLETED: // 0 1 + case CMSG_QUERY_TIME: // 0 1 + case CMSG_CORPSE_MAP_POSITION_QUERY: // 0 1 + case CMSG_MOVE_TIME_SKIPPED: // 0 1 + case MSG_QUERY_NEXT_MAIL_TIME: // 0 1 + case CMSG_SETSHEATHED: // 0 1 + case MSG_RAID_TARGET_UPDATE: // 0 1 + case CMSG_PLAYER_LOGOUT: // 0 1 + case CMSG_LOGOUT_REQUEST: // 0 1 + case CMSG_PET_RENAME: // 0 1 + case CMSG_QUESTGIVER_CANCEL: // 0 1 + case CMSG_QUESTGIVER_REQUEST_REWARD: // 0 1 + case CMSG_COMPLETE_CINEMATIC: // 0 1 + case CMSG_BANKER_ACTIVATE: // 0 1 + case CMSG_BUY_BANK_SLOT: // 0 1 + case CMSG_OPT_OUT_OF_LOOT: // 0 1 + case CMSG_DUEL_ACCEPTED: // 0 1 + case CMSG_DUEL_CANCELLED: // 0 1 + case CMSG_CALENDAR_COMPLAIN: // 0 1 + case CMSG_QUEST_QUERY: // 0 1.5 + case CMSG_ITEM_QUERY_SINGLE: // 0 1.5 + case CMSG_ITEM_NAME_QUERY: // 0 1.5 + case CMSG_GAMEOBJECT_QUERY: // 0 1.5 + case CMSG_CREATURE_QUERY: // 0 1.5 + case CMSG_QUESTGIVER_STATUS_QUERY: // 0 1.5 + case CMSG_GUILD_QUERY: // 0 1.5 + case CMSG_ARENA_TEAM_QUERY: // 0 1.5 + case CMSG_TAXINODE_STATUS_QUERY: // 0 1.5 + case CMSG_TAXIQUERYAVAILABLENODES: // 0 1.5 + case CMSG_QUESTGIVER_QUERY_QUEST: // 0 1.5 + case CMSG_PAGE_TEXT_QUERY: // 0 1.5 + case MSG_QUERY_GUILD_BANK_TEXT: // 0 1.5 + case MSG_CORPSE_QUERY: // 0 1.5 + case MSG_MOVE_SET_FACING: // 0 1.5 + case CMSG_REQUEST_PARTY_MEMBER_STATS: // 0 1.5 + case CMSG_QUESTGIVER_COMPLETE_QUEST: // 0 1.5 + case CMSG_SET_ACTION_BUTTON: // 0 1.5 + case CMSG_RESET_INSTANCES: // 0 1.5 + case CMSG_HEARTH_AND_RESURRECT: // 0 1.5 + case CMSG_TOGGLE_PVP: // 0 1.5 + case CMSG_PET_ABANDON: // 0 1.5 + case CMSG_ACTIVATETAXIEXPRESS: // 0 1.5 + case CMSG_ACTIVATETAXI: // 0 1.5 + case CMSG_SELF_RES: // 0 1.5 + case CMSG_UNLEARN_SKILL: // 0 1.5 + case CMSG_EQUIPMENT_SET_SAVE: // 0 1.5 + case CMSG_DELETEEQUIPMENT_SET: // 0 1.5 + case CMSG_DISMISS_CRITTER: // 0 1.5 + case CMSG_REPOP_REQUEST: // 0 1.5 + case CMSG_GROUP_INVITE: // 0 1.5 + case CMSG_GROUP_DECLINE: // 0 1.5 + case CMSG_GROUP_ACCEPT: // 0 1.5 + case CMSG_GROUP_UNINVITE_GUID: // 0 1.5 + case CMSG_GROUP_UNINVITE: // 0 1.5 + case CMSG_GROUP_DISBAND: // 0 1.5 + case CMSG_BATTLEMASTER_JOIN_ARENA: // 0 1.5 + case CMSG_LEAVE_BATTLEFIELD: // 0 1.5 + case MSG_GUILD_BANK_LOG_QUERY: // 0 2 + case CMSG_LOGOUT_CANCEL: // 0 2 + case CMSG_REALM_SPLIT: // 0 2 + case CMSG_ALTER_APPEARANCE: // 0 2 + case CMSG_QUEST_CONFIRM_ACCEPT: // 0 2 + case MSG_GUILD_EVENT_LOG_QUERY: // 0 2.5 + case CMSG_READY_FOR_ACCOUNT_DATA_TIMES: // 0 2.5 + case CMSG_QUESTGIVER_STATUS_MULTIPLE_QUERY: // 0 2.5 + case CMSG_BEGIN_TRADE: // 0 2.5 + case CMSG_INITIATE_TRADE: // 0 3 + case CMSG_MESSAGECHAT: // 0 3.5 + case CMSG_INSPECT: // 0 3.5 + case CMSG_AREA_SPIRIT_HEALER_QUERY: // not profiled + case CMSG_STANDSTATECHANGE: // not profiled + case MSG_RANDOM_ROLL: // not profiled + case CMSG_TIME_SYNC_RESP: // not profiled + case CMSG_TRAINER_BUY_SPELL: // not profiled + case CMSG_FORCE_RUN_SPEED_CHANGE_ACK: // not profiled + { + // "0" is a magic number meaning there's no limit for the opcode. + // All the opcodes above must cause little CPU usage and no sync/async database queries at all + maxPacketCounterAllowed = 0; + break; + } + + case CMSG_QUESTGIVER_ACCEPT_QUEST: // 0 4 + case CMSG_QUESTLOG_REMOVE_QUEST: // 0 4 + case CMSG_QUESTGIVER_CHOOSE_REWARD: // 0 4 + case CMSG_CONTACT_LIST: // 0 5 + case CMSG_LEARN_PREVIEW_TALENTS: // 0 6 + case CMSG_AUTOBANK_ITEM: // 0 6 + case CMSG_AUTOSTORE_BANK_ITEM: // 0 6 + case CMSG_WHO: // 0 7 + case CMSG_PLAYER_VEHICLE_ENTER: // 0 8 + case CMSG_LEARN_PREVIEW_TALENTS_PET: // not profiled + case MSG_MOVE_HEARTBEAT: + { + maxPacketCounterAllowed = 200; + break; + } + + case CMSG_GUILD_SET_PUBLIC_NOTE: // 1 2 1 async db query + case CMSG_GUILD_SET_OFFICER_NOTE: // 1 2 1 async db query + case CMSG_SET_CONTACT_NOTES: // 1 2.5 1 async db query + case CMSG_CALENDAR_GET_CALENDAR: // 0 1.5 medium upload bandwidth usage + case CMSG_GUILD_BANK_QUERY_TAB: // 0 3.5 medium upload bandwidth usage + case CMSG_QUERY_INSPECT_ACHIEVEMENTS: // 0 13 high upload bandwidth usage + case CMSG_GAMEOBJ_REPORT_USE: // not profiled + case CMSG_GAMEOBJ_USE: // not profiled + case MSG_PETITION_DECLINE: // not profiled + { + maxPacketCounterAllowed = 50; + break; + } + + case CMSG_QUEST_POI_QUERY: // 0 25 very high upload bandwidth usage + { + maxPacketCounterAllowed = MAX_QUEST_LOG_SIZE; + break; + } + + case CMSG_GM_REPORT_LAG: // 1 3 1 async db query + case CMSG_SPELLCLICK: // not profiled + case CMSG_REMOVE_GLYPH: // not profiled + case CMSG_DISMISS_CONTROLLED_VEHICLE: // not profiled + { + maxPacketCounterAllowed = 20; + break; + } + + case CMSG_PETITION_SIGN: // 9 4 2 sync 1 async db queries + case CMSG_TURN_IN_PETITION: // 8 5.5 2 sync db query + case CMSG_GROUP_CHANGE_SUB_GROUP: // 6 5 1 sync 1 async db queries + case CMSG_PETITION_QUERY: // 4 3.5 1 sync db query + case CMSG_CHAR_RACE_CHANGE: // 5 4 1 sync db query + case CMSG_CHAR_CUSTOMIZE: // 5 5 1 sync db query + case CMSG_CHAR_FACTION_CHANGE: // 5 5 1 sync db query + case CMSG_CHAR_DELETE: // 4 4 1 sync db query + case CMSG_DEL_FRIEND: // 7 5 1 async db query + case CMSG_ADD_FRIEND: // 6 4 1 async db query + case CMSG_CHAR_RENAME: // 5 3 1 async db query + case CMSG_GMSURVEY_SUBMIT: // 2 3 1 async db query + case CMSG_BUG: // 1 1 1 async db query + case CMSG_GROUP_SET_LEADER: // 1 2 1 async db query + case CMSG_GROUP_RAID_CONVERT: // 1 5 1 async db query + case CMSG_GROUP_ASSISTANT_LEADER: // 1 2 1 async db query + case CMSG_PETITION_BUY: // not profiled 1 sync 1 async db queries + case CMSG_CHANGE_SEATS_ON_CONTROLLED_VEHICLE: // not profiled + case CMSG_REQUEST_VEHICLE_PREV_SEAT: // not profiled + case CMSG_REQUEST_VEHICLE_NEXT_SEAT: // not profiled + case CMSG_REQUEST_VEHICLE_SWITCH_SEAT: // not profiled + case CMSG_REQUEST_VEHICLE_EXIT: // not profiled + case CMSG_CONTROLLER_EJECT_PASSENGER: // not profiled + case CMSG_ITEM_REFUND: // not profiled + case CMSG_SOCKET_GEMS: // not profiled + case CMSG_WRAP_ITEM: // not profiled + case CMSG_REPORT_PVP_AFK: // not profiled + { + maxPacketCounterAllowed = 10; + break; + } + + case CMSG_CHAR_CREATE: // 7 5 3 async db queries + case CMSG_CHAR_ENUM: // 22 3 2 async db queries + case CMSG_GMTICKET_CREATE: // 1 25 1 async db query + case CMSG_GMTICKET_UPDATETEXT: // 0 15 1 async db query + case CMSG_GMTICKET_DELETETICKET: // 1 25 1 async db query + case CMSG_GMRESPONSE_RESOLVE: // 1 25 1 async db query + case CMSG_CALENDAR_ADD_EVENT: // 21 10 2 async db query + case CMSG_CALENDAR_UPDATE_EVENT: // not profiled + case CMSG_CALENDAR_REMOVE_EVENT: // not profiled + case CMSG_CALENDAR_COPY_EVENT: // not profiled + case CMSG_CALENDAR_EVENT_INVITE: // not profiled + case CMSG_CALENDAR_EVENT_SIGNUP: // not profiled + case CMSG_CALENDAR_EVENT_RSVP: // not profiled + case CMSG_CALENDAR_EVENT_REMOVE_INVITE: // not profiled + case CMSG_CALENDAR_EVENT_MODERATOR_STATUS: // not profiled + case CMSG_ARENA_TEAM_INVITE: // not profiled + case CMSG_ARENA_TEAM_ACCEPT: // not profiled + case CMSG_ARENA_TEAM_DECLINE: // not profiled + case CMSG_ARENA_TEAM_LEAVE: // not profiled + case CMSG_ARENA_TEAM_DISBAND: // not profiled + case CMSG_ARENA_TEAM_REMOVE: // not profiled + case CMSG_ARENA_TEAM_LEADER: // not profiled + case CMSG_LOOT_METHOD: // not profiled + case CMSG_GUILD_INVITE: // not profiled + case CMSG_GUILD_ACCEPT: // not profiled + case CMSG_GUILD_DECLINE: // not profiled + case CMSG_GUILD_LEAVE: // not profiled + case CMSG_GUILD_DISBAND: // not profiled + case CMSG_GUILD_LEADER: // not profiled + case CMSG_GUILD_MOTD: // not profiled + case CMSG_GUILD_RANK: // not profiled + case CMSG_GUILD_ADD_RANK: // not profiled + case CMSG_GUILD_DEL_RANK: // not profiled + case CMSG_GUILD_INFO_TEXT: // not profiled + case CMSG_GUILD_BANK_DEPOSIT_MONEY: // not profiled + case CMSG_GUILD_BANK_WITHDRAW_MONEY: // not profiled + case CMSG_GUILD_BANK_BUY_TAB: // not profiled + case CMSG_GUILD_BANK_UPDATE_TAB: // not profiled + case CMSG_SET_GUILD_BANK_TEXT: // not profiled + case MSG_SAVE_GUILD_EMBLEM: // not profiled + case MSG_PETITION_RENAME: // not profiled + case MSG_TALENT_WIPE_CONFIRM: // not profiled + case MSG_SET_DUNGEON_DIFFICULTY: // not profiled + case MSG_SET_RAID_DIFFICULTY: // not profiled + case MSG_PARTY_ASSIGNMENT: // not profiled + case MSG_RAID_READY_CHECK: // not profiled + { + maxPacketCounterAllowed = 3; + break; + } + + case CMSG_ITEM_REFUND_INFO: // not profiled + { + maxPacketCounterAllowed = PLAYER_SLOTS_COUNT; + break; + } + + default: + { + maxPacketCounterAllowed = 100; + break; + } + } + + return maxPacketCounterAllowed; +} diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 181c04bad..5bd4f0759 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -19,6 +19,9 @@ #include "WorldPacket.h" #include "GossipDef.h" #include "Cryptography/BigNumber.h" +#include "AccountMgr.h" +#include "BanManager.h" +#include "Opcodes.h" class Creature; class GameObject; @@ -181,6 +184,12 @@ class CharacterCreateInfo virtual ~CharacterCreateInfo(){}; }; +struct PacketCounter +{ + time_t lastReceiveTime; + uint32 amountCounter; +}; + /// Player session in the World class WorldSession { @@ -937,6 +946,36 @@ class WorldSession QueryResultHolderFuture _loadPetFromDBSecondCallback; QueryCallback_3 _openWrappedItemCallback; + friend class World; + protected: + class DosProtection + { + friend class World; + public: + DosProtection(WorldSession* s) : Session(s), _policy((Policy)sWorld->getIntConfig(CONFIG_PACKET_SPOOF_POLICY)) { } + bool EvaluateOpcode(WorldPacket& p, time_t time) const; + protected: + enum Policy + { + POLICY_LOG, + POLICY_KICK, + POLICY_BAN + }; + + uint32 GetMaxPacketCounterAllowed(uint16 opcode) const; + + WorldSession* Session; + + private: + Policy _policy; + typedef std::unordered_map 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; + } AntiDOS; + public: // xinef: those must be public, requires calls out of worldsession :( QueryCallback_2 _loadPetFromDBFirstCallback; diff --git a/src/server/game/Weather/Weather.cpp b/src/server/game/Weather/Weather.cpp index a989a1661..21074247a 100644 --- a/src/server/game/Weather/Weather.cpp +++ b/src/server/game/Weather/Weather.cpp @@ -16,7 +16,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) diff --git a/src/server/game/Weather/WeatherMgr.cpp b/src/server/game/Weather/WeatherMgr.cpp index d3215e74d..0e685b008 100644 --- a/src/server/game/Weather/WeatherMgr.cpp +++ b/src/server/game/Weather/WeatherMgr.cpp @@ -15,7 +15,7 @@ #include "AutoPtr.h" #include "Player.h" #include "WorldPacket.h" -#include "Opcodes.h" +#include "WorldSession.h" namespace WeatherMgr { diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index e7467cfa8..a795d9da2 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1351,6 +1351,14 @@ void World::LoadConfigSettings(bool reload) m_bool_configs[CONFIG_SET_ALL_CREATURES_WITH_WAYPOINT_MOVEMENT_ACTIVE] = sConfigMgr->GetBoolDefault("SetAllCreaturesWithWaypointMovementActive", false); + //packet spoof punishment + m_int_configs[CONFIG_PACKET_SPOOF_POLICY] = sConfigMgr->GetIntDefault("PacketSpoof.Policy", (uint32)WorldSession::DosProtection::POLICY_KICK); + m_int_configs[CONFIG_PACKET_SPOOF_BANMODE] = sConfigMgr->GetIntDefault("PacketSpoof.BanMode", (uint32)0); + if (m_int_configs[CONFIG_PACKET_SPOOF_BANMODE] > 1) + m_int_configs[CONFIG_PACKET_SPOOF_BANMODE] = (uint32)0; + + m_int_configs[CONFIG_PACKET_SPOOF_BANDURATION] = sConfigMgr->GetIntDefault("PacketSpoof.BanDuration", 86400); + // call ScriptMgr if we're reloading the configuration sScriptMgr->OnAfterConfigLoad(reload); } diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index 3b4178fbd..3fb6180c6 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -334,6 +334,9 @@ enum WorldIntConfigs CONFIG_WINTERGRASP_BATTLETIME, CONFIG_WINTERGRASP_NOBATTLETIME, CONFIG_WINTERGRASP_RESTART_AFTER_CRASH, + CONFIG_PACKET_SPOOF_POLICY, + CONFIG_PACKET_SPOOF_BANMODE, + CONFIG_PACKET_SPOOF_BANDURATION, CONFIG_WARDEN_CLIENT_RESPONSE_DELAY, CONFIG_WARDEN_CLIENT_CHECK_HOLDOFF, CONFIG_WARDEN_CLIENT_FAIL_ACTION, diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index 58b5bfcfc..352ce24f5 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -3377,3 +3377,39 @@ ICC.Buff.Alliance = 73828 # ################################################################################################### + +################################################################################################### +# +# Packet Spoof Protection Settings +# +# These settings determine which action to take when harmful packet spoofing is detected. +# +# PacketSpoof.Policy +# Description: Determines the course of action when packet spoofing is detected. +# Values: 0 - Log only +# 1 - Log + kick +# 2 - Log + kick + ban + +PacketSpoof.Policy = 1 + +# +# PacketSpoof.BanMode +# Description: If PacketSpoof.Policy equals 2, this will determine the ban mode. +# Values: 0 - Ban Account +# 1 - Ban IP +# Note: Banning by character not supported for logical reasons. +# + +PacketSpoof.BanMode = 0 + +# +# PacketSpoof.BanDuration +# Description: Duration of the ban in seconds. Only valid if PacketSpoof.Policy is set to 2. +# Set to 0 for permanent ban. +# Default: 86400 seconds (1 day) +# + +PacketSpoof.BanDuration = 86400 + +# +###################################################################################################