Skip to content

Commit

Permalink
Core/Misc: Support IPv6 ip2location
Browse files Browse the repository at this point in the history
  • Loading branch information
Shauren committed Nov 13, 2024
1 parent e8d949c commit c522e5f
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 27 deletions.
1 change: 1 addition & 0 deletions src/common/Asio/IpAddress.h
Original file line number Diff line number Diff line change
Expand Up @@ -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(); }
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/common/Cryptography/BigNumber.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions src/common/Cryptography/BigNumber.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ class TC_COMMON_API BigNumber
void SetBinary(uint8 const* bytes, int32 len, bool littleEndian = true);
template <typename Container>
auto SetBinary(Container const& c, bool littleEndian = true) -> std::enable_if_t<!std::is_pointer_v<std::decay_t<Container>>> { 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()); }

Expand Down
63 changes: 41 additions & 22 deletions src/common/IPLocation/IPLocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <algorithm>
#include <fstream>

IpLocationStore::IpLocationStore()
{
}

IpLocationStore::~IpLocationStore()
{
}
IpLocationStore::IpLocationStore() = default;
IpLocationStore::~IpLocationStore() = default;

void IpLocationStore::Load()
{
Expand Down Expand Up @@ -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<std::array<uint8, 16>>
{
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())
{
Expand All @@ -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<uint32> from = Trinity::StringTo<uint32>(ipFrom);
Optional<std::array<uint8, 16>> from = parseStringToIPv6(ipFrom);
if (!from)
continue;

Optional<uint32> to = Trinity::StringTo<uint32>(ipTo);
Optional<std::array<uint8, 16>> 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();
Expand All @@ -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<uint8, 16> bytes = [&]() -> std::array<uint8, 16>
{
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);
Expand Down
13 changes: 9 additions & 4 deletions src/common/IPLocation/IPLocation.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,18 @@
#define IPLOCATION_H

#include "Define.h"
#include <array>
#include <string>
#include <vector>

struct IpLocationRecord
{
IpLocationRecord() : IpFrom(0), IpTo(0) { }
IpLocationRecord(uint32 ipFrom, uint32 ipTo, std::string countryCode, std::string countryName)
IpLocationRecord() : IpFrom(), IpTo() { }
IpLocationRecord(std::array<uint8, 16> ipFrom, std::array<uint8, 16> 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<uint8, 16> IpFrom;
std::array<uint8, 16> IpTo;
std::string CountryCode;
std::string CountryName;
};
Expand All @@ -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();

Expand Down
2 changes: 1 addition & 1 deletion src/server/game/Server/WorldSession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit c522e5f

Please sign in to comment.