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
32struct bdaddr_t {
33  uint8_t b[6];
34} __attribute__((packed));
35
36// From <bluetooth/l2cap.h>:
37struct 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
47namespace proximity_auth {
48namespace bluetooth_util {
49namespace {
50
51using device::BluetoothDevice;
52
53const char kInvalidDeviceAddress[] =
54    "Given address is not a valid Bluetooth device.";
55const 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.
60const int kCloseSDPConnectionDelaySec = 5;
61
62struct 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.
72bool 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|.
94void 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.
102SeekDeviceResult 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
135void 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
146void 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
159void 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