1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // Windows Vista uses the Native Wifi (WLAN) API for accessing WiFi cards. See
6 // http://msdn.microsoft.com/en-us/library/ms705945(VS.85).aspx. Windows XP
7 // Service Pack 3 (and Windows XP Service Pack 2, if upgraded with a hot fix)
8 // also support a limited version of the WLAN API. See
9 // http://msdn.microsoft.com/en-us/library/bb204766.aspx. The WLAN API uses
10 // wlanapi.h, which is not part of the SDK used by Gears, so is replicated
11 // locally using data from the MSDN.
12 //\
13 // Windows XP from Service Pack 2 onwards supports the Wireless Zero
14 // Configuration (WZC) programming interface. See
15 // http://msdn.microsoft.com/en-us/library/ms706587(VS.85).aspx.
16 //
17 // The MSDN recommends that one use the WLAN API where available, and WZC
18 // otherwise.
19 //
20 // However, it seems that WZC fails for some wireless cards. Also, WLAN seems
21 // not to work on XP SP3. So we use WLAN on Vista, and use NDIS directly
22 // otherwise.
23 
24 // MOZILLA NOTE:
25 // This code is ported from chromium:
26 // https://chromium.googlesource.com/chromium/src/+/master/content/browser/geolocation/wifi_data_provider_win.cc
27 // Based on changeset 42c5878
28 
29 #include "win_xp_wifiScanner.h"
30 #include "nsWifiAccessPoint.h"
31 #include <windows.h>
32 #include <winioctl.h>
33 #include <wlanapi.h>
34 #include <string>
35 #include <vector>
36 
37 // Taken from ndis.h for WinCE.
38 #define NDIS_STATUS_INVALID_LENGTH   ((NDIS_STATUS)0xC0010014L)
39 #define NDIS_STATUS_BUFFER_TOO_SHORT ((NDIS_STATUS)0xC0010016L)
40 
41 namespace {
42 // The limits on the size of the buffer used for the OID query.
43 const int kInitialBufferSize = 2 << 12;  // Good for about 50 APs.
44 const int kMaximumBufferSize = 2 << 20;  // 2MB
45 
46 // Length for generic string buffers passed to Win32 APIs.
47 const int kStringLength = 512;
48 
49 // WlanOpenHandle
50 typedef DWORD (WINAPI* WlanOpenHandleFunction)(DWORD dwClientVersion,
51                                                PVOID pReserved,
52                                                PDWORD pdwNegotiatedVersion,
53                                                PHANDLE phClientHandle);
54 
55 // WlanEnumInterfaces
56 typedef DWORD (WINAPI* WlanEnumInterfacesFunction)(
57     HANDLE hClientHandle,
58     PVOID pReserved,
59     PWLAN_INTERFACE_INFO_LIST* ppInterfaceList);
60 
61 // WlanGetNetworkBssList
62 typedef DWORD (WINAPI* WlanGetNetworkBssListFunction)(
63     HANDLE hClientHandle,
64     const GUID* pInterfaceGuid,
65     const  PDOT11_SSID pDot11Ssid,
66     DOT11_BSS_TYPE dot11BssType,
67     BOOL bSecurityEnabled,
68     PVOID pReserved,
69     PWLAN_BSS_LIST* ppWlanBssList
70 );
71 
72 // WlanFreeMemory
73 typedef VOID (WINAPI* WlanFreeMemoryFunction)(PVOID pMemory);
74 
75 // WlanCloseHandle
76 typedef DWORD (WINAPI* WlanCloseHandleFunction)(HANDLE hClientHandle,
77                                                 PVOID pReserved);
78 
79 // Extracts data for an access point and converts to Gears format.
80 bool UndefineDosDevice(const std::string& device_name);
81 bool DefineDosDeviceIfNotExists(const std::string& device_name);
82 HANDLE GetFileHandle(const std::string& device_name);
83 // Makes the OID query and returns a Win32 error code.
84 int PerformQuery(HANDLE adapter_handle, std::vector<char>& buffer, DWORD* bytes_out);
85 bool ResizeBuffer(size_t requested_size, std::vector<char>& buffer);
86 // Gets the system directory and appends a trailing slash if not already
87 // present.
88 bool GetSystemDirectory(std::string* path);
89 
90 bool ConvertToAccessPointData(const NDIS_WLAN_BSSID& data, nsWifiAccessPoint* access_point_data);
91 int GetDataFromBssIdList(const NDIS_802_11_BSSID_LIST& bss_id_list,
92                          int list_size,
93                          nsCOMArray<nsWifiAccessPoint>& outData);
94 } // namespace
95 
96 class WindowsNdisApi
97 {
98 public:
99   virtual ~WindowsNdisApi();
100   static WindowsNdisApi* Create();
101   virtual bool GetAccessPointData(nsCOMArray<nsWifiAccessPoint>& outData);
102 
103 private:
104   static bool GetInterfacesNDIS(std::vector<std::string>& interface_service_names_out);
105   // Swaps in content of the vector passed
106   explicit WindowsNdisApi(std::vector<std::string>* interface_service_names);
107   bool GetInterfaceDataNDIS(HANDLE adapter_handle, nsCOMArray<nsWifiAccessPoint>& outData);
108   // NDIS variables.
109   std::vector<std::string> interface_service_names_;
110   std::vector<char> _buffer;
111 };
112 
113 // WindowsNdisApi
WindowsNdisApi(std::vector<std::string> * interface_service_names)114 WindowsNdisApi::WindowsNdisApi(
115     std::vector<std::string>* interface_service_names)
116     : _buffer(kInitialBufferSize) {
117   interface_service_names_.swap(*interface_service_names);
118 }
119 
~WindowsNdisApi()120 WindowsNdisApi::~WindowsNdisApi() {
121 }
122 
Create()123 WindowsNdisApi* WindowsNdisApi::Create() {
124   std::vector<std::string> interface_service_names;
125   if (GetInterfacesNDIS(interface_service_names)) {
126     return new WindowsNdisApi(&interface_service_names);
127   }
128   return NULL;
129 }
130 
GetAccessPointData(nsCOMArray<nsWifiAccessPoint> & outData)131 bool WindowsNdisApi::GetAccessPointData(nsCOMArray<nsWifiAccessPoint>& outData) {
132   int interfaces_failed = 0;
133   int interfaces_succeeded = 0;
134 
135   for (int i = 0; i < static_cast<int>(interface_service_names_.size()); ++i) {
136     // First, check that we have a DOS device for this adapter.
137     if (!DefineDosDeviceIfNotExists(interface_service_names_[i])) {
138       continue;
139     }
140 
141     // Get the handle to the device. This will fail if the named device is not
142     // valid.
143     HANDLE adapter_handle = GetFileHandle(interface_service_names_[i]);
144     if (adapter_handle == INVALID_HANDLE_VALUE) {
145       continue;
146     }
147 
148     // Get the data.
149     if (GetInterfaceDataNDIS(adapter_handle, outData)) {
150       ++interfaces_succeeded;
151     } else {
152       ++interfaces_failed;
153     }
154 
155     // Clean up.
156     CloseHandle(adapter_handle);
157     UndefineDosDevice(interface_service_names_[i]);
158   }
159 
160   // Return true if at least one interface succeeded, or at the very least none
161   // failed.
162   return interfaces_succeeded > 0 || interfaces_failed == 0;
163 }
164 
GetInterfacesNDIS(std::vector<std::string> & interface_service_names_out)165 bool WindowsNdisApi::GetInterfacesNDIS(std::vector<std::string>& interface_service_names_out) {
166   HKEY network_cards_key = NULL;
167   if (RegOpenKeyEx(
168       HKEY_LOCAL_MACHINE,
169       "Software\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards",
170       0,
171       KEY_READ,
172       &network_cards_key) != ERROR_SUCCESS) {
173     return false;
174   }
175   if (!network_cards_key) {
176     return false;
177   }
178 
179   for (int i = 0; ; ++i) {
180     TCHAR name[kStringLength];
181     DWORD name_size = kStringLength;
182     FILETIME time;
183     if (RegEnumKeyEx(network_cards_key,
184                      i,
185                      name,
186                      &name_size,
187                      NULL,
188                      NULL,
189                      NULL,
190                      &time) != ERROR_SUCCESS) {
191       break;
192     }
193     HKEY hardware_key = NULL;
194     if (RegOpenKeyEx(network_cards_key, name, 0, KEY_READ, &hardware_key) !=
195         ERROR_SUCCESS) {
196       break;
197     }
198     if (!hardware_key) {
199       return false;
200     }
201 
202     TCHAR service_name[kStringLength];
203     DWORD service_name_size = kStringLength;
204     DWORD type = 0;
205     if (RegQueryValueEx(hardware_key,
206                         "ServiceName",
207                         NULL,
208                         &type,
209                         reinterpret_cast<LPBYTE>(service_name),
210                         &service_name_size) == ERROR_SUCCESS) {
211       interface_service_names_out.push_back(service_name);
212     }
213     RegCloseKey(hardware_key);
214   }
215 
216   RegCloseKey(network_cards_key);
217   return true;
218 }
219 
GetInterfaceDataNDIS(HANDLE adapter_handle,nsCOMArray<nsWifiAccessPoint> & outData)220 bool WindowsNdisApi::GetInterfaceDataNDIS(HANDLE adapter_handle,
221                                           nsCOMArray<nsWifiAccessPoint>& outData) {
222   DWORD bytes_out;
223   int result;
224 
225   while (true) {
226     bytes_out = 0;
227     result = PerformQuery(adapter_handle, _buffer, &bytes_out);
228     if (result == ERROR_GEN_FAILURE ||  // Returned by some Intel cards.
229         result == ERROR_INSUFFICIENT_BUFFER ||
230         result == ERROR_MORE_DATA ||
231         result == NDIS_STATUS_INVALID_LENGTH ||
232         result == NDIS_STATUS_BUFFER_TOO_SHORT) {
233       // The buffer we supplied is too small, so increase it. bytes_out should
234       // provide the required buffer size, but this is not always the case.
235       size_t newSize;
236       if (bytes_out > static_cast<DWORD>(_buffer.size())) {
237         newSize = bytes_out;
238       } else {
239         newSize = _buffer.size() * 2;
240       }
241       if (!ResizeBuffer(newSize, _buffer)) {
242         return false;
243       }
244     } else {
245       // The buffer is not too small.
246       break;
247     }
248   }
249 
250   if (result == ERROR_SUCCESS) {
251     NDIS_802_11_BSSID_LIST* bssid_list =
252         reinterpret_cast<NDIS_802_11_BSSID_LIST*>(&_buffer[0]);
253     GetDataFromBssIdList(*bssid_list, _buffer.size(), outData);
254   }
255 
256   return true;
257 }
258 
259 namespace {
260 #define uint8 unsigned char
261 
ConvertToAccessPointData(const NDIS_WLAN_BSSID & data,nsWifiAccessPoint * access_point_data)262 bool ConvertToAccessPointData(const NDIS_WLAN_BSSID& data, nsWifiAccessPoint* access_point_data)
263 {
264   access_point_data->setMac(data.MacAddress);
265   access_point_data->setSignal(data.Rssi);
266   // Note that _NDIS_802_11_SSID::Ssid::Ssid is not null-terminated.
267   const unsigned char* ssid = data.Ssid.Ssid;
268   size_t len = data.Ssid.SsidLength;
269   access_point_data->setSSID(reinterpret_cast<const char*>(ssid), len);
270   return true;
271 }
272 
GetDataFromBssIdList(const NDIS_802_11_BSSID_LIST & bss_id_list,int list_size,nsCOMArray<nsWifiAccessPoint> & outData)273 int GetDataFromBssIdList(const NDIS_802_11_BSSID_LIST& bss_id_list,
274                          int list_size,
275                          nsCOMArray<nsWifiAccessPoint>& outData)
276 {
277   // Walk through the BSS IDs.
278   int found = 0;
279   const uint8* iterator = reinterpret_cast<const uint8*>(&bss_id_list.Bssid[0]);
280   const uint8* end_of_buffer =
281       reinterpret_cast<const uint8*>(&bss_id_list) + list_size;
282   for (int i = 0; i < static_cast<int>(bss_id_list.NumberOfItems); ++i) {
283     const NDIS_WLAN_BSSID *bss_id =
284         reinterpret_cast<const NDIS_WLAN_BSSID*>(iterator);
285     // Check that the length of this BSS ID is reasonable.
286     if (bss_id->Length < sizeof(NDIS_WLAN_BSSID) ||
287         iterator + bss_id->Length > end_of_buffer) {
288       break;
289     }
290     nsWifiAccessPoint* ap = new nsWifiAccessPoint();
291     if (ConvertToAccessPointData(*bss_id, ap)) {
292       outData.AppendObject(ap);
293       ++found;
294     }
295     // Move to the next BSS ID.
296     iterator += bss_id->Length;
297   }
298   return found;
299 }
300 
301 
UndefineDosDevice(const std::string & device_name)302 bool UndefineDosDevice(const std::string& device_name) {
303   // We remove only the mapping we use, that is \Device\<device_name>.
304   std::string target_path = "\\Device\\" + device_name;
305   return DefineDosDevice(
306       DDD_RAW_TARGET_PATH | DDD_REMOVE_DEFINITION | DDD_EXACT_MATCH_ON_REMOVE,
307       device_name.c_str(),
308       target_path.c_str()) == TRUE;
309 }
310 
DefineDosDeviceIfNotExists(const std::string & device_name)311 bool DefineDosDeviceIfNotExists(const std::string& device_name) {
312   // We create a DOS device name for the device at \Device\<device_name>.
313   std::string target_path = "\\Device\\" + device_name;
314 
315   TCHAR target[kStringLength];
316   if (QueryDosDevice(device_name.c_str(), target, kStringLength) > 0 &&
317       target_path.compare(target) == 0) {
318     // Device already exists.
319     return true;
320   }
321 
322   if (GetLastError() != ERROR_FILE_NOT_FOUND) {
323     return false;
324   }
325 
326   if (!DefineDosDevice(DDD_RAW_TARGET_PATH,
327                        device_name.c_str(),
328                        target_path.c_str())) {
329     return false;
330   }
331 
332   // Check that the device is really there.
333   return QueryDosDevice(device_name.c_str(), target, kStringLength) > 0 &&
334       target_path.compare(target) == 0;
335 }
336 
GetFileHandle(const std::string & device_name)337 HANDLE GetFileHandle(const std::string& device_name) {
338   // We access a device with DOS path \Device\<device_name> at
339   // \\.\<device_name>.
340   std::string formatted_device_name = "\\\\.\\" + device_name;
341 
342   return CreateFile(formatted_device_name.c_str(),
343                     GENERIC_READ,
344                     FILE_SHARE_READ | FILE_SHARE_WRITE,  // share mode
345                     0,  // security attributes
346                     OPEN_EXISTING,
347                     0,  // flags and attributes
348                     INVALID_HANDLE_VALUE);
349 }
350 
PerformQuery(HANDLE adapter_handle,std::vector<char> & buffer,DWORD * bytes_out)351 int PerformQuery(HANDLE adapter_handle,
352                  std::vector<char>& buffer,
353                  DWORD* bytes_out) {
354   DWORD oid = OID_802_11_BSSID_LIST;
355   if (!DeviceIoControl(adapter_handle,
356                        IOCTL_NDIS_QUERY_GLOBAL_STATS,
357                        &oid,
358                        sizeof(oid),
359                        &buffer[0],
360                        buffer.size(),
361                        bytes_out,
362                        NULL)) {
363     return GetLastError();
364   }
365   return ERROR_SUCCESS;
366 }
367 
ResizeBuffer(size_t requested_size,std::vector<char> & buffer)368 bool ResizeBuffer(size_t requested_size, std::vector<char>& buffer) {
369   if (requested_size > kMaximumBufferSize) {
370     buffer.resize(kInitialBufferSize);
371     return false;
372   }
373 
374   buffer.resize(requested_size);
375   return true;
376 }
377 
378 } // namespace
379 
380 
381 nsresult
GetAccessPointsFromWLAN(nsCOMArray<nsWifiAccessPoint> & accessPoints)382 WinXPWifiScanner::GetAccessPointsFromWLAN(nsCOMArray<nsWifiAccessPoint> &accessPoints)
383 {
384   if (!mImplementation) {
385     mImplementation = WindowsNdisApi::Create();
386     if (!mImplementation) {
387       return NS_ERROR_FAILURE;
388     }
389   }
390 
391   accessPoints.Clear();
392   bool isOk = mImplementation->GetAccessPointData(accessPoints);
393   if (!isOk) {
394     mImplementation = 0;
395     return NS_ERROR_FAILURE;
396   }
397 
398   return NS_OK;
399 }
400