1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/extensions/api/music_manager_private/device_id.h" 6 7 // Note: The order of header includes is important, as we want both pre-Vista 8 // and post-Vista data structures to be defined, specifically 9 // PIP_ADAPTER_ADDRESSES and PMIB_IF_ROW2. 10 #include <winsock2.h> 11 #include <ws2def.h> 12 #include <ws2ipdef.h> 13 #include <iphlpapi.h> 14 15 #include <string> 16 17 #include "base/files/file_path.h" 18 #include "base/logging.h" 19 #include "base/scoped_native_library.h" 20 #include "base/strings/string_number_conversions.h" 21 #include "base/strings/string_util.h" 22 #include "base/threading/thread_restrictions.h" 23 #include "base/win/windows_version.h" 24 #include "content/public/browser/browser_thread.h" 25 #include "net/base/net_util.h" 26 27 #if defined(ENABLE_RLZ) 28 #include "rlz/lib/machine_id.h" 29 #endif 30 31 namespace { 32 33 using extensions::api::DeviceId; 34 35 typedef base::Callback<bool(const void* bytes, size_t size)> 36 IsValidMacAddressCallback; 37 38 class MacAddressProcessor { 39 public: 40 MacAddressProcessor(const IsValidMacAddressCallback& is_valid_mac_address) 41 : is_valid_mac_address_(is_valid_mac_address), 42 found_index_(ULONG_MAX) { 43 } 44 45 // Iterate through the interfaces, looking for the valid MAC address with the 46 // lowest IfIndex. 47 void ProcessAdapterAddress(PIP_ADAPTER_ADDRESSES address) { 48 if (address->IfType == IF_TYPE_TUNNEL) 49 return; 50 51 ProcessPhysicalAddress(address->IfIndex, 52 address->PhysicalAddress, 53 address->PhysicalAddressLength); 54 } 55 56 void ProcessInterfaceRow(const PMIB_IF_ROW2 row) { 57 if (row->Type == IF_TYPE_TUNNEL || 58 !row->InterfaceAndOperStatusFlags.HardwareInterface) { 59 return; 60 } 61 62 ProcessPhysicalAddress(row->InterfaceIndex, 63 row->PhysicalAddress, 64 row->PhysicalAddressLength); 65 } 66 67 std::string mac_address() const { return found_mac_address_; } 68 69 private: 70 void ProcessPhysicalAddress(NET_IFINDEX index, 71 const void* bytes, 72 size_t size) { 73 if (index >= found_index_ || size == 0) 74 return; 75 76 if (!is_valid_mac_address_.Run(bytes, size)) 77 return; 78 79 found_mac_address_ = StringToLowerASCII(base::HexEncode(bytes, size)); 80 found_index_ = index; 81 } 82 83 const IsValidMacAddressCallback& is_valid_mac_address_; 84 std::string found_mac_address_; 85 NET_IFINDEX found_index_; 86 }; 87 88 std::string GetMacAddressFromGetAdaptersAddresses( 89 const IsValidMacAddressCallback& is_valid_mac_address) { 90 base::ThreadRestrictions::AssertIOAllowed(); 91 92 // MS recommends a default size of 15k. 93 ULONG bufferSize = 15 * 1024; 94 // Disable as much as we can, since all we want is MAC addresses. 95 ULONG flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_DNS_SERVER | 96 GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_SKIP_MULTICAST | 97 GAA_FLAG_SKIP_UNICAST; 98 std::vector<unsigned char> buffer(bufferSize); 99 PIP_ADAPTER_ADDRESSES adapterAddresses = 100 reinterpret_cast<PIP_ADAPTER_ADDRESSES>(&buffer.front()); 101 102 DWORD result = GetAdaptersAddresses(AF_UNSPEC, flags, 0, 103 adapterAddresses, &bufferSize); 104 if (result == ERROR_BUFFER_OVERFLOW) { 105 buffer.resize(bufferSize); 106 adapterAddresses = 107 reinterpret_cast<PIP_ADAPTER_ADDRESSES>(&buffer.front()); 108 result = GetAdaptersAddresses(AF_UNSPEC, flags, 0, 109 adapterAddresses, &bufferSize); 110 } 111 112 if (result != NO_ERROR) { 113 VLOG(ERROR) << "GetAdapatersAddresses failed with error " << result; 114 return ""; 115 } 116 117 MacAddressProcessor processor(is_valid_mac_address); 118 for (; adapterAddresses != NULL; adapterAddresses = adapterAddresses->Next) { 119 processor.ProcessAdapterAddress(adapterAddresses); 120 } 121 return processor.mac_address(); 122 } 123 124 std::string GetMacAddressFromGetIfTable2( 125 const IsValidMacAddressCallback& is_valid_mac_address) { 126 base::ThreadRestrictions::AssertIOAllowed(); 127 128 // This is available on Vista+ only. 129 base::ScopedNativeLibrary library(base::FilePath(L"Iphlpapi.dll")); 130 131 typedef DWORD (NETIOAPI_API_ *GetIfTablePtr)(PMIB_IF_TABLE2*); 132 typedef void (NETIOAPI_API_ *FreeMibTablePtr)(PMIB_IF_TABLE2); 133 134 GetIfTablePtr getIfTable = reinterpret_cast<GetIfTablePtr>( 135 library.GetFunctionPointer("GetIfTable2")); 136 FreeMibTablePtr freeMibTablePtr = reinterpret_cast<FreeMibTablePtr>( 137 library.GetFunctionPointer("FreeMibTable")); 138 if (getIfTable == NULL || freeMibTablePtr == NULL) { 139 VLOG(ERROR) << "Could not get proc addresses for machine identifier."; 140 return ""; 141 } 142 143 PMIB_IF_TABLE2 ifTable = NULL; 144 DWORD result = getIfTable(&ifTable); 145 if (result != NO_ERROR || ifTable == NULL) { 146 VLOG(ERROR) << "GetIfTable failed with error " << result; 147 return ""; 148 } 149 150 MacAddressProcessor processor(is_valid_mac_address); 151 for (size_t i = 0; i < ifTable->NumEntries; i++) { 152 processor.ProcessInterfaceRow(&(ifTable->Table[i])); 153 } 154 155 if (ifTable != NULL) { 156 freeMibTablePtr(ifTable); 157 ifTable = NULL; 158 } 159 return processor.mac_address(); 160 } 161 162 void GetMacAddress(const IsValidMacAddressCallback& is_valid_mac_address, 163 const DeviceId::IdCallback& callback) { 164 base::ThreadRestrictions::AssertIOAllowed(); 165 166 std::string mac_address = 167 GetMacAddressFromGetAdaptersAddresses(is_valid_mac_address); 168 if (mac_address.empty()) 169 mac_address = GetMacAddressFromGetIfTable2(is_valid_mac_address); 170 171 static bool error_logged = false; 172 if (mac_address.empty() && !error_logged) { 173 error_logged = true; 174 LOG(ERROR) << "Could not find appropriate MAC address."; 175 } 176 177 content::BrowserThread::PostTask( 178 content::BrowserThread::UI, 179 FROM_HERE, 180 base::Bind(callback, mac_address)); 181 } 182 183 std::string GetRlzMachineId() { 184 #if defined(ENABLE_RLZ) 185 std::string machine_id; 186 if (!rlz_lib::GetMachineId(&machine_id)) 187 return ""; 188 return machine_id; 189 #else 190 return ""; 191 #endif 192 } 193 194 void GetMacAddressCallback(const DeviceId::IdCallback& callback, 195 const std::string& mac_address) { 196 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 197 198 std::string machine_id = GetRlzMachineId(); 199 if (mac_address.empty() || machine_id.empty()) { 200 callback.Run(""); 201 return; 202 } 203 callback.Run(mac_address + machine_id); 204 } 205 206 } // namespace 207 208 namespace extensions { 209 namespace api { 210 211 // static 212 void DeviceId::GetRawDeviceId(const IdCallback& callback) { 213 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 214 215 content::BrowserThread::PostTask( 216 content::BrowserThread::FILE, 217 FROM_HERE, 218 base::Bind(GetMacAddress, 219 base::Bind(DeviceId::IsValidMacAddress), 220 base::Bind(GetMacAddressCallback, callback))); 221 } 222 223 } // namespace api 224 } // namespace extensions 225