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 | |
52 | QT_BEGIN_NAMESPACE |
53 | |
54 | Q_DECLARE_LOGGING_CATEGORY(QT_BT_BLUEZ) |
55 | |
56 | QBluetoothSocket *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 | |
69 | QBluetoothServerPrivate::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 | |
79 | QBluetoothServerPrivate::~QBluetoothServerPrivate() |
80 | { |
81 | delete socketNotifier; |
82 | |
83 | delete socket; |
84 | } |
85 | |
86 | void QBluetoothServerPrivate::_q_newConnection() |
87 | { |
88 | // disable socket notifier until application calls nextPendingConnection(). |
89 | socketNotifier->setEnabled(false); |
90 | |
91 | emit q_ptr->newConnection(); |
92 | } |
93 | |
94 | void 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 | |
120 | QBluetooth::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 | |
145 | void QBluetoothServer::close() |
146 | { |
147 | Q_D(QBluetoothServer); |
148 | |
149 | delete d->socketNotifier; |
150 | d->socketNotifier = nullptr; |
151 | |
152 | d->socket->close(); |
153 | } |
154 | |
155 | bool 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 | |
264 | void QBluetoothServer::setMaxPendingConnections(int numConnections) |
265 | { |
266 | Q_D(QBluetoothServer); |
267 | |
268 | if (d->socket->state() == QBluetoothSocket::UnconnectedState) |
269 | d->maxPendingConnections = numConnections; |
270 | } |
271 | |
272 | bool 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 | |
283 | QBluetoothSocket *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 | |
320 | QBluetoothAddress QBluetoothServer::serverAddress() const |
321 | { |
322 | Q_D(const QBluetoothServer); |
323 | |
324 | return d->socket->localAddress(); |
325 | } |
326 | |
327 | quint16 QBluetoothServer::serverPort() const |
328 | { |
329 | Q_D(const QBluetoothServer); |
330 | |
331 | return d->socket->localPort(); |
332 | } |
333 | |
334 | void 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 | |
355 | QBluetooth::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 | |
365 | QT_END_NAMESPACE |
366 | |