summaryrefslogtreecommitdiffstats
path: root/components/proximity_auth/bluetooth_util_chromeos.cc
blob: 6a281fdd56c4576fc5df4af23ea3b7e574a0b0dd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// Copyright 2014 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 "components/proximity_auth/bluetooth_util.h"

#include <stdint.h>
#include <sys/socket.h>
#include <algorithm>
#include <vector>

#include "base/bind.h"
#include "base/callback.h"
#include "base/location.h"
#include "base/memory/ref_counted.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/sys_byteorder.h"
#include "base/task_runner_util.h"
#include "base/threading/sequenced_worker_pool.h"
#include "base/time/time.h"
#include "device/bluetooth/bluetooth_device.h"
#include "net/socket/socket_descriptor.h"

// The bluez headers are (intentionally) not available within the Chromium
// repository, so several definitions from these headers are replicated below.

// From <bluetooth/bluetooth.h>:
#define BTPROTO_L2CAP 0
struct bdaddr_t {
  uint8_t b[6];
} __attribute__((packed));

// From <bluetooth/l2cap.h>:
struct sockaddr_l2 {
  sa_family_t l2_family;
  unsigned short l2_psm;
  bdaddr_t l2_bdaddr;
  unsigned short l2_cid;
};

// From <bluetooth/sdp.h>:
#define SDP_PSM 0x0001

namespace proximity_auth {
namespace bluetooth_util {
namespace {

using device::BluetoothDevice;

const char kInvalidDeviceAddress[] =
    "Given address is not a valid Bluetooth device.";
const char kUnableToConnectToDevice[] =
    "Unable to connect to the remote device.";

// Delay prior to closing an SDP connection opened to register a Bluetooth
// device with the system BlueZ daemon.
const int kCloseSDPConnectionDelaySec = 5;

struct SeekDeviceResult {
  // Whether the connection to the device succeeded.
  bool success;

  // If the connection failed, an error message describing the failure.
  std::string error_message;
};

// Writes |address| into the |result|. Return true on success, false if the
// |address| is not a valid Bluetooth address.
bool BluetoothAddressToBdaddr(const std::string& address, bdaddr_t* result) {
  std::string canonical_address = BluetoothDevice::CanonicalizeAddress(address);
  if (canonical_address.empty())
    return false;

  std::vector<base::StringPiece> octets = base::SplitStringPiece(
      canonical_address, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
  DCHECK_EQ(octets.size(), 6U);

  // BlueZ expects the octets in the reverse order.
  std::reverse(octets.begin(), octets.end());
  for (size_t i = 0; i < octets.size(); ++i) {
    uint32_t octet;
    bool success = base::HexStringToUInt(octets[i], &octet);
    DCHECK(success);
    result->b[i] = base::checked_cast<uint8_t>(octet);
  }

  return true;
}

// Closes the socket with the given |socket_descriptor|.
void CloseSocket(net::SocketDescriptor socket_descriptor) {
  int result = close(socket_descriptor);
  DCHECK_EQ(result, 0);
}

// Connects to the SDP service on the Bluetooth device with the given
// |device_address|, if possible. Returns an indicator of success or an error
// message on failure.
SeekDeviceResult SeekDeviceByAddressImpl(
    const std::string& device_address,
    scoped_refptr<base::TaskRunner> task_runner) {
  SeekDeviceResult seek_result;
  seek_result.success = false;

  struct sockaddr_l2 addr;
  memset(&addr, 0, sizeof(addr));
  addr.l2_family = AF_BLUETOOTH;
  addr.l2_psm = base::ByteSwapToLE16(SDP_PSM);
  if (!BluetoothAddressToBdaddr(device_address, &addr.l2_bdaddr)) {
    seek_result.error_message = kInvalidDeviceAddress;
    return seek_result;
  }

  net::SocketDescriptor socket_descriptor =
      socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
  int result = connect(socket_descriptor,
                       reinterpret_cast<struct sockaddr*>(&addr),
                       sizeof(addr));
  if (result == 0) {
    seek_result.success = true;
    task_runner->PostDelayedTask(
        FROM_HERE,
        base::Bind(&CloseSocket, socket_descriptor),
        base::TimeDelta::FromSeconds(kCloseSDPConnectionDelaySec));
  } else {
    // TODO(isherman): Pass a better message based on the errno?
    seek_result.error_message = kUnableToConnectToDevice;
  }
  return seek_result;
}

void OnSeekDeviceResult(const base::Closure& callback,
                        const ErrorCallback& error_callback,
                        const SeekDeviceResult& result) {
  if (result.success)
    callback.Run();
  else
    error_callback.Run(result.error_message);
}

}  // namespace

void SeekDeviceByAddress(const std::string& device_address,
                         const base::Closure& callback,
                         const ErrorCallback& error_callback,
                         base::TaskRunner* task_runner) {
  base::PostTaskAndReplyWithResult(
      task_runner,
      FROM_HERE,
      base::Bind(&SeekDeviceByAddressImpl,
                 device_address,
                 make_scoped_refptr(task_runner)),
      base::Bind(&OnSeekDeviceResult, callback, error_callback));
}

}  // namespace bluetooth_util
}  // namespace proximity_auth