| // 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. |
| |
| #include "device/geolocation/wifi_data_provider_win.h" |
| |
| #include <windows.h> |
| #include <winioctl.h> |
| #include <wlanapi.h> |
| |
| #include "base/memory/free_deleter.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/timer/elapsed_timer.h" |
| #include "base/win/windows_version.h" |
| #include "device/geolocation/wifi_data_provider_common.h" |
| #include "device/geolocation/wifi_data_provider_common_win.h" |
| #include "device/geolocation/wifi_data_provider_manager.h" |
| |
| namespace device { |
| |
| namespace { |
| |
| static const int kDefaultPollingIntervalMs = 10 * 1000; // 10s |
| static const int kNoChangePollingIntervalMs = 2 * 60 * 1000; // 2 mins |
| static const int kTwoNoChangePollingIntervalMs = 10 * 60 * 1000; // 10 mins |
| static const int kNoWifiPollingIntervalMs = 20 * 1000; // 20s |
| |
| // 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 AccessPointData. |
| AccessPointData GetNetworkData(const WLAN_BSS_ENTRY& bss_entry) { |
| AccessPointData access_point_data; |
| // Currently we get only MAC address, signal strength and SSID. |
| access_point_data.mac_address = MacAddressAsString16(bss_entry.dot11Bssid); |
| access_point_data.radio_signal_strength = bss_entry.lRssi; |
| // bss_entry.dot11Ssid.ucSSID is not null-terminated. |
| base::UTF8ToUTF16(reinterpret_cast<const char*>(bss_entry.dot11Ssid.ucSSID), |
| static_cast<ULONG>(bss_entry.dot11Ssid.uSSIDLength), |
| &access_point_data.ssid); |
| |
| // TODO(steveblock): Is it possible to get the following? |
| // access_point_data.signal_to_noise |
| // access_point_data.age |
| // access_point_data.channel |
| return access_point_data; |
| } |
| |
| // This class encapsulates loading and interacting with wlan_api.dll, which can |
| // not be loaded statically because it's not available in Server 2008 R2, where |
| // it must be installed explicitly by the user if and when they wants to use the |
| // Wireless interface. |
| // https://www.bonusbits.com/wiki/KB:Wlanapi.dll_missing_on_Windows_Server_2008_R2 |
| class WindowsWlanApi : public WifiDataProviderCommon::WlanApiInterface { |
| public: |
| static std::unique_ptr<WindowsWlanApi> Create(); |
| |
| // Takes ownership of the library handle. |
| explicit WindowsWlanApi(HINSTANCE library); |
| ~WindowsWlanApi() override; |
| |
| // WlanApiInterface implementation |
| bool GetAccessPointData(WifiData::AccessPointDataSet* data) override; |
| |
| private: |
| // Logs number of detected wlan interfaces. |
| static void LogWlanInterfaceCount(int count); |
| |
| bool GetInterfaceDataWLAN(HANDLE wlan_handle, |
| const GUID& interface_id, |
| WifiData::AccessPointDataSet* data); |
| // Handle to the wlanapi.dll library. |
| HINSTANCE library_; |
| |
| // Function pointers for WLAN |
| WlanOpenHandleFunction WlanOpenHandle_function_; |
| WlanEnumInterfacesFunction WlanEnumInterfaces_function_; |
| WlanGetNetworkBssListFunction WlanGetNetworkBssList_function_; |
| WlanFreeMemoryFunction WlanFreeMemory_function_; |
| WlanCloseHandleFunction WlanCloseHandle_function_; |
| }; |
| |
| // static |
| std::unique_ptr<WindowsWlanApi> WindowsWlanApi::Create() { |
| // Use an absolute path to load the DLL to avoid DLL preloading attacks. |
| static const wchar_t* const kDLL = L"%WINDIR%\\system32\\wlanapi.dll"; |
| wchar_t path[MAX_PATH] = {0}; |
| ExpandEnvironmentStrings(kDLL, path, arraysize(path)); |
| HINSTANCE library = LoadLibraryEx(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); |
| if (!library) |
| return nullptr; |
| return std::make_unique<WindowsWlanApi>(library); |
| } |
| |
| WindowsWlanApi::WindowsWlanApi(HINSTANCE library) : library_(library) { |
| DCHECK(library_); |
| // Extract all methods from |library_|. |
| WlanOpenHandle_function_ = reinterpret_cast<WlanOpenHandleFunction>( |
| GetProcAddress(library_, "WlanOpenHandle")); |
| WlanEnumInterfaces_function_ = reinterpret_cast<WlanEnumInterfacesFunction>( |
| GetProcAddress(library_, "WlanEnumInterfaces")); |
| WlanGetNetworkBssList_function_ = |
| reinterpret_cast<WlanGetNetworkBssListFunction>( |
| GetProcAddress(library_, "WlanGetNetworkBssList")); |
| WlanFreeMemory_function_ = reinterpret_cast<WlanFreeMemoryFunction>( |
| GetProcAddress(library_, "WlanFreeMemory")); |
| WlanCloseHandle_function_ = reinterpret_cast<WlanCloseHandleFunction>( |
| GetProcAddress(library_, "WlanCloseHandle")); |
| |
| DCHECK(WlanOpenHandle_function_ && WlanEnumInterfaces_function_ && |
| WlanGetNetworkBssList_function_ && WlanFreeMemory_function_ && |
| WlanCloseHandle_function_); |
| } |
| |
| WindowsWlanApi::~WindowsWlanApi() { |
| FreeLibrary(library_); |
| } |
| |
| // static |
| void WindowsWlanApi::LogWlanInterfaceCount(int count) { |
| UMA_HISTOGRAM_CUSTOM_COUNTS("Net.Wifi.InterfaceCount", count, 1 /* min */, |
| 5 /* max */, 6 /* bucket_count */); |
| } |
| |
| bool WindowsWlanApi::GetAccessPointData(WifiData::AccessPointDataSet* data) { |
| DCHECK(data); |
| |
| DWORD negotiated_version; |
| HANDLE wlan_handle = nullptr; |
| // Highest WLAN API version supported by the client; pass the lowest. It seems |
| // that the negotiated version is the Vista version (the highest) irrespective |
| // of what we pass! |
| static const int kXpWlanClientVersion = 1; |
| if ((*WlanOpenHandle_function_)(kXpWlanClientVersion, NULL, |
| &negotiated_version, |
| &wlan_handle) != ERROR_SUCCESS) { |
| LogWlanInterfaceCount(0); |
| return false; |
| } |
| DCHECK(wlan_handle); |
| |
| // Get the list of interfaces. WlanEnumInterfaces allocates |interface_list|. |
| WLAN_INTERFACE_INFO_LIST* interface_list = nullptr; |
| if ((*WlanEnumInterfaces_function_)(wlan_handle, NULL, &interface_list) != |
| ERROR_SUCCESS) { |
| LogWlanInterfaceCount(0); |
| return false; |
| } |
| DCHECK(interface_list); |
| |
| LogWlanInterfaceCount(interface_list->dwNumberOfItems); |
| |
| // Go through the list of interfaces and get the data for each. |
| for (size_t i = 0; i < interface_list->dwNumberOfItems; ++i) { |
| const WLAN_INTERFACE_INFO interface_info = interface_list->InterfaceInfo[i]; |
| |
| // Skip any interface that is midway through association; the |
| // WlanGetNetworkBssList function call is known to hang indefinitely |
| // when it's in this state. https://crbug.com/39300 |
| if (interface_info.isState == wlan_interface_state_associating) { |
| DLOG(WARNING) << "Skipping wifi scan on adapter " << i << " (" |
| << interface_info.strInterfaceDescription |
| << ") in 'associating' state. Repeated occurrences " |
| "indicates a non-responding adapter."; |
| continue; |
| } |
| GetInterfaceDataWLAN(wlan_handle, interface_info.InterfaceGuid, data); |
| } |
| |
| (*WlanFreeMemory_function_)(interface_list); |
| |
| return (*WlanCloseHandle_function_)(wlan_handle, NULL) == ERROR_SUCCESS; |
| } |
| |
| // Appends the data for a single interface to |data|. Returns false for error. |
| bool WindowsWlanApi::GetInterfaceDataWLAN(const HANDLE wlan_handle, |
| const GUID& interface_id, |
| WifiData::AccessPointDataSet* data) { |
| base::ElapsedTimer wlan_get_network_list_timer; |
| // WlanGetNetworkBssList allocates |bss_list|. |
| WLAN_BSS_LIST* bss_list = nullptr; |
| if ((*WlanGetNetworkBssList_function_)(wlan_handle, &interface_id, |
| NULL, // Use all SSIDs. |
| dot11_BSS_type_any, |
| false, // bSecurityEnabled - unused |
| NULL, // reserved |
| &bss_list) != ERROR_SUCCESS) { |
| return false; |
| } |
| // WlanGetNetworkBssList() can return success without filling |bss_list|. |
| if (!bss_list) |
| return false; |
| |
| UMA_HISTOGRAM_CUSTOM_TIMES("Net.Wifi.ScanLatency", |
| wlan_get_network_list_timer.Elapsed(), |
| base::TimeDelta::FromMilliseconds(1), |
| base::TimeDelta::FromMinutes(1), 100); |
| |
| for (size_t i = 0; i < bss_list->dwNumberOfItems; ++i) |
| data->insert(GetNetworkData(bss_list->wlanBssEntries[i])); |
| |
| (*WlanFreeMemory_function_)(bss_list); |
| |
| return true; |
| } |
| |
| } // anonymous namespace |
| |
| WifiDataProvider* WifiDataProviderManager::DefaultFactoryFunction() { |
| return new WifiDataProviderWin(); |
| } |
| |
| WifiDataProviderWin::WifiDataProviderWin() = default; |
| |
| WifiDataProviderWin::~WifiDataProviderWin() = default; |
| |
| std::unique_ptr<WifiDataProviderCommon::WlanApiInterface> |
| WifiDataProviderWin::CreateWlanApi() { |
| return WindowsWlanApi::Create(); |
| } |
| |
| std::unique_ptr<WifiPollingPolicy> WifiDataProviderWin::CreatePollingPolicy() { |
| return std::make_unique<GenericWifiPollingPolicy< |
| kDefaultPollingIntervalMs, kNoChangePollingIntervalMs, |
| kTwoNoChangePollingIntervalMs, kNoWifiPollingIntervalMs>>(); |
| } |
| |
| } // namespace device |