From 3da83cc0e6862a5b005482996e3b6ca89495a793 Mon Sep 17 00:00:00 2001 From: Kargatum Date: Fri, 18 Jun 2021 03:02:08 +0700 Subject: [PATCH] feat(Common/IPLocation): replace ip2nation by ip2location (#5653) --- data/sql/base/db_auth/ip2nation.sql | 28 ---- data/sql/base/db_auth/ip2nationCountries.sql | 34 ----- .../rev_1620146306002634000.sql | 5 + src/common/IPLocation/IPLocation.cpp | 120 ++++++++++++++++++ src/common/IPLocation/IPLocation.h | 37 ++++++ src/server/authserver/Main.cpp | 25 ++-- src/server/authserver/Server/AuthSocket.cpp | 32 ++--- src/server/authserver/Server/AuthSocket.h | 1 + src/server/authserver/authserver.conf.dist | 9 ++ .../Database/Implementation/LoginDatabase.cpp | 4 +- .../Database/Implementation/LoginDatabase.h | 4 +- src/server/game/Server/WorldSocket.cpp | 35 ++--- src/server/game/Server/WorldSocket.h | 2 + src/server/game/World/World.cpp | 4 + src/server/scripts/Commands/cs_account.cpp | 102 ++++++++++----- src/server/scripts/Commands/cs_misc.cpp | 21 +-- src/server/worldserver/worldserver.conf.dist | 10 ++ 17 files changed, 306 insertions(+), 167 deletions(-) delete mode 100644 data/sql/base/db_auth/ip2nation.sql delete mode 100644 data/sql/base/db_auth/ip2nationCountries.sql create mode 100644 data/sql/updates/pending_db_auth/rev_1620146306002634000.sql create mode 100644 src/common/IPLocation/IPLocation.cpp create mode 100644 src/common/IPLocation/IPLocation.h diff --git a/data/sql/base/db_auth/ip2nation.sql b/data/sql/base/db_auth/ip2nation.sql deleted file mode 100644 index ba1aebf71..000000000 --- a/data/sql/base/db_auth/ip2nation.sql +++ /dev/null @@ -1,28 +0,0 @@ -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; -DROP TABLE IF EXISTS `ip2nation`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = UTF8MB4 */; -CREATE TABLE `ip2nation` -( - `ip` INT unsigned NOT NULL DEFAULT 0, - `country` char(2) NOT NULL DEFAULT '', - KEY `ip` (`ip`) -) ENGINE=InnoDB DEFAULT CHARSET=UTF8MB4; -/*!40101 SET character_set_client = @saved_cs_client */; - -LOCK TABLES `ip2nation` WRITE; -/*!40000 ALTER TABLE `ip2nation` DISABLE KEYS */; -/*!40000 ALTER TABLE `ip2nation` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - diff --git a/data/sql/base/db_auth/ip2nationCountries.sql b/data/sql/base/db_auth/ip2nationCountries.sql deleted file mode 100644 index a14ddd46c..000000000 --- a/data/sql/base/db_auth/ip2nationCountries.sql +++ /dev/null @@ -1,34 +0,0 @@ -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; -DROP TABLE IF EXISTS `ip2nationCountries`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = UTF8MB4 */; -CREATE TABLE `ip2nationCountries` -( - `code` varchar(4) NOT NULL DEFAULT '', - `iso_code_2` varchar(2) NOT NULL DEFAULT '', - `iso_code_3` varchar(3) DEFAULT '', - `iso_country` varchar(255) NOT NULL DEFAULT '', - `country` varchar(255) NOT NULL DEFAULT '', - `lat` float NOT NULL DEFAULT 0, - `lon` float NOT NULL DEFAULT 0, - PRIMARY KEY (`code`), - KEY `code` (`code`) -) ENGINE=InnoDB DEFAULT CHARSET=UTF8MB4; -/*!40101 SET character_set_client = @saved_cs_client */; - -LOCK TABLES `ip2nationCountries` WRITE; -/*!40000 ALTER TABLE `ip2nationCountries` DISABLE KEYS */; -/*!40000 ALTER TABLE `ip2nationCountries` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - diff --git a/data/sql/updates/pending_db_auth/rev_1620146306002634000.sql b/data/sql/updates/pending_db_auth/rev_1620146306002634000.sql new file mode 100644 index 000000000..37255cdde --- /dev/null +++ b/data/sql/updates/pending_db_auth/rev_1620146306002634000.sql @@ -0,0 +1,5 @@ +INSERT INTO `version_db_auth` (`sql_rev`) VALUES ('1620146306002634000'); + +-- +DROP TABLE IF EXISTS `ip2nation`; +DROP TABLE IF EXISTS `ip2nationCountries`; diff --git a/src/common/IPLocation/IPLocation.cpp b/src/common/IPLocation/IPLocation.cpp new file mode 100644 index 000000000..2ceada74a --- /dev/null +++ b/src/common/IPLocation/IPLocation.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2021+ WarheadCore + */ + +#include "IPLocation.h" +#include "Common.h" +#include "Config.h" +#include "Errors.h" +#include "IpAddress.h" +#include "Log.h" +#include "StringConvert.h" +#include +#include + +IpLocationStore::IpLocationStore() +{ +} + +IpLocationStore::~IpLocationStore() +{ +} + +void IpLocationStore::Load() +{ + _ipLocationStore.clear(); + LOG_INFO("server.loading", "Loading IP Location Database..."); + + std::string databaseFilePath = sConfigMgr->GetOption("IPLocationFile", ""); + if (databaseFilePath.empty()) + { + LOG_INFO("server.loading", " "); + return; + } + + // Check if file exists + std::ifstream databaseFile(databaseFilePath); + if (!databaseFile) + { + LOG_ERROR("server.loading", "IPLocation: No ip database file exists (%s).", databaseFilePath.c_str()); + return; + } + + if (!databaseFile.is_open()) + { + LOG_ERROR("server.loading", "IPLocation: Ip database file (%s) can not be opened.", databaseFilePath.c_str()); + return; + } + + std::string ipFrom; + std::string ipTo; + std::string countryCode; + std::string countryName; + + while (databaseFile.good()) + { + // Read lines + if (!std::getline(databaseFile, ipFrom, ',')) + break; + if (!std::getline(databaseFile, ipTo, ',')) + break; + if (!std::getline(databaseFile, countryCode, ',')) + break; + if (!std::getline(databaseFile, countryName, '\n')) + break; + + // Remove new lines and return + countryName.erase(std::remove(countryName.begin(), countryName.end(), '\r'), countryName.end()); + countryName.erase(std::remove(countryName.begin(), countryName.end(), '\n'), countryName.end()); + + // Remove quotation marks + ipFrom.erase(std::remove(ipFrom.begin(), ipFrom.end(), '"'), ipFrom.end()); + ipTo.erase(std::remove(ipTo.begin(), ipTo.end(), '"'), ipTo.end()); + countryCode.erase(std::remove(countryCode.begin(), countryCode.end(), '"'), countryCode.end()); + countryName.erase(std::remove(countryName.begin(), countryName.end(), '"'), countryName.end()); + + // Convert country code to lowercase + std::transform(countryCode.begin(), countryCode.end(), countryCode.begin(), ::tolower); + + auto IpFrom = Acore::StringTo(ipFrom); + auto IpTo = Acore::StringTo(ipTo); + + if (!IpFrom || !IpTo) + continue; + + _ipLocationStore.emplace_back(*IpFrom, *IpTo, std::move(countryCode), std::move(countryName)); + } + + std::sort(_ipLocationStore.begin(), _ipLocationStore.end(), [](IpLocationRecord const& a, IpLocationRecord const& b) { return a.IpFrom < b.IpFrom; }); + ASSERT(std::is_sorted(_ipLocationStore.begin(), _ipLocationStore.end(), [](IpLocationRecord const& a, IpLocationRecord const& b) { return a.IpFrom < b.IpTo; }), + "Overlapping IP ranges detected in database file"); + + databaseFile.close(); + + LOG_INFO("server.loading", ">> Loaded %u ip location entries.", static_cast(_ipLocationStore.size())); + LOG_INFO("server.loading", " "); +} + +IpLocationRecord const* IpLocationStore::GetLocationRecord(std::string const& ipAddress) const +{ + uint32 ip = Acore::Net::address_to_uint(Acore::Net::make_address_v4(ipAddress)); + auto itr = std::upper_bound(_ipLocationStore.begin(), _ipLocationStore.end(), ip, [](uint32 ip, IpLocationRecord const& loc) { return ip < loc.IpTo; }); + if (itr == _ipLocationStore.end()) + { + return nullptr; + } + + if (ip < itr->IpFrom) + { + return nullptr; + } + + return &(*itr); +} + +IpLocationStore* IpLocationStore::instance() +{ + static IpLocationStore instance; + return &instance; +} diff --git a/src/common/IPLocation/IPLocation.h b/src/common/IPLocation/IPLocation.h new file mode 100644 index 000000000..9a65f9290 --- /dev/null +++ b/src/common/IPLocation/IPLocation.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2021+ WarheadCore + */ + +#include "Define.h" +#include +#include + +struct IpLocationRecord +{ + IpLocationRecord() : + IpFrom(0), IpTo(0) { } + IpLocationRecord(uint32 ipFrom, uint32 ipTo, std::string countryCode, std::string countryName) : + IpFrom(ipFrom), IpTo(ipTo), CountryCode(std::move(countryCode)), CountryName(std::move(countryName)) { } + + uint32 IpFrom; + uint32 IpTo; + std::string CountryCode; + std::string CountryName; +}; + +class AC_COMMON_API IpLocationStore +{ +public: + IpLocationStore(); + ~IpLocationStore(); + static IpLocationStore* instance(); + + void Load(); + IpLocationRecord const* GetLocationRecord(std::string const& ipAddress) const; + +private: + std::vector _ipLocationStore; +}; + +#define sIPLocation IpLocationStore::instance() diff --git a/src/server/authserver/Main.cpp b/src/server/authserver/Main.cpp index 479efac5f..da4bd599f 100644 --- a/src/server/authserver/Main.cpp +++ b/src/server/authserver/Main.cpp @@ -12,29 +12,29 @@ * authentication server */ +#include "AppenderDB.h" #include "Banner.h" #include "Common.h" -#include "AppenderDB.h" -#include "DatabaseEnv.h" #include "Config.h" -#include "Log.h" -#include "GitRevision.h" -#include "Util.h" -#include "SignalHandler.h" -#include "RealmList.h" -#include "RealmAcceptor.h" +#include "DatabaseEnv.h" #include "DatabaseLoader.h" +#include "GitRevision.h" +#include "IPLocation.h" +#include "Log.h" +#include "RealmAcceptor.h" +#include "RealmList.h" #include "SecretMgr.h" #include "SharedDefines.h" +#include "SignalHandler.h" #include "Util.h" #include "ProcessPriority.h" -#include -#include #include +#include #include +#include #include -#include #include +#include #ifndef _ACORE_REALM_CONFIG #define _ACORE_REALM_CONFIG "authserver.conf" @@ -128,6 +128,9 @@ extern int main(int argc, char** argv) sSecretMgr->Initialize(); + // Load IP Location Database + sIPLocation->Load(); + // Get the list of realms for the server sRealmList->Initialize(sConfigMgr->GetOption("RealmsStateUpdateDelay", 20)); if (sRealmList->GetRealms().empty()) diff --git a/src/server/authserver/Server/AuthSocket.cpp b/src/server/authserver/Server/AuthSocket.cpp index bb4dff905..102ef5ca2 100644 --- a/src/server/authserver/Server/AuthSocket.cpp +++ b/src/server/authserver/Server/AuthSocket.cpp @@ -14,6 +14,7 @@ #include "CryptoHash.h" #include "CryptoRandom.h" #include "DatabaseEnv.h" +#include "IPLocation.h" #include "Log.h" #include "RealmList.h" #include "SecretMgr.h" @@ -419,31 +420,20 @@ bool AuthSocket::_HandleLogonChallenge() } else { - LOG_DEBUG("network", "[AuthChallenge] Account '%s' is not locked to ip", _accountInfo.Login.c_str()); + if (IpLocationRecord const* location = sIPLocation->GetLocationRecord(ipAddress)) + _ipCountry = location->CountryCode; + LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is not locked to ip", _accountInfo.Login.c_str()); if (_accountInfo.LockCountry.empty() || _accountInfo.LockCountry == "00") + LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is not locked to country", _accountInfo.Login.c_str()); + else if (!_ipCountry.empty()) { - LOG_DEBUG("network", "[AuthChallenge] Account '%s' is not locked to country", _accountInfo.Login.c_str()); - } - else if (!_accountInfo.LockCountry.empty()) - { - uint32 ip = inet_addr(ipAddress.c_str()); - EndianConvertReverse(ip); - - stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_LOGON_COUNTRY); - stmt->setUInt32(0, ip); - - if (PreparedQueryResult sessionCountryQuery = LoginDatabase.Query(stmt)) + LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is locked to country: '%s' Player country is '%s'", _accountInfo.Login.c_str(), _accountInfo.LockCountry.c_str(), _ipCountry.c_str()); + if (_ipCountry != _accountInfo.LockCountry) { - std::string loginCountry = (*sessionCountryQuery)[0].GetString(); - LOG_DEBUG("network", "[AuthChallenge] Account '%s' is locked to country: '%s' Player country is '%s'", _accountInfo.Login.c_str(), _accountInfo.LockCountry.c_str(), loginCountry.c_str()); - if (loginCountry != _accountInfo.LockCountry) - { - LOG_DEBUG("network", "[AuthChallenge] Account country differs."); - pkt << uint8(WOW_FAIL_UNLOCKABLE_LOCK); - SendAuthPacket(); - return true; - } + pkt << uint8(WOW_FAIL_UNLOCKABLE_LOCK); + SendAuthPacket(); + return true; } } } diff --git a/src/server/authserver/Server/AuthSocket.h b/src/server/authserver/Server/AuthSocket.h index 9d04cb530..20ca60e53 100644 --- a/src/server/authserver/Server/AuthSocket.h +++ b/src/server/authserver/Server/AuthSocket.h @@ -89,6 +89,7 @@ private: // between enUS and enGB, which is important for the patch system std::string _localizationName; std::string _os; + std::string _ipCountry; uint16 _build; uint8 _expversion; }; diff --git a/src/server/authserver/authserver.conf.dist b/src/server/authserver/authserver.conf.dist index 365ece324..11bd6853d 100644 --- a/src/server/authserver/authserver.conf.dist +++ b/src/server/authserver/authserver.conf.dist @@ -134,6 +134,15 @@ WrongPass.BanType = 0 WrongPass.Logging = 0 +# +# IPLocationFile +# Description: The path to your IP2Location database CSV file. +# Example: "C:/acore/IP2LOCATION-LITE-DB1.CSV" +# "/home/acore/IP2LOCATION-LITE-DB1.CSV" +# Default: "" - (Disabled) + +IPLocationFile = "" + # ################################################################################################### diff --git a/src/server/database/Database/Implementation/LoginDatabase.cpp b/src/server/database/Database/Implementation/LoginDatabase.cpp index 4d9305bbc..7c05d5024 100644 --- a/src/server/database/Database/Implementation/LoginDatabase.cpp +++ b/src/server/database/Database/Implementation/LoginDatabase.cpp @@ -35,7 +35,6 @@ void LoginDatabaseConnection::DoPrepareStatements() PrepareStatement(LOGIN_DEL_ACCOUNT_BANNED, "DELETE FROM account_banned WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_LOGON, "UPDATE account SET salt = ?, verifier = ? WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_LOGONPROOF, "UPDATE account SET session_key = ?, last_ip = ?, last_login = NOW(), locale = ?, failed_logins = 0, os = ? WHERE username = ?", CONNECTION_SYNCH); - PrepareStatement(LOGIN_SEL_LOGON_COUNTRY, "SELECT country FROM ip2nation WHERE ip < ? ORDER BY ip DESC LIMIT 0,1", CONNECTION_SYNCH); PrepareStatement(LOGIN_UPD_FAILEDLOGINS, "UPDATE account SET failed_logins = failed_logins + 1 WHERE username = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_SEL_FAILEDLOGINS, "SELECT id, failed_logins FROM account WHERE username = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_ACCOUNT_ID_BY_NAME, "SELECT id FROM account WHERE username = ?", CONNECTION_SYNCH); @@ -56,7 +55,7 @@ void LoginDatabaseConnection::DoPrepareStatements() PrepareStatement(LOGIN_INS_REALM_CHARACTERS_INIT, "INSERT INTO realmcharacters (realmid, acctid, numchars) SELECT realmlist.id, account.id, 0 FROM realmlist, account LEFT JOIN realmcharacters ON acctid=account.id WHERE acctid IS NULL", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_EXPANSION, "UPDATE account SET expansion = ? WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_ACCOUNT_LOCK, "UPDATE account SET locked = ? WHERE id = ?", CONNECTION_ASYNC); - PrepareStatement(LOGIN_UPD_ACCOUNT_LOCK_CONTRY, "UPDATE account SET lock_country = ? WHERE id = ?", CONNECTION_ASYNC); + PrepareStatement(LOGIN_UPD_ACCOUNT_LOCK_COUNTRY, "UPDATE account SET lock_country = ? WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_USERNAME, "UPDATE account SET username = ? WHERE id = ?", CONNECTION_ASYNC); 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); @@ -87,7 +86,6 @@ void LoginDatabaseConnection::DoPrepareStatements() 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_INS_ACCOUNT_MUTE, "INSERT INTO account_muted VALUES (?, UNIX_TIMESTAMP(), ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(LOGIN_SEL_ACCOUNT_MUTE_INFO, "SELECT mutedate, mutetime, mutereason, mutedby FROM account_muted WHERE guid = ? ORDER BY mutedate ASC", CONNECTION_SYNCH); diff --git a/src/server/database/Database/Implementation/LoginDatabase.h b/src/server/database/Database/Implementation/LoginDatabase.h index 7f88158da..d1126d10c 100644 --- a/src/server/database/Database/Implementation/LoginDatabase.h +++ b/src/server/database/Database/Implementation/LoginDatabase.h @@ -45,7 +45,6 @@ enum LoginDatabaseStatements LOGIN_UPD_LOGONPROOF, LOGIN_SEL_LOGONCHALLENGE, LOGIN_SEL_RECONNECTCHALLENGE, - LOGIN_SEL_LOGON_COUNTRY, LOGIN_UPD_FAILEDLOGINS, LOGIN_SEL_FAILEDLOGINS, LOGIN_SEL_ACCOUNT_ID_BY_NAME, @@ -69,7 +68,7 @@ enum LoginDatabaseStatements LOGIN_INS_REALM_CHARACTERS_INIT, LOGIN_UPD_EXPANSION, LOGIN_UPD_ACCOUNT_LOCK, - LOGIN_UPD_ACCOUNT_LOCK_CONTRY, + LOGIN_UPD_ACCOUNT_LOCK_COUNTRY, LOGIN_UPD_USERNAME, LOGIN_UPD_MUTE_TIME, LOGIN_UPD_MUTE_TIME_LOGIN, @@ -98,7 +97,6 @@ enum LoginDatabaseStatements LOGIN_SEL_ACCOUNT_WHOIS, LOGIN_SEL_REALMLIST_SECURITY_LEVEL, LOGIN_DEL_ACCOUNT, - LOGIN_SEL_IP2NATION_COUNTRY, LOGIN_SEL_AUTOBROADCAST, LOGIN_SEL_LAST_ATTEMPT_IP, LOGIN_SEL_LAST_IP, diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp index 93dcef7e4..3bf054ce0 100644 --- a/src/server/game/Server/WorldSocket.cpp +++ b/src/server/game/Server/WorldSocket.cpp @@ -11,6 +11,7 @@ #include "CryptoHash.h" #include "CryptoRandom.h" #include "DatabaseEnv.h" +#include "IPLocation.h" #include "Log.h" #include "Opcodes.h" #include "PacketLog.h" @@ -25,13 +26,13 @@ #include "WorldSocket.h" #include "WorldSocketMgr.h" #include +#include +#include +#include #include #include #include #include -#include -#include -#include #include #ifdef ELUNA @@ -921,8 +922,8 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) return -1; } - /*if (IpLocationRecord const* location = sIPLocation->GetLocationRecord(address)) - _ipCountry = location->CountryCode;*/ + if (IpLocationRecord const* location = sIPLocation->GetLocationRecord(address)) + _ipCountry = location->CountryCode; ///- Re-check ip locking (same check as in auth). if (account.IsLockedToIP) @@ -938,18 +939,18 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) return -1; } } - //else if (!account.LockCountry.empty() && account.LockCountry != "00" && !_ipCountry.empty()) - //{ - // if (account.LockCountry != _ipCountry) - // { - // packet.Initialize(SMSG_AUTH_RESPONSE, 1); - // packet << uint8(AUTH_REJECT); - // LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Sent Auth Response (Account country differs. Original country: %s, new country: %s).", account.LockCountry.c_str(), _ipCountry.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(account.Id); - // return -1; - // } - //} + else if (!account.LockCountry.empty() && account.LockCountry != "00" && !_ipCountry.empty()) + { + if (account.LockCountry != _ipCountry) + { + packet.Initialize(SMSG_AUTH_RESPONSE, 1); + packet << uint8(AUTH_REJECT); + LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Sent Auth Response (Account country differs. Original country: %s, new country: %s).", account.LockCountry.c_str(), _ipCountry.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(account.Id); + return -1; + } + } if (account.IsBanned) { diff --git a/src/server/game/Server/WorldSocket.h b/src/server/game/Server/WorldSocket.h index b273d8904..3bd34e2bd 100644 --- a/src/server/game/Server/WorldSocket.h +++ b/src/server/game/Server/WorldSocket.h @@ -191,6 +191,8 @@ private: bool m_OutActive; std::array m_Seed; + + std::string _ipCountry; }; #endif /* _WORLDSOCKET_H */ diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index a434a8e95..c9f6c3aac 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -41,6 +41,7 @@ #include "GuildMgr.h" #include "InstanceSaveMgr.h" #include "ItemEnchantmentMgr.h" +#include "IPLocation.h" #include "Language.h" #include "LFGMgr.h" #include "Log.h" @@ -1503,6 +1504,9 @@ void World::SetInitialWorldSettings() LoadDBCStores(m_dataPath); DetectDBCLang(); + // Load IP Location Database + sIPLocation->Load(); + std::vector mapIds; for (auto const& map : sMapStore) mapIds.emplace_back(map->MapID); diff --git a/src/server/scripts/Commands/cs_account.cpp b/src/server/scripts/Commands/cs_account.cpp index 6444a06d3..c5a1056c4 100644 --- a/src/server/scripts/Commands/cs_account.cpp +++ b/src/server/scripts/Commands/cs_account.cpp @@ -16,6 +16,7 @@ EndScriptData */ #include "Base32.h" #include "Chat.h" #include "CryptoGenerics.h" +#include "IPLocation.h" #include "Language.h" #include "Player.h" #include "Realm.h" @@ -23,8 +24,8 @@ EndScriptData */ #include "SecretMgr.h" #include "StringConvert.h" #include "TOTP.h" -#include #include +#include class account_commandscript : public CommandScript { @@ -35,35 +36,41 @@ public: { static std::vector accountSetCommandTable = { - { "addon", SEC_GAMEMASTER, true, &HandleAccountSetAddonCommand, "" }, - { "gmlevel", SEC_CONSOLE, true, &HandleAccountSetGmLevelCommand, "" }, - { "password", SEC_CONSOLE, true, &HandleAccountSetPasswordCommand, "" }, - { "2fa", SEC_PLAYER, true, &HandleAccountSet2FACommand, "" } + { "addon", SEC_GAMEMASTER, true, &HandleAccountSetAddonCommand, "" }, + { "gmlevel", SEC_CONSOLE, true, &HandleAccountSetGmLevelCommand, "" }, + { "password", SEC_CONSOLE, true, &HandleAccountSetPasswordCommand, "" }, + { "2fa", SEC_PLAYER, true, &HandleAccountSet2FACommand, "" } }; static std::vector accountLockCommandTable { - { "country", SEC_PLAYER, true, &HandleAccountLockCountryCommand, "" }, - { "ip", SEC_PLAYER, true, &HandleAccountLockIpCommand, "" } + { "country", SEC_PLAYER, true, &HandleAccountLockCountryCommand, "" }, + { "ip", SEC_PLAYER, true, &HandleAccountLockIpCommand, "" } }; static std::vector account2faCommandTable { - { "setup", SEC_PLAYER, false, &HandleAccount2FASetupCommand, "" }, - { "remove", SEC_PLAYER, false, &HandleAccount2FARemoveCommand, "" }, + { "setup", SEC_PLAYER, false, &HandleAccount2FASetupCommand, "" }, + { "remove", SEC_PLAYER, false, &HandleAccount2FARemoveCommand, "" }, + }; + + static std::vector accountRemoveCommandTable + { + { "country", SEC_ADMINISTRATOR, true, &HandleAccountRemoveLockCountryCommand, "" } }; static std::vector accountCommandTable = { - { "2fa", SEC_PLAYER, true, nullptr, "", account2faCommandTable }, - { "addon", SEC_MODERATOR, false, &HandleAccountAddonCommand, "" }, - { "create", SEC_CONSOLE, true, &HandleAccountCreateCommand, "" }, - { "delete", SEC_CONSOLE, true, &HandleAccountDeleteCommand, "" }, - { "onlinelist", SEC_CONSOLE, true, &HandleAccountOnlineListCommand, "" }, - { "lock", SEC_PLAYER, false, nullptr, "", accountLockCommandTable }, - { "set", SEC_ADMINISTRATOR, true, nullptr, "", accountSetCommandTable }, - { "password", SEC_PLAYER, false, &HandleAccountPasswordCommand, "" }, - { "", SEC_PLAYER, false, &HandleAccountCommand, "" } + { "2fa", SEC_PLAYER, true, nullptr, "", account2faCommandTable }, + { "addon", SEC_MODERATOR, false, &HandleAccountAddonCommand, "" }, + { "create", SEC_CONSOLE, true, &HandleAccountCreateCommand, "" }, + { "delete", SEC_CONSOLE, true, &HandleAccountDeleteCommand, "" }, + { "onlinelist", SEC_CONSOLE, true, &HandleAccountOnlineListCommand, "" }, + { "lock", SEC_PLAYER, false, nullptr, "", accountLockCommandTable }, + { "set", SEC_ADMINISTRATOR, true, nullptr, "", accountSetCommandTable }, + { "password", SEC_PLAYER, false, &HandleAccountPasswordCommand, "" }, + { "remove", SEC_ADMINISTRATOR, true, nullptr, "", accountRemoveCommandTable }, + { "", SEC_PLAYER, false, &HandleAccountCommand, "" } }; static std::vector commandTable = @@ -412,6 +419,45 @@ public: return true; } + static bool HandleAccountRemoveLockCountryCommand(ChatHandler* handler, char const* args) + { + if (!*args) + { + handler->SendSysMessage(LANG_CMD_SYNTAX); + handler->SetSentErrorMessage(true); + return false; + } + + ///- %Parse the command line arguments + char* _accountName = strtok((char*)args, " "); + if (!_accountName) + return false; + + std::string accountName = _accountName; + if (!Utf8ToUpperOnlyLatin(accountName)) + { + handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str()); + handler->SetSentErrorMessage(true); + return false; + } + + uint32 accountId = AccountMgr::GetId(accountName); + if (!accountId) + { + handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str()); + handler->SetSentErrorMessage(true); + return false; + } + + auto* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_ACCOUNT_LOCK_COUNTRY); + stmt->setString(0, "00"); + stmt->setUInt32(1, accountId); + LoginDatabase.Execute(stmt); + handler->PSendSysMessage(LANG_COMMAND_ACCLOCKUNLOCKED); + + return true; + } + static bool HandleAccountLockCountryCommand(ChatHandler* handler, char const* args) { if (!*args) @@ -427,30 +473,24 @@ public: { if (param == "on") { - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_LOGON_COUNTRY); - uint32 ip = inet_addr(handler->GetSession()->GetRemoteAddress().c_str()); - EndianConvertReverse(ip); - stmt->setUInt32(0, ip); - PreparedQueryResult result = LoginDatabase.Query(stmt); - if (result) + if (IpLocationRecord const* location = sIPLocation->GetLocationRecord(handler->GetSession()->GetRemoteAddress())) { - Field* fields = result->Fetch(); - std::string country = fields[0].GetString(); - stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_ACCOUNT_LOCK_CONTRY); - stmt->setString(0, country); + auto* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_ACCOUNT_LOCK_COUNTRY); + stmt->setString(0, location->CountryCode); stmt->setUInt32(1, handler->GetSession()->GetAccountId()); LoginDatabase.Execute(stmt); handler->PSendSysMessage(LANG_COMMAND_ACCLOCKLOCKED); } else { - handler->PSendSysMessage("[IP2NATION] Table empty"); - ;//LOG_DEBUG("network", "[IP2NATION] Table empty"); + handler->PSendSysMessage("No IP2Location information - account not locked"); + handler->SetSentErrorMessage(true); + return false; } } else if (param == "off") { - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_ACCOUNT_LOCK_CONTRY); + auto* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_ACCOUNT_LOCK_COUNTRY); stmt->setString(0, "00"); stmt->setUInt32(1, handler->GetSession()->GetAccountId()); LoginDatabase.Execute(stmt); diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 5542d9e35..fc0f1b1ec 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -16,6 +16,7 @@ #include "GroupMgr.h" #include "GuildMgr.h" #include "InstanceSaveMgr.h" +#include "IPLocation.h" #include "Language.h" #include "LFG.h" #include "MapManager.h" @@ -1928,29 +1929,11 @@ public: lastIp = fields[4].GetString(); lastLogin = fields[5].GetString(); - /** if (IpLocationRecord const* location = sIPLocation->GetLocationRecord(lastIp)) + if (IpLocationRecord const* location = sIPLocation->GetLocationRecord(lastIp)) { lastIp.append(" ("); lastIp.append(location->CountryName); lastIp.append(")"); - } **/ - - uint32 ip = inet_addr(lastIp.c_str()); -#if ACORE_ENDIAN == BIGENDIAN - EndianConvertReverse(ip); -#endif - stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_IP2NATION_COUNTRY); - - stmt->setUInt32(0, ip); - - PreparedQueryResult result2 = LoginDatabase.Query(stmt); - - if (result2) - { - Field* fields2 = result2->Fetch(); - lastIp.append(" ("); - lastIp.append(fields2[0].GetString()); - lastIp.append(")"); } } else diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index d1f03a7c8..00f8ef3ba 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -157,6 +157,16 @@ WorldServerPort = 8085 BindIP = "0.0.0.0" +# +# IPLocationFile +# Description: The path to your IP2Location database CSV file. +# Example: "C:/acore/IP2LOCATION-LITE-DB1.CSV" +# "/home/acore/IP2LOCATION-LITE-DB1.CSV" +# Default: "" - (Disabled) +# + +IPLocationFile = "" + # ###################################################################################################