blob: 1bbe3c2fcaafcc2786105acadec166493435234a [file] [log] [blame]
// Copyright 2013 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 "chrome/browser/extensions/api/music_manager_private/device_id.h"
// Note: The order of header includes is important, as we want both pre-Vista
// and post-Vista data structures to be defined, specifically
// PIP_ADAPTER_ADDRESSES and PMIB_IF_ROW2.
#include <winsock2.h>
#include <ws2def.h>
#include <ws2ipdef.h>
#include <iphlpapi.h>
#include <string>
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/scoped_native_library.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/threading/thread_restrictions.h"
#include "base/win/windows_version.h"
#include "content/public/browser/browser_thread.h"
#include "net/base/net_util.h"
#if defined(ENABLE_RLZ)
#include "rlz/lib/machine_id.h"
#endif
namespace {
using extensions::api::DeviceId;
typedef base::Callback<bool(const void* bytes, size_t size)>
IsValidMacAddressCallback;
class MacAddressProcessor {
public:
MacAddressProcessor(const IsValidMacAddressCallback& is_valid_mac_address)
: is_valid_mac_address_(is_valid_mac_address),
found_index_(ULONG_MAX) {
}
// Iterate through the interfaces, looking for the valid MAC address with the
// lowest IfIndex.
void ProcessAdapterAddress(PIP_ADAPTER_ADDRESSES address) {
if (address->IfType == IF_TYPE_TUNNEL)
return;
ProcessPhysicalAddress(address->IfIndex,
address->PhysicalAddress,
address->PhysicalAddressLength);
}
void ProcessInterfaceRow(const PMIB_IF_ROW2 row) {
if (row->Type == IF_TYPE_TUNNEL ||
!row->InterfaceAndOperStatusFlags.HardwareInterface) {
return;
}
ProcessPhysicalAddress(row->InterfaceIndex,
row->PhysicalAddress,
row->PhysicalAddressLength);
}
std::string mac_address() const { return found_mac_address_; }
private:
void ProcessPhysicalAddress(NET_IFINDEX index,
const void* bytes,
size_t size) {
if (index >= found_index_ || size == 0)
return;
if (!is_valid_mac_address_.Run(bytes, size))
return;
found_mac_address_ = StringToLowerASCII(base::HexEncode(bytes, size));
found_index_ = index;
}
const IsValidMacAddressCallback& is_valid_mac_address_;
std::string found_mac_address_;
NET_IFINDEX found_index_;
};
std::string GetMacAddressFromGetAdaptersAddresses(
const IsValidMacAddressCallback& is_valid_mac_address) {
base::ThreadRestrictions::AssertIOAllowed();
// MS recommends a default size of 15k.
ULONG bufferSize = 15 * 1024;
// Disable as much as we can, since all we want is MAC addresses.
ULONG flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_DNS_SERVER |
GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_SKIP_MULTICAST |
GAA_FLAG_SKIP_UNICAST;
std::vector<unsigned char> buffer(bufferSize);
PIP_ADAPTER_ADDRESSES adapterAddresses =
reinterpret_cast<PIP_ADAPTER_ADDRESSES>(&buffer.front());
DWORD result = GetAdaptersAddresses(AF_UNSPEC, flags, 0,
adapterAddresses, &bufferSize);
if (result == ERROR_BUFFER_OVERFLOW) {
buffer.resize(bufferSize);
adapterAddresses =
reinterpret_cast<PIP_ADAPTER_ADDRESSES>(&buffer.front());
result = GetAdaptersAddresses(AF_UNSPEC, flags, 0,
adapterAddresses, &bufferSize);
}
if (result != NO_ERROR) {
VLOG(ERROR) << "GetAdapatersAddresses failed with error " << result;
return "";
}
MacAddressProcessor processor(is_valid_mac_address);
for (; adapterAddresses != NULL; adapterAddresses = adapterAddresses->Next) {
processor.ProcessAdapterAddress(adapterAddresses);
}
return processor.mac_address();
}
std::string GetMacAddressFromGetIfTable2(
const IsValidMacAddressCallback& is_valid_mac_address) {
base::ThreadRestrictions::AssertIOAllowed();
// This is available on Vista+ only.
base::ScopedNativeLibrary library(base::FilePath(L"Iphlpapi.dll"));
typedef DWORD (NETIOAPI_API_ *GetIfTablePtr)(PMIB_IF_TABLE2*);
typedef void (NETIOAPI_API_ *FreeMibTablePtr)(PMIB_IF_TABLE2);
GetIfTablePtr getIfTable = reinterpret_cast<GetIfTablePtr>(
library.GetFunctionPointer("GetIfTable2"));
FreeMibTablePtr freeMibTablePtr = reinterpret_cast<FreeMibTablePtr>(
library.GetFunctionPointer("FreeMibTable"));
if (getIfTable == NULL || freeMibTablePtr == NULL) {
VLOG(ERROR) << "Could not get proc addresses for machine identifier.";
return "";
}
PMIB_IF_TABLE2 ifTable = NULL;
DWORD result = getIfTable(&ifTable);
if (result != NO_ERROR || ifTable == NULL) {
VLOG(ERROR) << "GetIfTable failed with error " << result;
return "";
}
MacAddressProcessor processor(is_valid_mac_address);
for (size_t i = 0; i < ifTable->NumEntries; i++) {
processor.ProcessInterfaceRow(&(ifTable->Table[i]));
}
if (ifTable != NULL) {
freeMibTablePtr(ifTable);
ifTable = NULL;
}
return processor.mac_address();
}
void GetMacAddress(const IsValidMacAddressCallback& is_valid_mac_address,
const DeviceId::IdCallback& callback) {
base::ThreadRestrictions::AssertIOAllowed();
std::string mac_address =
GetMacAddressFromGetAdaptersAddresses(is_valid_mac_address);
if (mac_address.empty())
mac_address = GetMacAddressFromGetIfTable2(is_valid_mac_address);
static bool error_logged = false;
if (mac_address.empty() && !error_logged) {
error_logged = true;
LOG(ERROR) << "Could not find appropriate MAC address.";
}
content::BrowserThread::PostTask(
content::BrowserThread::UI,
FROM_HERE,
base::Bind(callback, mac_address));
}
std::string GetRlzMachineId() {
#if defined(ENABLE_RLZ)
std::string machine_id;
if (!rlz_lib::GetMachineId(&machine_id))
return "";
return machine_id;
#else
return "";
#endif
}
void GetMacAddressCallback(const DeviceId::IdCallback& callback,
const std::string& mac_address) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
std::string machine_id = GetRlzMachineId();
if (mac_address.empty() || machine_id.empty()) {
callback.Run("");
return;
}
callback.Run(mac_address + machine_id);
}
} // namespace
namespace extensions {
namespace api {
// static
void DeviceId::GetRawDeviceId(const IdCallback& callback) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
content::BrowserThread::PostTask(
content::BrowserThread::FILE,
FROM_HERE,
base::Bind(GetMacAddress,
base::Bind(DeviceId::IsValidMacAddress),
base::Bind(GetMacAddressCallback, callback)));
}
} // namespace api
} // namespace extensions