mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-30 13:47:23 +01:00
Core/Network: Refactor local/remote ip address selection code and allow using hostnames in LoginREST bnetserver config options
This commit is contained in:
@@ -38,6 +38,11 @@ namespace boost
|
||||
namespace ip
|
||||
{
|
||||
class address;
|
||||
class address_v4;
|
||||
class address_v6;
|
||||
|
||||
class network_v4;
|
||||
class network_v6;
|
||||
|
||||
class tcp;
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ namespace Trinity
|
||||
{
|
||||
using boost::asio::ip::make_address;
|
||||
using boost::asio::ip::make_address_v4;
|
||||
using boost::asio::ip::make_address_v6;
|
||||
inline uint32 address_to_uint(boost::asio::ip::address_v4 const& address) { return address.to_uint(); }
|
||||
}
|
||||
}
|
||||
|
||||
284
src/common/Asio/IpNetwork.cpp
Normal file
284
src/common/Asio/IpNetwork.cpp
Normal file
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 "IpNetwork.h"
|
||||
#include "IpAddress.h"
|
||||
#include <boost/asio/ip/network_v4.hpp>
|
||||
#include <boost/asio/ip/network_v6.hpp>
|
||||
|
||||
namespace
|
||||
{
|
||||
std::vector<boost::asio::ip::network_v4> LocalV4Networks;
|
||||
std::vector<boost::asio::ip::network_v6> LocalV6Networks;
|
||||
}
|
||||
|
||||
namespace Trinity::Net
|
||||
{
|
||||
bool IsInLocalNetwork(boost::asio::ip::address const& clientAddress)
|
||||
{
|
||||
if (clientAddress.is_v4())
|
||||
{
|
||||
return std::any_of(LocalV4Networks.begin(), LocalV4Networks.end(), [clientAddressV4 = clientAddress.to_v4()](boost::asio::ip::network_v4 const& network)
|
||||
{
|
||||
return IsInNetwork(network, clientAddressV4);
|
||||
});
|
||||
}
|
||||
|
||||
if (clientAddress.is_v6())
|
||||
{
|
||||
return std::any_of(LocalV6Networks.begin(), LocalV6Networks.end(), [clientAddressV6 = clientAddress.to_v6()](boost::asio::ip::network_v6 const& network)
|
||||
{
|
||||
return IsInNetwork(network, clientAddressV6);
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
bool IsInNetwork(boost::asio::ip::network_v4 const& network, boost::asio::ip::address_v4 const& clientAddress)
|
||||
{
|
||||
if (clientAddress == network.address())
|
||||
return true;
|
||||
|
||||
boost::asio::ip::network_v4 endpointAsNetwork = boost::asio::ip::make_network_v4(clientAddress, 32);
|
||||
return endpointAsNetwork.is_subnet_of(network);
|
||||
}
|
||||
|
||||
bool IsInNetwork(boost::asio::ip::network_v6 const& network, boost::asio::ip::address_v6 const& clientAddress)
|
||||
{
|
||||
if (clientAddress == network.address())
|
||||
return true;
|
||||
|
||||
boost::asio::ip::network_v6 endpointAsNetwork = boost::asio::ip::make_network_v6(clientAddress, 128);
|
||||
return endpointAsNetwork.is_subnet_of(network);
|
||||
}
|
||||
|
||||
Optional<std::size_t> SelectAddressForClient(boost::asio::ip::address const& clientAddress, std::span<boost::asio::ip::address> const& addresses)
|
||||
{
|
||||
Optional<std::size_t> localIpv6Index;
|
||||
Optional<std::size_t> externalIpv6Index;
|
||||
Optional<std::size_t> loopbackIpv6Index;
|
||||
Optional<std::size_t> localIpv4Index;
|
||||
Optional<std::size_t> externalIpv4Index;
|
||||
Optional<std::size_t> loopbackIpv4Index;
|
||||
|
||||
for (std::size_t i = 0; i < addresses.size(); ++i)
|
||||
{
|
||||
boost::asio::ip::address const& address = addresses[i];
|
||||
|
||||
if (IsInLocalNetwork(address))
|
||||
{
|
||||
if (address.is_v6() && !localIpv6Index)
|
||||
localIpv6Index = i;
|
||||
|
||||
if (address.is_v4() && !localIpv4Index)
|
||||
localIpv4Index = i;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (address.is_v6() && !externalIpv6Index)
|
||||
externalIpv6Index = i;
|
||||
|
||||
if (address.is_v4() && !externalIpv4Index)
|
||||
externalIpv4Index = i;
|
||||
}
|
||||
|
||||
if (address.is_loopback())
|
||||
{
|
||||
if (address.is_v6() && !loopbackIpv6Index)
|
||||
loopbackIpv6Index = i;
|
||||
|
||||
if (address.is_v4() && !loopbackIpv4Index)
|
||||
loopbackIpv4Index = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsInLocalNetwork(clientAddress) || clientAddress.is_loopback())
|
||||
{
|
||||
// client is in the same network as this process, prefer local addresses
|
||||
|
||||
// first, try finding a local ipv6 address
|
||||
if (clientAddress.is_v6() && localIpv6Index)
|
||||
{
|
||||
// we have a usable ipv6 local address
|
||||
return localIpv6Index;
|
||||
}
|
||||
|
||||
// we dont have a local v6, return local v4
|
||||
if (localIpv4Index)
|
||||
return localIpv4Index;
|
||||
}
|
||||
|
||||
if (clientAddress.is_loopback())
|
||||
{
|
||||
// fallback, search for a loopback address in configuration
|
||||
if (clientAddress.is_v6() && loopbackIpv6Index)
|
||||
return loopbackIpv6Index;
|
||||
|
||||
if (loopbackIpv4Index)
|
||||
return loopbackIpv4Index;
|
||||
}
|
||||
|
||||
// client is NOT in the same network as this process
|
||||
if (clientAddress.is_v6() && externalIpv6Index)
|
||||
return externalIpv6Index;
|
||||
|
||||
return externalIpv4Index;
|
||||
}
|
||||
}
|
||||
|
||||
#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS
|
||||
|
||||
#include <boost/dll/shared_library.hpp>
|
||||
#include <iphlpapi.h>
|
||||
|
||||
void Trinity::Net::ScanLocalNetworks()
|
||||
{
|
||||
LocalV4Networks.clear();
|
||||
LocalV6Networks.clear();
|
||||
|
||||
boost::system::error_code dllError;
|
||||
boost::dll::shared_library iphlp("Iphlpapi.dll", dllError, boost::dll::load_mode::search_system_folders);
|
||||
if (dllError || !iphlp.is_loaded())
|
||||
return;
|
||||
|
||||
auto getAdaptersAddresses = iphlp.get<decltype(GetAdaptersAddresses)>("GetAdaptersAddresses");
|
||||
if (!getAdaptersAddresses)
|
||||
return;
|
||||
|
||||
ULONG queryFlags = GAA_FLAG_SKIP_UNICAST | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_SKIP_FRIENDLY_NAME;
|
||||
ULONG bufferSize = 0;
|
||||
|
||||
if (getAdaptersAddresses(AF_UNSPEC, queryFlags, nullptr, nullptr, &bufferSize) != ERROR_BUFFER_OVERFLOW)
|
||||
return;
|
||||
|
||||
std::unique_ptr<std::byte[]> addressesBuffer = std::make_unique<std::byte[]>(bufferSize);
|
||||
if (getAdaptersAddresses(AF_UNSPEC, queryFlags, nullptr, reinterpret_cast<IP_ADAPTER_ADDRESSES*>(addressesBuffer.get()), &bufferSize) != ERROR_SUCCESS)
|
||||
return;
|
||||
|
||||
for (IP_ADAPTER_ADDRESSES* itr = reinterpret_cast<IP_ADAPTER_ADDRESSES*>(addressesBuffer.get()); itr; itr = itr->Next)
|
||||
{
|
||||
if (itr->IfType == IF_TYPE_SOFTWARE_LOOPBACK)
|
||||
continue;
|
||||
|
||||
if (itr->OperStatus != IfOperStatusUp)
|
||||
continue;
|
||||
|
||||
for (IP_ADAPTER_PREFIX_XP* prefix = itr->FirstPrefix; prefix; prefix = prefix->Next)
|
||||
{
|
||||
switch (prefix->Address.lpSockaddr->sa_family)
|
||||
{
|
||||
case AF_INET:
|
||||
{
|
||||
SOCKADDR_IN* ipv4raw = reinterpret_cast<SOCKADDR_IN*>(prefix->Address.lpSockaddr);
|
||||
boost::asio::ip::address_v4::bytes_type addressBytes;
|
||||
std::memcpy(addressBytes.data(), &ipv4raw->sin_addr.s_addr, addressBytes.size());
|
||||
boost::asio::ip::address_v4 address = make_address_v4(addressBytes);
|
||||
if (address.is_unspecified() || address.is_multicast() || address == boost::asio::ip::address_v4::broadcast())
|
||||
continue;
|
||||
|
||||
LocalV4Networks.push_back(boost::asio::ip::make_network_v4(address, prefix->PrefixLength));
|
||||
break;
|
||||
}
|
||||
case AF_INET6:
|
||||
{
|
||||
SOCKADDR_IN6* ipv6raw = reinterpret_cast<SOCKADDR_IN6*>(prefix->Address.lpSockaddr);
|
||||
boost::asio::ip::address_v6::bytes_type addressBytes;
|
||||
std::memcpy(addressBytes.data(), ipv6raw->sin6_addr.s6_addr, addressBytes.size());
|
||||
boost::asio::ip::address_v6 address = make_address_v6(addressBytes, ipv6raw->sin6_scope_id);
|
||||
if (address.is_unspecified() || address.is_multicast())
|
||||
continue;
|
||||
|
||||
LocalV6Networks.push_back(boost::asio::ip::make_network_v6(address, prefix->PrefixLength));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include <numeric>
|
||||
#include <ifaddrs.h>
|
||||
|
||||
void Trinity::Net::ScanLocalNetworks()
|
||||
{
|
||||
LocalV4Networks.clear();
|
||||
LocalV6Networks.clear();
|
||||
|
||||
ifaddrs* addressesLinkedList = nullptr;
|
||||
if (getifaddrs(&addressesLinkedList) == -1)
|
||||
return;
|
||||
|
||||
for (ifaddrs* itr = addressesLinkedList; itr; itr = itr->ifa_next)
|
||||
{
|
||||
if (!itr->ifa_addr)
|
||||
continue;
|
||||
|
||||
switch (itr->ifa_addr->sa_family)
|
||||
{
|
||||
case AF_INET:
|
||||
{
|
||||
sockaddr_in* ipv4raw = reinterpret_cast<sockaddr_in*>(itr->ifa_addr);
|
||||
boost::asio::ip::address_v4::bytes_type addressBytes;
|
||||
std::memcpy(addressBytes.data(), &ipv4raw->sin_addr.s_addr, addressBytes.size());
|
||||
boost::asio::ip::address_v4 address = make_address_v4(addressBytes);
|
||||
if (address.is_unspecified() || address.is_loopback() || address.is_multicast() || address == boost::asio::ip::address_v4::broadcast())
|
||||
continue;
|
||||
|
||||
if (sockaddr_in* netmask4raw = reinterpret_cast<sockaddr_in*>(itr->ifa_netmask))
|
||||
{
|
||||
boost::asio::ip::address_v4::bytes_type netmaskBytes;
|
||||
std::memcpy(netmaskBytes.data(), &netmask4raw->sin_addr.s_addr, netmaskBytes.size());
|
||||
boost::asio::ip::address_v4 netmask = make_address_v4(netmaskBytes);
|
||||
LocalV4Networks.push_back(boost::asio::ip::make_network_v4(address, netmask));
|
||||
}
|
||||
else
|
||||
LocalV4Networks.push_back(boost::asio::ip::make_network_v4(address, 32));
|
||||
break;
|
||||
}
|
||||
case AF_INET6:
|
||||
{
|
||||
sockaddr_in6* ipv6raw = reinterpret_cast<sockaddr_in6*>(itr->ifa_addr);
|
||||
boost::asio::ip::address_v6::bytes_type addressBytes;
|
||||
std::memcpy(addressBytes.data(), ipv6raw->sin6_addr.s6_addr, addressBytes.size());
|
||||
boost::asio::ip::address_v6 address = make_address_v6(addressBytes, ipv6raw->sin6_scope_id);
|
||||
if (address.is_unspecified() || address.is_loopback() || address.is_multicast())
|
||||
continue;
|
||||
|
||||
if (sockaddr_in6* netmask6raw = reinterpret_cast<sockaddr_in6*>(itr->ifa_netmask))
|
||||
{
|
||||
int32 prefixLength = 0;
|
||||
for (uint8 addressByte : netmask6raw->sin6_addr.s6_addr)
|
||||
prefixLength += std::countl_one(addressByte);
|
||||
|
||||
LocalV6Networks.push_back(boost::asio::ip::make_network_v6(address, prefixLength));
|
||||
}
|
||||
else
|
||||
LocalV6Networks.push_back(boost::asio::ip::make_network_v6(address, 128));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
freeifaddrs(addressesLinkedList);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -18,40 +18,22 @@
|
||||
#ifndef IpNetwork_h__
|
||||
#define IpNetwork_h__
|
||||
|
||||
#include "AsioHacksFwd.h"
|
||||
#include "Define.h"
|
||||
#include "IpAddress.h"
|
||||
#include <boost/asio/ip/network_v4.hpp>
|
||||
#include <boost/asio/ip/network_v6.hpp>
|
||||
#include "Optional.h"
|
||||
#include <span>
|
||||
|
||||
namespace Trinity
|
||||
namespace Trinity::Net
|
||||
{
|
||||
namespace Net
|
||||
{
|
||||
inline bool IsInNetwork(boost::asio::ip::address_v4 const& networkAddress, boost::asio::ip::address_v4 const& mask, boost::asio::ip::address_v4 const& clientAddress)
|
||||
{
|
||||
boost::asio::ip::network_v4 network = boost::asio::ip::make_network_v4(networkAddress, mask);
|
||||
boost::asio::ip::address_v4_range hosts = network.hosts();
|
||||
return hosts.find(clientAddress) != hosts.end();
|
||||
}
|
||||
TC_COMMON_API bool IsInLocalNetwork(boost::asio::ip::address const& clientAddress);
|
||||
|
||||
inline boost::asio::ip::address_v4 GetDefaultNetmaskV4(boost::asio::ip::address_v4 const& networkAddress)
|
||||
{
|
||||
if ((address_to_uint(networkAddress) & 0x80000000) == 0)
|
||||
return boost::asio::ip::address_v4(0xFF000000);
|
||||
if ((address_to_uint(networkAddress) & 0xC0000000) == 0x80000000)
|
||||
return boost::asio::ip::address_v4(0xFFFF0000);
|
||||
if ((address_to_uint(networkAddress) & 0xE0000000) == 0xC0000000)
|
||||
return boost::asio::ip::address_v4(0xFFFFFF00);
|
||||
return boost::asio::ip::address_v4(0xFFFFFFFF);
|
||||
}
|
||||
TC_COMMON_API bool IsInNetwork(boost::asio::ip::network_v4 const& network, boost::asio::ip::address_v4 const& clientAddress);
|
||||
|
||||
inline bool IsInNetwork(boost::asio::ip::address_v6 const& networkAddress, uint16 prefixLength, boost::asio::ip::address_v6 const& clientAddress)
|
||||
{
|
||||
boost::asio::ip::network_v6 network = boost::asio::ip::make_network_v6(networkAddress, prefixLength);
|
||||
boost::asio::ip::address_v6_range hosts = network.hosts();
|
||||
return hosts.find(clientAddress) != hosts.end();
|
||||
}
|
||||
}
|
||||
TC_COMMON_API bool IsInNetwork(boost::asio::ip::network_v6 const& network, boost::asio::ip::address_v6 const& clientAddress);
|
||||
|
||||
TC_COMMON_API Optional<std::size_t> SelectAddressForClient(boost::asio::ip::address const& clientAddress, std::span<boost::asio::ip::address> const& addresses);
|
||||
|
||||
TC_COMMON_API void ScanLocalNetworks();
|
||||
}
|
||||
|
||||
#endif // IpNetwork_h__
|
||||
|
||||
Reference in New Issue
Block a user