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 | |