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