diff options
author | Vincent-Michael <Vincent_Michael@gmx.de> | 2014-06-23 23:47:48 +0200 |
---|---|---|
committer | Vincent-Michael <Vincent_Michael@gmx.de> | 2014-06-23 23:47:48 +0200 |
commit | d8234bed420728aaa2e0f329778730a906f7dd12 (patch) | |
tree | be30b4b452967bed923c106971c35014ab4c9412 /src | |
parent | 9bac986f35067941552b7dec8da81eac127d2f66 (diff) | |
parent | aa93a975469cca56e35adc1b5b501f4536be61f1 (diff) |
Merge branch 'master' of github.com:TrinityCore/TrinityCore into 4.3.4
Conflicts:
src/server/game/Accounts/AccountMgr.cpp
src/server/game/Entities/Player/Player.h
src/server/game/Server/WorldSocket.cpp
src/server/game/World/World.cpp
Diffstat (limited to 'src')
26 files changed, 607 insertions, 67 deletions
diff --git a/src/server/authserver/Server/AuthSocket.cpp b/src/server/authserver/Server/AuthSocket.cpp index 7ca49b03b56..f1cb3af3f51 100644 --- a/src/server/authserver/Server/AuthSocket.cpp +++ b/src/server/authserver/Server/AuthSocket.cpp @@ -704,13 +704,25 @@ bool AuthSocket::_HandleLogonProof() char data[4] = { AUTH_LOGON_PROOF, WOW_FAIL_UNKNOWN_ACCOUNT, 3, 0 }; socket().send(data, sizeof(data)); - TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] account %s tried to login with invalid password!", socket().getRemoteAddress().c_str(), socket().getRemotePort(), _login.c_str ()); + TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] account %s tried to login with invalid password!", socket().getRemoteAddress().c_str(), socket().getRemotePort(), _login.c_str()); uint32 MaxWrongPassCount = sConfigMgr->GetIntDefault("WrongPass.MaxCount", 0); + + // We can not include the failed account login hook. However, this is a workaround to still log this. + if (sConfigMgr->GetBoolDefault("Additional.IP.Based.Login.Logging", false)) + { + PreparedStatement* logstmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_FALP_IP_LOGGING); + logstmt->setString(0, _login); + logstmt->setString(1, socket().getRemoteAddress()); + logstmt->setString(2, "Logged on failed AccountLogin due wrong password"); + + LoginDatabase.Execute(logstmt); + } + if (MaxWrongPassCount > 0) { //Increment number of failed logins by one and if it reaches the limit temporarily ban that account or IP - PreparedStatement *stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_FAILEDLOGINS); + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_FAILEDLOGINS); stmt->setString(0, _login); LoginDatabase.Execute(stmt); diff --git a/src/server/authserver/authserver.conf.dist b/src/server/authserver/authserver.conf.dist index 83edf9669ac..b7dee9ac08b 100644 --- a/src/server/authserver/authserver.conf.dist +++ b/src/server/authserver/authserver.conf.dist @@ -148,6 +148,13 @@ LoginDatabaseInfo = "127.0.0.1;3306;trinity;trinity;auth" LoginDatabase.WorkerThreads = 1 # +# Wrong.Password.Login.Logging +# Description: Additionally log attempted wrong password logging +# Default: 0 - (Disabled) +# 1 - (Enabled) + +Wrong.Password.Login.Logging = 0 +# ################################################################################################### ################################################################################################### diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp index ecaa20284d7..e36433dd8c0 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.cpp +++ b/src/server/game/AI/SmartScripts/SmartAI.cpp @@ -944,7 +944,7 @@ class SmartTrigger : public AreaTriggerScript } }; -void AddSC_SmartSCripts() +void AddSC_SmartScripts() { new SmartTrigger(); } diff --git a/src/server/game/Accounts/AccountMgr.cpp b/src/server/game/Accounts/AccountMgr.cpp index 59a8f046a6b..c7437909f05 100644 --- a/src/server/game/Accounts/AccountMgr.cpp +++ b/src/server/game/Accounts/AccountMgr.cpp @@ -21,6 +21,7 @@ #include "DatabaseEnv.h" #include "ObjectAccessor.h" #include "Player.h" +#include "ScriptMgr.h" #include "Util.h" #include "SHA1.h" #include "WorldSession.h" @@ -166,10 +167,16 @@ AccountOpResult AccountMgr::ChangePassword(uint32 accountId, std::string newPass std::string username; if (!GetName(accountId, username)) + { + sScriptMgr->OnFailedPasswordChange(accountId); return AccountOpResult::AOR_NAME_NOT_EXIST; // account doesn't exist + } if (utf8length(newPassword) > MAX_ACCOUNT_STR) + { + sScriptMgr->OnFailedPasswordChange(accountId); return AccountOpResult::AOR_PASS_TOO_LONG; + } Utf8ToUpperOnlyLatin(username); Utf8ToUpperOnlyLatin(newPassword); @@ -189,6 +196,7 @@ AccountOpResult AccountMgr::ChangePassword(uint32 accountId, std::string newPass LoginDatabase.Execute(stmt); + sScriptMgr->OnPasswordChange(accountId); return AccountOpResult::AOR_OK; } @@ -197,10 +205,16 @@ AccountOpResult AccountMgr::ChangeEmail(uint32 accountId, std::string newEmail) std::string username; if (!GetName(accountId, username)) + { + sScriptMgr->OnFailedEmailChange(accountId); return AccountOpResult::AOR_NAME_NOT_EXIST; // account doesn't exist + } if (utf8length(newEmail) > MAX_EMAIL_STR) + { + sScriptMgr->OnFailedEmailChange(accountId); return AccountOpResult::AOR_EMAIL_TOO_LONG; + } Utf8ToUpperOnlyLatin(username); Utf8ToUpperOnlyLatin(newEmail); @@ -212,6 +226,7 @@ AccountOpResult AccountMgr::ChangeEmail(uint32 accountId, std::string newEmail) LoginDatabase.Execute(stmt); + sScriptMgr->OnEmailChange(accountId); return AccountOpResult::AOR_OK; } @@ -223,7 +238,10 @@ AccountOpResult AccountMgr::ChangeRegEmail(uint32 accountId, std::string newEmai return AccountOpResult::AOR_NAME_NOT_EXIST; // account doesn't exist if (utf8length(newEmail) > MAX_EMAIL_STR) + { + sScriptMgr->OnFailedEmailChange(accountId); return AccountOpResult::AOR_EMAIL_TOO_LONG; + } Utf8ToUpperOnlyLatin(username); Utf8ToUpperOnlyLatin(newEmail); @@ -235,6 +253,7 @@ AccountOpResult AccountMgr::ChangeRegEmail(uint32 accountId, std::string newEmai LoginDatabase.Execute(stmt); + sScriptMgr->OnEmailChange(accountId); return AccountOpResult::AOR_OK; } diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 3d0300a681b..f60cf38cbc8 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -988,7 +988,7 @@ class PlayerTaxi bool SetTaximaskNode(uint32 nodeidx) { uint8 field = uint8((nodeidx - 1) / 8); - uint32 submask = 1 << ((nodeidx-1) % 8); + uint32 submask = 1 << ((nodeidx- 1) % 8); if ((m_taximask[field] & submask) != submask) { m_taximask[field] |= submask; @@ -1021,7 +1021,7 @@ class PlayerTaxi std::deque<uint32> m_TaxiDestinations; }; -std::ostringstream& operator<< (std::ostringstream& ss, PlayerTaxi const& taxi); +std::ostringstream& operator << (std::ostringstream& ss, PlayerTaxi const& taxi); class Player; @@ -2890,8 +2890,8 @@ class Player : public Unit, public GridObject<Player> uint32 _maxPersonalArenaRate; }; -void AddItemsSetItem(Player*player, Item* item); -void RemoveItemsSetItem(Player*player, ItemTemplate const* proto); +void AddItemsSetItem(Player* player, Item* item); +void RemoveItemsSetItem(Player* player, ItemTemplate const* proto); // "the bodies of template functions must be made available in a header file" template <class T> T Player::ApplySpellMod(uint32 spellId, SpellModOp op, T &basevalue, Spell* spell) diff --git a/src/server/game/Handlers/CalendarHandler.cpp b/src/server/game/Handlers/CalendarHandler.cpp index 0461d7290d1..a7de4491a85 100644 --- a/src/server/game/Handlers/CalendarHandler.cpp +++ b/src/server/game/Handlers/CalendarHandler.cpp @@ -264,26 +264,43 @@ void WorldSession::HandleCalendarAddEvent(WorldPacket& recvData) } else { + // client limits the amount of players to be invited to 100 + const uint32 MaxPlayerInvites = 100; + uint32 inviteCount; - recvData >> inviteCount; + uint64 invitee[MaxPlayerInvites]; + uint8 status[MaxPlayerInvites]; + uint8 rank[MaxPlayerInvites]; + + memset(invitee, 0, sizeof(invitee)); + memset(status, 0, sizeof(status)); + memset(rank, 0, sizeof(rank)); + + try + { + recvData >> inviteCount; + + for (uint32 i = 0; i < inviteCount && i < MaxPlayerInvites; ++i) + { + recvData.readPackGUID(invitee[i]); + recvData >> status[i] >> rank[i]; + } + } + catch (ByteBufferException const&) + { + delete calendarEvent; + calendarEvent = NULL; + throw; + } SQLTransaction trans; if (inviteCount > 1) trans = CharacterDatabase.BeginTransaction(); - // client limits the amount of players to be invited to 100 - const uint32 MaxPlayerInvites = 100; - for (uint32 i = 0; i < inviteCount && i < MaxPlayerInvites; ++i) { - uint64 invitee = 0; - uint8 status = 0; - uint8 rank = 0; - recvData.readPackGUID(invitee); - recvData >> status >> rank; - // 946684800 is 01/01/2000 00:00:00 - default response time - CalendarInvite* invite = new CalendarInvite(sCalendarMgr->GetFreeInviteId(), calendarEvent->GetEventId(), invitee, guid, 946684800, CalendarInviteStatus(status), CalendarModerationRank(rank), ""); + CalendarInvite* invite = new CalendarInvite(sCalendarMgr->GetFreeInviteId(), calendarEvent->GetEventId(), invitee[i], guid, 946684800, CalendarInviteStatus(status[i]), CalendarModerationRank(rank[i]), ""); sCalendarMgr->AddInvite(calendarEvent, invite, trans); } diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 86f978f1848..70547136bf8 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -721,10 +721,15 @@ void WorldSession::HandleCharDeleteOpcode(WorldPacket& recvData) { uint64 guid; recvData >> guid; + // Initiating + uint32 initAccountId = GetAccountId(); // can't delete loaded character if (ObjectAccessor::FindPlayer(guid)) + { + sScriptMgr->OnPlayerFailedDelete(guid, initAccountId); return; + } uint32 accountId = 0; uint8 level = 0; @@ -733,6 +738,7 @@ void WorldSession::HandleCharDeleteOpcode(WorldPacket& recvData) // is guild leader if (sGuildMgr->GetGuildByLeader(guid)) { + sScriptMgr->OnPlayerFailedDelete(guid, initAccountId); WorldPacket data(SMSG_CHAR_DELETE, 1); data << uint8(CHAR_DELETE_FAILED_GUILD_LEADER); SendPacket(&data); @@ -742,6 +748,7 @@ void WorldSession::HandleCharDeleteOpcode(WorldPacket& recvData) // is arena team captain if (sArenaTeamMgr->GetArenaTeamByCaptain(guid)) { + sScriptMgr->OnPlayerFailedDelete(guid, initAccountId); WorldPacket data(SMSG_CHAR_DELETE, 1); data << uint8(CHAR_DELETE_FAILED_ARENA_CAPTAIN); SendPacket(&data); @@ -760,12 +767,18 @@ void WorldSession::HandleCharDeleteOpcode(WorldPacket& recvData) } // prevent deleting other players' characters using cheating tools - if (accountId != GetAccountId()) + if (accountId != initAccountId) + { + sScriptMgr->OnPlayerFailedDelete(guid, initAccountId); return; + } std::string IP_str = GetRemoteAddress(); TC_LOG_INFO("entities.player.character", "Account: %d, IP: %s deleted character: %s, GUID: %u, Level: %u", accountId, IP_str.c_str(), name.c_str(), GUID_LOPART(guid), level); - sScriptMgr->OnPlayerDelete(guid); + + // To prevent hook failure, place hook before removing reference from DB + sScriptMgr->OnPlayerDelete(guid, initAccountId); // To prevent race conditioning, but as it also makes sense, we hand the accountId over for successful delete. + // Shouldn't interfere with character deletion though if (sLog->ShouldLog("entities.player.dump", LOG_LEVEL_INFO)) // optimize GetPlayerDump call { diff --git a/src/server/game/Handlers/ItemHandler.cpp b/src/server/game/Handlers/ItemHandler.cpp index 741dec9bfec..d1d2988b255 100644 --- a/src/server/game/Handlers/ItemHandler.cpp +++ b/src/server/game/Handlers/ItemHandler.cpp @@ -1325,6 +1325,10 @@ void WorldSession::HandleItemRefund(WorldPacket &recvData) return; } + // Don't try to refund item currently being disenchanted + if (_player->GetLootGUID() == guid) + return; + GetPlayer()->RefundItem(item); } diff --git a/src/server/game/Handlers/LootHandler.cpp b/src/server/game/Handlers/LootHandler.cpp index b1132efd6a1..f2aee8f8de5 100644 --- a/src/server/game/Handlers/LootHandler.cpp +++ b/src/server/game/Handlers/LootHandler.cpp @@ -347,7 +347,8 @@ void WorldSession::DoLootRelease(uint64 lguid) } else { - if (pItem->loot.isLooted()) // Only delete item if no loot or money (unlooted loot is saved to db) + // Only delete item if no loot or money (unlooted loot is saved to db) or if it isn't an openable item + if (pItem->loot.isLooted() || !(proto->Flags & ITEM_PROTO_FLAG_OPENABLE)) player->DestroyItem(pItem->GetBagSlot(), pItem->GetSlot(), true); } return; // item can be looted only single player diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp index 3c9a43a4557..89cb5745267 100755 --- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp @@ -191,6 +191,10 @@ bool WaypointMovementGenerator<Creature>::DoUpdate(Creature* creature, uint32 di } else { + // Set home position at place on waypoint movement. + if (!creature->GetTransGUID()) + creature->SetHomePosition(creature->GetPosition()); + if (creature->IsStopped()) Stop(STOP_TIME_FOR_PLAYER); else if (creature->movespline->Finalized()) diff --git a/src/server/game/Scripting/ScriptLoader.cpp b/src/server/game/Scripting/ScriptLoader.cpp index d996a7b633e..6c77daa19d2 100644 --- a/src/server/game/Scripting/ScriptLoader.cpp +++ b/src/server/game/Scripting/ScriptLoader.cpp @@ -16,6 +16,7 @@ */ #include "ScriptLoader.h" +#include "World.h" //examples void AddSC_example_creature(); @@ -41,7 +42,7 @@ void AddSC_item_spell_scripts(); void AddSC_example_spell_scripts(); void AddSC_holiday_spell_scripts(); -void AddSC_SmartSCripts(); +void AddSC_SmartScripts(); //Commands void AddSC_account_commandscript(); @@ -98,6 +99,7 @@ void AddSC_npc_innkeeper(); void AddSC_npcs_special(); void AddSC_npc_taxi(); void AddSC_achievement_scripts(); +void AddSC_action_ip_logger(); //eastern kingdoms void AddSC_alterac_valley(); //Alterac Valley @@ -701,6 +703,7 @@ void AddSC_outdoorpvp_zm(); // player void AddSC_chat_log(); +void AddSC_action_ip_logger(); #endif @@ -708,7 +711,7 @@ void AddScripts() { AddExampleScripts(); AddSpellScripts(); - AddSC_SmartSCripts(); + AddSC_SmartScripts(); AddCommandScripts(); #ifdef SCRIPTS AddWorldScripts(); @@ -811,7 +814,10 @@ void AddWorldScripts() AddSC_npcs_special(); AddSC_npc_taxi(); AddSC_achievement_scripts(); - AddSC_chat_log(); + AddSC_chat_log(); // location: scripts\World\chat_log.cpp + // To avoid duplicate code, we check once /*ONLY*/ if logging is permitted or not. + if (sWorld->getBoolConfig(CONFIG_IP_BASED_ACTION_LOGGING)) + AddSC_action_ip_logger(); // location: scripts\World\action_ip_logger.cpp #endif } diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp index 82189485ee6..ee0cb11983e 100644 --- a/src/server/game/Scripting/ScriptMgr.cpp +++ b/src/server/game/Scripting/ScriptMgr.cpp @@ -224,6 +224,7 @@ void ScriptMgr::Unload() SCR_CLEAR(TransportScript); SCR_CLEAR(AchievementCriteriaScript); SCR_CLEAR(PlayerScript); + SCR_CLEAR(AccountScript); SCR_CLEAR(GuildScript); SCR_CLEAR(GroupScript); SCR_CLEAR(UnitScript); @@ -1257,9 +1258,14 @@ void ScriptMgr::OnPlayerCreate(Player* player) FOREACH_SCRIPT(PlayerScript)->OnCreate(player); } -void ScriptMgr::OnPlayerDelete(uint64 guid) +void ScriptMgr::OnPlayerDelete(uint64 guid, uint32 accountId) { - FOREACH_SCRIPT(PlayerScript)->OnDelete(guid); + FOREACH_SCRIPT(PlayerScript)->OnDelete(guid, accountId); +} + +void ScriptMgr::OnPlayerFailedDelete(uint64 guid, uint32 accountId) +{ + FOREACH_SCRIPT(PlayerScript)->OnFailedDelete(guid, accountId); } void ScriptMgr::OnPlayerSave(Player* player) @@ -1277,6 +1283,37 @@ void ScriptMgr::OnPlayerUpdateZone(Player* player, uint32 newZone, uint32 newAre FOREACH_SCRIPT(PlayerScript)->OnUpdateZone(player, newZone, newArea); } +// Account +void ScriptMgr::OnAccountLogin(uint32 accountId) +{ + FOREACH_SCRIPT(AccountScript)->OnAccountLogin(accountId); +} + +void ScriptMgr::OnFailedAccountLogin(uint32 accountId) +{ + FOREACH_SCRIPT(AccountScript)->OnFailedAccountLogin(accountId); +} + +void ScriptMgr::OnEmailChange(uint32 accountId) +{ + FOREACH_SCRIPT(AccountScript)->OnEmailChange(accountId); +} + +void ScriptMgr::OnFailedEmailChange(uint32 accountId) +{ + FOREACH_SCRIPT(AccountScript)->OnFailedEmailChange(accountId); +} + +void ScriptMgr::OnPasswordChange(uint32 accountId) +{ + FOREACH_SCRIPT(AccountScript)->OnPasswordChange(accountId); +} + +void ScriptMgr::OnFailedPasswordChange(uint32 accountId) +{ + FOREACH_SCRIPT(AccountScript)->OnFailedPasswordChange(accountId); +} + // Guild void ScriptMgr::OnGuildAddMember(Guild* guild, Player* player, uint8& plRank) { @@ -1539,6 +1576,12 @@ PlayerScript::PlayerScript(const char* name) ScriptRegistry<PlayerScript>::AddScript(this); } +AccountScript::AccountScript(const char* name) + : ScriptObject(name) +{ + ScriptRegistry<AccountScript>::AddScript(this); +} + GuildScript::GuildScript(const char* name) : ScriptObject(name) { @@ -1581,6 +1624,7 @@ template class ScriptRegistry<PlayerScript>; template class ScriptRegistry<GuildScript>; template class ScriptRegistry<GroupScript>; template class ScriptRegistry<UnitScript>; +template class ScriptRegistry<AccountScript>; // Undefine utility macros. #undef GET_SCRIPT_RET diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index a0845b4c3d1..b8cea691ff8 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -29,6 +29,7 @@ #include "World.h" #include "Weather.h" +class AccountMgr; class AuctionHouseObject; class AuraScript; class Battleground; @@ -752,7 +753,10 @@ class PlayerScript : public UnitScript virtual void OnCreate(Player* /*player*/) { } // Called when a player is deleted. - virtual void OnDelete(uint64 /*guid*/) { } + virtual void OnDelete(uint64 /*guid*/, uint32 /*accountId*/) { } + + // Called when a player delete failed + virtual void OnFailedDelete(uint64 /*guid*/, uint32 /*accountId*/) { } // Called when a player is about to be saved. virtual void OnSave(Player* /*player*/) { } @@ -767,6 +771,33 @@ class PlayerScript : public UnitScript virtual void OnMapChanged(Player* /*player*/) { } }; +class AccountScript : public ScriptObject +{ + protected: + + AccountScript(const char* name); + + public: + + // Called when an account logged in succesfully + virtual void OnAccountLogin(uint32 /*accountI*/d) {} + + // Called when an account login failed + virtual void OnFailedAccountLogin(uint32 /*accountI*/) {} + + // Called when Email is successfully changed for Account + virtual void OnEmailChange(uint32 /*accountI*/) {} + + // Called when Email failed to change for Account + virtual void OnFailedEmailChange(uint32 /*accountI*/) {} + + // Called when Password is successfully changed for Account + virtual void OnPasswordChange(uint32 /*accountI*/) {} + + // Called when Password failed to change for Account + virtual void OnFailedPasswordChange(uint32 /*accountI*/) {} +}; + class GuildScript : public ScriptObject { protected: @@ -1036,11 +1067,21 @@ class ScriptMgr void OnPlayerLogin(Player* player, bool firstLogin); void OnPlayerLogout(Player* player); void OnPlayerCreate(Player* player); - void OnPlayerDelete(uint64 guid); + void OnPlayerDelete(uint64 guid, uint32 accountId); + void OnPlayerFailedDelete(uint64 guid, uint32 accountId); void OnPlayerSave(Player* player); void OnPlayerBindToInstance(Player* player, Difficulty difficulty, uint32 mapid, bool permanent); void OnPlayerUpdateZone(Player* player, uint32 newZone, uint32 newArea); + public: /* AccountScript */ + + void OnAccountLogin(uint32 accountId); + void OnFailedAccountLogin(uint32 accountId); + void OnEmailChange(uint32 accountId); + void OnFailedEmailChange(uint32 accountId); + void OnPasswordChange(uint32 accountId); + void OnFailedPasswordChange(uint32 accountId); + public: /* GuildScript */ void OnGuildAddMember(Guild* guild, Player* player, uint8& plRank); diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 9c44fa8fad3..16ec751f137 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -1037,10 +1037,11 @@ class WorldSession // characters who failed on Player::BuildEnumData shouldn't login std::set<uint32> _legitCharacters; - uint32 m_GUIDLow; // set loggined or recently logout player (while m_playerRecentlyLogout set) + uint32 m_GUIDLow; // set logined or recently logout player (while m_playerRecentlyLogout set) Player* _player; WorldSocket* m_Socket; - std::string m_Address; + std::string m_Address; // Current Remote Address + // std::string m_LAddress; // Last Attempted Remote Adress - we can not set attempted ip for a non-existing session! AccountTypes _security; uint32 _accountId; diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp index 58299a7194c..4e791cef767 100644 --- a/src/server/game/Server/WorldSocket.cpp +++ b/src/server/game/Server/WorldSocket.cpp @@ -276,7 +276,7 @@ int WorldSocket::open (void *a) return 0; } -int WorldSocket::close (u_long) +int WorldSocket::close(u_long) { shutdown(); @@ -287,7 +287,7 @@ int WorldSocket::close (u_long) return 0; } -int WorldSocket::handle_input (ACE_HANDLE) +int WorldSocket::handle_input(ACE_HANDLE) { if (closing_) return -1; @@ -323,7 +323,7 @@ int WorldSocket::handle_input (ACE_HANDLE) ACE_NOTREACHED(return -1); } -int WorldSocket::handle_output (ACE_HANDLE) +int WorldSocket::handle_output(ACE_HANDLE) { ACE_GUARD_RETURN (LockType, Guard, m_OutBufferLock, -1); @@ -369,7 +369,7 @@ int WorldSocket::handle_output (ACE_HANDLE) ACE_NOTREACHED (return 0); } -int WorldSocket::handle_output_queue (GuardType& g) +int WorldSocket::handle_output_queue(GuardType& g) { if (msg_queue()->is_empty()) return cancel_wakeup_output(g); @@ -430,7 +430,7 @@ int WorldSocket::handle_output_queue (GuardType& g) ACE_NOTREACHED(return -1); } -int WorldSocket::handle_close (ACE_HANDLE h, ACE_Reactor_Mask) +int WorldSocket::handle_close(ACE_HANDLE h, ACE_Reactor_Mask) { // Critical section { @@ -630,7 +630,7 @@ int WorldSocket::handle_input_missing_data (void) return size_t(n) == recv_size ? 1 : 2; } -int WorldSocket::cancel_wakeup_output (GuardType& g) +int WorldSocket::cancel_wakeup_output(GuardType& g) { if (!m_OutActive) return 0; @@ -650,7 +650,7 @@ int WorldSocket::cancel_wakeup_output (GuardType& g) return 0; } -int WorldSocket::schedule_wakeup_output (GuardType& g) +int WorldSocket::schedule_wakeup_output(GuardType& g) { if (m_OutActive) return 0; @@ -803,6 +803,7 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) std::string account; SHA1Hash sha; BigNumber k; + bool wardenActive = sWorld->getBoolConfig(CONFIG_WARDEN_ENABLED); WorldPacket addonsData; recvPacket.read_skip<uint32>(); @@ -877,6 +878,7 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) // Stop if the account is not found if (!result) { + // We can not log here, as we do not know the account. Thus, no accountId. SendAuthResponseError(AUTH_UNKNOWN_ACCOUNT); TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Sent Auth Response (unknown account)."); return -1; @@ -889,19 +891,34 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) if (expansion > world_expansion) expansion = world_expansion; + // For hook purposes, we get Remoteaddress at this point. + std::string address = GetRemoteAddress(); + + // As we don't know if attempted login process by ip works, we update last_attempt_ip right away + stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LAST_ATTEMPT_IP); + + stmt->setString(0, address); + stmt->setString(1, account); + + LoginDatabase.Execute(stmt); + // This also allows to check for possible "hack" attempts on account + + // id has to be fetched at this point, so that first actual account response that fails can be logged + id = fields[0].GetUInt32(); + ///- Re-check ip locking (same check as in realmd). if (fields[3].GetUInt8() == 1) // if ip is locked { - if (strcmp (fields[2].GetCString(), GetRemoteAddress().c_str())) + if (strcmp (fields[2].GetCString(), address.c_str())) { SendAuthResponseError(AUTH_FAILED); - TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Sent Auth Response (Account IP differs)."); + TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Sent Auth Response (Account IP differs. Original IP: %s, new IP: %s).", fields[2].GetCString(), address.c_str()); + // We could log on hook only instead of an additional db log, however action logger is config based. Better keep DB logging as well + sScriptMgr->OnFailedAccountLogin(id); return -1; } } - id = fields[0].GetUInt32(); - k.SetHexStr(fields[1].GetCString()); int64 mutetime = fields[5].GetInt64(); @@ -926,10 +943,10 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) std::string os = fields[8].GetString(); // Must be done before WorldSession is created - if (sWorld->getBoolConfig(CONFIG_WARDEN_ENABLED) && os != "Win" && os != "OSX") + if (wardenActive && os != "Win" && os != "OSX") { SendAuthResponseError(AUTH_REJECT); - TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Client %s attempted to log in using invalid client OS (%s).", GetRemoteAddress().c_str(), os.c_str()); + TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Client %s attempted to log in using invalid client OS (%s).", address.c_str(), os.c_str()); return -1; } @@ -953,7 +970,7 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BANS); stmt->setUInt32(0, id); - stmt->setString(1, GetRemoteAddress()); + stmt->setString(1, address); PreparedQueryResult banresult = LoginDatabase.Query(stmt); @@ -961,6 +978,7 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) { SendAuthResponseError(AUTH_BANNED); TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Sent Auth Response (Account banned)."); + sScriptMgr->OnFailedAccountLogin(id); return -1; } @@ -971,6 +989,7 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) { SendAuthResponseError(AUTH_UNAVAILABLE); TC_LOG_INFO("network", "WorldSocket::HandleAuthSession: User tries to login but his security level is not enough"); + sScriptMgr->OnFailedAccountLogin(id); return -1; } @@ -985,8 +1004,6 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) sha.UpdateBigNumbers(&k, NULL); sha.Finalize(); - std::string address = GetRemoteAddress(); - if (memcmp(sha.GetDigest(), digest, 20)) { SendAuthResponseError(AUTH_FAILED); @@ -1009,8 +1026,7 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) if (result) isRecruiter = true; - // Update the last_ip in the database - + // Update the last_ip in the database as it was successful for login stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LAST_IP); stmt->setString(0, address); @@ -1028,8 +1044,11 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) m_Session->ReadAddonsInfo(addonsData); m_Session->LoadPermissions(); + // At this point, we can safely hook a successful login + sScriptMgr->OnAccountLogin(id); + // Initialize Warden system only if it is enabled by config - if (sWorld->getBoolConfig(CONFIG_WARDEN_ENABLED)) + if (wardenActive) m_Session->InitWarden(&k, os); // Sleep this Network thread for @@ -1040,7 +1059,7 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) return 0; } -int WorldSocket::HandlePing (WorldPacket& recvPacket) +int WorldSocket::HandlePing(WorldPacket& recvPacket) { uint32 ping; uint32 latency; diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index bdfe78d941a..708ff579182 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -700,10 +700,10 @@ void Spell::EffectTriggerSpell(SpellEffIndex effIndex) { // remove all harmful spells on you... SpellInfo const* spell = iter->second->GetBase()->GetSpellInfo(); - if ((spell->DmgClass == SPELL_DAMAGE_CLASS_MAGIC // only affect magic spells - || ((spell->GetDispelMask()) & dispelMask)) + if (((spell->DmgClass == SPELL_DAMAGE_CLASS_MAGIC && spell->GetSchoolMask() != SPELL_SCHOOL_MASK_NORMAL) // only affect magic spells + || (spell->GetDispelMask() & dispelMask)) && // ignore positive and passive auras - && !iter->second->IsPositive() && !iter->second->GetBase()->IsPassive()) + !iter->second->IsPositive() && !iter->second->GetBase()->IsPassive()) { m_caster->RemoveAura(iter); } diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 4375ad09e4c..d04b8b6015a 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1297,6 +1297,10 @@ void World::LoadConfigSettings(bool reload) m_int_configs[CONFIG_PACKET_SPOOF_BANDURATION] = sConfigMgr->GetIntDefault("PacketSpoof.BanDuration", 86400); + m_bool_configs[CONFIG_IP_BASED_ACTION_LOGGING] = sConfigMgr->GetBoolDefault("Allow.IP.Based.Action.Logging", false); + + m_bool_configs[CONFIG_IP_BASED_LOGIN_LOGGING] = sConfigMgr->GetBoolDefault("Wrong.Password.Login.Logging", false); + // call ScriptMgr if we're reloading the configuration if (reload) sScriptMgr->OnConfigLoad(reload); diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index 7495939887a..9afb3c5e788 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -162,6 +162,8 @@ enum WorldBoolConfigs CONFIG_EVENT_ANNOUNCE, CONFIG_STATS_LIMITS_ENABLE, CONFIG_INSTANCES_RESET_ANNOUNCE, + CONFIG_IP_BASED_ACTION_LOGGING, + CONFIG_IP_BASED_LOGIN_LOGGING, BOOL_CONFIG_VALUE_COUNT }; diff --git a/src/server/scripts/Commands/cs_account.cpp b/src/server/scripts/Commands/cs_account.cpp index 3406c25eb16..c040f04b2bb 100644 --- a/src/server/scripts/Commands/cs_account.cpp +++ b/src/server/scripts/Commands/cs_account.cpp @@ -376,6 +376,7 @@ public: if (!AccountMgr::CheckEmail(handler->GetSession()->GetAccountId(), std::string(oldEmail))) { handler->SendSysMessage(LANG_COMMAND_WRONGEMAIL); + sScriptMgr->OnFailedEmailChange(handler->GetSession()->GetAccountId()); handler->SetSentErrorMessage(true); TC_LOG_INFO("entities.player.character", "Account: %u (IP: %s) Character:[%s] (GUID: %u) Tried to change email, but the provided email [%s] is not equal to registration email [%s].", handler->GetSession()->GetAccountId(), handler->GetSession()->GetRemoteAddress().c_str(), @@ -387,6 +388,7 @@ public: if (!AccountMgr::CheckPassword(handler->GetSession()->GetAccountId(), std::string(password))) { handler->SendSysMessage(LANG_COMMAND_WRONGOLDPASSWORD); + sScriptMgr->OnFailedEmailChange(handler->GetSession()->GetAccountId()); handler->SetSentErrorMessage(true); TC_LOG_INFO("entities.player.character", "Account: %u (IP: %s) Character:[%s] (GUID: %u) Tried to change email, but the provided password is wrong.", handler->GetSession()->GetAccountId(), handler->GetSession()->GetRemoteAddress().c_str(), @@ -397,6 +399,7 @@ public: if (strcmp(email, oldEmail) == 0) { handler->SendSysMessage(LANG_OLD_EMAIL_IS_NEW_EMAIL); + sScriptMgr->OnFailedEmailChange(handler->GetSession()->GetAccountId()); handler->SetSentErrorMessage(true); return false; } @@ -404,6 +407,7 @@ public: if (strcmp(email, emailConfirmation) != 0) { handler->SendSysMessage(LANG_NEW_EMAILS_NOT_MATCH); + sScriptMgr->OnFailedEmailChange(handler->GetSession()->GetAccountId()); handler->SetSentErrorMessage(true); TC_LOG_INFO("entities.player.character", "Account: %u (IP: %s) Character:[%s] (GUID: %u) Tried to change email, but the provided password is wrong.", handler->GetSession()->GetAccountId(), handler->GetSession()->GetRemoteAddress().c_str(), @@ -417,6 +421,7 @@ public: { case AccountOpResult::AOR_OK: handler->SendSysMessage(LANG_COMMAND_EMAIL); + sScriptMgr->OnEmailChange(handler->GetSession()->GetAccountId()); TC_LOG_INFO("entities.player.character", "Account: %u (IP: %s) Character:[%s] (GUID: %u) Changed Email from [%s] to [%s].", handler->GetSession()->GetAccountId(), handler->GetSession()->GetRemoteAddress().c_str(), handler->GetSession()->GetPlayer()->GetName().c_str(), handler->GetSession()->GetPlayer()->GetGUIDLow(), @@ -424,6 +429,7 @@ public: break; case AccountOpResult::AOR_EMAIL_TOO_LONG: handler->SendSysMessage(LANG_EMAIL_TOO_LONG); + sScriptMgr->OnFailedEmailChange(handler->GetSession()->GetAccountId()); handler->SetSentErrorMessage(true); return false; default: @@ -468,6 +474,7 @@ public: if (!AccountMgr::CheckPassword(handler->GetSession()->GetAccountId(), std::string(oldPassword))) { handler->SendSysMessage(LANG_COMMAND_WRONGOLDPASSWORD); + sScriptMgr->OnFailedPasswordChange(handler->GetSession()->GetAccountId()); handler->SetSentErrorMessage(true); TC_LOG_INFO("entities.player.character", "Account: %u (IP: %s) Character:[%s] (GUID: %u) Tried to change password, but the provided old password is wrong.", handler->GetSession()->GetAccountId(), handler->GetSession()->GetRemoteAddress().c_str(), @@ -480,6 +487,7 @@ public: && !AccountMgr::CheckEmail(handler->GetSession()->GetAccountId(), std::string(emailConfirmation))) // ... and returns false if the comparison fails. { handler->SendSysMessage(LANG_COMMAND_WRONGEMAIL); + sScriptMgr->OnFailedPasswordChange(handler->GetSession()->GetAccountId()); handler->SetSentErrorMessage(true); TC_LOG_INFO("entities.player.character", "Account: %u (IP: %s) Character:[%s] (GUID: %u) Tried to change password, but the entered email [%s] is wrong.", handler->GetSession()->GetAccountId(), handler->GetSession()->GetRemoteAddress().c_str(), @@ -492,6 +500,7 @@ public: if (strcmp(newPassword, passwordConfirmation) != 0) { handler->SendSysMessage(LANG_NEW_PASSWORDS_NOT_MATCH); + sScriptMgr->OnFailedPasswordChange(handler->GetSession()->GetAccountId()); handler->SetSentErrorMessage(true); return false; } @@ -502,12 +511,14 @@ public: { case AccountOpResult::AOR_OK: handler->SendSysMessage(LANG_COMMAND_PASSWORD); + sScriptMgr->OnPasswordChange(handler->GetSession()->GetAccountId()); TC_LOG_INFO("entities.player.character", "Account: %u (IP: %s) Character:[%s] (GUID: %u) Changed Password.", handler->GetSession()->GetAccountId(), handler->GetSession()->GetRemoteAddress().c_str(), handler->GetSession()->GetPlayer()->GetName().c_str(), handler->GetSession()->GetPlayer()->GetGUIDLow()); break; case AccountOpResult::AOR_PASS_TOO_LONG: handler->SendSysMessage(LANG_PASSWORD_TOO_LONG); + sScriptMgr->OnFailedPasswordChange(handler->GetSession()->GetAccountId()); handler->SetSentErrorMessage(true); return false; default: diff --git a/src/server/scripts/Custom/CMakeLists.txt b/src/server/scripts/Custom/CMakeLists.txt index 78db719ae6e..80ebe36b555 100644 --- a/src/server/scripts/Custom/CMakeLists.txt +++ b/src/server/scripts/Custom/CMakeLists.txt @@ -8,8 +8,11 @@ # WITHOUT ANY WARRANTY, to the extent permitted by law; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# file(GLOB_RECURSE sources_Custom Custom/*.cpp Custom/*.h) + set(scripts_STAT_SRCS ${scripts_STAT_SRCS} +# ${sources_Custom} ) message(" -> Prepared: Custom") diff --git a/src/server/scripts/Events/CMakeLists.txt b/src/server/scripts/Events/CMakeLists.txt index e45bc585007..3bdb6e6eac2 100644 --- a/src/server/scripts/Events/CMakeLists.txt +++ b/src/server/scripts/Events/CMakeLists.txt @@ -8,9 +8,11 @@ # WITHOUT ANY WARRANTY, to the extent permitted by law; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +file(GLOB_RECURSE sources_Events Events/*.cpp Events/*.h) + set(scripts_STAT_SRCS ${scripts_STAT_SRCS} - Events/childrens_week.cpp + ${sources_Events} ) message(" -> Prepared: Events") diff --git a/src/server/scripts/World/CMakeLists.txt b/src/server/scripts/World/CMakeLists.txt index 7d1b46732cf..56a0a1eb4c7 100644 --- a/src/server/scripts/World/CMakeLists.txt +++ b/src/server/scripts/World/CMakeLists.txt @@ -8,20 +8,11 @@ # WITHOUT ANY WARRANTY, to the extent permitted by law; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +file(GLOB_RECURSE sources_World World/*.cpp World/*.h) + set(scripts_STAT_SRCS ${scripts_STAT_SRCS} - World/achievement_scripts.cpp - World/areatrigger_scripts.cpp - World/boss_emerald_dragons.cpp - World/chat_log.cpp - World/go_scripts.cpp - World/guards.cpp - World/item_scripts.cpp - World/mob_generic_creature.cpp - World/npc_innkeeper.cpp - World/npc_professions.cpp - World/npc_taxi.cpp - World/npcs_special.cpp + ${sources_World} ) message(" -> Prepared: World") diff --git a/src/server/scripts/World/action_ip_logger.cpp b/src/server/scripts/World/action_ip_logger.cpp new file mode 100644 index 00000000000..73d92fb631a --- /dev/null +++ b/src/server/scripts/World/action_ip_logger.cpp @@ -0,0 +1,315 @@ +/* + * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 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 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 <http://www.gnu.org/licenses/>. + */ + +#include "ScriptMgr.h" +#include "Channel.h" +#include "Guild.h" +#include "Group.h" + +enum IPLoggingTypes +{ + + // AccountActionIpLogger(); + ACCOUNT_LOGIN = 0, + ACCOUNT_FAIL_LOGIN = 1, + ACCOUNT_CHANGE_PW = 2, + ACCOUNT_CHANGE_PW_FAIL = 3, // Only two types of account changes exist... + ACCOUNT_CHANGE_EMAIL = 4, + ACCOUNT_CHANGE_EMAIL_FAIL = 5, // ...so we log them individually + // OBSOLETE - ACCOUNT_LOGOUT = 6, /* Can not be logged. We still keep the type however */ + // CharacterActionIpLogger(); + CHARACTER_CREATE = 7, + CHARACTER_LOGIN = 8, + CHARACTER_LOGOUT = 9, + // CharacterDeleteActionIpLogger(); + CHARACTER_DELETE = 10, + CHARACTER_FAILED_DELETE = 11, + // AccountActionIpLogger(), CharacterActionIpLogger(), CharacterActionIpLogger(); + UNKNOWN_ACTION = 12 +}; + +class AccountActionIpLogger : public AccountScript +{ + public: + AccountActionIpLogger() : AccountScript("AccountActionIpLogger") { } + + // We log last_ip instead of last_attempt_ip, as login was successful + // ACCOUNT_LOGIN = 0 + void OnAccountLogin(uint32 accountId) + { + AccountIPLogAction(accountId, ACCOUNT_LOGIN); + } + + // We log last_attempt_ip instead of last_ip, as failed login doesn't necessarily mean approperiate user + // ACCOUNT_FAIL_LOGIN = 1 + void OnFailedAccountLogin(uint32 accountId) + { + AccountIPLogAction(accountId, ACCOUNT_FAIL_LOGIN); + } + + // ACCOUNT_CHANGE_PW = 2 + void OnPasswordChange(uint32 accountId) + { + AccountIPLogAction(accountId, ACCOUNT_CHANGE_PW); + } + + // ACCOUNT_CHANGE_PW_FAIL = 3 + void OnFailedPasswordChange(uint32 accountId) + { + AccountIPLogAction(accountId, ACCOUNT_CHANGE_PW_FAIL); + } + + // Registration Email can NOT be changed apart from GM level users. Thus, we do not require to log them... + // ACCOUNT_CHANGE_EMAIL = 4 + void OnEmailChange(uint32 accountId) + { + AccountIPLogAction(accountId, ACCOUNT_CHANGE_EMAIL); // ... they get logged by gm command logger anyway + } + + // ACCOUNT_CHANGE_EMAIL_FAIL = 5 + void OnFailedEmailChange(uint32 accountId) + { + AccountIPLogAction(accountId, ACCOUNT_CHANGE_EMAIL_FAIL); + } + + /* It's impossible to log the account logout process out of character selection - shouldn't matter anyway, + * as ip doesn't change through playing (obviously).*/ + // ACCOUNT_LOGOUT = 6 + void AccountIPLogAction(uint32 accountId, IPLoggingTypes aType) + { + // Action IP Logger is only intialized if config is set up + // Else, this script isn't loaded in the first place: We require no config check. + + // We declare all the required variables + uint32 playerGuid = accountId; + uint32 characterGuid = 0; + std::string systemNote = "ERROR"; // "ERROR" is a placeholder here. We change it later. + + // With this switch, we change systemNote so that we have a more accurate phrasing of what type it is. + // Avoids Magicnumbers in SQL table + switch (aType) + { + case ACCOUNT_LOGIN: + systemNote = "Logged on Successful AccountLogin"; + break; + case ACCOUNT_FAIL_LOGIN: + systemNote = "Logged on Failed AccountLogin"; + break; + case ACCOUNT_CHANGE_PW: + systemNote = "Logged on Successful Account Password Change"; + break; + case ACCOUNT_CHANGE_PW_FAIL: + systemNote = "Logged on Failed Account Password Change"; + break; + case ACCOUNT_CHANGE_EMAIL: + systemNote = "Logged on Successful Account Email Change"; + break; + case ACCOUNT_CHANGE_EMAIL_FAIL: + systemNote = "Logged on Failed Account Email Change"; + break; + /*case ACCOUNT_LOGOUT: + systemNote = "Logged on AccountLogout"; //Can not be logged + break;*/ + // Neither should happen. Ever. Period. If it does, call Ghostbusters and all your local software defences to investigate. + case UNKNOWN_ACTION: + default: + systemNote = "ERROR! Unknown action!"; + break; + } + + // Once we have done everything, we can insert the new log. + // Seeing as the time differences should be minimal, we do not get unixtime and the timestamp right now; + // Rather, we let it be added with the SQL query. + if (aType != ACCOUNT_FAIL_LOGIN) + { + // As we can assume most account actions are NOT failed login, so this is the more accurate check. + // For those, we need last_ip... + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_ALDL_IP_LOGGING); + + stmt->setUInt32(0, playerGuid); + stmt->setUInt32(1, characterGuid); + stmt->setUInt8(2, aType); + stmt->setUInt32(3, playerGuid); + stmt->setString(4, systemNote.c_str()); + LoginDatabase.Execute(stmt); + } + else // ... but for failed login, we query last_attempt_ip from account table. Which we do with an unique query + { + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_FACL_IP_LOGGING); + + stmt->setUInt32(0, playerGuid); + stmt->setUInt32(1, characterGuid); + stmt->setUInt8(2, aType); + stmt->setUInt32(3, playerGuid); + stmt->setString(4, systemNote.c_str()); + LoginDatabase.Execute(stmt); + } + return; + } +}; + +class CharacterActionIpLogger : public PlayerScript +{ + public: + CharacterActionIpLogger() : PlayerScript("CharacterActionIpLogger") { } + + // CHARACTER_CREATE = 7 + void OnCreate(Player* player) + { + CharacterIPLogAction(player, CHARACTER_CREATE); + } + + // CHARACTER_LOGIN = 8 + void OnLogin(Player* player, bool /*firstLogin*/) + { + CharacterIPLogAction(player, CHARACTER_LOGIN); + } + + // CHARACTER_LOGOUT = 9 + void OnLogout(Player* player) + { + CharacterIPLogAction(player, CHARACTER_LOGOUT); + } + + // CHARACTER_DELETE = 10 + // CHARACTER_FAILED_DELETE = 11 + // We don't log either here - they require a guid + + // UNKNOWN_ACTION = 12 + // There is no real hook we could use for that. + // Shouldn't happen anyway, should it ? Nothing to see here. + + /// Logs a number of actions done by players with an IP + void CharacterIPLogAction(Player* player, IPLoggingTypes aType) + { + // Action IP Logger is only intialized if config is set up + // Else, this script isn't loaded in the first place: We require no config check. + + // We declare all the required variables + uint32 playerGuid = player->GetSession()->GetAccountId(); + uint32 characterGuid = player->GetGUIDLow(); + const std::string currentIp = player->GetSession()->GetRemoteAddress(); + std::string systemNote = "ERROR"; // "ERROR" is a placeholder here. We change it... + + // ... with this switch, so that we have a more accurate phrasing of what type it is + switch (aType) + { + case CHARACTER_CREATE: + systemNote = "Logged on CharacterCreate"; + break; + case CHARACTER_LOGIN: + systemNote = "Logged on CharacterLogin"; + break; + case CHARACTER_LOGOUT: + systemNote = "Logged on CharacterLogout"; + break; + case CHARACTER_DELETE: + systemNote = "Logged on CharacterDelete"; + break; + case CHARACTER_FAILED_DELETE: + systemNote = "Logged on Failed CharacterDelete"; + break; + // Neither should happen. Ever. Period. If it does, call Mythbusters. + case UNKNOWN_ACTION: + default: + systemNote = "ERROR! Unknown action!"; + break; + } + + // Once we have done everything, we can insert the new log. + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_CHAR_IP_LOGGING); + + stmt->setUInt32(0, playerGuid); + stmt->setUInt32(1, characterGuid); + stmt->setUInt8(2, aType); + stmt->setString(3, currentIp.c_str()); // We query the ip here. + stmt->setString(4, systemNote.c_str()); + // Seeing as the time differences should be minimal, we do not get unixtime and the timestamp right now; + // Rather, we let it be added with the SQL query. + + LoginDatabase.Execute(stmt); + return; + } +}; + +class CharacterDeleteActionIpLogger : public PlayerScript +{ +public: + CharacterDeleteActionIpLogger() : PlayerScript("CharacterDeleteActionIpLogger") { } + + // CHARACTER_DELETE = 10 + void OnDelete(uint64 guid, uint32 accountId) + { + DeleteIPLogAction(guid, accountId, CHARACTER_DELETE); + } + + // CHARACTER_FAILED_DELETE = 11 + void OnFailedDelete(uint64 guid, uint32 accountId) + { + DeleteIPLogAction(guid, accountId, CHARACTER_FAILED_DELETE); + } + + void DeleteIPLogAction(uint64 guid, uint32 playerGuid, IPLoggingTypes aType) + { + // Action IP Logger is only intialized if config is set up + // Else, this script isn't loaded in the first place: We require no config check. + + // We declare all the required variables + uint32 characterGuid = GUID_LOPART(guid); // We have no access to any member function of Player* or WorldSession*. So use old-fashioned way. + // Query playerGuid/accountId, as we only have characterGuid + std::string systemNote = "ERROR"; // "ERROR" is a placeholder here. We change it later. + + // With this switch, we change systemNote so that we have a more accurate phrasing of what type it is. + // Avoids Magicnumbers in SQL table + switch (aType) + { + case CHARACTER_DELETE: + systemNote = "Logged on CharacterDelete"; + break; + case CHARACTER_FAILED_DELETE: + systemNote = "Logged on Failed CharacterDelete"; + break; + // Neither should happen. Ever. Period. If it does, call to whatever god you have for mercy and guidance. + case UNKNOWN_ACTION: + default: + systemNote = "ERROR! Unknown action!"; + break; + } + + // Once we have done everything, we can insert the new log. + PreparedStatement* stmt2 = LoginDatabase.GetPreparedStatement(LOGIN_INS_ALDL_IP_LOGGING); + + stmt2->setUInt32(0, playerGuid); + stmt2->setUInt32(1, characterGuid); + stmt2->setUInt8(2, aType); + stmt2->setUInt32(3, playerGuid); + stmt2->setString(4, systemNote.c_str()); + // Seeing as the time differences should be minimal, we do not get unixtime and the timestamp right now; + // Rather, we let it be added with the SQL query. + + LoginDatabase.Execute(stmt2); + return; + } +}; + + +void AddSC_action_ip_logger() +{ + new AccountActionIpLogger(); + new CharacterActionIpLogger(); + new CharacterDeleteActionIpLogger(); +} diff --git a/src/server/shared/Database/Implementation/LoginDatabase.cpp b/src/server/shared/Database/Implementation/LoginDatabase.cpp index 5623bd56a95..8dc63fa48f2 100644 --- a/src/server/shared/Database/Implementation/LoginDatabase.cpp +++ b/src/server/shared/Database/Implementation/LoginDatabase.cpp @@ -70,6 +70,7 @@ void LoginDatabaseConnection::DoPrepareStatements() PrepareStatement(LOGIN_UPD_MUTE_TIME, "UPDATE account SET mutetime = ? , mutereason = ? , muteby = ? WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_MUTE_TIME_LOGIN, "UPDATE account SET mutetime = ? WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_LAST_IP, "UPDATE account SET last_ip = ? WHERE username = ?", CONNECTION_ASYNC); + PrepareStatement(LOGIN_UPD_LAST_ATTEMPT_IP, "UPDATE account SET last_attempt_ip = ? WHERE username = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_ACCOUNT_ONLINE, "UPDATE account SET online = 1 WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_UPTIME_PLAYERS, "UPDATE uptime SET uptime = ?, maxplayers = ? WHERE realmid = ? AND starttime = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_DEL_OLD_LOGS, "DELETE FROM logs WHERE (time + ?) < ?", CONNECTION_ASYNC); @@ -91,12 +92,21 @@ void LoginDatabaseConnection::DoPrepareStatements() PrepareStatement(LOGIN_SEL_ACCOUNT_RECRUITER, "SELECT 1 FROM account WHERE recruiter = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_BANS, "SELECT 1 FROM account_banned WHERE id = ? AND active = 1 UNION SELECT 1 FROM ip_banned WHERE ip = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_ACCOUNT_WHOIS, "SELECT username, email, last_ip FROM account WHERE id = ?", CONNECTION_SYNCH); + PrepareStatement(LOGIN_SEL_LAST_ATTEMPT_IP, "SELECT last_attempt_ip FROM account WHERE id = ?", CONNECTION_SYNCH); + PrepareStatement(LOGIN_SEL_LAST_IP, "SELECT last_ip FROM account WHERE id = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_REALMLIST_SECURITY_LEVEL, "SELECT allowedSecurityLevel from realmlist WHERE id = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_DEL_ACCOUNT, "DELETE FROM account WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_SEL_IP2NATION_COUNTRY, "SELECT c.country FROM ip2nationCountries c, ip2nation i WHERE i.ip < ? AND c.code = i.country ORDER BY i.ip DESC LIMIT 0,1", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_AUTOBROADCAST, "SELECT id, weight, text FROM autobroadcast WHERE realmid = ? OR realmid = -1", CONNECTION_SYNCH); PrepareStatement(LOGIN_GET_EMAIL_BY_ID, "SELECT email FROM account WHERE id = ?", CONNECTION_SYNCH); - + // 0: uint32, 1: uint32, 2: uint8, 3: uint32, 4: string // Complete name: "Login_Insert_AccountLoginDeLete_IP_Logging" + PrepareStatement(LOGIN_INS_ALDL_IP_LOGGING, "INSERT INTO logs_ip_actions (account_id,character_guid,type,ip,systemnote,unixtime,time) VALUES (?, ?, ?, (SELECT last_ip FROM account WHERE id = ?), ?, unix_timestamp(NOW()), NOW())", CONNECTION_ASYNC); + // 0: uint32, 1: uint32, 2: uint8, 3: uint32, 4: string // Complete name: "Login_Insert_FailedAccountLogin_IP_Logging" + PrepareStatement(LOGIN_INS_FACL_IP_LOGGING, "INSERT INTO logs_ip_actions (account_id,character_guid,type,ip,systemnote,unixtime,time) VALUES (?, ?, ?, (SELECT last_attempt_ip FROM account WHERE id = ?), ?, unix_timestamp(NOW()), NOW())", CONNECTION_ASYNC); + // 0: uint32, 1: uint32, 2: uint8, 3: string, 4: string // Complete name: "Login_Insert_CharacterDelete_IP_Logging" + PrepareStatement(LOGIN_INS_CHAR_IP_LOGGING, "INSERT INTO logs_ip_actions (account_id,character_guid,type,ip,systemnote,unixtime,time) VALUES (?, ?, ?, ?, ?, unix_timestamp(NOW()), NOW())", CONNECTION_ASYNC); + // 0: string, 1: string, 2: string // Complete name: "Login_Insert_Failed_Account_Login_due_password_IP_Logging" + PrepareStatement(LOGIN_INS_FALP_IP_LOGGING, "INSERT INTO logs_ip_actions (account_id,character_guid,type,ip,systemnote,unixtime,time) VALUES ((SELECT id FROM account WHERE username = ?), 0, 1, ?, ?, unix_timestamp(NOW()), NOW())", CONNECTION_ASYNC); PrepareStatement(LOGIN_SEL_ACCOUNT_ACCESS_BY_ID, "SELECT gmlevel, RealmID FROM account_access WHERE id = ? and (RealmID = ? OR RealmID = -1) ORDER BY gmlevel desc", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_RBAC_ACCOUNT_PERMISSIONS, "SELECT permissionId, granted FROM rbac_account_permissions WHERE accountId = ? AND (realmId = ? OR realmId = -1) ORDER BY permissionId, realmId", CONNECTION_SYNCH); diff --git a/src/server/shared/Database/Implementation/LoginDatabase.h b/src/server/shared/Database/Implementation/LoginDatabase.h index 3b4558874bc..83432009cbf 100644 --- a/src/server/shared/Database/Implementation/LoginDatabase.h +++ b/src/server/shared/Database/Implementation/LoginDatabase.h @@ -90,6 +90,7 @@ enum LoginDatabaseStatements LOGIN_UPD_MUTE_TIME, LOGIN_UPD_MUTE_TIME_LOGIN, LOGIN_UPD_LAST_IP, + LOGIN_UPD_LAST_ATTEMPT_IP, LOGIN_UPD_ACCOUNT_ONLINE, LOGIN_UPD_UPTIME_PLAYERS, LOGIN_DEL_OLD_LOGS, @@ -115,7 +116,13 @@ enum LoginDatabaseStatements LOGIN_DEL_ACCOUNT, LOGIN_SEL_IP2NATION_COUNTRY, LOGIN_SEL_AUTOBROADCAST, + LOGIN_SEL_LAST_ATTEMPT_IP, + LOGIN_SEL_LAST_IP, LOGIN_GET_EMAIL_BY_ID, + LOGIN_INS_ALDL_IP_LOGGING, + LOGIN_INS_FACL_IP_LOGGING, + LOGIN_INS_CHAR_IP_LOGGING, + LOGIN_INS_FALP_IP_LOGGING, LOGIN_SEL_ACCOUNT_ACCESS_BY_ID, LOGIN_SEL_RBAC_ACCOUNT_PERMISSIONS, diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index 12d25d98b19..11a4bffee38 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -2694,6 +2694,13 @@ Logger.sql.sql=5,Console DBErrors Log.Async.Enable = 0 # +# Allow.IP.Based.Action.Logging +# Description: Logs actions, e.g. account login and logout to name a few, based on IP of current session. +# Default: 0 - (Disabled) +# 1 - (Enabled) + +Allow.IP.Based.Action.Logging = 0 +# ################################################################################################### ################################################################################################### |