[cstdstring] demise Format, replacing with StringUtils::Format
[vuplus_xbmc] / xbmc / network / windows / NetworkWin32.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include <errno.h>
22 #include <iphlpapi.h>
23 #include <IcmpAPI.h>
24 #include "PlatformDefs.h"
25 #include "NetworkWin32.h"
26 #include "utils/log.h"
27 #include "threads/SingleLock.h"
28 #include "utils/CharsetConverter.h"
29 #include "utils/StringUtils.h"
30
31 // undefine if you want to build without the wlan stuff
32 // might be needed for VS2003
33 #define HAS_WIN32_WLAN_API
34
35 #ifdef HAS_WIN32_WLAN_API
36 #include "Wlanapi.h"
37 #pragma comment (lib,"Wlanapi.lib")
38 #endif
39
40
41 using namespace std;
42
43 CNetworkInterfaceWin32::CNetworkInterfaceWin32(CNetworkWin32* network, IP_ADAPTER_INFO adapter)
44
45 {
46    m_network = network;
47    m_adapter = adapter;
48    m_adaptername = adapter.Description;
49 }
50
51 CNetworkInterfaceWin32::~CNetworkInterfaceWin32(void)
52 {
53 }
54
55 CStdString& CNetworkInterfaceWin32::GetName(void)
56 {
57   if (!g_charsetConverter.isValidUtf8(m_adaptername))
58     g_charsetConverter.unknownToUTF8(m_adaptername);
59   return m_adaptername;
60 }
61
62 bool CNetworkInterfaceWin32::IsWireless()
63 {
64   return (m_adapter.Type == IF_TYPE_IEEE80211);
65 }
66
67 bool CNetworkInterfaceWin32::IsEnabled()
68 {
69   return true;
70 }
71
72 bool CNetworkInterfaceWin32::IsConnected()
73 {
74   CStdString strIP = m_adapter.IpAddressList.IpAddress.String;
75   return (strIP != "0.0.0.0");
76 }
77
78 CStdString CNetworkInterfaceWin32::GetMacAddress()
79 {
80   CStdString result;
81   unsigned char* mAddr = m_adapter.Address;
82   result = StringUtils::Format("%02X:%02X:%02X:%02X:%02X:%02X", mAddr[0], mAddr[1], mAddr[2], mAddr[3], mAddr[4], mAddr[5]);
83   return result;
84 }
85
86 void CNetworkInterfaceWin32::GetMacAddressRaw(char rawMac[6])
87 {
88   memcpy(rawMac, m_adapter.Address, 6);
89 }
90
91 CStdString CNetworkInterfaceWin32::GetCurrentIPAddress(void)
92 {
93   return m_adapter.IpAddressList.IpAddress.String;
94 }
95
96 CStdString CNetworkInterfaceWin32::GetCurrentNetmask(void)
97 {
98   return m_adapter.IpAddressList.IpMask.String;
99 }
100
101 CStdString CNetworkInterfaceWin32::GetCurrentWirelessEssId(void)
102 {
103   CStdString result = "";
104
105 #ifdef HAS_WIN32_WLAN_API
106   if(IsWireless())
107   {
108     HANDLE hClientHdl = NULL;
109     DWORD dwVersion = 0;
110     DWORD dwret = 0;
111     PWLAN_CONNECTION_ATTRIBUTES pAttributes;
112     DWORD dwSize = 0;
113
114     if(WlanOpenHandle(1,NULL,&dwVersion, &hClientHdl) == ERROR_SUCCESS)
115     {
116       PWLAN_INTERFACE_INFO_LIST ppInterfaceList;
117       if(WlanEnumInterfaces(hClientHdl,NULL, &ppInterfaceList ) == ERROR_SUCCESS)
118       {
119         for(unsigned int i=0; i<ppInterfaceList->dwNumberOfItems;i++)
120         {
121           GUID guid = ppInterfaceList->InterfaceInfo[i].InterfaceGuid;
122           WCHAR wcguid[64];
123           StringFromGUID2(guid, (LPOLESTR)&wcguid, 64);
124           CStdStringW strGuid = wcguid;
125           CStdStringW strAdaptername = m_adapter.AdapterName;
126           if( strGuid == strAdaptername)
127           {
128             if(WlanQueryInterface(hClientHdl,&ppInterfaceList->InterfaceInfo[i].InterfaceGuid,wlan_intf_opcode_current_connection, NULL, &dwSize, (PVOID*)&pAttributes, NULL ) == ERROR_SUCCESS)
129             {
130               result = (char*)pAttributes->wlanAssociationAttributes.dot11Ssid.ucSSID;
131               WlanFreeMemory((PVOID*)&pAttributes);
132             }
133             else
134               OutputDebugString("Can't query wlan interface\n");
135           }
136         }
137       }
138       WlanCloseHandle(&hClientHdl, NULL);
139     }
140     else
141       OutputDebugString("Can't open wlan handle\n");
142   }
143 #endif
144   return result;
145 }
146
147 CStdString CNetworkInterfaceWin32::GetCurrentDefaultGateway(void)
148 {
149   return m_adapter.GatewayList.IpAddress.String;
150 }
151
152 CNetworkWin32::CNetworkWin32(void)
153 {
154   queryInterfaceList();
155 }
156
157 CNetworkWin32::~CNetworkWin32(void)
158 {
159   CleanInterfaceList();
160   m_netrefreshTimer.Stop();
161 }
162
163 void CNetworkWin32::CleanInterfaceList()
164 {
165   vector<CNetworkInterface*>::iterator it = m_interfaces.begin();
166   while(it != m_interfaces.end())
167   {
168     CNetworkInterface* nInt = *it;
169     delete nInt;
170     it = m_interfaces.erase(it);
171   }
172 }
173
174 std::vector<CNetworkInterface*>& CNetworkWin32::GetInterfaceList(void)
175 {
176   CSingleLock lock (m_critSection);
177   if(m_netrefreshTimer.GetElapsedSeconds() >= 5.0f)
178     queryInterfaceList();
179
180   return m_interfaces;
181 }
182
183 void CNetworkWin32::queryInterfaceList()
184 {
185   CleanInterfaceList();
186   m_netrefreshTimer.StartZero();
187
188   PIP_ADAPTER_INFO adapterInfo;
189   PIP_ADAPTER_INFO adapter = NULL;
190
191   ULONG ulOutBufLen = sizeof (IP_ADAPTER_INFO);
192
193   adapterInfo = (IP_ADAPTER_INFO *) malloc(sizeof (IP_ADAPTER_INFO));
194   if (adapterInfo == NULL)
195     return;
196
197   if (GetAdaptersInfo(adapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW)
198   {
199     free(adapterInfo);
200     adapterInfo = (IP_ADAPTER_INFO *) malloc(ulOutBufLen);
201     if (adapterInfo == NULL)
202     {
203       OutputDebugString("Error allocating memory needed to call GetAdaptersinfo\n");
204       return;
205     }
206   }
207
208   if ((GetAdaptersInfo(adapterInfo, &ulOutBufLen)) == NO_ERROR)
209   {
210     adapter = adapterInfo;
211     while (adapter)
212     {
213       m_interfaces.push_back(new CNetworkInterfaceWin32(this, *adapter));
214       adapter = adapter->Next;
215     }
216   }
217   free(adapterInfo);
218 }
219
220 std::vector<CStdString> CNetworkWin32::GetNameServers(void)
221 {
222   std::vector<CStdString> result;
223
224   FIXED_INFO *pFixedInfo;
225   ULONG ulOutBufLen;
226   IP_ADDR_STRING *pIPAddr;
227
228   pFixedInfo = (FIXED_INFO *) malloc(sizeof (FIXED_INFO));
229   if (pFixedInfo == NULL)
230   {
231     OutputDebugString("Error allocating memory needed to call GetNetworkParams\n");
232     return result;
233   }
234   ulOutBufLen = sizeof (FIXED_INFO);
235   if (GetNetworkParams(pFixedInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW)
236   {
237     free(pFixedInfo);
238     pFixedInfo = (FIXED_INFO *) malloc(ulOutBufLen);
239     if (pFixedInfo == NULL)
240     {
241       OutputDebugString("Error allocating memory needed to call GetNetworkParams\n");
242       return result;
243     }
244   }
245
246   if (GetNetworkParams(pFixedInfo, &ulOutBufLen) == NO_ERROR)
247   {
248     result.push_back(pFixedInfo->DnsServerList.IpAddress.String);
249     pIPAddr = pFixedInfo->DnsServerList.Next;
250     while(pIPAddr)
251     {
252       result.push_back(pIPAddr->IpAddress.String);
253       pIPAddr = pIPAddr->Next;
254     }
255
256   }
257   free(pFixedInfo);
258
259   return result;
260 }
261
262 void CNetworkWin32::SetNameServers(std::vector<CStdString> nameServers)
263 {
264   return;
265 }
266
267 bool CNetworkWin32::PingHost(unsigned long host, unsigned int timeout_ms /* = 2000 */)
268 {
269   char SendData[]    = "poke";
270   HANDLE hIcmpFile   = IcmpCreateFile();
271   BYTE ReplyBuffer [sizeof(ICMP_ECHO_REPLY) + sizeof(SendData)];
272
273   SetLastError(ERROR_SUCCESS);
274
275   DWORD dwRetVal = IcmpSendEcho(hIcmpFile, host, SendData, sizeof(SendData), 
276                                 NULL, ReplyBuffer, sizeof(ReplyBuffer), timeout_ms);
277
278   DWORD lastErr = GetLastError();
279   if (lastErr != ERROR_SUCCESS && lastErr != IP_REQ_TIMED_OUT)
280     CLog::Log(LOGERROR, "%s - IcmpSendEcho failed - %s", __FUNCTION__, WUSysMsg(lastErr).c_str());
281
282   IcmpCloseHandle (hIcmpFile);
283
284   if (dwRetVal != 0)
285   {
286     PICMP_ECHO_REPLY pEchoReply = (PICMP_ECHO_REPLY)ReplyBuffer;
287     return (pEchoReply->Status == IP_SUCCESS);
288   }
289   return false;
290 }
291
292 bool CNetworkInterfaceWin32::GetHostMacAddress(unsigned long host, CStdString& mac)
293 {
294   IPAddr src_ip = inet_addr(GetCurrentIPAddress().c_str());
295   BYTE bPhysAddr[6];      // for 6-byte hardware addresses
296   ULONG PhysAddrLen = 6;  // default to length of six bytes
297
298   memset(&bPhysAddr, 0xff, sizeof (bPhysAddr));
299
300   DWORD dwRetVal = SendARP(host, src_ip, &bPhysAddr, &PhysAddrLen);
301   if (dwRetVal == NO_ERROR)
302   {
303     if (PhysAddrLen == 6)
304     {
305       mac = StringUtils::Format("%02X:%02X:%02X:%02X:%02X:%02X", 
306         bPhysAddr[0], bPhysAddr[1], bPhysAddr[2], 
307         bPhysAddr[3], bPhysAddr[4], bPhysAddr[5]);
308       return true;
309     }
310     else
311       CLog::Log(LOGERROR, "%s - SendArp completed successfully, but mac address has length != 6 (%d)", __FUNCTION__, PhysAddrLen);
312   }
313   else
314     CLog::Log(LOGERROR, "%s - SendArp failed with error (%d)", __FUNCTION__, dwRetVal);
315
316   return false;
317 }
318
319 std::vector<NetworkAccessPoint> CNetworkInterfaceWin32::GetAccessPoints(void)
320 {
321    std::vector<NetworkAccessPoint> result;
322 #ifdef HAS_WIN32_WLAN_API
323   if (!IsWireless())
324     return result;
325
326   // According to Mozilla: "We could be executing on either Windows XP or Windows
327   // Vista, so use the lower version of the client WLAN API. It seems that the
328   // negotiated version is the Vista version irrespective of what we pass!"
329   // http://dxr.mozilla.org/mozilla-central/source/netwerk/wifi/nsWifiScannerWin.cpp#l51
330   static const int xpWlanClientVersion = 1;
331   DWORD negotiated_version;
332   DWORD dwResult;
333   HANDLE wlan_handle = NULL;
334
335   // Get the handle to the WLAN API
336   dwResult = WlanOpenHandle(xpWlanClientVersion, NULL, &negotiated_version, &wlan_handle);
337   if (dwResult != ERROR_SUCCESS || !wlan_handle)
338   {
339     CLog::Log(LOGERROR, "Could not load the client WLAN API");
340     return result;
341   }
342
343   // Get the list of interfaces (WlanEnumInterfaces allocates interface_list)
344   WLAN_INTERFACE_INFO_LIST *interface_list = NULL;
345   dwResult = WlanEnumInterfaces(wlan_handle, NULL, &interface_list);
346   if (dwResult != ERROR_SUCCESS || !interface_list)
347   {
348     WlanCloseHandle(wlan_handle, NULL);
349     CLog::Log(LOGERROR, "Failed to get the list of interfaces");
350     return result;
351   }
352
353   for (unsigned int i = 0; i < interface_list->dwNumberOfItems; ++i)
354   {
355     GUID guid = interface_list->InterfaceInfo[i].InterfaceGuid;
356     WCHAR wcguid[64];
357     StringFromGUID2(guid, (LPOLESTR)&wcguid, 64);
358     CStdStringW strGuid = wcguid;
359     CStdStringW strAdaptername = m_adapter.AdapterName;
360     if (strGuid == strAdaptername)
361     {
362       WLAN_BSS_LIST *bss_list;
363       HRESULT rv = WlanGetNetworkBssList(wlan_handle,
364                                          &interface_list->InterfaceInfo[i].InterfaceGuid,
365                                          NULL,               // Get all SSIDs
366                                          dot11_BSS_type_any, // unused
367                                          false,              // bSecurityEnabled - unused
368                                          NULL,               // reserved
369                                          &bss_list);
370       if (rv != ERROR_SUCCESS || !bss_list)
371         break;
372       for (unsigned int j = 0; j < bss_list->dwNumberOfItems; ++j)
373       {
374         const WLAN_BSS_ENTRY bss_entry = bss_list->wlanBssEntries[j];
375         // Add the access point info to the list of results
376         CStdString essId((char*)bss_entry.dot11Ssid.ucSSID, (unsigned int)bss_entry.dot11Ssid.uSSIDLength);
377         CStdString macAddress;
378         // macAddress is big-endian, write in byte chunks
379         macAddress = StringUtils::Format("%02x-%02x-%02x-%02x-%02x-%02x",
380           bss_entry.dot11Bssid[0], bss_entry.dot11Bssid[1], bss_entry.dot11Bssid[2],
381           bss_entry.dot11Bssid[3], bss_entry.dot11Bssid[4], bss_entry.dot11Bssid[5]);
382         int signalLevel = bss_entry.lRssi;
383         EncMode encryption = ENC_NONE; // TODO
384         int channel = NetworkAccessPoint::FreqToChannel((float)bss_entry.ulChCenterFrequency * 1000);
385         result.push_back(NetworkAccessPoint(essId, macAddress, signalLevel, encryption, channel));
386       }
387       WlanFreeMemory(bss_list);
388       break;
389     }
390   }
391
392   // Free the interface list
393   WlanFreeMemory(interface_list);
394
395   // Close the handle
396   WlanCloseHandle(wlan_handle, NULL);
397
398 #endif
399
400   return result;
401 }
402
403 void CNetworkInterfaceWin32::GetSettings(NetworkAssignment& assignment, CStdString& ipAddress, CStdString& networkMask, CStdString& defaultGateway, CStdString& essId, CStdString& key, EncMode& encryptionMode)
404 {
405   ipAddress = "0.0.0.0";
406   networkMask = "0.0.0.0";
407   defaultGateway = "0.0.0.0";
408   essId = "";
409   key = "";
410   encryptionMode = ENC_NONE;
411   assignment = NETWORK_DISABLED;
412
413
414   PIP_ADAPTER_INFO adapterInfo;
415   PIP_ADAPTER_INFO adapter = NULL;
416
417   ULONG ulOutBufLen = sizeof (IP_ADAPTER_INFO);
418
419   adapterInfo = (IP_ADAPTER_INFO *) malloc(sizeof (IP_ADAPTER_INFO));
420   if (adapterInfo == NULL)
421     return;
422
423   if (GetAdaptersInfo(adapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW)
424   {
425     free(adapterInfo);
426     adapterInfo = (IP_ADAPTER_INFO *) malloc(ulOutBufLen);
427     if (adapterInfo == NULL)
428     {
429       OutputDebugString("Error allocating memory needed to call GetAdaptersinfo\n");
430       return;
431     }
432   }
433
434   if ((GetAdaptersInfo(adapterInfo, &ulOutBufLen)) == NO_ERROR)
435   {
436     adapter = adapterInfo;
437     while (adapter)
438     {
439       if(m_adapter.Index == adapter->Index)
440       {
441         ipAddress = adapter->IpAddressList.IpAddress.String;
442         networkMask = adapter->IpAddressList.IpMask.String;
443         defaultGateway = adapter->GatewayList.IpAddress.String;
444         if (adapter->DhcpEnabled)
445           assignment = NETWORK_DHCP;
446         else
447           assignment = NETWORK_STATIC;
448
449       }
450       adapter = adapter->Next;
451     }
452   }
453   free(adapterInfo);
454
455 #ifdef HAS_WIN32_WLAN_API
456   if(IsWireless())
457   {
458     HANDLE hClientHdl = NULL;
459     DWORD dwVersion = 0;
460     DWORD dwret = 0;
461     PWLAN_CONNECTION_ATTRIBUTES pAttributes;
462     DWORD dwSize = 0;
463
464     if(WlanOpenHandle(1,NULL,&dwVersion, &hClientHdl) == ERROR_SUCCESS)
465     {
466       PWLAN_INTERFACE_INFO_LIST ppInterfaceList;
467       if(WlanEnumInterfaces(hClientHdl,NULL, &ppInterfaceList ) == ERROR_SUCCESS)
468       {
469         for(unsigned int i=0; i<ppInterfaceList->dwNumberOfItems;i++)
470         {
471           GUID guid = ppInterfaceList->InterfaceInfo[i].InterfaceGuid;
472           WCHAR wcguid[64];
473           StringFromGUID2(guid, (LPOLESTR)&wcguid, 64);
474           CStdStringW strGuid = wcguid;
475           CStdStringW strAdaptername = m_adapter.AdapterName;
476           if( strGuid == strAdaptername)
477           {
478             if(WlanQueryInterface(hClientHdl,&ppInterfaceList->InterfaceInfo[i].InterfaceGuid,wlan_intf_opcode_current_connection, NULL, &dwSize, (PVOID*)&pAttributes, NULL ) == ERROR_SUCCESS)
479             {
480               essId = (char*)pAttributes->wlanAssociationAttributes.dot11Ssid.ucSSID;
481               if(pAttributes->wlanSecurityAttributes.bSecurityEnabled)
482               {
483                 switch(pAttributes->wlanSecurityAttributes.dot11AuthAlgorithm)
484                 {
485                 case DOT11_AUTH_ALGO_80211_SHARED_KEY:
486                   encryptionMode = ENC_WEP;
487                   break;
488                 case DOT11_AUTH_ALGO_WPA:
489                 case DOT11_AUTH_ALGO_WPA_PSK:
490                   encryptionMode = ENC_WPA;
491                   break;
492                 case DOT11_AUTH_ALGO_RSNA:
493                 case DOT11_AUTH_ALGO_RSNA_PSK:
494                   encryptionMode = ENC_WPA2;
495                 }
496               }
497               WlanFreeMemory((PVOID*)&pAttributes);
498             }
499             else
500               OutputDebugString("Can't query wlan interface\n");
501           }
502         }
503       }
504       WlanCloseHandle(&hClientHdl, NULL);
505     }
506     else
507       OutputDebugString("Can't open wlan handle\n");
508   }
509   // Todo: get the key (WlanGetProfile, CryptUnprotectData?)
510 #endif
511 }
512
513 void CNetworkInterfaceWin32::SetSettings(NetworkAssignment& assignment, CStdString& ipAddress, CStdString& networkMask, CStdString& defaultGateway, CStdString& essId, CStdString& key, EncMode& encryptionMode)
514 {
515   return;
516 }
517
518 void CNetworkInterfaceWin32::WriteSettings(FILE* fw, NetworkAssignment assignment, CStdString& ipAddress, CStdString& networkMask, CStdString& defaultGateway, CStdString& essId, CStdString& key, EncMode& encryptionMode)
519 {
520   return;
521 }