diff options
| -rw-r--r-- | src/server/game/Handlers/CharacterHandler.cpp | 2 | ||||
| -rw-r--r-- | src/server/game/Server/WorldSession.cpp | 14 | ||||
| -rw-r--r-- | src/server/game/Server/WorldSession.h | 77 | ||||
| -rw-r--r-- | src/server/game/World/World.cpp | 14 | ||||
| -rw-r--r-- | src/server/game/World/World.h | 4 | ||||
| -rw-r--r-- | src/server/worldserver/worldserver.conf.dist | 36 | 
6 files changed, 144 insertions, 3 deletions
diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 0554820364d..aa766d5b1ac 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -243,6 +243,8 @@ void WorldSession::HandleCharEnum(PreparedQueryResult result)  void WorldSession::HandleCharEnumOpcode(WorldPacket & /*recvData*/)  { +    AntiDOS.AllowOpcode(CMSG_CHAR_ENUM, false); +      // remove expired bans      PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_EXPIRED_BANS);      CharacterDatabase.Execute(stmt); diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 5519f1d4d0e..ce1db7fcac7 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -118,7 +118,8 @@ WorldSession::WorldSession(uint32 id, WorldSocket* sock, AccountTypes sec, uint8      recruiterId(recruiter),      isRecruiter(isARecruiter),      timeLastWhoCommand(0), -    _RBACData(NULL) +    _RBACData(NULL), +    AntiDOS(this)  {      if (sock)      { @@ -274,13 +275,19 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)              !_recvQueue.empty() && _recvQueue.peek(true) != firstDelayedPacket &&              _recvQueue.next(packet, updater))      { -        if (packet->GetOpcode() >= NUM_MSG_TYPES) +        if (!AntiDOS.EvaluateOpcode(*packet)) +        { +            delete packet; +            KickPlayer(); +        } + +        if (packet && packet->GetOpcode() >= NUM_MSG_TYPES)          {              TC_LOG_ERROR(LOG_FILTER_OPCODES, "Received non-existed opcode %s from %s", GetOpcodeNameForLogging(packet->GetOpcode()).c_str()                              , GetPlayerInfo().c_str());              sScriptMgr->OnUnknownPacketReceive(m_Socket, WorldPacket(*packet));          } -        else +        else if (packet)          {              OpcodeHandler &opHandle = opcodeTable[packet->GetOpcode()];              try @@ -547,6 +554,7 @@ void WorldSession::LogoutPlayer(bool save)      m_playerLogout = false;      m_playerSave = false;      m_playerRecentlyLogout = true; +    AntiDOS.AllowOpcode(CMSG_CHAR_ENUM, true);      LogoutRequest(0);  } diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index bf79b34822d..721bdb06c8a 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -30,6 +30,7 @@  #include "World.h"  #include "WorldPacket.h"  #include "Cryptography/BigNumber.h" +#include "AccountMgr.h"  class Creature;  class GameObject; @@ -911,6 +912,82 @@ class WorldSession          QueryCallback<PreparedQueryResult, CharacterCreateInfo*, true> _charCreateCallback;          QueryResultHolderFuture _charLoginCallback; +    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) const +                { +                    if (IsOpcodeAllowed(p.GetOpcode())) +                        return true; + +                    // Opcode not allowed, let the punishment begin +                    sLog->outInfo(LOG_FILTER_NETWORKIO, "AntiDOS: Account %u, IP: %s, sent unacceptable packet (opc: %u, size: %u)", +                        Session->GetAccountId(), Session->GetRemoteAddress().c_str(), p.GetOpcode(), p.size()); + +                    switch (_policy) +                    { +                        case POLICY_LOG: +                            return true; +                        case POLICY_KICK: +                            sLog->outInfo(LOG_FILTER_NETWORKIO, "AntiDOS: Player kicked!"); +                            return false; +                        case POLICY_BAN: +                        { +                            BanMode bm = (BanMode)sWorld->getIntConfig(CONFIG_PACKET_SPOOF_BANMODE); +                            int64 duration = (int64)sWorld->getIntConfig(CONFIG_PACKET_SPOOF_BANDURATION); // in seconds +                            std::string nameOrIp = ""; +                            switch (bm) +                            { +                                case BAN_ACCOUNT: (void)sAccountMgr->GetName(Session->GetAccountId(), nameOrIp); break; +                                case BAN_IP: nameOrIp = Session->GetRemoteAddress(); break; +                            } +                            sWorld->BanAccount(bm, nameOrIp, duration, "DOS (Packet Flooding/Spoofing", "Server: AutoDOS"); +                            sLog->outInfo(LOG_FILTER_NETWORKIO, "AntiDOS: Player automatically banned for "I64FMT" seconds.", duration); +                             +                            return false; +                        } +                        default: // invalid policy +                            return true; +                    } +                } + +                void AllowOpcode(uint16 opcode, bool allow) +                { +                    _isOpcodeAllowed[opcode] = allow; +                } + +            protected: +                enum Policy +                { +                    POLICY_LOG, +                    POLICY_KICK, +                    POLICY_BAN, +                }; + +                bool IsOpcodeAllowed(uint16 opcode) const +                { +                    OpcodeStatusMap::const_iterator itr = _isOpcodeAllowed.find(opcode); +                    if (itr == _isOpcodeAllowed.end()) +                        return true;    // No presence in the map indicates this is the first time the opcode was sent this session, so allow + +                    return itr->second; +                } + +                WorldSession* Session; + +            private: +                typedef 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; + +                 +        } AntiDOS; +      private:          // private trade methods          void moveItems(Item* myItems[], Item* hisItems[]); diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 52ac2d28a41..681f47976e3 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1229,6 +1229,14 @@ void World::LoadConfigSettings(bool reload)      m_float_configs[CONFIG_STATS_LIMITS_BLOCK] = sConfigMgr->GetFloatDefault("Stats.Limits.Block", 95.0f);      m_float_configs[CONFIG_STATS_LIMITS_CRIT] = sConfigMgr->GetFloatDefault("Stats.Limits.Crit", 95.0f); +    //packet spoof punishment +    m_int_configs[CONFIG_PACKET_SPOOF_POLICY] = sConfigMgr->GetIntDefault("PacketSpoof.Policy", (uint32)WorldSession::DosProtection::Policy::POLICY_KICK); +    m_int_configs[CONFIG_PACKET_SPOOF_BANMODE] = sConfigMgr->GetIntDefault("PacketSpoof.BanMode", (uint32)BAN_ACCOUNT); +    if (m_int_configs[CONFIG_PACKET_SPOOF_BANMODE] == BAN_CHARACTER || m_int_configs[CONFIG_PACKET_SPOOF_BANMODE] > BAN_IP) +        m_int_configs[CONFIG_PACKET_SPOOF_BANMODE] = BAN_ACCOUNT; + +    m_int_configs[CONFIG_PACKET_SPOOF_BANDURATION] = sConfigMgr->GetIntDefault("PacketSpoof.BanDuration", 86400); +      // call ScriptMgr if we're reloading the configuration      if (reload)          sScriptMgr->OnConfigLoad(reload); @@ -2307,6 +2315,12 @@ void World::KickAllLess(AccountTypes sec)  BanReturn World::BanAccount(BanMode mode, std::string const& nameOrIP, std::string const& duration, std::string const& reason, std::string const& author)  {      uint32 duration_secs = TimeStringToSecs(duration); +    return BanAccount(mode, nameOrIP, duration_secs, reason, author); +} + +/// Ban an account or ban an IP address, duration is in seconds if positive, otherwise permban +BanReturn World::BanAccount(BanMode mode, std::string const& nameOrIP, uint32 duration_secs, std::string const& reason, std::string const& author) +{      PreparedQueryResult resultAccounts = PreparedQueryResult(NULL); //used for kicking      PreparedStatement* stmt = NULL; diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index 12586ed4969..bf90b5ac01a 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -326,6 +326,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,      INT_CONFIG_VALUE_COUNT  }; @@ -687,6 +690,7 @@ class World          void KickAll();          void KickAllLess(AccountTypes sec);          BanReturn BanAccount(BanMode mode, std::string const& nameOrIP, std::string const& duration, std::string const& reason, std::string const& author); +        BanReturn BanAccount(BanMode mode, std::string const& nameOrIP, uint32 duration_secs, std::string const& reason, std::string const& author);          bool RemoveBanAccount(BanMode mode, std::string const& nameOrIP);          BanReturn BanCharacter(std::string const& name, std::string const& duration, std::string const& reason, std::string const& author);          bool RemoveBanCharacter(std::string const& name); diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index c4e19e851b3..a98a4e61448 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -2794,3 +2794,39 @@ Log.Async.Enable = 0  #  ################################################################################################### + +################################################################################################### +# +# 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 (LOG_FILTER_NETWORKIO) +#                    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 +#                    2 - 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 + +# +###################################################################################################
\ No newline at end of file  | 
