/**************************************************************************** ** ** 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_win_p.h" #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS) QBluetoothSocketPrivateWin::QBluetoothSocketPrivateWin() : QBluetoothSocketBasePrivate() { WSAData wsadata = {}; ::WSAStartup(MAKEWORD(2, 0), &wsadata); } QBluetoothSocketPrivateWin::~QBluetoothSocketPrivateWin() { abort(); } bool QBluetoothSocketPrivateWin::ensureNativeSocket(QBluetoothServiceInfo::Protocol type) { Q_Q(QBluetoothSocket); if (static_cast(socket) != INVALID_SOCKET) { if (socketType == type) return true; abort(); } socketType = type; if (type != QBluetoothServiceInfo::RfcommProtocol) { socket = int(INVALID_SOCKET); errorString = QBluetoothSocket::tr("Unsupported protocol. Win32 only supports RFCOMM sockets"); q->setSocketError(QBluetoothSocket::UnsupportedProtocolError); return false; } socket = ::socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM); if (static_cast(socket) == INVALID_SOCKET) { const int error = ::WSAGetLastError(); qCWarning(QT_BT_WINDOWS) << "Failed to create socket:" << error << qt_error_string(error); errorString = QBluetoothSocket::tr("Failed to create socket"); q->setSocketError(QBluetoothSocket::UnknownSocketError); return false; } if (!createNotifiers()) return false; return true; } void QBluetoothSocketPrivateWin::connectToServiceHelper(const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode) { Q_Q(QBluetoothSocket); if (static_cast(socket) == INVALID_SOCKET && !ensureNativeSocket(socketType)) return; if (!configureSecurity()) return; SOCKADDR_BTH addr = {}; addr.addressFamily = AF_BTH; addr.port = port; addr.btAddr = address.toUInt64(); switch (socketType) { case QBluetoothServiceInfo::RfcommProtocol: addr.serviceClassId = RFCOMM_PROTOCOL_UUID; break; default: errorString = QBluetoothSocket::tr("Socket type not handled: %1").arg(socketType); q->setSocketError(QBluetoothSocket::UnsupportedProtocolError); return; } connectWriteNotifier->setEnabled(true); readNotifier->setEnabled(true); exceptNotifier->setEnabled(true); const int result = ::connect(socket, reinterpret_cast(&addr), sizeof(addr)); const int error = ::WSAGetLastError(); if (result != SOCKET_ERROR || error == WSAEWOULDBLOCK) { q->setSocketState(QBluetoothSocket::ConnectingState); q->setOpenMode(openMode); } else { errorString = qt_error_string(error); q->setSocketError(QBluetoothSocket::UnknownSocketError); } } void QBluetoothSocketPrivateWin::connectToService( const QBluetoothServiceInfo &service, QIODevice::OpenMode openMode) { Q_Q(QBluetoothSocket); if (q->state() != QBluetoothSocket::UnconnectedState && q->state() != QBluetoothSocket::ServiceLookupState) { //qCWarning(QT_BT_WINDOWS) << "QBluetoothSocketPrivateWIN::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::RfcommProtocol) { qCWarning(QT_BT_WINDOWS) << "QBluetoothSocket::connectToService called with unsupported protocol"; errorString = QBluetoothSocket::tr("Socket type not supported"); q->setSocketError(QBluetoothSocket::UnsupportedProtocolError); return; } if (service.serverChannel() > 0) { 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_WINDOWS) << "No port, no PSM, and no UUID provided. Unable to connect"; return; } qCDebug(QT_BT_WINDOWS) << "Need a port/psm, doing discovery"; q->doDeviceDiscovery(service, openMode); } } void QBluetoothSocketPrivateWin::_q_writeNotify() { Q_Q(QBluetoothSocket); if (state == QBluetoothSocket::ConnectingState) { q->setSocketState(QBluetoothSocket::ConnectedState); connectWriteNotifier->setEnabled(false); } else { if (txBuffer.isEmpty()) { connectWriteNotifier->setEnabled(false); return; } char buf[1024]; const int size = txBuffer.read(&buf[0], sizeof(buf)); const int writtenBytes = ::send(socket, &buf[0], size, 0); if (writtenBytes == SOCKET_ERROR) { // every other case returns error const int error = ::WSAGetLastError(); errorString = QBluetoothSocket::tr("Network Error: %1").arg(qt_error_string(error)); q->setSocketError(QBluetoothSocket::NetworkError); } else if (writtenBytes <= size) { // add remainder back to buffer const char *remainder = &buf[writtenBytes]; txBuffer.ungetBlock(remainder, size - writtenBytes); if (writtenBytes > 0) emit q->bytesWritten(writtenBytes); } else { errorString = QBluetoothSocket::tr("Logic error: more bytes sent than passed to ::send"); q->setSocketError(QBluetoothSocket::NetworkError); } if (!txBuffer.isEmpty()) { connectWriteNotifier->setEnabled(true); } else if (state == QBluetoothSocket::ClosingState) { connectWriteNotifier->setEnabled(false); this->close(); } } } void QBluetoothSocketPrivateWin::_q_readNotify() { Q_Q(QBluetoothSocket); char *writePointer = buffer.reserve(QPRIVATELINEARBUFFER_BUFFERSIZE); const int bytesRead = ::recv(socket, writePointer, QPRIVATELINEARBUFFER_BUFFERSIZE, 0); if (bytesRead == SOCKET_ERROR) { const int error = ::WSAGetLastError(); buffer.chop(QPRIVATELINEARBUFFER_BUFFERSIZE); readNotifier->setEnabled(false); connectWriteNotifier->setEnabled(false); errorString = qt_error_string(error); qCWarning(QT_BT_WINDOWS) << Q_FUNC_INFO << socket << "error:" << error << errorString; switch (error) { case WSAEHOSTDOWN: q->setSocketError(QBluetoothSocket::HostNotFoundError); break; case WSAECONNRESET: q->setSocketError(QBluetoothSocket::RemoteHostClosedError); break; default: q->setSocketError(QBluetoothSocket::UnknownSocketError); break; } q->disconnectFromService(); } else if (bytesRead == 0) { q->setSocketError(QBluetoothSocket::RemoteHostClosedError); q->setSocketState(QBluetoothSocket::UnconnectedState); } else { const int unusedBytes = QPRIVATELINEARBUFFER_BUFFERSIZE - bytesRead; buffer.chop(unusedBytes); if (bytesRead > 0) emit q->readyRead(); } } void QBluetoothSocketPrivateWin::_q_exceptNotify() { Q_Q(QBluetoothSocket); const int error = ::WSAGetLastError(); errorString = qt_error_string(error); q->setSocketError(QBluetoothSocket::UnknownSocketError); if (state == QBluetoothSocket::ConnectingState) abort(); } void QBluetoothSocketPrivateWin::connectToService( const QBluetoothAddress &address, const QBluetoothUuid &uuid, QIODevice::OpenMode openMode) { Q_Q(QBluetoothSocket); if (q->state() != QBluetoothSocket::UnconnectedState) { qCWarning(QT_BT_WINDOWS) << "QBluetoothSocketPrivateWin::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_WINDOWS) << "QBluetoothSocketPrivateWin::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 QBluetoothSocketPrivateWin::connectToService( const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode) { Q_Q(QBluetoothSocket); if (q->socketType() == QBluetoothServiceInfo::UnknownProtocol) { qCWarning(QT_BT_WINDOWS) << "QBluetoothSocketPrivateWin::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_WINDOWS) << "QBluetoothSocketPrivateWin::connectToService called on busy socket"; errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress"); q->setSocketError(QBluetoothSocket::OperationError); return; } q->setOpenMode(openMode); connectToServiceHelper(address, port, openMode); } void QBluetoothSocketPrivateWin::abort() { delete readNotifier; readNotifier = nullptr; delete connectWriteNotifier; connectWriteNotifier = nullptr; delete exceptNotifier; exceptNotifier = nullptr; // We don't transition through Closing for abort, so // we don't call disconnectFromService or QBluetoothSocket::close ::closesocket(socket); socket = int(INVALID_SOCKET); Q_Q(QBluetoothSocket); const bool wasConnected = q->state() == QBluetoothSocket::ConnectedState; q->setSocketState(QBluetoothSocket::UnconnectedState); if (wasConnected) { q->setOpenMode(QIODevice::NotOpen); emit q->readChannelFinished(); } } QString QBluetoothSocketPrivateWin::localName() const { const QBluetoothAddress localAddr = localAddress(); if (localAddr == QBluetoothAddress()) return {}; const QBluetoothLocalDevice device(localAddr); return device.name(); } QBluetoothAddress QBluetoothSocketPrivateWin::localAddress() const { if (static_cast(socket) == INVALID_SOCKET) return {}; SOCKADDR_BTH localAddr = {}; int localAddrLength = sizeof(localAddr); const int localResult = ::getsockname(socket, reinterpret_cast(&localAddr), &localAddrLength); if (localResult == SOCKET_ERROR) { const int error = ::WSAGetLastError(); qCWarning(QT_BT_WINDOWS) << "Error getting local address" << error << qt_error_string(error); return {}; } return QBluetoothAddress(localAddr.btAddr); } quint16 QBluetoothSocketPrivateWin::localPort() const { if (static_cast(socket) == INVALID_SOCKET) return {}; SOCKADDR_BTH localAddr = {}; int localAddrLength = sizeof(localAddr); const int localResult = ::getsockname(socket, reinterpret_cast(&localAddr), &localAddrLength); if (localResult == SOCKET_ERROR) { const int error = ::WSAGetLastError(); qCWarning(QT_BT_WINDOWS) << "Error getting local port" << error << qt_error_string(error); return {}; } return localAddr.port; } QString QBluetoothSocketPrivateWin::peerName() const { const QBluetoothAddress peerAddr = peerAddress(); if (peerAddr == QBluetoothAddress()) return {}; BLUETOOTH_DEVICE_INFO bdi = {}; bdi.dwSize = sizeof(bdi); bdi.Address.ullLong = peerAddr.toUInt64(); const DWORD res = ::BluetoothGetDeviceInfo(nullptr, &bdi); if (res != ERROR_SUCCESS) { qCWarning(QT_BT_WINDOWS) << "Error calling BluetoothGetDeviceInfo" << res << qt_error_string(res); return {}; } return QString::fromWCharArray(&bdi.szName[0]); } QBluetoothAddress QBluetoothSocketPrivateWin::peerAddress() const { if (static_cast(socket) == INVALID_SOCKET) return {}; SOCKADDR_BTH peerAddr = {}; int peerAddrLength = sizeof(peerAddr); const int peerResult = ::getpeername(socket, reinterpret_cast(&peerAddr), &peerAddrLength); if (peerResult == SOCKET_ERROR) { const int error = ::WSAGetLastError(); qCWarning(QT_BT_WINDOWS) << "Error getting peer address and port" << error << qt_error_string(error); return {}; } return QBluetoothAddress(peerAddr.btAddr); } quint16 QBluetoothSocketPrivateWin::peerPort() const { if (static_cast(socket) == INVALID_SOCKET) return {}; SOCKADDR_BTH peerAddr = {}; int peerAddrLength = sizeof(peerAddr); const int peerResult = ::getpeername(socket, reinterpret_cast(&peerAddr), &peerAddrLength); if (peerResult == SOCKET_ERROR) { const int error = ::WSAGetLastError(); qCWarning(QT_BT_WINDOWS) << "Error getting peer address and port" << error << qt_error_string(error); return {}; } return peerAddr.port; } qint64 QBluetoothSocketPrivateWin::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) { const int bytesWritten = ::send(socket, data, maxSize, 0); if (bytesWritten == SOCKET_ERROR) { const int error = ::WSAGetLastError(); errorString = QBluetoothSocket::tr("Network Error: %1").arg(qt_error_string(error)); q->setSocketError(QBluetoothSocket::NetworkError); } if (bytesWritten > 0) emit q->bytesWritten(bytesWritten); return bytesWritten; } else { if (!connectWriteNotifier) return -1; if (txBuffer.isEmpty()) { connectWriteNotifier->setEnabled(true); QMetaObject::invokeMethod(this, "_q_writeNotify", Qt::QueuedConnection); } char *txbuf = txBuffer.reserve(maxSize); ::memcpy(txbuf, data, maxSize); return maxSize; } } qint64 QBluetoothSocketPrivateWin::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; } const int bytesRead = buffer.read(data, maxSize); return bytesRead; } void QBluetoothSocketPrivateWin::close() { if (txBuffer.isEmpty()) abort(); else connectWriteNotifier->setEnabled(true); } bool QBluetoothSocketPrivateWin::setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol protocol, QBluetoothSocket::SocketState socketState, QBluetoothSocket::OpenMode openMode) { Q_Q(QBluetoothSocket); abort(); socketType = protocol; socket = socketDescriptor; if (!createNotifiers()) return false; q->setSocketState(socketState); q->setOpenMode(openMode); if (socketState == QBluetoothSocket::ConnectedState) { connectWriteNotifier->setEnabled(true); readNotifier->setEnabled(true); exceptNotifier->setEnabled(true); } return true; } qint64 QBluetoothSocketPrivateWin::bytesAvailable() const { return buffer.size(); } bool QBluetoothSocketPrivateWin::canReadLine() const { return buffer.canReadLine(); } qint64 QBluetoothSocketPrivateWin::bytesToWrite() const { return txBuffer.size(); } bool QBluetoothSocketPrivateWin::createNotifiers() { Q_Q(QBluetoothSocket); ULONG mode = 1; // 1 to enable non-blocking socket const int result = ::ioctlsocket(socket, FIONBIO, &mode); if (result == SOCKET_ERROR) { const int error = ::WSAGetLastError(); errorString = qt_error_string(error); q->setSocketError(QBluetoothSocket::UnknownSocketError); qCWarning(QT_BT_WINDOWS) << "Error setting socket to non-blocking" << error << errorString; abort(); return false; } readNotifier = new QSocketNotifier(socket, QSocketNotifier::Read); QObject::connect(readNotifier, &QSocketNotifier::activated, this, &QBluetoothSocketPrivateWin::_q_readNotify); connectWriteNotifier = new QSocketNotifier(socket, QSocketNotifier::Write, q); QObject::connect(connectWriteNotifier, &QSocketNotifier::activated, this, &QBluetoothSocketPrivateWin::_q_writeNotify); exceptNotifier = new QSocketNotifier(socket, QSocketNotifier::Exception, q); QObject::connect(exceptNotifier, &QSocketNotifier::activated, this, &QBluetoothSocketPrivateWin::_q_exceptNotify); connectWriteNotifier->setEnabled(false); readNotifier->setEnabled(false); exceptNotifier->setEnabled(false); return true; } bool QBluetoothSocketPrivateWin::configureSecurity() { Q_Q(QBluetoothSocket); if (secFlags & QBluetooth::Authorization) { ULONG authenticate = TRUE; const int result = ::setsockopt(socket, SOL_RFCOMM, SO_BTH_AUTHENTICATE, reinterpret_cast(&authenticate), sizeof(authenticate)); if (result == SOCKET_ERROR) { const int error = ::WSAGetLastError(); qCWarning(QT_BT_WINDOWS) << "Failed to set socket option, closing socket for safety" << error; qCWarning(QT_BT_WINDOWS) << "Error: " << qt_error_string(error); errorString = QBluetoothSocket::tr("Cannot set connection security level"); q->setSocketError(QBluetoothSocket::UnknownSocketError); return false; } } if (secFlags & QBluetooth::Encryption) { ULONG encrypt = TRUE; const int result = ::setsockopt(socket, SOL_RFCOMM, SO_BTH_ENCRYPT, reinterpret_cast(&encrypt), sizeof(encrypt)); if (result == SOCKET_ERROR) { const int error = ::WSAGetLastError(); qCWarning(QT_BT_WINDOWS) << "Failed to set socket option, closing socket for safety" << error; qCWarning(QT_BT_WINDOWS) << "Error: " << qt_error_string(error); errorString = QBluetoothSocket::tr("Cannot set connection security level"); q->setSocketError(QBluetoothSocket::UnknownSocketError); return false; } } return true; } QT_END_NAMESPACE