Home | History | Annotate | Download | only in proximity_auth
      1 // Copyright 2014 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 "components/proximity_auth/bluetooth_util.h"
      6 
      7 #include <stdint.h>
      8 #include <sys/socket.h>
      9 #include <algorithm>
     10 #include <vector>
     11 
     12 #include "base/bind.h"
     13 #include "base/callback.h"
     14 #include "base/location.h"
     15 #include "base/memory/ref_counted.h"
     16 #include "base/numerics/safe_conversions.h"
     17 #include "base/strings/string_number_conversions.h"
     18 #include "base/strings/string_split.h"
     19 #include "base/sys_byteorder.h"
     20 #include "base/task_runner_util.h"
     21 #include "base/threading/sequenced_worker_pool.h"
     22 #include "base/time/time.h"
     23 #include "device/bluetooth/bluetooth_device.h"
     24 #include "device/bluetooth/bluetooth_device_chromeos.h"
     25 #include "net/socket/socket_descriptor.h"
     26 
     27 // The bluez headers are (intentionally) not available within the Chromium
     28 // repository, so several definitions from these headers are replicated below.
     29 
     30 // From <bluetooth/bluetooth.h>:
     31 #define BTPROTO_L2CAP 0
     32 struct bdaddr_t {
     33   uint8_t b[6];
     34 } __attribute__((packed));
     35 
     36 // From <bluetooth/l2cap.h>:
     37 struct sockaddr_l2 {
     38   sa_family_t l2_family;
     39   unsigned short l2_psm;
     40   bdaddr_t l2_bdaddr;
     41   unsigned short l2_cid;
     42 };
     43 
     44 // From <bluetooth/sdp.h>:
     45 #define SDP_PSM 0x0001
     46 
     47 namespace proximity_auth {
     48 namespace bluetooth_util {
     49 namespace {
     50 
     51 using device::BluetoothDevice;
     52 
     53 const char kInvalidDeviceAddress[] =
     54     "Given address is not a valid Bluetooth device.";
     55 const char kUnableToConnectToDevice[] =
     56     "Unable to connect to the remote device.";
     57 
     58 // Delay prior to closing an SDP connection opened to register a Bluetooth
     59 // device with the system BlueZ daemon.
     60 const int kCloseSDPConnectionDelaySec = 5;
     61 
     62 struct SeekDeviceResult {
     63   // Whether the connection to the device succeeded.
     64   bool success;
     65 
     66   // If the connection failed, an error message describing the failure.
     67   std::string error_message;
     68 };
     69 
     70 // Writes |address| into the |result|. Return true on success, false if the
     71 // |address| is not a valid Bluetooth address.
     72 bool BluetoothAddressToBdaddr(const std::string& address, bdaddr_t* result) {
     73   std::string canonical_address = BluetoothDevice::CanonicalizeAddress(address);
     74   if (canonical_address.empty())
     75     return false;
     76 
     77   std::vector<std::string> octets;
     78   base::SplitString(canonical_address, ':', &octets);
     79   DCHECK_EQ(octets.size(), 6U);
     80 
     81   // BlueZ expects the octets in the reverse order.
     82   std::reverse(octets.begin(), octets.end());
     83   for (size_t i = 0; i < octets.size(); ++i) {
     84     uint32_t octet;
     85     bool success = base::HexStringToUInt(octets[i], &octet);
     86     DCHECK(success);
     87     result->b[i] = base::checked_cast<uint8_t>(octet);
     88   }
     89 
     90   return true;
     91 }
     92 
     93 // Closes the socket with the given |socket_descriptor|.
     94 void CloseSocket(net::SocketDescriptor socket_descriptor) {
     95   int result = close(socket_descriptor);
     96   DCHECK_EQ(result, 0);
     97 }
     98 
     99 // Connects to the SDP service on the Bluetooth device with the given
    100 // |device_address|, if possible. Returns an indicator of success or an error
    101 // message on failure.
    102 SeekDeviceResult SeekDeviceByAddressImpl(
    103     const std::string& device_address,
    104     scoped_refptr<base::TaskRunner> task_runner) {
    105   SeekDeviceResult seek_result;
    106   seek_result.success = false;
    107 
    108   struct sockaddr_l2 addr;
    109   memset(&addr, 0, sizeof(addr));
    110   addr.l2_family = AF_BLUETOOTH;
    111   addr.l2_psm = base::ByteSwapToLE16(SDP_PSM);
    112   if (!BluetoothAddressToBdaddr(device_address, &addr.l2_bdaddr)) {
    113     seek_result.error_message = kInvalidDeviceAddress;
    114     return seek_result;
    115   }
    116 
    117   net::SocketDescriptor socket_descriptor =
    118       socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
    119   int result = connect(socket_descriptor,
    120                        reinterpret_cast<struct sockaddr*>(&addr),
    121                        sizeof(addr));
    122   if (result == 0) {
    123     seek_result.success = true;
    124     task_runner->PostDelayedTask(
    125         FROM_HERE,
    126         base::Bind(&CloseSocket, socket_descriptor),
    127         base::TimeDelta::FromSeconds(kCloseSDPConnectionDelaySec));
    128   } else {
    129     // TODO(isherman): Pass a better message based on the errno?
    130     seek_result.error_message = kUnableToConnectToDevice;
    131   }
    132   return seek_result;
    133 }
    134 
    135 void OnSeekDeviceResult(const base::Closure& callback,
    136                         const ErrorCallback& error_callback,
    137                         const SeekDeviceResult& result) {
    138   if (result.success)
    139     callback.Run();
    140   else
    141     error_callback.Run(result.error_message);
    142 }
    143 
    144 }  // namespace
    145 
    146 void SeekDeviceByAddress(const std::string& device_address,
    147                          const base::Closure& callback,
    148                          const ErrorCallback& error_callback,
    149                          base::TaskRunner* task_runner) {
    150   base::PostTaskAndReplyWithResult(
    151       task_runner,
    152       FROM_HERE,
    153       base::Bind(&SeekDeviceByAddressImpl,
    154                  device_address,
    155                  make_scoped_refptr(task_runner)),
    156       base::Bind(&OnSeekDeviceResult, callback, error_callback));
    157 }
    158 
    159 void ConnectToServiceInsecurely(
    160     device::BluetoothDevice* device,
    161     const device::BluetoothUUID& uuid,
    162     const BluetoothDevice::ConnectToServiceCallback& callback,
    163     const BluetoothDevice::ConnectToServiceErrorCallback& error_callback) {
    164   static_cast<chromeos::BluetoothDeviceChromeOS*>(device)
    165       ->ConnectToServiceInsecurely(uuid, callback, error_callback);
    166 }
    167 
    168 }  // namespace bluetooth_util
    169 }  // namespace proximity_auth
    170