blob: b5a8adebda11eabf3bb201321b2583f916cdc6cd [file] [log] [blame]
// 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 "services/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/stl_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/timer/elapsed_timer.h"
#include "base/win/windows_version.h"
#include "services/device/geolocation/wifi_data_provider_common.h"
#include "services/device/geolocation/wifi_data_provider_common_win.h"
#include "services/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, base::size(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