/* * 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 . */ #include "IpNetwork.h" #include "IpAddress.h" #include #include #include namespace { std::vector LocalV4Networks; std::vector 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 SelectAddressForClient(boost::asio::ip::address const& clientAddress, std::span const& addresses) { Optional localIpv6Index; Optional externalIpv6Index; Optional loopbackIpv6Index; Optional localIpv4Index; Optional externalIpv4Index; Optional loopbackIpv4Index; for (std::size_t i = 0; i < addresses.size(); ++i) { boost::asio::ip::address const& address = addresses[i]; if (address.is_loopback()) { if (address.is_v6() && !loopbackIpv6Index) loopbackIpv6Index = i; if (address.is_v4() && !loopbackIpv4Index) loopbackIpv4Index = i; } else 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 (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 #include 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("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 addressesBuffer = std::make_unique(bufferSize); if (getAdaptersAddresses(AF_UNSPEC, queryFlags, nullptr, reinterpret_cast(addressesBuffer.get()), &bufferSize) != ERROR_SUCCESS) return; for (IP_ADAPTER_ADDRESSES* itr = reinterpret_cast(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(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(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 #include 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(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(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(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(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