1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
8
9 #include "NetworkWin10.h"
10 #include "filesystem/SpecialProtocol.h"
11 #include "platform/win10/AsyncHelpers.h"
12 #include "platform/win32/WIN32Util.h"
13 #include "settings/Settings.h"
14 #include "threads/SingleLock.h"
15 #include "utils/log.h"
16 #include "utils/StringUtils.h"
17
18 #include <errno.h>
19 #include <iphlpapi.h>
20 #include <ppltasks.h>
21 #include <string.h>
22 #include <Ws2tcpip.h>
23 #include <ws2ipdef.h>
24
25 #include <Ipexport.h>
26 #ifndef IP_STATUS_BASE
27
28 // --- c&p from Ipexport.h ----------------
29 typedef ULONG IPAddr; // An IP address.
30
31 typedef struct ip_option_information {
32 UCHAR Ttl; // Time To Live
33 UCHAR Tos; // Type Of Service
34 UCHAR Flags; // IP header flags
35 UCHAR OptionsSize; // Size in bytes of options data
36 _Field_size_bytes_(OptionsSize)
37 PUCHAR OptionsData; // Pointer to options data
38 } IP_OPTION_INFORMATION, *PIP_OPTION_INFORMATION;
39
40 typedef struct icmp_echo_reply {
41 IPAddr Address; // Replying address
42 ULONG Status; // Reply IP_STATUS
43 ULONG RoundTripTime; // RTT in milliseconds
44 USHORT DataSize; // Reply data size in bytes
45 USHORT Reserved; // Reserved for system use
46 _Field_size_bytes_(DataSize)
47 PVOID Data; // Pointer to the reply data
48 struct ip_option_information Options; // Reply options
49 } ICMP_ECHO_REPLY, *PICMP_ECHO_REPLY;
50
51 #define IP_STATUS_BASE 11000
52 #define IP_SUCCESS 0
53 #define IP_REQ_TIMED_OUT (IP_STATUS_BASE + 10)
54
55 #endif //! IP_STATUS_BASE
56 #include <Icmpapi.h>
57
58 using namespace winrt::Windows::Networking::Connectivity;
59
CNetworkInterfaceWin10(const PIP_ADAPTER_ADDRESSES address)60 CNetworkInterfaceWin10::CNetworkInterfaceWin10(const PIP_ADAPTER_ADDRESSES address)
61 {
62 m_adapterAddr = address;
63 }
64
65 CNetworkInterfaceWin10::~CNetworkInterfaceWin10(void) = default;
66
IsEnabled() const67 bool CNetworkInterfaceWin10::IsEnabled() const
68 {
69 return true;
70 }
71
IsConnected() const72 bool CNetworkInterfaceWin10::IsConnected() const
73 {
74 return m_adapterAddr->OperStatus == IF_OPER_STATUS::IfOperStatusUp;
75 }
76
GetMacAddress() const77 std::string CNetworkInterfaceWin10::GetMacAddress() const
78 {
79 std::string result;
80 unsigned char* mAddr = m_adapterAddr->PhysicalAddress;
81 result = StringUtils::Format("%02X:%02X:%02X:%02X:%02X:%02X", mAddr[0], mAddr[1], mAddr[2], mAddr[3], mAddr[4], mAddr[5]);
82 return result;
83 }
84
GetMacAddressRaw(char rawMac[6]) const85 void CNetworkInterfaceWin10::GetMacAddressRaw(char rawMac[6]) const
86 {
87 memcpy(rawMac, m_adapterAddr->PhysicalAddress, 6);
88 }
89
GetHostMacAddress(unsigned long host,std::string & mac) const90 bool CNetworkInterfaceWin10::GetHostMacAddress(unsigned long host, std::string& mac) const
91 {
92 mac = "";
93 //! @todo implement raw ARP requests
94 return false;
95 }
96
GetCurrentIPAddress(void) const97 std::string CNetworkInterfaceWin10::GetCurrentIPAddress(void) const
98 {
99 std::string result = "0.0.0.0";
100
101 PIP_ADAPTER_UNICAST_ADDRESS_LH address = m_adapterAddr->FirstUnicastAddress;
102 while (address)
103 {
104 if (address->Address.lpSockaddr->sa_family == AF_INET)
105 {
106 result = CNetworkBase::GetIpStr(address->Address.lpSockaddr);
107 break;
108 }
109 address = address->Next;
110 }
111
112 return result;
113 }
114
GetCurrentNetmask(void) const115 std::string CNetworkInterfaceWin10::GetCurrentNetmask(void) const
116 {
117 std::string result = "0.0.0.0";
118
119 PIP_ADAPTER_UNICAST_ADDRESS_LH address = m_adapterAddr->FirstUnicastAddress;
120 while (address)
121 {
122 if (address->Address.lpSockaddr->sa_family == AF_INET)
123 {
124 result = CNetworkBase::GetMaskByPrefixLength(address->OnLinkPrefixLength);
125 break;
126 }
127 address = address->Next;
128 }
129
130 return result;
131 }
132
GetCurrentDefaultGateway(void) const133 std::string CNetworkInterfaceWin10::GetCurrentDefaultGateway(void) const
134 {
135 std::string result = "";
136
137 PIP_ADAPTER_GATEWAY_ADDRESS_LH address = m_adapterAddr->FirstGatewayAddress;
138 while (address)
139 {
140 if (address->Address.lpSockaddr->sa_family == AF_INET)
141 {
142 result = CNetworkBase::GetIpStr(address->Address.lpSockaddr);
143 break;
144 }
145 address = address->Next;
146 }
147
148 return result;
149 }
150
CNetworkWin10()151 CNetworkWin10::CNetworkWin10()
152 : CNetworkBase()
153 , m_adapterAddresses(nullptr)
154 {
155 queryInterfaceList();
156 NetworkInformation::NetworkStatusChanged([this](auto&&) {
157 CSingleLock lock(m_critSection);
158 queryInterfaceList();
159 });
160 }
161
~CNetworkWin10(void)162 CNetworkWin10::~CNetworkWin10(void)
163 {
164 CleanInterfaceList();
165 }
166
CleanInterfaceList()167 void CNetworkWin10::CleanInterfaceList()
168 {
169 std::vector<CNetworkInterface*>::iterator it = m_interfaces.begin();
170 while(it != m_interfaces.end())
171 {
172 CNetworkInterface* nInt = *it;
173 delete nInt;
174 it = m_interfaces.erase(it);
175 }
176 free(m_adapterAddresses);
177 m_adapterAddresses = nullptr;
178 }
179
GetInterfaceList(void)180 std::vector<CNetworkInterface*>& CNetworkWin10::GetInterfaceList(void)
181 {
182 CSingleLock lock (m_critSection);
183 return m_interfaces;
184 }
185
GetFirstConnectedInterface()186 CNetworkInterface* CNetworkWin10::GetFirstConnectedInterface()
187 {
188 CSingleLock lock(m_critSection);
189 for (CNetworkInterface* intf : m_interfaces)
190 {
191 if (intf->IsEnabled() && intf->IsConnected() && !intf->GetCurrentDefaultGateway().empty())
192 return intf;
193 }
194
195 // fallback to default
196 return CNetwork::GetFirstConnectedInterface();
197 }
198
queryInterfaceList()199 void CNetworkWin10::queryInterfaceList()
200 {
201 CleanInterfaceList();
202
203 struct ci_less
204 {
205 // case-independent (ci) compare_less
206 struct nocase_compare
207 {
208 bool operator() (const unsigned int& c1, const unsigned int& c2) const {
209 return tolower(c1) < tolower(c2);
210 }
211 };
212 bool operator()(const std::wstring & s1, const std::wstring & s2) const {
213 return std::lexicographical_compare
214 (s1.begin(), s1.end(),
215 s2.begin(), s2.end(),
216 nocase_compare());
217 }
218 };
219
220 // collect all adapters from WinRT
221 std::map<std::wstring, IUnknown*, ci_less> adapters;
222 for (auto& profile : NetworkInformation::GetConnectionProfiles())
223 {
224 if (profile && profile.NetworkAdapter())
225 {
226 auto adapter = profile.NetworkAdapter();
227
228 wchar_t* guidStr = nullptr;
229 StringFromIID(adapter.NetworkAdapterId(), &guidStr);
230 adapters[guidStr] = reinterpret_cast<IUnknown*>(winrt::detach_abi(adapter));
231
232 CoTaskMemFree(guidStr);
233 }
234 }
235
236 const ULONG flags = GAA_FLAG_INCLUDE_GATEWAYS | GAA_FLAG_INCLUDE_PREFIX;
237 ULONG ulOutBufLen;
238
239 if (GetAdaptersAddresses(AF_INET, flags, nullptr, nullptr, &ulOutBufLen) != ERROR_BUFFER_OVERFLOW)
240 return;
241
242 m_adapterAddresses = static_cast<PIP_ADAPTER_ADDRESSES>(malloc(ulOutBufLen));
243 if (m_adapterAddresses == nullptr)
244 return;
245
246 if (GetAdaptersAddresses(AF_INET, flags, nullptr, m_adapterAddresses, &ulOutBufLen) == NO_ERROR)
247 {
248 for (PIP_ADAPTER_ADDRESSES adapter = m_adapterAddresses; adapter; adapter = adapter->Next)
249 {
250 if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK)
251 continue;
252
253 m_interfaces.push_back(new CNetworkInterfaceWin10(adapter));
254 }
255 }
256 }
257
GetNameServers(void)258 std::vector<std::string> CNetworkWin10::GetNameServers(void)
259 {
260 std::vector<std::string> result;
261
262 ULONG ulOutBufLen;
263 if (GetNetworkParams(nullptr, &ulOutBufLen) != ERROR_BUFFER_OVERFLOW)
264 return result;
265
266 PFIXED_INFO pInfo = static_cast<PFIXED_INFO>(malloc(ulOutBufLen));
267 if (pInfo == nullptr)
268 return result;
269
270 if (GetNetworkParams(pInfo, &ulOutBufLen) == ERROR_SUCCESS)
271 {
272 PIP_ADDR_STRING addr = &pInfo->DnsServerList;
273 while (addr)
274 {
275 std::string strIp = addr->IpAddress.String;
276 if (!strIp.empty())
277 result.push_back(strIp);
278
279 addr = addr->Next;
280 }
281 }
282 free(pInfo);
283
284 return result;
285 }
286
PingHost(unsigned long host,unsigned int timeout_ms)287 bool CNetworkWin10::PingHost(unsigned long host, unsigned int timeout_ms /* = 2000 */)
288 {
289 char SendData[] = "poke";
290 HANDLE hIcmpFile = IcmpCreateFile();
291 BYTE ReplyBuffer[sizeof(ICMP_ECHO_REPLY) + sizeof(SendData)];
292
293 SetLastError(ERROR_SUCCESS);
294
295 DWORD dwRetVal = IcmpSendEcho2(hIcmpFile, nullptr, nullptr, nullptr,
296 host, SendData, sizeof(SendData), nullptr,
297 ReplyBuffer, sizeof(ReplyBuffer), timeout_ms);
298
299 DWORD lastErr = GetLastError();
300 if (lastErr != ERROR_SUCCESS && lastErr != IP_REQ_TIMED_OUT)
301 CLog::LogF(LOGERROR, "IcmpSendEcho2 failed - {}", CWIN32Util::WUSysMsg(lastErr));
302
303 IcmpCloseHandle(hIcmpFile);
304
305 if (dwRetVal != 0)
306 {
307 PICMP_ECHO_REPLY pEchoReply = (PICMP_ECHO_REPLY)ReplyBuffer;
308 return (pEchoReply->Status == IP_SUCCESS);
309 }
310 return false;
311 }
312