Commit d0591a30 authored by btolsch's avatar btolsch Committed by Commit Bot

Add Windows implementation of GetDiscoveryNetworkInfoList

This change enables DiscoveryNetworkMonitor to work on Windows.  It
supports both MAC addresses and WiFi SSIDs for the network ID.

Bug: 698943
Change-Id: I0c94743fd51019438fd53472f38a2d83e21a3246
Reviewed-on: https://chromium-review.googlesource.com/527774
Commit-Queue: Brandon Tolsch <btolsch@chromium.org>
Reviewed-by: default avatarKevin Marshall <kmarshall@chromium.org>
Reviewed-by: default avatarDerek Cheng <imcheng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#485431}
parent c1665b3a
...@@ -62,6 +62,11 @@ static_library("discovery") { ...@@ -62,6 +62,11 @@ static_library("discovery") {
] ]
} }
if (is_win) {
libs = [ "wlanapi.lib" ]
ldflags = [ "/DELAYLOAD:wlanapi.dll" ]
}
if (is_mac) { if (is_mac) {
libs = [ "CoreWLAN.framework" ] libs = [ "CoreWLAN.framework" ]
} }
......
...@@ -4,7 +4,243 @@ ...@@ -4,7 +4,243 @@
#include "chrome/browser/media/router/discovery/discovery_network_list.h" #include "chrome/browser/media/router/discovery/discovery_network_list.h"
// TODO(btolsch): Implement this for Windows. #include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h> // NOLINT
#include <windot11.h> // NOLINT
#include <wlanapi.h> // NOLINT
#include <algorithm>
#include <cstring>
#include <map>
#include <utility>
#include <vector>
#include "base/containers/small_map.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string_number_conversions.h"
namespace {
struct GuidOperatorLess {
bool operator()(const GUID& guid1, const GUID& guid2) const {
return memcmp(&guid1, &guid2, sizeof(GUID)) < 0;
}
};
void IfTable2Deleter(PMIB_IF_TABLE2 interface_table) {
if (interface_table) {
FreeMibTable(interface_table);
}
}
void WlanApiDeleter(void* p) {
if (p) {
WlanFreeMemory(p);
}
}
class ScopedWlanClientHandle {
public:
ScopedWlanClientHandle() {}
~ScopedWlanClientHandle() {
if (handle != nullptr) {
WlanCloseHandle(handle, nullptr);
}
}
HANDLE handle = nullptr;
private:
DISALLOW_COPY_AND_ASSIGN(ScopedWlanClientHandle);
};
// Returns a map from a network interface's GUID to its MAC address. This
// enumerates all network interfaces, not just wireless interfaces.
base::small_map<std::map<GUID, std::string, GuidOperatorLess>>
GetInterfaceGuidMacMap() {
PMIB_IF_TABLE2 interface_table_raw = nullptr;
auto result = GetIfTable2(&interface_table_raw);
if (result != ERROR_SUCCESS) {
LOG(WARNING) << "GetIfTable2() failed: " << result;
return {};
}
std::unique_ptr<MIB_IF_TABLE2, decltype(&IfTable2Deleter)> interface_table(
interface_table_raw, IfTable2Deleter);
base::small_map<std::map<GUID, std::string, GuidOperatorLess>> guid_mac_map;
for (ULONG i = 0; i < interface_table->NumEntries; ++i) {
const auto* interface_row = &interface_table->Table[i];
guid_mac_map.insert(std::make_pair(
interface_row->InterfaceGuid,
std::string{
reinterpret_cast<const char*>(interface_row->PhysicalAddress),
interface_row->PhysicalAddressLength}));
}
return guid_mac_map;
}
// Returns the associated SSID of an interface identified by its interface GUID.
// If it is not a wireless interface or if it's not currently associated with a
// network, it returns an empty string.
std::string GetSsidForInterfaceGuid(const HANDLE wlan_client_handle,
const GUID& interface_guid) {
WLAN_CONNECTION_ATTRIBUTES* connection_info_raw = nullptr;
DWORD connection_info_size = 0;
auto result = WlanQueryInterface(
wlan_client_handle, &interface_guid, wlan_intf_opcode_current_connection,
nullptr, &connection_info_size,
reinterpret_cast<void**>(&connection_info_raw), nullptr);
if (result != ERROR_SUCCESS) {
// We can't get the SSID for this interface so its network ID will
// fall back to its MAC address below.
DVLOG(2) << "Failed to get wireless connection info: " << result;
return {};
}
std::unique_ptr<WLAN_CONNECTION_ATTRIBUTES, decltype(&WlanApiDeleter)>
connection_info(connection_info_raw, WlanApiDeleter);
if (connection_info->isState != wlan_interface_state_connected) {
return {};
}
const auto* ssid = &connection_info->wlanAssociationAttributes.dot11Ssid;
return std::string(reinterpret_cast<const char*>(ssid->ucSSID),
ssid->uSSIDLength);
}
// Returns a map from a network adapter's MAC address to its currently
// associated WiFi SSID.
base::small_map<std::map<std::string, std::string>> GetMacSsidMap() {
ScopedWlanClientHandle wlan_client_handle;
constexpr DWORD kWlanClientVersion = 2;
DWORD wlan_current_version = 0;
auto result =
WlanOpenHandle(kWlanClientVersion, nullptr, &wlan_current_version,
&wlan_client_handle.handle);
if (result != ERROR_SUCCESS) {
LOG(WARNING) << "Failed to open Wlan client handle: " << result;
return {};
}
PWLAN_INTERFACE_INFO_LIST wlan_interface_list_raw = nullptr;
result = WlanEnumInterfaces(wlan_client_handle.handle, nullptr,
&wlan_interface_list_raw);
if (result != ERROR_SUCCESS) {
LOG(WARNING) << "Failed to enumerate wireless interfaces: " << result;
return {};
}
std::unique_ptr<WLAN_INTERFACE_INFO_LIST, decltype(&WlanApiDeleter)>
wlan_interface_list(wlan_interface_list_raw, WlanApiDeleter);
auto guid_mac_map = GetInterfaceGuidMacMap();
base::small_map<std::map<std::string, std::string>> mac_ssid_map;
// This loop takes each wireless interface and maps its MAC address to its
// associated SSID, if it has one. Each wireless interface has an interface
// GUID which we can use to get its MAC address via |guid_mac_map| and its
// associated SSID via WlanQueryInterface.
for (DWORD i = 0; i < wlan_interface_list->dwNumberOfItems; ++i) {
const auto* interface_info = &wlan_interface_list->InterfaceInfo[i];
const auto mac_entry = guid_mac_map.find(interface_info->InterfaceGuid);
if (mac_entry == guid_mac_map.end()) {
continue;
}
auto ssid = GetSsidForInterfaceGuid(wlan_client_handle.handle,
interface_info->InterfaceGuid);
if (ssid.empty()) {
continue;
}
mac_ssid_map.insert(std::make_pair(mac_entry->second, std::move(ssid)));
}
return mac_ssid_map;
}
} // namespace
std::vector<DiscoveryNetworkInfo> GetDiscoveryNetworkInfoList() { std::vector<DiscoveryNetworkInfo> GetDiscoveryNetworkInfoList() {
return std::vector<DiscoveryNetworkInfo>(); // Max number of times to retry GetAdaptersAddresses due to
// ERROR_BUFFER_OVERFLOW. If GetAdaptersAddresses returns this indefinitely
// due to an unforeseen reason, we don't want to be stuck in an endless loop.
constexpr int kMaxGetAdaptersAddressTries = 10;
// Use an initial buffer size of 15KB, as recommended by MSDN. See:
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365915(v=vs.85).aspx
constexpr int kInitialAddressBufferSize = 15000;
constexpr ULONG kAddressFlags =
GAA_FLAG_SKIP_UNICAST | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST |
GAA_FLAG_SKIP_DNS_SERVER;
// Although we need to provide GetAdaptersAddresses with a buffer, there's no
// way to know what size to use. We use a best-guess here but when
// GetAdaptersAddresses returns ERROR_BUFFER_OVERFLOW, it means our guess was
// too small. When this happens it will also reset |addresses_buffer_size| to
// the required size. Although it's very unlikely that two successive calls
// will both require increasing the buffer size, there's no guarantee that
// this won't happen; this is what the maximum retry count guards against.
ULONG addresses_buffer_size = kInitialAddressBufferSize;
std::unique_ptr<char[]> addresses_buffer;
PIP_ADAPTER_ADDRESSES adapter_addresses = nullptr;
ULONG result = ERROR_BUFFER_OVERFLOW;
for (int i = 0;
result == ERROR_BUFFER_OVERFLOW && i < kMaxGetAdaptersAddressTries;
++i) {
addresses_buffer.reset(new char[addresses_buffer_size]);
adapter_addresses =
reinterpret_cast<PIP_ADAPTER_ADDRESSES>(addresses_buffer.get());
result = GetAdaptersAddresses(AF_UNSPEC, kAddressFlags, nullptr,
adapter_addresses, &addresses_buffer_size);
}
if (result != NO_ERROR) {
return {};
}
std::vector<DiscoveryNetworkInfo> network_ids;
auto mac_ssid_map = GetMacSsidMap();
for (const IP_ADAPTER_ADDRESSES* current_adapter = adapter_addresses;
current_adapter != nullptr; current_adapter = current_adapter->Next) {
// We only want adapters which are up and either Ethernet or wireless, so we
// skip everything else here.
if (current_adapter->OperStatus != IfOperStatusUp ||
(current_adapter->IfType != IF_TYPE_ETHERNET_CSMACD &&
current_adapter->IfType != IF_TYPE_IEEE80211)) {
continue;
}
// We have to use a slightly roundabout way to get the SSID for each
// adapter:
// - Enumerate wifi devices to get list of interface GUIDs.
// - Enumerate interfaces to get interface GUID -> physical address map.
// - Map interface GUIDs to SSID.
// - Use GUID -> MAC map to do MAC -> interface GUID -> SSID.
// Although it's theoretically possible to have multiple interfaces per
// adapter, most wireless cards don't actually allow multiple
// managed-mode interfaces. However, in the event that there really
// are multiple interfaces per adapter (i.e. physical address), we will
// simply use the SSID of the first match. It's unclear how Windows would
// handle this case since it's somewhat loose with its use of the words
// "adapter" and "interface".
std::string name(current_adapter->AdapterName);
if (current_adapter->IfType == IF_TYPE_IEEE80211) {
std::string adapter_mac(
reinterpret_cast<const char*>(current_adapter->PhysicalAddress),
current_adapter->PhysicalAddressLength);
const auto ssid_entry = mac_ssid_map.find(adapter_mac);
if (ssid_entry != mac_ssid_map.end()) {
network_ids.emplace_back(name, ssid_entry->second);
continue;
}
}
network_ids.emplace_back(
name, base::HexEncode(current_adapter->PhysicalAddress,
current_adapter->PhysicalAddressLength));
}
StableSortDiscoveryNetworkInfo(network_ids.begin(), network_ids.end());
return network_ids;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment