/**************************************************************************** ** ** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtBluetooth module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qbluetoothsocket.h" #include "qbluetoothsocket_bluez_p.h" #include "qbluetoothdeviceinfo.h" #include "bluez/manager_p.h" #include "bluez/adapter_p.h" #include "bluez/device_p.h" #include "bluez/objectmanager_p.h" #include #include "bluez/bluez_data_p.h" #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(QT_BT_BLUEZ) QBluetoothSocketPrivateBluez::QBluetoothSocketPrivateBluez() : QBluetoothSocketBasePrivate() { secFlags = QBluetooth::Authorization; } QBluetoothSocketPrivateBluez::~QBluetoothSocketPrivateBluez() { delete readNotifier; readNotifier = nullptr; delete connectWriteNotifier; connectWriteNotifier = nullptr; } bool QBluetoothSocketPrivateBluez::ensureNativeSocket(QBluetoothServiceInfo::Protocol type) { if (socket != -1) { if (socketType == type) return true; delete readNotifier; readNotifier = nullptr; delete connectWriteNotifier; connectWriteNotifier = nullptr; QT_CLOSE(socket); } socketType = type; switch (type) { case QBluetoothServiceInfo::L2capProtocol: socket = ::socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); break; case QBluetoothServiceInfo::RfcommProtocol: socket = ::socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); break; default: socket = -1; } if (socket == -1) return false; int flags = fcntl(socket, F_GETFL, 0); fcntl(socket, F_SETFL, flags | O_NONBLOCK); Q_Q(QBluetoothSocket); readNotifier = new QSocketNotifier(socket, QSocketNotifier::Read); QObject::connect(readNotifier, SIGNAL(activated(int)), this, SLOT(_q_readNotify())); connectWriteNotifier = new QSocketNotifier(socket, QSocketNotifier::Write, q); QObject::connect(connectWriteNotifier, SIGNAL(activated(int)), this, SLOT(_q_writeNotify())); connectWriteNotifier->setEnabled(false); readNotifier->setEnabled(false); return true; } void QBluetoothSocketPrivateBluez::connectToServiceHelper(const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode) { Q_Q(QBluetoothSocket); int result = -1; if (socket == -1 && !ensureNativeSocket(socketType)) { errorString = QBluetoothSocket::tr("Unknown socket error"); q->setSocketError(QBluetoothSocket::UnknownSocketError); return; } // apply preferred security level // ignore QBluetooth::Authentication -> not used anymore by kernel struct bt_security security; memset(&security, 0, sizeof(security)); if (secFlags & QBluetooth::Authorization) security.level = BT_SECURITY_LOW; if (secFlags & QBluetooth::Encryption) security.level = BT_SECURITY_MEDIUM; if (secFlags & QBluetooth::Secure) security.level = BT_SECURITY_HIGH; if (setsockopt(socket, SOL_BLUETOOTH, BT_SECURITY, &security, sizeof(security)) != 0) { qCWarning(QT_BT_BLUEZ) << "Failed to set socket option, closing socket for safety" << errno; qCWarning(QT_BT_BLUEZ) << "Error: " << qt_error_string(errno); errorString = QBluetoothSocket::tr("Cannot set connection security level"); q->setSocketError(QBluetoothSocket::UnknownSocketError); return; } if (socketType == QBluetoothServiceInfo::RfcommProtocol) { sockaddr_rc addr; memset(&addr, 0, sizeof(addr)); addr.rc_family = AF_BLUETOOTH; addr.rc_channel = port; convertAddress(address.toUInt64(), addr.rc_bdaddr.b); connectWriteNotifier->setEnabled(true); readNotifier->setEnabled(true); result = ::connect(socket, (sockaddr *)&addr, sizeof(addr)); } else if (socketType == QBluetoothServiceInfo::L2capProtocol) { sockaddr_l2 addr; memset(&addr, 0, sizeof(addr)); addr.l2_family = AF_BLUETOOTH; // This is an ugly hack but the socket class does what's needed already. // For L2CP GATT we need a channel rather than a socket and the LE address type // We don't want to make this public API offering for now especially since // only Linux (of all platforms supported by this library) supports this type // of socket. #if QT_CONFIG(bluez) && !defined(QT_BLUEZ_NO_BTLE) if (lowEnergySocketType) { addr.l2_cid = htobs(port); addr.l2_bdaddr_type = lowEnergySocketType; } else { addr.l2_psm = htobs(port); } #else addr.l2_psm = htobs(port); #endif convertAddress(address.toUInt64(), addr.l2_bdaddr.b); connectWriteNotifier->setEnabled(true); readNotifier->setEnabled(true); result = ::connect(socket, (sockaddr *)&addr, sizeof(addr)); } if (result >= 0 || (result == -1 && errno == EINPROGRESS)) { connecting = true; q->setSocketState(QBluetoothSocket::ConnectingState); q->setOpenMode(openMode); } else { errorString = qt_error_string(errno); q->setSocketError(QBluetoothSocket::UnknownSocketError); } } void QBluetoothSocketPrivateBluez::connectToService( const QBluetoothServiceInfo &service, QIODevice::OpenMode openMode) { Q_Q(QBluetoothSocket); if (q->state() != QBluetoothSocket::UnconnectedState && q->state() != QBluetoothSocket::ServiceLookupState) { qCWarning(QT_BT_BLUEZ) << "QBluetoothSocketPrivateBluez::connectToService called on busy socket"; errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress"); q->setSocketError(QBluetoothSocket::OperationError); return; } // we are checking the service protocol and not socketType() // socketType will change in ensureNativeSocket() if (service.socketProtocol() == QBluetoothServiceInfo::UnknownProtocol) { qCWarning(QT_BT_BLUEZ) << "QBluetoothSocket::connectToService cannot " "connect with 'UnknownProtocol' (type provided by given service)"; errorString = QBluetoothSocket::tr("Socket type not supported"); q->setSocketError(QBluetoothSocket::UnsupportedProtocolError); return; } if (service.protocolServiceMultiplexer() > 0) { Q_ASSERT(service.socketProtocol() == QBluetoothServiceInfo::L2capProtocol); if (!ensureNativeSocket(QBluetoothServiceInfo::L2capProtocol)) { errorString = QBluetoothSocket::tr("Unknown socket error"); q->setSocketError(QBluetoothSocket::UnknownSocketError); return; } connectToServiceHelper(service.device().address(), service.protocolServiceMultiplexer(), openMode); } else if (service.serverChannel() > 0) { Q_ASSERT(service.socketProtocol() == QBluetoothServiceInfo::RfcommProtocol); if (!ensureNativeSocket(QBluetoothServiceInfo::RfcommProtocol)) { errorString = QBluetoothSocket::tr("Unknown socket error"); q->setSocketError(QBluetoothSocket::UnknownSocketError); return; } connectToServiceHelper(service.device().address(), service.serverChannel(), openMode); } else { // try doing service discovery to see if we can find the socket if (service.serviceUuid().isNull() && !service.serviceClassUuids().contains(QBluetoothUuid::SerialPort)) { qCWarning(QT_BT_BLUEZ) << "No port, no PSM, and no UUID provided. Unable to connect"; return; } qCDebug(QT_BT_BLUEZ) << "Need a port/psm, doing discovery"; q->doDeviceDiscovery(service, openMode); } } void QBluetoothSocketPrivateBluez::connectToService( const QBluetoothAddress &address, const QBluetoothUuid &uuid, QIODevice::OpenMode openMode) { Q_Q(QBluetoothSocket); if (q->state() != QBluetoothSocket::UnconnectedState) { qCWarning(QT_BT_BLUEZ) << "QBluetoothSocketPrivateBluez::connectToService called on busy socket"; errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress"); q->setSocketError(QBluetoothSocket::OperationError); return; } if (q->socketType() == QBluetoothServiceInfo::UnknownProtocol) { qCWarning(QT_BT_BLUEZ) << "QBluetoothSocketPrivateBluez::connectToService cannot " "connect with 'UnknownProtocol' (type provided by given service)"; errorString = QBluetoothSocket::tr("Socket type not supported"); q->setSocketError(QBluetoothSocket::UnsupportedProtocolError); return; } QBluetoothServiceInfo service; QBluetoothDeviceInfo device(address, QString(), QBluetoothDeviceInfo::MiscellaneousDevice); service.setDevice(device); service.setServiceUuid(uuid); q->doDeviceDiscovery(service, openMode); } void QBluetoothSocketPrivateBluez::connectToService( const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode) { Q_Q(QBluetoothSocket); if (q->socketType() == QBluetoothServiceInfo::UnknownProtocol) { qCWarning(QT_BT_BLUEZ) << "QBluetoothSocketPrivateBluez::connectToService cannot " "connect with 'UnknownProtocol' (type provided by given service)"; errorString = QBluetoothSocket::tr("Socket type not supported"); q->setSocketError(QBluetoothSocket::UnsupportedProtocolError); return; } if (q->state() != QBluetoothSocket::UnconnectedState) { qCWarning(QT_BT_BLUEZ) << "QBluetoothSocketPrivateBluez::connectToService called on busy socket"; errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress"); q->setSocketError(QBluetoothSocket::OperationError); return; } connectToServiceHelper(address, port, openMode); } void QBluetoothSocketPrivateBluez::_q_writeNotify() { Q_Q(QBluetoothSocket); if(connecting && state == QBluetoothSocket::ConnectingState){ int errorno, len; len = sizeof(errorno); ::getsockopt(socket, SOL_SOCKET, SO_ERROR, &errorno, (socklen_t*)&len); if(errorno) { errorString = qt_error_string(errorno); q->setSocketError(QBluetoothSocket::UnknownSocketError); return; } q->setSocketState(QBluetoothSocket::ConnectedState); connectWriteNotifier->setEnabled(false); connecting = false; } else { if (txBuffer.size() == 0) { connectWriteNotifier->setEnabled(false); return; } char buf[1024]; int size = txBuffer.read(buf, 1024); int writtenBytes = qt_safe_write(socket, buf, size); if (writtenBytes < 0) { switch (errno) { case EAGAIN: writtenBytes = 0; txBuffer.ungetBlock(buf, size); break; default: // every other case returns error errorString = QBluetoothSocket::tr("Network Error: %1").arg(qt_error_string(errno)) ; q->setSocketError(QBluetoothSocket::NetworkError); break; } } else { if (writtenBytes < size) { // add remainder back to buffer char* remainder = buf + writtenBytes; txBuffer.ungetBlock(remainder, size - writtenBytes); } if (writtenBytes > 0) emit q->bytesWritten(writtenBytes); } if (txBuffer.size()) { connectWriteNotifier->setEnabled(true); } else if (state == QBluetoothSocket::ClosingState) { connectWriteNotifier->setEnabled(false); this->close(); } } } void QBluetoothSocketPrivateBluez::_q_readNotify() { Q_Q(QBluetoothSocket); char *writePointer = buffer.reserve(QPRIVATELINEARBUFFER_BUFFERSIZE); // qint64 readFromDevice = q->readData(writePointer, QPRIVATELINEARBUFFER_BUFFERSIZE); int readFromDevice = ::read(socket, writePointer, QPRIVATELINEARBUFFER_BUFFERSIZE); buffer.chop(QPRIVATELINEARBUFFER_BUFFERSIZE - (readFromDevice < 0 ? 0 : readFromDevice)); if(readFromDevice <= 0){ int errsv = errno; readNotifier->setEnabled(false); connectWriteNotifier->setEnabled(false); errorString = qt_error_string(errsv); qCWarning(QT_BT_BLUEZ) << Q_FUNC_INFO << socket << "error:" << readFromDevice << errorString; if (errsv == EHOSTDOWN) q->setSocketError(QBluetoothSocket::HostNotFoundError); else if (errsv == ECONNRESET) q->setSocketError(QBluetoothSocket::RemoteHostClosedError); else q->setSocketError(QBluetoothSocket::UnknownSocketError); q->disconnectFromService(); } else { emit q->readyRead(); } } void QBluetoothSocketPrivateBluez::abort() { delete readNotifier; readNotifier = nullptr; delete connectWriteNotifier; connectWriteNotifier = nullptr; // We don't transition through Closing for abort, so // we don't call disconnectFromService or // QBluetoothSocket::close QT_CLOSE(socket); socket = -1; Q_Q(QBluetoothSocket); q->setOpenMode(QIODevice::NotOpen); q->setSocketState(QBluetoothSocket::UnconnectedState); emit q->readChannelFinished(); emit q->disconnected(); } QString QBluetoothSocketPrivateBluez::localName() const { const QBluetoothAddress address = localAddress(); if (address.isNull()) return QString(); QBluetoothLocalDevice device(address); return device.name(); } QBluetoothAddress QBluetoothSocketPrivateBluez::localAddress() const { if (socketType == QBluetoothServiceInfo::RfcommProtocol) { sockaddr_rc addr; socklen_t addrLength = sizeof(addr); if (::getsockname(socket, reinterpret_cast(&addr), &addrLength) == 0) return QBluetoothAddress(convertAddress(addr.rc_bdaddr.b)); } else if (socketType == QBluetoothServiceInfo::L2capProtocol) { sockaddr_l2 addr; socklen_t addrLength = sizeof(addr); if (::getsockname(socket, reinterpret_cast(&addr), &addrLength) == 0) return QBluetoothAddress(convertAddress(addr.l2_bdaddr.b)); } return QBluetoothAddress(); } quint16 QBluetoothSocketPrivateBluez::localPort() const { if (socketType == QBluetoothServiceInfo::RfcommProtocol) { sockaddr_rc addr; socklen_t addrLength = sizeof(addr); if (::getsockname(socket, reinterpret_cast(&addr), &addrLength) == 0) return addr.rc_channel; } else if (socketType == QBluetoothServiceInfo::L2capProtocol) { sockaddr_l2 addr; socklen_t addrLength = sizeof(addr); if (::getsockname(socket, reinterpret_cast(&addr), &addrLength) == 0) return addr.l2_psm; } return 0; } QString QBluetoothSocketPrivateBluez::peerName() const { quint64 bdaddr; if (socketType == QBluetoothServiceInfo::RfcommProtocol) { sockaddr_rc addr; socklen_t addrLength = sizeof(addr); if (::getpeername(socket, reinterpret_cast(&addr), &addrLength) < 0) return QString(); convertAddress(addr.rc_bdaddr.b, &bdaddr); } else if (socketType == QBluetoothServiceInfo::L2capProtocol) { sockaddr_l2 addr; socklen_t addrLength = sizeof(addr); if (::getpeername(socket, reinterpret_cast(&addr), &addrLength) < 0) return QString(); convertAddress(addr.l2_bdaddr.b, &bdaddr); } else { qCWarning(QT_BT_BLUEZ) << "peerName() called on socket of unknown type"; return QString(); } const QString peerAddress = QBluetoothAddress(bdaddr).toString(); const QString localAdapter = localAddress().toString(); if (isBluez5()) { OrgFreedesktopDBusObjectManagerInterface manager(QStringLiteral("org.bluez"), QStringLiteral("/"), QDBusConnection::systemBus()); QDBusPendingReply reply = manager.GetManagedObjects(); reply.waitForFinished(); if (reply.isError()) return QString(); ManagedObjectList managedObjectList = reply.value(); for (ManagedObjectList::const_iterator it = managedObjectList.constBegin(); it != managedObjectList.constEnd(); ++it) { const InterfaceList &ifaceList = it.value(); for (InterfaceList::const_iterator jt = ifaceList.constBegin(); jt != ifaceList.constEnd(); ++jt) { const QString &iface = jt.key(); const QVariantMap &ifaceValues = jt.value(); if (iface == QStringLiteral("org.bluez.Device1")) { if (ifaceValues.value(QStringLiteral("Address")).toString() == peerAddress) return ifaceValues.value(QStringLiteral("Alias")).toString(); } } } return QString(); } else { OrgBluezManagerInterface manager(QStringLiteral("org.bluez"), QStringLiteral("/"), QDBusConnection::systemBus()); QDBusPendingReply reply = manager.FindAdapter(localAdapter); reply.waitForFinished(); if (reply.isError()) return QString(); OrgBluezAdapterInterface adapter(QStringLiteral("org.bluez"), reply.value().path(), QDBusConnection::systemBus()); QDBusPendingReply deviceObjectPath = adapter.FindDevice(peerAddress); deviceObjectPath.waitForFinished(); if (deviceObjectPath.isError()) { if (deviceObjectPath.error().name() != QStringLiteral("org.bluez.Error.DoesNotExist")) return QString(); deviceObjectPath = adapter.CreateDevice(peerAddress); deviceObjectPath.waitForFinished(); if (deviceObjectPath.isError()) return QString(); } OrgBluezDeviceInterface device(QStringLiteral("org.bluez"), deviceObjectPath.value().path(), QDBusConnection::systemBus()); QDBusPendingReply properties = device.GetProperties(); properties.waitForFinished(); if (properties.isError()) return QString(); return properties.value().value(QStringLiteral("Alias")).toString(); } } QBluetoothAddress QBluetoothSocketPrivateBluez::peerAddress() const { if (socketType == QBluetoothServiceInfo::RfcommProtocol) { sockaddr_rc addr; socklen_t addrLength = sizeof(addr); if (::getpeername(socket, reinterpret_cast(&addr), &addrLength) == 0) return QBluetoothAddress(convertAddress(addr.rc_bdaddr.b)); } else if (socketType == QBluetoothServiceInfo::L2capProtocol) { sockaddr_l2 addr; socklen_t addrLength = sizeof(addr); if (::getpeername(socket, reinterpret_cast(&addr), &addrLength) == 0) return QBluetoothAddress(convertAddress(addr.l2_bdaddr.b)); } return QBluetoothAddress(); } quint16 QBluetoothSocketPrivateBluez::peerPort() const { if (socketType == QBluetoothServiceInfo::RfcommProtocol) { sockaddr_rc addr; socklen_t addrLength = sizeof(addr); if (::getpeername(socket, reinterpret_cast(&addr), &addrLength) == 0) return addr.rc_channel; } else if (socketType == QBluetoothServiceInfo::L2capProtocol) { sockaddr_l2 addr; socklen_t addrLength = sizeof(addr); if (::getpeername(socket, reinterpret_cast(&addr), &addrLength) == 0) return addr.l2_psm; } return 0; } qint64 QBluetoothSocketPrivateBluez::writeData(const char *data, qint64 maxSize) { Q_Q(QBluetoothSocket); if (state != QBluetoothSocket::ConnectedState) { errorString = QBluetoothSocket::tr("Cannot write while not connected"); q->setSocketError(QBluetoothSocket::OperationError); return -1; } if (q->openMode() & QIODevice::Unbuffered) { int sz = ::qt_safe_write(socket, data, maxSize); if (sz < 0) { switch (errno) { case EAGAIN: sz = 0; break; default: errorString = QBluetoothSocket::tr("Network Error: %1").arg(qt_error_string(errno)); q->setSocketError(QBluetoothSocket::NetworkError); } } if (sz > 0) emit q->bytesWritten(sz); return sz; } else { if(!connectWriteNotifier) return -1; if(txBuffer.size() == 0) { connectWriteNotifier->setEnabled(true); QMetaObject::invokeMethod(this, "_q_writeNotify", Qt::QueuedConnection); } char *txbuf = txBuffer.reserve(maxSize); memcpy(txbuf, data, maxSize); return maxSize; } } qint64 QBluetoothSocketPrivateBluez::readData(char *data, qint64 maxSize) { Q_Q(QBluetoothSocket); if (state != QBluetoothSocket::ConnectedState) { errorString = QBluetoothSocket::tr("Cannot read while not connected"); q->setSocketError(QBluetoothSocket::OperationError); return -1; } if (!buffer.isEmpty()) { int i = buffer.read(data, maxSize); return i; } return 0; } void QBluetoothSocketPrivateBluez::close() { if (txBuffer.size() > 0) connectWriteNotifier->setEnabled(true); else abort(); } bool QBluetoothSocketPrivateBluez::setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType_, QBluetoothSocket::SocketState socketState, QBluetoothSocket::OpenMode openMode) { Q_Q(QBluetoothSocket); delete readNotifier; readNotifier = nullptr; delete connectWriteNotifier; connectWriteNotifier = nullptr; socketType = socketType_; if (socket != -1) QT_CLOSE(socket); socket = socketDescriptor; // ensure that O_NONBLOCK is set on new connections. int flags = fcntl(socket, F_GETFL, 0); if (!(flags & O_NONBLOCK)) fcntl(socket, F_SETFL, flags | O_NONBLOCK); readNotifier = new QSocketNotifier(socket, QSocketNotifier::Read); QObject::connect(readNotifier, SIGNAL(activated(int)), this, SLOT(_q_readNotify())); connectWriteNotifier = new QSocketNotifier(socket, QSocketNotifier::Write, q); QObject::connect(connectWriteNotifier, SIGNAL(activated(int)), this, SLOT(_q_writeNotify())); q->setSocketState(socketState); q->setOpenMode(openMode); return true; } qint64 QBluetoothSocketPrivateBluez::bytesAvailable() const { return buffer.size(); } qint64 QBluetoothSocketPrivateBluez::bytesToWrite() const { return txBuffer.size(); } bool QBluetoothSocketPrivateBluez::canReadLine() const { return buffer.canReadLine(); } QT_END_NAMESPACE