Home | History | Annotate | Download | only in music_manager_private
      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