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