From c522e5f4c22b53b349b05486fa28cdf58f4ffc26 Mon Sep 17 00:00:00 2001 From: Shauren Date: Thu, 14 Nov 2024 00:13:18 +0100 Subject: [PATCH] Core/Misc: Support IPv6 ip2location --- src/common/Asio/IpAddress.h | 1 + src/common/Cryptography/BigNumber.cpp | 6 +++ src/common/Cryptography/BigNumber.h | 2 + src/common/IPLocation/IPLocation.cpp | 63 ++++++++++++++++--------- src/common/IPLocation/IPLocation.h | 13 +++-- src/server/game/Server/WorldSession.cpp | 2 +- 6 files changed, 60 insertions(+), 27 deletions(-) diff --git a/src/common/Asio/IpAddress.h b/src/common/Asio/IpAddress.h index 22af8bbfd41dd..7d85b0028acc5 100644 --- a/src/common/Asio/IpAddress.h +++ b/src/common/Asio/IpAddress.h @@ -28,6 +28,7 @@ namespace Trinity using boost::asio::ip::make_address; using boost::asio::ip::make_address_v4; using boost::asio::ip::make_address_v6; + using boost::asio::ip::v4_mapped_t::v4_mapped; inline uint32 address_to_uint(boost::asio::ip::address_v4 const& address) { return address.to_uint(); } } } diff --git a/src/common/Cryptography/BigNumber.cpp b/src/common/Cryptography/BigNumber.cpp index f220ca319beac..66071006a7f41 100644 --- a/src/common/Cryptography/BigNumber.cpp +++ b/src/common/Cryptography/BigNumber.cpp @@ -61,6 +61,12 @@ void BigNumber::SetBinary(uint8 const* bytes, int32 len, bool littleEndian) BN_bin2bn(bytes, len, _bn); } +bool BigNumber::SetDecStr(char const* str) +{ + int n = BN_dec2bn(&_bn, str); + return n > 0; +} + bool BigNumber::SetHexStr(char const* str) { int n = BN_hex2bn(&_bn, str); diff --git a/src/common/Cryptography/BigNumber.h b/src/common/Cryptography/BigNumber.h index 3ec6cc655905d..3815339d9cf0c 100644 --- a/src/common/Cryptography/BigNumber.h +++ b/src/common/Cryptography/BigNumber.h @@ -46,6 +46,8 @@ class TC_COMMON_API BigNumber void SetBinary(uint8 const* bytes, int32 len, bool littleEndian = true); template auto SetBinary(Container const& c, bool littleEndian = true) -> std::enable_if_t>> { SetBinary(std::data(c), std::size(c), littleEndian); } + bool SetDecStr(char const* str); + bool SetDecStr(std::string const& str) { return SetDecStr(str.c_str()); } bool SetHexStr(char const* str); bool SetHexStr(std::string const& str) { return SetHexStr(str.c_str()); } diff --git a/src/common/IPLocation/IPLocation.cpp b/src/common/IPLocation/IPLocation.cpp index 7c6bb455e2202..72383bd355454 100644 --- a/src/common/IPLocation/IPLocation.cpp +++ b/src/common/IPLocation/IPLocation.cpp @@ -16,22 +16,17 @@ */ #include "IPLocation.h" +#include "BigNumber.h" #include "Config.h" #include "Errors.h" #include "IpAddress.h" #include "Log.h" -#include "StringConvert.h" #include "Util.h" #include #include -IpLocationStore::IpLocationStore() -{ -} - -IpLocationStore::~IpLocationStore() -{ -} +IpLocationStore::IpLocationStore() = default; +IpLocationStore::~IpLocationStore() = default; void IpLocationStore::Load() { @@ -60,6 +55,21 @@ void IpLocationStore::Load() std::string ipTo; std::string countryCode; std::string countryName; + BigNumber bnParser; + BigNumber ipv4Max(0xFFFFFFFF); + BigNumber ipv6MappedMask(0xFFFF); + ipv6MappedMask <<= 32; + + auto parseStringToIPv6 = [&](std::string const& str) -> Optional> + { + bnParser.SetDecStr(str); + if (!bnParser.SetDecStr(str)) + return {}; + // convert ipv4 to ipv6 v4 mapped value + if (bnParser <= ipv4Max) + bnParser += ipv6MappedMask; + return bnParser.ToByteArray<16>(false); + }; while (databaseFile.good()) { @@ -74,31 +84,33 @@ void IpLocationStore::Load() 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()); + std::erase_if(countryName, [](char c) { return c == '\r' || c == '\n'; }); // 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()); + std::erase(ipFrom, '"'); + std::erase(ipTo, '"'); + std::erase(countryCode, '"'); + std::erase(countryName, '"'); + + if (countryCode == "-") + continue; // Convert country code to lowercase strToLower(countryCode); - Optional from = Trinity::StringTo(ipFrom); + Optional> from = parseStringToIPv6(ipFrom); if (!from) continue; - Optional to = Trinity::StringTo(ipTo); + Optional> to = parseStringToIPv6(ipTo); if (!to) continue; _ipLocationStore.emplace_back(*from, *to, 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; }), + std::ranges::sort(_ipLocationStore, {}, &IpLocationRecord::IpFrom); + ASSERT(std::ranges::is_sorted(_ipLocationStore, [](IpLocationRecord const& a, IpLocationRecord const& b) { return a.IpFrom < b.IpTo; }), "Overlapping IP ranges detected in database file"); databaseFile.close(); @@ -109,16 +121,23 @@ void IpLocationStore::Load() IpLocationRecord const* IpLocationStore::GetLocationRecord(std::string const& ipAddress) const { boost::system::error_code error; - boost::asio::ip::address_v4 address = Trinity::Net::make_address_v4(ipAddress, error); + boost::asio::ip::address address = Trinity::Net::make_address(ipAddress, error); if (error) return nullptr; - uint32 ip = Trinity::Net::address_to_uint(address); - auto itr = std::upper_bound(_ipLocationStore.begin(), _ipLocationStore.end(), ip, [](uint32 ip, IpLocationRecord const& loc) { return ip < loc.IpTo; }); + std::array bytes = [&]() -> std::array + { + if (address.is_v6()) + return address.to_v6().to_bytes(); + if (address.is_v4()) + return Trinity::Net::make_address_v6(Trinity::Net::v4_mapped, address.to_v4()).to_bytes(); + return {}; + }(); + auto itr = std::ranges::upper_bound(_ipLocationStore, bytes, {}, &IpLocationRecord::IpTo); if (itr == _ipLocationStore.end()) return nullptr; - if (ip < itr->IpFrom) + if (bytes < itr->IpFrom) return nullptr; return &(*itr); diff --git a/src/common/IPLocation/IPLocation.h b/src/common/IPLocation/IPLocation.h index ba97e5caa962b..5471948586b33 100644 --- a/src/common/IPLocation/IPLocation.h +++ b/src/common/IPLocation/IPLocation.h @@ -19,17 +19,18 @@ #define IPLOCATION_H #include "Define.h" +#include #include #include struct IpLocationRecord { - IpLocationRecord() : IpFrom(0), IpTo(0) { } - IpLocationRecord(uint32 ipFrom, uint32 ipTo, std::string countryCode, std::string countryName) + IpLocationRecord() : IpFrom(), IpTo() { } + IpLocationRecord(std::array ipFrom, std::array ipTo, std::string&& countryCode, std::string&& countryName) : IpFrom(ipFrom), IpTo(ipTo), CountryCode(std::move(countryCode)), CountryName(std::move(countryName)) { } - uint32 IpFrom; - uint32 IpTo; + std::array IpFrom; + std::array IpTo; std::string CountryCode; std::string CountryName; }; @@ -38,6 +39,10 @@ class TC_COMMON_API IpLocationStore { public: IpLocationStore(); + IpLocationStore(IpLocationStore const&) = delete; + IpLocationStore(IpLocationStore&&) = delete; + IpLocationStore& operator=(IpLocationStore const&) = delete; + IpLocationStore& operator=(IpLocationStore&&) = delete; ~IpLocationStore(); static IpLocationStore* Instance(); diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 0f19c3115aa9b..a1f5fb23d42bb 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -827,7 +827,7 @@ void WorldSession::SendConnectToInstance(WorldPackets::Auth::ConnectToSerial ser } else if (v6.is_v4_mapped()) { - memcpy(connectTo.Payload.Where.Address.V4.data(), Trinity::Net::make_address_v4(boost::asio::ip::v4_mapped, v6).to_bytes().data(), 4); + memcpy(connectTo.Payload.Where.Address.V4.data(), Trinity::Net::make_address_v4(Trinity::Net::v4_mapped, v6).to_bytes().data(), 4); connectTo.Payload.Where.Type = WorldPackets::Auth::ConnectTo::IPv4; } else