Index: chrome/browser/media/router/discovery/discovery_network_list_win.cc |
diff --git a/chrome/browser/media/router/discovery/discovery_network_list_win.cc b/chrome/browser/media/router/discovery/discovery_network_list_win.cc |
index a18dd909e08d0d4920ef55d19483baf6c4342ec6..fed47395a36d9726ab9266d7977316cf0935a73e 100644 |
--- a/chrome/browser/media/router/discovery/discovery_network_list_win.cc |
+++ b/chrome/browser/media/router/discovery/discovery_network_list_win.cc |
@@ -4,7 +4,214 @@ |
#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 <utility> |
+#include <vector> |
+ |
+#include "base/memory/ptr_util.h" |
+#include "base/strings/string_number_conversions.h" |
+ |
+namespace { |
+ |
+void IfTable2Deleter(PMIB_IF_TABLE2 interface_table) { |
+ if (interface_table) { |
+ FreeMibTable(interface_table); |
+ } |
+} |
+ |
+void WlanApiDeleter(void* p) { |
+ if (p) { |
+ WlanFreeMemory(p); |
+ } |
+} |
+ |
+struct MAC { |
+ MAC() : data{} {} |
+ MAC(const uint8_t* src, ULONG length) : data{} { assign(src, length); } |
+ void assign(const uint8_t* src, ULONG length) { |
+ DCHECK_LE(length, 6UL); |
+ memcpy(data, src, length); |
+ } |
+ bool operator==(const MAC& o) const { return memcmp(o.data, data, 6) == 0; } |
+ uint8_t data[6]; |
+}; |
+ |
+std::vector<std::pair<GUID, MAC>> GetInterfaceGuidMap() { |
+ PMIB_IF_TABLE2 interface_table_raw = nullptr; |
+ auto result = GetIfTable2(&interface_table_raw); |
+ if (result != ERROR_SUCCESS) { |
+ DVLOG(2) << "GetIfTable2() failed"; |
+ return {}; |
+ } |
+ auto interface_table = |
+ std::unique_ptr<MIB_IF_TABLE2, decltype(&IfTable2Deleter)>( |
+ interface_table_raw, IfTable2Deleter); |
+ |
+ std::vector<std::pair<GUID, MAC>> guid_map; |
+ guid_map.reserve(interface_table->NumEntries); |
+ for (ULONG i = 0; i < interface_table->NumEntries; ++i) { |
+ const auto* interface_row = &interface_table->Table[i]; |
+ guid_map.push_back( |
+ std::make_pair(interface_row->InterfaceGuid, |
+ MAC{interface_row->PhysicalAddress, |
+ interface_row->PhysicalAddressLength})); |
+ } |
+ |
+ return guid_map; |
+} |
+ |
+std::vector<std::pair<MAC, std::string>> GetConnectionInfo() { |
+ HANDLE client_handle = nullptr; |
+ constexpr DWORD client_version = 2; |
+ DWORD current_version = 0; |
+ |
+ auto result = |
+ WlanOpenHandle(client_version, nullptr, ¤t_version, &client_handle); |
+ if (result != ERROR_SUCCESS) { |
+ DVLOG(2) << "Failed to open Wlan client handle"; |
+ return {}; |
+ } |
+ |
+ PWLAN_INTERFACE_INFO_LIST wlan_interface_list_raw = nullptr; |
+ result = WlanEnumInterfaces(client_handle, nullptr, &wlan_interface_list_raw); |
+ if (result != ERROR_SUCCESS) { |
+ DVLOG(2) << "Failed to enumerate wireless interfaces"; |
+ return {}; |
+ } |
+ auto wlan_interface_list = |
+ std::unique_ptr<WLAN_INTERFACE_INFO_LIST, decltype(&WlanApiDeleter)>( |
+ wlan_interface_list_raw, WlanApiDeleter); |
+ |
+ auto guid_mac_map = GetInterfaceGuidMap(); |
+ |
+ std::vector<std::pair<MAC, std::string>> mac_ssid_map; |
+ for (DWORD i = 0; i < wlan_interface_list->dwNumberOfItems; ++i) { |
+ const auto* interface_info = &wlan_interface_list->InterfaceInfo[i]; |
+ MAC interface_mac; |
+ bool found_mac = false; |
+ for (const auto& guid_entry : guid_mac_map) { |
+ if (guid_entry.first == interface_info->InterfaceGuid) { |
+ interface_mac = guid_entry.second; |
+ found_mac = true; |
+ break; |
+ } |
+ } |
+ if (!found_mac) { |
+ continue; |
+ } |
+ |
+ WLAN_CONNECTION_ATTRIBUTES* connection_info = nullptr; |
+ DWORD connection_info_size = 0; |
+ result = WlanQueryInterface( |
+ client_handle, &interface_info->InterfaceGuid, |
+ wlan_intf_opcode_current_connection, nullptr, &connection_info_size, |
+ reinterpret_cast<void**>(&connection_info), 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"; |
+ continue; |
+ } |
+ auto connection_attributes = |
+ std::unique_ptr<WLAN_CONNECTION_ATTRIBUTES, decltype(&WlanApiDeleter)>( |
+ connection_info, WlanApiDeleter); |
+ const auto* ssid = |
+ &connection_attributes->wlanAssociationAttributes.dot11Ssid; |
+ mac_ssid_map.push_back(std::make_pair( |
+ interface_mac, |
+ std::string(reinterpret_cast<const char*>(&ssid->ucSSID[0]), |
+ ssid->uSSIDLength))); |
+ } |
+ return mac_ssid_map; |
+} |
+ |
+} // namespace |
+ |
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 unforseen reason, we don't want to be stuck in an endless loop. |
+ static 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 |
+ static constexpr int kInitialBufferSize = 15000; |
+ constexpr ULONG address_flags = // TODO: include all interfaces flag? |
+ GAA_FLAG_SKIP_UNICAST | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | |
+ GAA_FLAG_SKIP_DNS_SERVER; |
+ std::vector<DiscoveryNetworkInfo> network_ids; |
+ |
+ ULONG addresses_buffer_size = kInitialBufferSize; |
+ char initial_buf[kInitialBufferSize]; |
+ std::unique_ptr<char[]> addresses_buffer; |
+ |
+ PIP_ADAPTER_ADDRESSES adapter_addresses = |
+ reinterpret_cast<PIP_ADAPTER_ADDRESSES>(initial_buf); |
+ auto result = GetAdaptersAddresses(AF_UNSPEC, address_flags, nullptr, |
+ adapter_addresses, &addresses_buffer_size); |
+ |
+ for (int i = 1; |
+ 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, address_flags, nullptr, |
+ adapter_addresses, &addresses_buffer_size); |
+ } |
+ |
+ if (result != NO_ERROR) { |
+ return std::vector<DiscoveryNetworkInfo>(); |
+ } |
+ |
+ auto mac_ssid_map = GetConnectionInfo(); |
+ for (const IP_ADAPTER_ADDRESSES* current_adapter = adapter_addresses; |
+ current_adapter != nullptr; current_adapter = current_adapter->Next) { |
+ if (current_adapter->OperStatus != IfOperStatusUp || |
+ (current_adapter->IfType != IF_TYPE_ETHERNET_CSMACD && |
+ current_adapter->IfType != IF_TYPE_IEEE80211)) { |
+ continue; |
+ } |
+ std::string name(current_adapter->AdapterName); |
+ // 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, we will simply choose the first |
+ // one in this list. |
+ if (current_adapter->IfType == IF_TYPE_IEEE80211) { |
+ MAC adapter_mac(current_adapter->PhysicalAddress, |
+ current_adapter->PhysicalAddressLength); |
+ bool found_ssid = false; |
+ for (const auto& ssid_entry : mac_ssid_map) { |
+ if (ssid_entry.first == adapter_mac) { |
+ network_ids.push_back({name, ssid_entry.second}); |
+ found_ssid = true; |
+ break; |
+ } |
+ } |
+ if (found_ssid) { |
+ continue; |
+ } |
+ } |
+ network_ids.push_back( |
+ {name, base::HexEncode(current_adapter->PhysicalAddress, |
+ current_adapter->PhysicalAddressLength)}); |
+ } |
+ |
+ StableSortDiscoveryNetworkInfo(network_ids.begin(), network_ids.end()); |
+ |
+ return network_ids; |
} |