// 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 "device/bluetooth/bluetooth_device_chromeos.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<std::string> octets; base::SplitString(canonical_address, ':', &octets); 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)); } void ConnectToServiceInsecurely( device::BluetoothDevice* device, const device::BluetoothUUID& uuid, const BluetoothDevice::ConnectToServiceCallback& callback, const BluetoothDevice::ConnectToServiceErrorCallback& error_callback) { static_cast<chromeos::BluetoothDeviceChromeOS*>(device) ->ConnectToServiceInsecurely(uuid, callback, error_callback); } } // namespace bluetooth_util } // namespace proximity_auth