| // 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 |