diff options
Diffstat (limited to 'netwerk/wifi/win_xp_wifiScanner.cpp')
-rw-r--r-- | netwerk/wifi/win_xp_wifiScanner.cpp | 399 |
1 files changed, 399 insertions, 0 deletions
diff --git a/netwerk/wifi/win_xp_wifiScanner.cpp b/netwerk/wifi/win_xp_wifiScanner.cpp new file mode 100644 index 000000000..4dcd34034 --- /dev/null +++ b/netwerk/wifi/win_xp_wifiScanner.cpp @@ -0,0 +1,399 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Windows Vista uses the Native Wifi (WLAN) API for accessing WiFi cards. See +// http://msdn.microsoft.com/en-us/library/ms705945(VS.85).aspx. Windows XP +// Service Pack 3 (and Windows XP Service Pack 2, if upgraded with a hot fix) +// also support a limited version of the WLAN API. See +// http://msdn.microsoft.com/en-us/library/bb204766.aspx. The WLAN API uses +// wlanapi.h, which is not part of the SDK used by Gears, so is replicated +// locally using data from the MSDN. +//\ +// Windows XP from Service Pack 2 onwards supports the Wireless Zero +// Configuration (WZC) programming interface. See +// http://msdn.microsoft.com/en-us/library/ms706587(VS.85).aspx. +// +// The MSDN recommends that one use the WLAN API where available, and WZC +// otherwise. +// +// However, it seems that WZC fails for some wireless cards. Also, WLAN seems +// not to work on XP SP3. So we use WLAN on Vista, and use NDIS directly +// otherwise. + +// MOZILLA NOTE: +// This code is ported from chromium: +// https://chromium.googlesource.com/chromium/src/+/master/content/browser/geolocation/wifi_data_provider_win.cc +// Based on changeset 42c5878 + +#include "win_xp_wifiScanner.h" +#include "nsWifiAccessPoint.h" +#include <windows.h> +#include <winioctl.h> +#include <wlanapi.h> +#include <string> +#include <vector> + +// Taken from ndis.h for WinCE. +#define NDIS_STATUS_INVALID_LENGTH ((NDIS_STATUS)0xC0010014L) +#define NDIS_STATUS_BUFFER_TOO_SHORT ((NDIS_STATUS)0xC0010016L) + +namespace { +// The limits on the size of the buffer used for the OID query. +const int kInitialBufferSize = 2 << 12; // Good for about 50 APs. +const int kMaximumBufferSize = 2 << 20; // 2MB + +// Length for generic string buffers passed to Win32 APIs. +const int kStringLength = 512; + +// WlanOpenHandle +typedef DWORD (WINAPI* WlanOpenHandleFunction)(DWORD dwClientVersion, + PVOID pReserved, + PDWORD pdwNegotiatedVersion, + PHANDLE phClientHandle); + +// WlanEnumInterfaces +typedef DWORD (WINAPI* WlanEnumInterfacesFunction)( + HANDLE hClientHandle, + PVOID pReserved, + PWLAN_INTERFACE_INFO_LIST* ppInterfaceList); + +// WlanGetNetworkBssList +typedef DWORD (WINAPI* WlanGetNetworkBssListFunction)( + HANDLE hClientHandle, + const GUID* pInterfaceGuid, + const PDOT11_SSID pDot11Ssid, + DOT11_BSS_TYPE dot11BssType, + BOOL bSecurityEnabled, + PVOID pReserved, + PWLAN_BSS_LIST* ppWlanBssList +); + +// WlanFreeMemory +typedef VOID (WINAPI* WlanFreeMemoryFunction)(PVOID pMemory); + +// WlanCloseHandle +typedef DWORD (WINAPI* WlanCloseHandleFunction)(HANDLE hClientHandle, + PVOID pReserved); + +// Extracts data for an access point and converts to Gears format. +bool UndefineDosDevice(const std::string& device_name); +bool DefineDosDeviceIfNotExists(const std::string& device_name); +HANDLE GetFileHandle(const std::string& device_name); +// Makes the OID query and returns a Win32 error code. +int PerformQuery(HANDLE adapter_handle, std::vector<char>& buffer, DWORD* bytes_out); +bool ResizeBuffer(size_t requested_size, std::vector<char>& buffer); +// Gets the system directory and appends a trailing slash if not already +// present. +bool GetSystemDirectory(std::string* path); + +bool ConvertToAccessPointData(const NDIS_WLAN_BSSID& data, nsWifiAccessPoint* access_point_data); +int GetDataFromBssIdList(const NDIS_802_11_BSSID_LIST& bss_id_list, + int list_size, + nsCOMArray<nsWifiAccessPoint>& outData); +} // namespace + +class WindowsNdisApi +{ +public: + virtual ~WindowsNdisApi(); + static WindowsNdisApi* Create(); + virtual bool GetAccessPointData(nsCOMArray<nsWifiAccessPoint>& outData); + +private: + static bool GetInterfacesNDIS(std::vector<std::string>& interface_service_names_out); + // Swaps in content of the vector passed + explicit WindowsNdisApi(std::vector<std::string>* interface_service_names); + bool GetInterfaceDataNDIS(HANDLE adapter_handle, nsCOMArray<nsWifiAccessPoint>& outData); + // NDIS variables. + std::vector<std::string> interface_service_names_; + std::vector<char> _buffer; +}; + +// WindowsNdisApi +WindowsNdisApi::WindowsNdisApi( + std::vector<std::string>* interface_service_names) + : _buffer(kInitialBufferSize) { + interface_service_names_.swap(*interface_service_names); +} + +WindowsNdisApi::~WindowsNdisApi() { +} + +WindowsNdisApi* WindowsNdisApi::Create() { + std::vector<std::string> interface_service_names; + if (GetInterfacesNDIS(interface_service_names)) { + return new WindowsNdisApi(&interface_service_names); + } + return NULL; +} + +bool WindowsNdisApi::GetAccessPointData(nsCOMArray<nsWifiAccessPoint>& outData) { + int interfaces_failed = 0; + int interfaces_succeeded = 0; + + for (int i = 0; i < static_cast<int>(interface_service_names_.size()); ++i) { + // First, check that we have a DOS device for this adapter. + if (!DefineDosDeviceIfNotExists(interface_service_names_[i])) { + continue; + } + + // Get the handle to the device. This will fail if the named device is not + // valid. + HANDLE adapter_handle = GetFileHandle(interface_service_names_[i]); + if (adapter_handle == INVALID_HANDLE_VALUE) { + continue; + } + + // Get the data. + if (GetInterfaceDataNDIS(adapter_handle, outData)) { + ++interfaces_succeeded; + } else { + ++interfaces_failed; + } + + // Clean up. + CloseHandle(adapter_handle); + UndefineDosDevice(interface_service_names_[i]); + } + + // Return true if at least one interface succeeded, or at the very least none + // failed. + return interfaces_succeeded > 0 || interfaces_failed == 0; +} + +bool WindowsNdisApi::GetInterfacesNDIS(std::vector<std::string>& interface_service_names_out) { + HKEY network_cards_key = NULL; + if (RegOpenKeyEx( + HKEY_LOCAL_MACHINE, + "Software\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards", + 0, + KEY_READ, + &network_cards_key) != ERROR_SUCCESS) { + return false; + } + if (!network_cards_key) { + return false; + } + + for (int i = 0; ; ++i) { + TCHAR name[kStringLength]; + DWORD name_size = kStringLength; + FILETIME time; + if (RegEnumKeyEx(network_cards_key, + i, + name, + &name_size, + NULL, + NULL, + NULL, + &time) != ERROR_SUCCESS) { + break; + } + HKEY hardware_key = NULL; + if (RegOpenKeyEx(network_cards_key, name, 0, KEY_READ, &hardware_key) != + ERROR_SUCCESS) { + break; + } + if (!hardware_key) { + return false; + } + + TCHAR service_name[kStringLength]; + DWORD service_name_size = kStringLength; + DWORD type = 0; + if (RegQueryValueEx(hardware_key, + "ServiceName", + NULL, + &type, + reinterpret_cast<LPBYTE>(service_name), + &service_name_size) == ERROR_SUCCESS) { + interface_service_names_out.push_back(service_name); + } + RegCloseKey(hardware_key); + } + + RegCloseKey(network_cards_key); + return true; +} + +bool WindowsNdisApi::GetInterfaceDataNDIS(HANDLE adapter_handle, + nsCOMArray<nsWifiAccessPoint>& outData) { + DWORD bytes_out; + int result; + + while (true) { + bytes_out = 0; + result = PerformQuery(adapter_handle, _buffer, &bytes_out); + if (result == ERROR_GEN_FAILURE || // Returned by some Intel cards. + result == ERROR_INSUFFICIENT_BUFFER || + result == ERROR_MORE_DATA || + result == NDIS_STATUS_INVALID_LENGTH || + result == NDIS_STATUS_BUFFER_TOO_SHORT) { + // The buffer we supplied is too small, so increase it. bytes_out should + // provide the required buffer size, but this is not always the case. + size_t newSize; + if (bytes_out > static_cast<DWORD>(_buffer.size())) { + newSize = bytes_out; + } else { + newSize = _buffer.size() * 2; + } + if (!ResizeBuffer(newSize, _buffer)) { + return false; + } + } else { + // The buffer is not too small. + break; + } + } + + if (result == ERROR_SUCCESS) { + NDIS_802_11_BSSID_LIST* bssid_list = + reinterpret_cast<NDIS_802_11_BSSID_LIST*>(&_buffer[0]); + GetDataFromBssIdList(*bssid_list, _buffer.size(), outData); + } + + return true; +} + +namespace { +#define uint8 unsigned char + +bool ConvertToAccessPointData(const NDIS_WLAN_BSSID& data, nsWifiAccessPoint* access_point_data) +{ + access_point_data->setMac(data.MacAddress); + access_point_data->setSignal(data.Rssi); + // Note that _NDIS_802_11_SSID::Ssid::Ssid is not null-terminated. + const unsigned char* ssid = data.Ssid.Ssid; + size_t len = data.Ssid.SsidLength; + access_point_data->setSSID(reinterpret_cast<const char*>(ssid), len); + return true; +} + +int GetDataFromBssIdList(const NDIS_802_11_BSSID_LIST& bss_id_list, + int list_size, + nsCOMArray<nsWifiAccessPoint>& outData) +{ + // Walk through the BSS IDs. + int found = 0; + const uint8* iterator = reinterpret_cast<const uint8*>(&bss_id_list.Bssid[0]); + const uint8* end_of_buffer = + reinterpret_cast<const uint8*>(&bss_id_list) + list_size; + for (int i = 0; i < static_cast<int>(bss_id_list.NumberOfItems); ++i) { + const NDIS_WLAN_BSSID *bss_id = + reinterpret_cast<const NDIS_WLAN_BSSID*>(iterator); + // Check that the length of this BSS ID is reasonable. + if (bss_id->Length < sizeof(NDIS_WLAN_BSSID) || + iterator + bss_id->Length > end_of_buffer) { + break; + } + nsWifiAccessPoint* ap = new nsWifiAccessPoint(); + if (ConvertToAccessPointData(*bss_id, ap)) { + outData.AppendObject(ap); + ++found; + } + // Move to the next BSS ID. + iterator += bss_id->Length; + } + return found; +} + + +bool UndefineDosDevice(const std::string& device_name) { + // We remove only the mapping we use, that is \Device\<device_name>. + std::string target_path = "\\Device\\" + device_name; + return DefineDosDevice( + DDD_RAW_TARGET_PATH | DDD_REMOVE_DEFINITION | DDD_EXACT_MATCH_ON_REMOVE, + device_name.c_str(), + target_path.c_str()) == TRUE; +} + +bool DefineDosDeviceIfNotExists(const std::string& device_name) { + // We create a DOS device name for the device at \Device\<device_name>. + std::string target_path = "\\Device\\" + device_name; + + TCHAR target[kStringLength]; + if (QueryDosDevice(device_name.c_str(), target, kStringLength) > 0 && + target_path.compare(target) == 0) { + // Device already exists. + return true; + } + + if (GetLastError() != ERROR_FILE_NOT_FOUND) { + return false; + } + + if (!DefineDosDevice(DDD_RAW_TARGET_PATH, + device_name.c_str(), + target_path.c_str())) { + return false; + } + + // Check that the device is really there. + return QueryDosDevice(device_name.c_str(), target, kStringLength) > 0 && + target_path.compare(target) == 0; +} + +HANDLE GetFileHandle(const std::string& device_name) { + // We access a device with DOS path \Device\<device_name> at + // \\.\<device_name>. + std::string formatted_device_name = "\\\\.\\" + device_name; + + return CreateFile(formatted_device_name.c_str(), + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, // share mode + 0, // security attributes + OPEN_EXISTING, + 0, // flags and attributes + INVALID_HANDLE_VALUE); +} + +int PerformQuery(HANDLE adapter_handle, + std::vector<char>& buffer, + DWORD* bytes_out) { + DWORD oid = OID_802_11_BSSID_LIST; + if (!DeviceIoControl(adapter_handle, + IOCTL_NDIS_QUERY_GLOBAL_STATS, + &oid, + sizeof(oid), + &buffer[0], + buffer.size(), + bytes_out, + NULL)) { + return GetLastError(); + } + return ERROR_SUCCESS; +} + +bool ResizeBuffer(size_t requested_size, std::vector<char>& buffer) { + if (requested_size > kMaximumBufferSize) { + buffer.resize(kInitialBufferSize); + return false; + } + + buffer.resize(requested_size); + return true; +} + +} // namespace + + +nsresult +WinXPWifiScanner::GetAccessPointsFromWLAN(nsCOMArray<nsWifiAccessPoint> &accessPoints) +{ + if (!mImplementation) { + mImplementation = WindowsNdisApi::Create(); + if (!mImplementation) { + return NS_ERROR_FAILURE; + } + } + + accessPoints.Clear(); + bool isOk = mImplementation->GetAccessPointData(accessPoints); + if (!isOk) { + mImplementation = 0; + return NS_ERROR_FAILURE; + } + + return NS_OK; +} |