Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / extensions / api / music_manager_private / device_id_win.cc
blobc418624b0298f7207fa93071a1c2cb8bd8368f9c
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.
5 #include "chrome/browser/extensions/api/music_manager_private/device_id.h"
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>
15 #include <string>
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"
27 #if defined(ENABLE_RLZ)
28 #include "rlz/lib/machine_id.h"
29 #endif
31 namespace {
33 using extensions::api::DeviceId;
35 typedef base::Callback<bool(const void* bytes, size_t size)>
36 IsValidMacAddressCallback;
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) {
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;
51 ProcessPhysicalAddress(address->IfIndex,
52 address->PhysicalAddress,
53 address->PhysicalAddressLength);
56 void ProcessInterfaceRow(const PMIB_IF_ROW2 row) {
57 if (row->Type == IF_TYPE_TUNNEL ||
58 !row->InterfaceAndOperStatusFlags.HardwareInterface) {
59 return;
62 ProcessPhysicalAddress(row->InterfaceIndex,
63 row->PhysicalAddress,
64 row->PhysicalAddressLength);
67 std::string mac_address() const { return found_mac_address_; }
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;
76 if (!is_valid_mac_address_.Run(bytes, size))
77 return;
79 found_mac_address_ = base::ToLowerASCII(base::HexEncode(bytes, size));
80 found_index_ = index;
83 const IsValidMacAddressCallback& is_valid_mac_address_;
84 std::string found_mac_address_;
85 NET_IFINDEX found_index_;
88 std::string GetMacAddressFromGetAdaptersAddresses(
89 const IsValidMacAddressCallback& is_valid_mac_address) {
90 base::ThreadRestrictions::AssertIOAllowed();
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());
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);
112 if (result != NO_ERROR) {
113 VLOG(ERROR) << "GetAdapatersAddresses failed with error " << result;
114 return "";
117 MacAddressProcessor processor(is_valid_mac_address);
118 for (; adapterAddresses != NULL; adapterAddresses = adapterAddresses->Next) {
119 processor.ProcessAdapterAddress(adapterAddresses);
121 return processor.mac_address();
124 std::string GetMacAddressFromGetIfTable2(
125 const IsValidMacAddressCallback& is_valid_mac_address) {
126 base::ThreadRestrictions::AssertIOAllowed();
128 // This is available on Vista+ only.
129 base::ScopedNativeLibrary library(base::FilePath(L"Iphlpapi.dll"));
131 typedef DWORD (NETIOAPI_API_ *GetIfTablePtr)(PMIB_IF_TABLE2*);
132 typedef void (NETIOAPI_API_ *FreeMibTablePtr)(PMIB_IF_TABLE2);
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 "";
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 "";
150 MacAddressProcessor processor(is_valid_mac_address);
151 for (size_t i = 0; i < ifTable->NumEntries; i++) {
152 processor.ProcessInterfaceRow(&(ifTable->Table[i]));
155 if (ifTable != NULL) {
156 freeMibTablePtr(ifTable);
157 ifTable = NULL;
159 return processor.mac_address();
162 void GetMacAddress(const IsValidMacAddressCallback& is_valid_mac_address,
163 const DeviceId::IdCallback& callback) {
164 base::ThreadRestrictions::AssertIOAllowed();
166 std::string mac_address =
167 GetMacAddressFromGetAdaptersAddresses(is_valid_mac_address);
168 if (mac_address.empty())
169 mac_address = GetMacAddressFromGetIfTable2(is_valid_mac_address);
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.";
177 content::BrowserThread::PostTask(
178 content::BrowserThread::UI,
179 FROM_HERE,
180 base::Bind(callback, mac_address));
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
194 void GetMacAddressCallback(const DeviceId::IdCallback& callback,
195 const std::string& mac_address) {
196 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
198 std::string machine_id = GetRlzMachineId();
199 if (mac_address.empty() || machine_id.empty()) {
200 callback.Run("");
201 return;
203 callback.Run(mac_address + machine_id);
206 } // namespace
208 namespace extensions {
209 namespace api {
211 // static
212 void DeviceId::GetRawDeviceId(const IdCallback& callback) {
213 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
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)));
223 } // namespace api
224 } // namespace extensions