TrinityCore
IpNetwork.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "IpNetwork.h"
19#include "IpAddress.h"
20#include <boost/asio/ip/network_v4.hpp>
21#include <boost/asio/ip/network_v6.hpp>
22#include <algorithm>
23
24namespace
25{
26std::vector<boost::asio::ip::network_v4> LocalV4Networks;
27std::vector<boost::asio::ip::network_v6> LocalV6Networks;
28}
29
30namespace Trinity::Net
31{
32bool IsInLocalNetwork(boost::asio::ip::address const& clientAddress)
33{
34 if (clientAddress.is_v4())
35 {
36 return std::any_of(LocalV4Networks.begin(), LocalV4Networks.end(), [clientAddressV4 = clientAddress.to_v4()](boost::asio::ip::network_v4 const& network)
37 {
38 return IsInNetwork(network, clientAddressV4);
39 });
40 }
41
42 if (clientAddress.is_v6())
43 {
44 return std::any_of(LocalV6Networks.begin(), LocalV6Networks.end(), [clientAddressV6 = clientAddress.to_v6()](boost::asio::ip::network_v6 const& network)
45 {
46 return IsInNetwork(network, clientAddressV6);
47 });
48 }
49
50 return false;
51};
52
53bool IsInNetwork(boost::asio::ip::network_v4 const& network, boost::asio::ip::address_v4 const& clientAddress)
54{
55 if (clientAddress == network.address())
56 return true;
57
58 boost::asio::ip::network_v4 endpointAsNetwork = boost::asio::ip::make_network_v4(clientAddress, 32);
59 return endpointAsNetwork.is_subnet_of(network);
60}
61
62bool IsInNetwork(boost::asio::ip::network_v6 const& network, boost::asio::ip::address_v6 const& clientAddress)
63{
64 if (clientAddress == network.address())
65 return true;
66
67 boost::asio::ip::network_v6 endpointAsNetwork = boost::asio::ip::make_network_v6(clientAddress, 128);
68 return endpointAsNetwork.is_subnet_of(network);
69}
70
71Optional<std::size_t> SelectAddressForClient(boost::asio::ip::address const& clientAddress, std::span<boost::asio::ip::address const> const& addresses)
72{
73 Optional<std::size_t> localIpv6Index;
74 Optional<std::size_t> externalIpv6Index;
75 Optional<std::size_t> loopbackIpv6Index;
76 Optional<std::size_t> localIpv4Index;
77 Optional<std::size_t> externalIpv4Index;
78 Optional<std::size_t> loopbackIpv4Index;
79
80 for (std::size_t i = 0; i < addresses.size(); ++i)
81 {
82 boost::asio::ip::address const& address = addresses[i];
83
84 if (address.is_loopback())
85 {
86 if (address.is_v6() && !loopbackIpv6Index)
87 loopbackIpv6Index = i;
88
89 if (address.is_v4() && !loopbackIpv4Index)
90 loopbackIpv4Index = i;
91 }
92 else if (IsInLocalNetwork(address))
93 {
94 if (address.is_v6() && !localIpv6Index)
95 localIpv6Index = i;
96
97 if (address.is_v4() && !localIpv4Index)
98 localIpv4Index = i;
99 }
100 else
101 {
102 if (address.is_v6() && !externalIpv6Index)
103 externalIpv6Index = i;
104
105 if (address.is_v4() && !externalIpv4Index)
106 externalIpv4Index = i;
107 }
108 }
109
110 if (IsInLocalNetwork(clientAddress) || clientAddress.is_loopback())
111 {
112 // client is in the same network as this process, prefer local addresses
113
114 // first, try finding a local ipv6 address
115 if (clientAddress.is_v6() && localIpv6Index)
116 {
117 // we have a usable ipv6 local address
118 return localIpv6Index;
119 }
120
121 // we dont have a local v6, return local v4
122 if (localIpv4Index)
123 return localIpv4Index;
124 }
125
126 if (clientAddress.is_loopback())
127 {
128 // fallback, search for a loopback address in configuration
129 if (clientAddress.is_v6() && loopbackIpv6Index)
130 return loopbackIpv6Index;
131
132 if (loopbackIpv4Index)
133 return loopbackIpv4Index;
134 }
135
136 // client is NOT in the same network as this process
137 if (clientAddress.is_v6() && externalIpv6Index)
138 return externalIpv6Index;
139
140 return externalIpv4Index;
141}
142}
143
144#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS
145
146#include <boost/dll/shared_library.hpp>
147#include <iphlpapi.h>
148
150{
151 LocalV4Networks.clear();
152 LocalV6Networks.clear();
153
154 boost::system::error_code dllError;
155 boost::dll::shared_library iphlp("Iphlpapi.dll", dllError, boost::dll::load_mode::search_system_folders);
156 if (dllError || !iphlp.is_loaded())
157 return;
158
159 auto getAdaptersAddresses = iphlp.get<decltype(GetAdaptersAddresses)>("GetAdaptersAddresses");
160 if (!getAdaptersAddresses)
161 return;
162
163 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;
164 ULONG bufferSize = 0;
165
166 if (getAdaptersAddresses(AF_UNSPEC, queryFlags, nullptr, nullptr, &bufferSize) != ERROR_BUFFER_OVERFLOW)
167 return;
168
169 std::unique_ptr<std::byte[]> addressesBuffer = std::make_unique<std::byte[]>(bufferSize);
170 if (getAdaptersAddresses(AF_UNSPEC, queryFlags, nullptr, reinterpret_cast<IP_ADAPTER_ADDRESSES*>(addressesBuffer.get()), &bufferSize) != ERROR_SUCCESS)
171 return;
172
173 for (IP_ADAPTER_ADDRESSES* itr = reinterpret_cast<IP_ADAPTER_ADDRESSES*>(addressesBuffer.get()); itr; itr = itr->Next)
174 {
175 if (itr->IfType == IF_TYPE_SOFTWARE_LOOPBACK)
176 continue;
177
178 if (itr->OperStatus != IfOperStatusUp)
179 continue;
180
181 for (IP_ADAPTER_PREFIX_XP* prefix = itr->FirstPrefix; prefix; prefix = prefix->Next)
182 {
183 switch (prefix->Address.lpSockaddr->sa_family)
184 {
185 case AF_INET:
186 {
187 SOCKADDR_IN* ipv4raw = reinterpret_cast<SOCKADDR_IN*>(prefix->Address.lpSockaddr);
188 boost::asio::ip::address_v4::bytes_type addressBytes;
189 std::memcpy(addressBytes.data(), &ipv4raw->sin_addr.s_addr, addressBytes.size());
190 boost::asio::ip::address_v4 address = make_address_v4(addressBytes);
191 if (address.is_unspecified() || address.is_multicast() || address == boost::asio::ip::address_v4::broadcast())
192 continue;
193
194 LocalV4Networks.push_back(boost::asio::ip::make_network_v4(address, prefix->PrefixLength));
195 break;
196 }
197 case AF_INET6:
198 {
199 SOCKADDR_IN6* ipv6raw = reinterpret_cast<SOCKADDR_IN6*>(prefix->Address.lpSockaddr);
200 boost::asio::ip::address_v6::bytes_type addressBytes;
201 std::memcpy(addressBytes.data(), ipv6raw->sin6_addr.s6_addr, addressBytes.size());
202 boost::asio::ip::address_v6 address = make_address_v6(addressBytes, ipv6raw->sin6_scope_id);
203 if (address.is_unspecified() || address.is_multicast())
204 continue;
205
206 LocalV6Networks.push_back(boost::asio::ip::make_network_v6(address, prefix->PrefixLength));
207 break;
208 }
209 default:
210 break;
211 }
212 }
213 }
214}
215
216#else
217
218#include <numeric>
219#include <ifaddrs.h>
220
222{
223 LocalV4Networks.clear();
224 LocalV6Networks.clear();
225
226 ifaddrs* addressesLinkedList = nullptr;
227 if (getifaddrs(&addressesLinkedList) == -1)
228 return;
229
230 for (ifaddrs* itr = addressesLinkedList; itr; itr = itr->ifa_next)
231 {
232 if (!itr->ifa_addr)
233 continue;
234
235 switch (itr->ifa_addr->sa_family)
236 {
237 case AF_INET:
238 {
239 sockaddr_in* ipv4raw = reinterpret_cast<sockaddr_in*>(itr->ifa_addr);
240 boost::asio::ip::address_v4::bytes_type addressBytes;
241 std::memcpy(addressBytes.data(), &ipv4raw->sin_addr.s_addr, addressBytes.size());
242 boost::asio::ip::address_v4 address = make_address_v4(addressBytes);
243 if (address.is_unspecified() || address.is_loopback() || address.is_multicast() || address == boost::asio::ip::address_v4::broadcast())
244 continue;
245
246 if (sockaddr_in* netmask4raw = reinterpret_cast<sockaddr_in*>(itr->ifa_netmask))
247 {
248 boost::asio::ip::address_v4::bytes_type netmaskBytes;
249 std::memcpy(netmaskBytes.data(), &netmask4raw->sin_addr.s_addr, netmaskBytes.size());
250 boost::asio::ip::address_v4 netmask = make_address_v4(netmaskBytes);
251 LocalV4Networks.push_back(boost::asio::ip::make_network_v4(address, netmask));
252 }
253 else
254 LocalV4Networks.push_back(boost::asio::ip::make_network_v4(address, 32));
255 break;
256 }
257 case AF_INET6:
258 {
259 sockaddr_in6* ipv6raw = reinterpret_cast<sockaddr_in6*>(itr->ifa_addr);
260 boost::asio::ip::address_v6::bytes_type addressBytes;
261 std::memcpy(addressBytes.data(), ipv6raw->sin6_addr.s6_addr, addressBytes.size());
262 boost::asio::ip::address_v6 address = make_address_v6(addressBytes, ipv6raw->sin6_scope_id);
263 if (address.is_unspecified() || address.is_loopback() || address.is_multicast())
264 continue;
265
266 if (sockaddr_in6* netmask6raw = reinterpret_cast<sockaddr_in6*>(itr->ifa_netmask))
267 {
268 int32 prefixLength = 0;
269 for (uint8 addressByte : netmask6raw->sin6_addr.s6_addr)
270 prefixLength += std::countl_one(addressByte);
271
272 LocalV6Networks.push_back(boost::asio::ip::make_network_v6(address, prefixLength));
273 }
274 else
275 LocalV6Networks.push_back(boost::asio::ip::make_network_v6(address, 128));
276 break;
277 }
278 }
279 }
280
281 freeifaddrs(addressesLinkedList);
282}
283
284#endif
uint8_t uint8
Definition: Define.h:144
int32_t int32
Definition: Define.h:138
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition: Optional.h:25
const size_t bufferSize
Definition: RASession.h:27
bool IsInNetwork(boost::asio::ip::network_v4 const &network, boost::asio::ip::address_v4 const &clientAddress)
Definition: IpNetwork.cpp:53
bool IsInLocalNetwork(boost::asio::ip::address const &clientAddress)
Definition: IpNetwork.cpp:32
Optional< std::size_t > SelectAddressForClient(boost::asio::ip::address const &clientAddress, std::span< boost::asio::ip::address const > const &addresses)
Definition: IpNetwork.cpp:71
TC_COMMON_API void ScanLocalNetworks()
Definition: IpNetwork.cpp:149