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