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