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