| 1 | // Copyright (C) 2016 The Qt Company Ltd. |
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
| 3 | |
| 4 | #include "qbluetoothserver.h" |
| 5 | #include "qbluetoothserver_p.h" |
| 6 | #include "qbluetoothsocket.h" |
| 7 | #include "qbluetoothsocket_bluez_p.h" |
| 8 | #include "qbluetoothlocaldevice.h" |
| 9 | #include "bluez/bluez_data_p.h" |
| 10 | |
| 11 | #include <QtCore/QLoggingCategory> |
| 12 | #include <QtCore/QSocketNotifier> |
| 13 | |
| 14 | #include <errno.h> |
| 15 | |
| 16 | QT_BEGIN_NAMESPACE |
| 17 | |
| 18 | Q_DECLARE_LOGGING_CATEGORY(QT_BT_BLUEZ) |
| 19 | |
| 20 | QBluetoothSocket *QBluetoothServerPrivate::createSocketForServer( |
| 21 | QBluetoothServiceInfo::Protocol socketType) |
| 22 | { |
| 23 | // QBluetoothServer does not work with the BluetoothSocket implementation for DBus. |
| 24 | // Fall back to the raw socket implementation. |
| 25 | // Usually the private implementation is picked based on detected BlueZ version. |
| 26 | |
| 27 | // ownership of these objects is taken care of inside QBluetoothSocket and QBluetoothServer |
| 28 | QBluetoothSocketPrivateBluez *rawSocketPrivate = new QBluetoothSocketPrivateBluez(); |
| 29 | QBluetoothSocket *socket = new QBluetoothSocket(rawSocketPrivate, socketType); |
| 30 | return socket; |
| 31 | } |
| 32 | |
| 33 | QBluetoothServerPrivate::QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol sType, |
| 34 | QBluetoothServer *parent) |
| 35 | : securityFlags(QBluetooth::Security::Authorization), |
| 36 | serverType(sType), |
| 37 | q_ptr(parent) |
| 38 | { |
| 39 | if (sType == QBluetoothServiceInfo::RfcommProtocol) |
| 40 | socket = createSocketForServer(socketType: QBluetoothServiceInfo::RfcommProtocol); |
| 41 | else |
| 42 | socket = createSocketForServer(socketType: QBluetoothServiceInfo::L2capProtocol); |
| 43 | } |
| 44 | |
| 45 | QBluetoothServerPrivate::~QBluetoothServerPrivate() |
| 46 | { |
| 47 | delete socketNotifier; |
| 48 | |
| 49 | delete socket; |
| 50 | } |
| 51 | |
| 52 | void QBluetoothServerPrivate::_q_newConnection() |
| 53 | { |
| 54 | // disable socket notifier until application calls nextPendingConnection(). |
| 55 | socketNotifier->setEnabled(false); |
| 56 | |
| 57 | emit q_ptr->newConnection(); |
| 58 | } |
| 59 | |
| 60 | void QBluetoothServerPrivate::setSocketSecurityLevel( |
| 61 | QBluetooth::SecurityFlags requestedSecLevel, int *errnoCode) |
| 62 | { |
| 63 | if (requestedSecLevel == QBluetooth::SecurityFlags(QBluetooth::Security::NoSecurity)) { |
| 64 | qCWarning(QT_BT_BLUEZ) << "Cannot set NoSecurity on server socket" ; |
| 65 | return; |
| 66 | } |
| 67 | |
| 68 | struct bt_security security; |
| 69 | memset(s: &security, c: 0, n: sizeof(security)); |
| 70 | |
| 71 | // ignore QBluetooth::Security::Authentication -> not used anymore |
| 72 | if (requestedSecLevel & QBluetooth::Security::Authorization) |
| 73 | security.level = BT_SECURITY_LOW; |
| 74 | if (requestedSecLevel & QBluetooth::Security::Encryption) |
| 75 | security.level = BT_SECURITY_MEDIUM; |
| 76 | if (requestedSecLevel & QBluetooth::Security::Secure) |
| 77 | security.level = BT_SECURITY_HIGH; |
| 78 | |
| 79 | if (setsockopt(fd: socket->socketDescriptor(), SOL_BLUETOOTH, BT_SECURITY, |
| 80 | optval: &security, optlen: sizeof(security)) != 0) { |
| 81 | if (errnoCode) |
| 82 | *errnoCode = errno; |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | QBluetooth::SecurityFlags QBluetoothServerPrivate::socketSecurityLevel() const |
| 87 | { |
| 88 | struct bt_security security; |
| 89 | memset(s: &security, c: 0, n: sizeof(security)); |
| 90 | socklen_t length = sizeof(security); |
| 91 | |
| 92 | if (getsockopt(fd: socket->socketDescriptor(), SOL_BLUETOOTH, BT_SECURITY, |
| 93 | optval: &security, optlen: &length) != 0) { |
| 94 | qCWarning(QT_BT_BLUEZ) << "Failed to get security flags" << qt_error_string(errno); |
| 95 | return QBluetooth::Security::NoSecurity; |
| 96 | } |
| 97 | |
| 98 | switch (security.level) { |
| 99 | case BT_SECURITY_LOW: |
| 100 | return QBluetooth::Security::Authorization; |
| 101 | case BT_SECURITY_MEDIUM: |
| 102 | return QBluetooth::Security::Encryption; |
| 103 | case BT_SECURITY_HIGH: |
| 104 | return QBluetooth::Security::Secure; |
| 105 | default: |
| 106 | qCWarning(QT_BT_BLUEZ) << "Unknown server socket security level" << security.level; |
| 107 | return QBluetooth::Security::NoSecurity; |
| 108 | } |
| 109 | } |
| 110 | |
| 111 | void QBluetoothServer::close() |
| 112 | { |
| 113 | Q_D(QBluetoothServer); |
| 114 | |
| 115 | delete d->socketNotifier; |
| 116 | d->socketNotifier = nullptr; |
| 117 | |
| 118 | d->socket->close(); |
| 119 | } |
| 120 | |
| 121 | bool QBluetoothServer::listen(const QBluetoothAddress &address, quint16 port) |
| 122 | { |
| 123 | Q_D(QBluetoothServer); |
| 124 | |
| 125 | if (d->socket->state() == QBluetoothSocket::SocketState::ListeningState) { |
| 126 | qCWarning(QT_BT_BLUEZ) << "Socket already in listen mode, close server first" ; |
| 127 | return false; //already listening, nothing to do |
| 128 | } |
| 129 | |
| 130 | QBluetoothLocalDevice device(address); |
| 131 | if (!device.isValid()) { |
| 132 | qCWarning(QT_BT_BLUEZ) << "Device does not support Bluetooth or" |
| 133 | << address.toString() << "is not a valid local adapter" ; |
| 134 | d->m_lastError = QBluetoothServer::UnknownError; |
| 135 | emit errorOccurred(error: d->m_lastError); |
| 136 | return false; |
| 137 | } |
| 138 | |
| 139 | QBluetoothLocalDevice::HostMode hostMode = device.hostMode(); |
| 140 | if (hostMode == QBluetoothLocalDevice::HostPoweredOff) { |
| 141 | d->m_lastError = QBluetoothServer::PoweredOffError; |
| 142 | emit errorOccurred(error: d->m_lastError); |
| 143 | qCWarning(QT_BT_BLUEZ) << "Bluetooth device is powered off" ; |
| 144 | return false; |
| 145 | } |
| 146 | |
| 147 | int sock = d->socket->socketDescriptor(); |
| 148 | if (sock < 0) { |
| 149 | /* Negative socket descriptor is not always an error case |
| 150 | * Another cause could be a call to close()/abort() |
| 151 | * Check whether we can recover by re-creating the socket |
| 152 | * we should really call Bluez::QBluetoothSocketPrivateDarwin::ensureNativeSocket |
| 153 | * but a re-creation of the socket will do as well. |
| 154 | */ |
| 155 | |
| 156 | delete d->socket; |
| 157 | if (serverType() == QBluetoothServiceInfo::RfcommProtocol) |
| 158 | d->socket = QBluetoothServerPrivate::createSocketForServer(socketType: QBluetoothServiceInfo::RfcommProtocol); |
| 159 | else |
| 160 | d->socket = QBluetoothServerPrivate::createSocketForServer(socketType: QBluetoothServiceInfo::L2capProtocol); |
| 161 | |
| 162 | sock = d->socket->socketDescriptor(); |
| 163 | if (sock < 0) { |
| 164 | d->m_lastError = InputOutputError; |
| 165 | emit errorOccurred(error: d->m_lastError); |
| 166 | return false; |
| 167 | } |
| 168 | } |
| 169 | |
| 170 | if (d->serverType == QBluetoothServiceInfo::RfcommProtocol) { |
| 171 | sockaddr_rc addr; |
| 172 | |
| 173 | addr.rc_family = AF_BLUETOOTH; |
| 174 | addr.rc_channel = port; |
| 175 | |
| 176 | if (!address.isNull()) |
| 177 | convertAddress(from: address.toUInt64(), to&: addr.rc_bdaddr.b); |
| 178 | else |
| 179 | convertAddress(from: device.address().toUInt64(), to&: addr.rc_bdaddr.b); |
| 180 | |
| 181 | if (::bind(fd: sock, addr: reinterpret_cast<sockaddr *>(&addr), len: sizeof(sockaddr_rc)) < 0) { |
| 182 | if (errno == EADDRINUSE) |
| 183 | d->m_lastError = ServiceAlreadyRegisteredError; |
| 184 | else |
| 185 | d->m_lastError = InputOutputError; |
| 186 | emit errorOccurred(error: d->m_lastError); |
| 187 | return false; |
| 188 | } |
| 189 | } else { |
| 190 | sockaddr_l2 addr; |
| 191 | |
| 192 | memset(s: &addr, c: 0, n: sizeof(sockaddr_l2)); |
| 193 | addr.l2_family = AF_BLUETOOTH; |
| 194 | addr.l2_psm = port; |
| 195 | |
| 196 | if (!address.isNull()) |
| 197 | convertAddress(from: address.toUInt64(), to&: addr.l2_bdaddr.b); |
| 198 | else |
| 199 | convertAddress(Q_UINT64_C(0), to&: addr.l2_bdaddr.b); |
| 200 | |
| 201 | if (::bind(fd: sock, addr: reinterpret_cast<sockaddr *>(&addr), len: sizeof(sockaddr_l2)) < 0) { |
| 202 | d->m_lastError = InputOutputError; |
| 203 | emit errorOccurred(error: d->m_lastError); |
| 204 | return false; |
| 205 | } |
| 206 | } |
| 207 | |
| 208 | d->setSocketSecurityLevel(requestedSecLevel: d->securityFlags, errnoCode: nullptr); |
| 209 | |
| 210 | if (::listen(fd: sock, n: d->maxPendingConnections) < 0) { |
| 211 | d->m_lastError = InputOutputError; |
| 212 | emit errorOccurred(error: d->m_lastError); |
| 213 | return false; |
| 214 | } |
| 215 | |
| 216 | d->socket->setSocketState(QBluetoothSocket::SocketState::ListeningState); |
| 217 | |
| 218 | if (!d->socketNotifier) { |
| 219 | d->socketNotifier = new QSocketNotifier(d->socket->socketDescriptor(), |
| 220 | QSocketNotifier::Read); |
| 221 | connect(sender: d->socketNotifier, signal: &QSocketNotifier::activated, |
| 222 | context: this, slot: [d](){ |
| 223 | d->_q_newConnection(); |
| 224 | }); |
| 225 | } |
| 226 | |
| 227 | return true; |
| 228 | } |
| 229 | |
| 230 | void QBluetoothServer::setMaxPendingConnections(int numConnections) |
| 231 | { |
| 232 | Q_D(QBluetoothServer); |
| 233 | |
| 234 | if (d->socket->state() == QBluetoothSocket::SocketState::UnconnectedState) |
| 235 | d->maxPendingConnections = numConnections; |
| 236 | } |
| 237 | |
| 238 | bool QBluetoothServer::hasPendingConnections() const |
| 239 | { |
| 240 | Q_D(const QBluetoothServer); |
| 241 | |
| 242 | if (!d || !d->socketNotifier) |
| 243 | return false; |
| 244 | |
| 245 | // if the socket notifier is disabled there is a pending connection waiting for us to accept. |
| 246 | return !d->socketNotifier->isEnabled(); |
| 247 | } |
| 248 | |
| 249 | QBluetoothSocket *QBluetoothServer::nextPendingConnection() |
| 250 | { |
| 251 | Q_D(QBluetoothServer); |
| 252 | |
| 253 | if (!hasPendingConnections()) |
| 254 | return nullptr; |
| 255 | |
| 256 | int pending; |
| 257 | if (d->serverType == QBluetoothServiceInfo::RfcommProtocol) { |
| 258 | sockaddr_rc addr; |
| 259 | socklen_t length = sizeof(sockaddr_rc); |
| 260 | pending = ::accept(fd: d->socket->socketDescriptor(), |
| 261 | addr: reinterpret_cast<sockaddr *>(&addr), addr_len: &length); |
| 262 | } else { |
| 263 | sockaddr_l2 addr; |
| 264 | socklen_t length = sizeof(sockaddr_l2); |
| 265 | pending = ::accept(fd: d->socket->socketDescriptor(), |
| 266 | addr: reinterpret_cast<sockaddr *>(&addr), addr_len: &length); |
| 267 | } |
| 268 | |
| 269 | if (pending >= 0) { |
| 270 | QBluetoothSocket *newSocket = QBluetoothServerPrivate::createSocketForServer(); |
| 271 | if (d->serverType == QBluetoothServiceInfo::RfcommProtocol) |
| 272 | newSocket->setSocketDescriptor(socketDescriptor: pending, socketType: QBluetoothServiceInfo::RfcommProtocol); |
| 273 | else |
| 274 | newSocket->setSocketDescriptor(socketDescriptor: pending, socketType: QBluetoothServiceInfo::L2capProtocol); |
| 275 | |
| 276 | d->socketNotifier->setEnabled(true); |
| 277 | |
| 278 | return newSocket; |
| 279 | } else { |
| 280 | d->socketNotifier->setEnabled(true); |
| 281 | } |
| 282 | |
| 283 | return nullptr; |
| 284 | } |
| 285 | |
| 286 | QBluetoothAddress QBluetoothServer::serverAddress() const |
| 287 | { |
| 288 | Q_D(const QBluetoothServer); |
| 289 | |
| 290 | return d->socket->localAddress(); |
| 291 | } |
| 292 | |
| 293 | quint16 QBluetoothServer::serverPort() const |
| 294 | { |
| 295 | Q_D(const QBluetoothServer); |
| 296 | |
| 297 | return d->socket->localPort(); |
| 298 | } |
| 299 | |
| 300 | void QBluetoothServer::setSecurityFlags(QBluetooth::SecurityFlags security) |
| 301 | { |
| 302 | Q_D(QBluetoothServer); |
| 303 | |
| 304 | if (d->socket->state() == QBluetoothSocket::SocketState::UnconnectedState) { |
| 305 | // nothing to set beyond the fact to remember the sec level for the next listen() |
| 306 | d->securityFlags = security; |
| 307 | return; |
| 308 | } |
| 309 | |
| 310 | int errorCode = 0; |
| 311 | d->setSocketSecurityLevel(requestedSecLevel: security, errnoCode: &errorCode); |
| 312 | if (errorCode) { |
| 313 | qCWarning(QT_BT_BLUEZ) << "Failed to set socket option, closing socket for safety" << errorCode; |
| 314 | qCWarning(QT_BT_BLUEZ) << "Error: " << qt_error_string(errorCode); |
| 315 | d->m_lastError = InputOutputError; |
| 316 | emit errorOccurred(error: d->m_lastError); |
| 317 | d->socket->close(); |
| 318 | } |
| 319 | } |
| 320 | |
| 321 | QBluetooth::SecurityFlags QBluetoothServer::securityFlags() const |
| 322 | { |
| 323 | Q_D(const QBluetoothServer); |
| 324 | |
| 325 | if (d->socket->state() == QBluetoothSocket::SocketState::UnconnectedState) |
| 326 | return d->securityFlags; |
| 327 | |
| 328 | return d->socketSecurityLevel(); |
| 329 | } |
| 330 | |
| 331 | QT_END_NAMESPACE |
| 332 | |