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 plugins 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 "qnativewifiengine.h"
41 #include "platformdefs.h"
42 #include "../qnetworksession_impl.h"
43
44 #include <QtNetwork/private/qnetworkconfiguration_p.h>
45
46 #include <QtCore/qstringlist.h>
47 #include <QtCore/qcoreapplication.h>
48 #include <QtCore/qoperatingsystemversion.h>
49
50 #include <QtCore/qdebug.h>
51
52 #ifndef QT_NO_BEARERMANAGEMENT
53
54 QT_BEGIN_NAMESPACE
55
56 WlanOpenHandleProto local_WlanOpenHandle = 0;
57 WlanRegisterNotificationProto local_WlanRegisterNotification = 0;
58 WlanEnumInterfacesProto local_WlanEnumInterfaces = 0;
59 WlanGetAvailableNetworkListProto local_WlanGetAvailableNetworkList = 0;
60 WlanQueryInterfaceProto local_WlanQueryInterface = 0;
61 WlanConnectProto local_WlanConnect = 0;
62 WlanDisconnectProto local_WlanDisconnect = 0;
63 WlanScanProto local_WlanScan = 0;
64 WlanFreeMemoryProto local_WlanFreeMemory = 0;
65 WlanCloseHandleProto local_WlanCloseHandle = 0;
66
qNotificationCallback(WLAN_NOTIFICATION_DATA * data,QNativeWifiEngine * d)67 void qNotificationCallback(WLAN_NOTIFICATION_DATA *data, QNativeWifiEngine *d)
68 {
69 Q_UNUSED(d);
70
71 if (data->NotificationSource == WLAN_NOTIFICATION_SOURCE_ACM) {
72 switch (data->NotificationCode) {
73 case wlan_notification_acm_connection_complete:
74 case wlan_notification_acm_disconnected:
75 case wlan_notification_acm_scan_complete:
76 case wlan_notification_acm_scan_fail:
77 QMetaObject::invokeMethod(d, "scanComplete", Qt::QueuedConnection);
78 break;
79 default:
80 #ifdef BEARER_MANAGEMENT_DEBUG
81 qDebug() << "wlan acm notification" << (int)data->NotificationCode;
82 #endif
83 break;
84 }
85 } else {
86 #ifdef BEARER_MANAGEMENT_DEBUG
87 qDebug() << "wlan notification source" << (int)data->NotificationSource << "code" << (int)data->NotificationCode;
88 #endif
89 }
90 }
91
QNativeWifiEngine(QObject * parent)92 QNativeWifiEngine::QNativeWifiEngine(QObject *parent)
93 : QBearerEngineImpl(parent), handle(INVALID_HANDLE_VALUE)
94 {
95 connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(closeHandle()));
96 }
97
~QNativeWifiEngine()98 QNativeWifiEngine::~QNativeWifiEngine()
99 {
100 closeHandle();
101 }
102
scanComplete()103 void QNativeWifiEngine::scanComplete()
104 {
105 QMutexLocker locker(&mutex);
106
107 if (!available()) {
108 locker.unlock();
109 emit updateCompleted();
110 return;
111 }
112
113 // enumerate interfaces
114 WLAN_INTERFACE_INFO_LIST *interfaceList;
115 DWORD result = local_WlanEnumInterfaces(handle, 0, &interfaceList);
116 if (result != ERROR_SUCCESS) {
117 #ifdef BEARER_MANAGEMENT_DEBUG
118 qDebug("%s: WlanEnumInterfaces failed with error %ld\n", __FUNCTION__, result);
119 #endif
120
121 locker.unlock();
122 emit updateCompleted();
123
124 return;
125 }
126
127 QStringList previous = accessPointConfigurations.keys();
128
129 for (unsigned int i = 0; i < interfaceList->dwNumberOfItems; ++i) {
130 const WLAN_INTERFACE_INFO &interface = interfaceList->InterfaceInfo[i];
131
132 WLAN_AVAILABLE_NETWORK_LIST *networkList;
133 result = local_WlanGetAvailableNetworkList(handle, &interface.InterfaceGuid,
134 3, 0, &networkList);
135 if (result != ERROR_SUCCESS) {
136 #ifdef BEARER_MANAGEMENT_DEBUG
137 qDebug("%s: WlanGetAvailableNetworkList failed with error %ld\n",
138 __FUNCTION__, result);
139 #endif
140 continue;
141 }
142
143 QStringList seenNetworks;
144
145 for (unsigned int j = 0; j < networkList->dwNumberOfItems; ++j) {
146 WLAN_AVAILABLE_NETWORK &network = networkList->Network[j];
147
148 QString networkName;
149
150 if (network.strProfileName[0] != 0) {
151 networkName = QString::fromWCharArray(network.strProfileName);
152 } else {
153 networkName = QByteArray(reinterpret_cast<char *>(network.dot11Ssid.ucSSID),
154 network.dot11Ssid.uSSIDLength);
155 }
156
157 const QString id = QString::number(qHash(QLatin1String("WLAN:") + networkName));
158
159 previous.removeAll(id);
160
161 QNetworkConfiguration::StateFlags state = QNetworkConfiguration::Undefined;
162
163 if (!(network.dwFlags & WLAN_AVAILABLE_NETWORK_HAS_PROFILE))
164 state = QNetworkConfiguration::Undefined;
165
166 if (network.strProfileName[0] != 0) {
167 if (network.bNetworkConnectable) {
168 if (network.dwFlags & WLAN_AVAILABLE_NETWORK_CONNECTED)
169 state = QNetworkConfiguration::Active;
170 else
171 state = QNetworkConfiguration::Discovered;
172 } else {
173 state = QNetworkConfiguration::Defined;
174 }
175 }
176
177 if (seenNetworks.contains(networkName))
178 continue;
179 else
180 seenNetworks.append(networkName);
181
182 if (accessPointConfigurations.contains(id)) {
183 QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(id);
184
185 bool changed = false;
186
187 ptr->mutex.lock();
188
189 if (!ptr->isValid) {
190 ptr->isValid = true;
191 changed = true;
192 }
193
194 if (ptr->name != networkName) {
195 ptr->name = networkName;
196 changed = true;
197 }
198
199 if (ptr->state != state) {
200 ptr->state = state;
201 changed = true;
202 }
203
204 ptr->mutex.unlock();
205
206 if (changed) {
207 locker.unlock();
208 emit configurationChanged(ptr);
209 locker.relock();
210 }
211 } else {
212 QNetworkConfigurationPrivatePointer ptr(new QNetworkConfigurationPrivate);
213
214 ptr->name = networkName;
215 ptr->isValid = true;
216 ptr->id = id;
217 ptr->state = state;
218 ptr->type = QNetworkConfiguration::InternetAccessPoint;
219 ptr->bearerType = QNetworkConfiguration::BearerWLAN;
220
221 accessPointConfigurations.insert(id, ptr);
222
223 locker.unlock();
224 emit configurationAdded(ptr);
225 locker.relock();
226 }
227 }
228
229 local_WlanFreeMemory(networkList);
230 }
231
232 local_WlanFreeMemory(interfaceList);
233
234 while (!previous.isEmpty()) {
235 QNetworkConfigurationPrivatePointer ptr =
236 accessPointConfigurations.take(previous.takeFirst());
237
238 locker.unlock();
239 emit configurationRemoved(ptr);
240 locker.relock();
241 }
242
243 locker.unlock();
244 emit updateCompleted();
245 }
246
getInterfaceFromId(const QString & id)247 QString QNativeWifiEngine::getInterfaceFromId(const QString &id)
248 {
249 QMutexLocker locker(&mutex);
250
251 if (!available())
252 return QString();
253
254 // enumerate interfaces
255 WLAN_INTERFACE_INFO_LIST *interfaceList;
256 DWORD result = local_WlanEnumInterfaces(handle, 0, &interfaceList);
257 if (result != ERROR_SUCCESS) {
258 #ifdef BEARER_MANAGEMENT_DEBUG
259 qDebug("%s: WlanEnumInterfaces failed with error %ld\n", __FUNCTION__, result);
260 #endif
261 return QString();
262 }
263
264 for (unsigned int i = 0; i < interfaceList->dwNumberOfItems; ++i) {
265 const WLAN_INTERFACE_INFO &interface = interfaceList->InterfaceInfo[i];
266
267 DWORD dataSize;
268 WLAN_CONNECTION_ATTRIBUTES *connectionAttributes;
269 result = local_WlanQueryInterface(handle, &interface.InterfaceGuid,
270 wlan_intf_opcode_current_connection, 0, &dataSize,
271 reinterpret_cast<PVOID *>(&connectionAttributes), 0);
272 if (result != ERROR_SUCCESS) {
273 #ifdef BEARER_MANAGEMENT_DEBUG
274 if (result != ERROR_INVALID_STATE)
275 qDebug("%s: WlanQueryInterface failed with error %ld\n", __FUNCTION__, result);
276 #endif
277
278 continue;
279 }
280
281 if (qHash(QLatin1String("WLAN:") +
282 QString::fromWCharArray(connectionAttributes->strProfileName)) == id.toUInt()) {
283 QString guid("{%1-%2-%3-%4%5-%6%7%8%9%10%11}");
284
285 guid = guid.arg(interface.InterfaceGuid.Data1, 8, 16, QChar('0'));
286 guid = guid.arg(interface.InterfaceGuid.Data2, 4, 16, QChar('0'));
287 guid = guid.arg(interface.InterfaceGuid.Data3, 4, 16, QChar('0'));
288 for (int i = 0; i < 8; ++i)
289 guid = guid.arg(interface.InterfaceGuid.Data4[i], 2, 16, QChar('0'));
290
291 local_WlanFreeMemory(connectionAttributes);
292 local_WlanFreeMemory(interfaceList);
293
294 return guid.toUpper();
295 }
296
297 local_WlanFreeMemory(connectionAttributes);
298 }
299
300 local_WlanFreeMemory(interfaceList);
301
302 return QString();
303 }
304
hasIdentifier(const QString & id)305 bool QNativeWifiEngine::hasIdentifier(const QString &id)
306 {
307 QMutexLocker locker(&mutex);
308
309 if (!available())
310 return false;
311
312 // enumerate interfaces
313 WLAN_INTERFACE_INFO_LIST *interfaceList;
314 DWORD result = local_WlanEnumInterfaces(handle, 0, &interfaceList);
315 if (result != ERROR_SUCCESS) {
316 #ifdef BEARER_MANAGEMENT_DEBUG
317 qDebug("%s: WlanEnumInterfaces failed with error %ld\n", __FUNCTION__, result);
318 #endif
319 return false;
320 }
321
322 for (unsigned int i = 0; i < interfaceList->dwNumberOfItems; ++i) {
323 const WLAN_INTERFACE_INFO &interface = interfaceList->InterfaceInfo[i];
324
325 WLAN_AVAILABLE_NETWORK_LIST *networkList;
326 result = local_WlanGetAvailableNetworkList(handle, &interface.InterfaceGuid,
327 3, 0, &networkList);
328 if (result != ERROR_SUCCESS) {
329 #ifdef BEARER_MANAGEMENT_DEBUG
330 qDebug("%s: WlanGetAvailableNetworkList failed with error %ld\n",
331 __FUNCTION__, result);
332 #endif
333 continue;
334 }
335
336 for (unsigned int j = 0; j < networkList->dwNumberOfItems; ++j) {
337 WLAN_AVAILABLE_NETWORK &network = networkList->Network[j];
338
339 QString networkName;
340
341 if (network.strProfileName[0] != 0) {
342 networkName = QString::fromWCharArray(network.strProfileName);
343 } else {
344 networkName = QByteArray(reinterpret_cast<char *>(network.dot11Ssid.ucSSID),
345 network.dot11Ssid.uSSIDLength);
346 }
347
348 if (qHash(QLatin1String("WLAN:") + networkName) == id.toUInt()) {
349 local_WlanFreeMemory(networkList);
350 local_WlanFreeMemory(interfaceList);
351 return true;
352 }
353 }
354
355 local_WlanFreeMemory(networkList);
356 }
357
358 local_WlanFreeMemory(interfaceList);
359
360 return false;
361 }
362
connectToId(const QString & id)363 void QNativeWifiEngine::connectToId(const QString &id)
364 {
365 QMutexLocker locker(&mutex);
366
367 if (!available()) {
368 locker.unlock();
369 emit connectionError(id, InterfaceLookupError);
370 return;
371 }
372
373 WLAN_INTERFACE_INFO_LIST *interfaceList;
374 DWORD result = local_WlanEnumInterfaces(handle, 0, &interfaceList);
375 if (result != ERROR_SUCCESS) {
376 #ifdef BEARER_MANAGEMENT_DEBUG
377 qDebug("%s: WlanEnumInterfaces failed with error %ld\n", __FUNCTION__, result);
378 #endif
379 locker.unlock();
380 emit connectionError(id, InterfaceLookupError);
381 return;
382 }
383
384 QString profile;
385
386 for (unsigned int i = 0; i < interfaceList->dwNumberOfItems; ++i) {
387 const WLAN_INTERFACE_INFO &interface = interfaceList->InterfaceInfo[i];
388
389 WLAN_AVAILABLE_NETWORK_LIST *networkList;
390 result = local_WlanGetAvailableNetworkList(handle, &interface.InterfaceGuid,
391 3, 0, &networkList);
392 if (result != ERROR_SUCCESS) {
393 #ifdef BEARER_MANAGEMENT_DEBUG
394 qDebug("%s: WlanGetAvailableNetworkList failed with error %ld\n",
395 __FUNCTION__, result);
396 #endif
397 continue;
398 }
399
400 for (unsigned int j = 0; j < networkList->dwNumberOfItems; ++j) {
401 WLAN_AVAILABLE_NETWORK &network = networkList->Network[j];
402
403 profile = QString::fromWCharArray(network.strProfileName);
404
405 if (qHash(QLatin1String("WLAN:") + profile) == id.toUInt())
406 break;
407 else
408 profile.clear();
409 }
410
411 local_WlanFreeMemory(networkList);
412
413 if (!profile.isEmpty()) {
414 WLAN_CONNECTION_PARAMETERS parameters;
415 parameters.wlanConnectionMode = wlan_connection_mode_profile;
416 parameters.strProfile = reinterpret_cast<LPCWSTR>(profile.utf16());
417 parameters.pDot11Ssid = 0;
418 parameters.pDesiredBssidList = 0;
419 parameters.dot11BssType = dot11_BSS_type_any;
420 parameters.dwFlags = 0;
421
422 DWORD result = local_WlanConnect(handle, &interface.InterfaceGuid, ¶meters, 0);
423 if (result != ERROR_SUCCESS) {
424 #ifdef BEARER_MANAGEMENT_DEBUG
425 qDebug("%s: WlanConnect failed with error %ld\n", __FUNCTION__, result);
426 #endif
427 locker.unlock();
428 emit connectionError(id, ConnectError);
429 locker.relock();
430 break;
431 }
432
433 break;
434 }
435 }
436
437 local_WlanFreeMemory(interfaceList);
438
439 if (profile.isEmpty()) {
440 locker.unlock();
441 emit connectionError(id, InterfaceLookupError);
442 }
443 }
444
disconnectFromId(const QString & id)445 void QNativeWifiEngine::disconnectFromId(const QString &id)
446 {
447 QMutexLocker locker(&mutex);
448
449 if (!available()) {
450 locker.unlock();
451 emit connectionError(id, InterfaceLookupError);
452 return;
453 }
454
455 QString interface = getInterfaceFromId(id);
456
457 if (interface.isEmpty()) {
458 locker.unlock();
459 emit connectionError(id, InterfaceLookupError);
460 return;
461 }
462
463 const QVector<QStringRef> split = interface.midRef(1, interface.length() - 2).split(QLatin1Char('-'));
464
465 GUID guid;
466 guid.Data1 = split.at(0).toUInt(0, 16);
467 guid.Data2 = split.at(1).toUShort(0, 16);
468 guid.Data3 = split.at(2).toUShort(0, 16);
469 guid.Data4[0] = split.at(3).left(2).toUShort(0, 16);
470 guid.Data4[1] = split.at(3).right(2).toUShort(0, 16);
471 for (int i = 0; i < 6; ++i)
472 guid.Data4[i + 2] = split.at(4).mid(i*2, 2).toUShort(0, 16);
473
474 DWORD result = local_WlanDisconnect(handle, &guid, 0);
475 if (result != ERROR_SUCCESS) {
476 #ifdef BEARER_MANAGEMENT_DEBUG
477 qDebug("%s: WlanDisconnect failed with error %ld\n", __FUNCTION__, result);
478 #endif
479 locker.unlock();
480 emit connectionError(id, DisconnectionError);
481 return;
482 }
483 }
484
initialize()485 void QNativeWifiEngine::initialize()
486 {
487 scanComplete();
488 }
489
requestUpdate()490 void QNativeWifiEngine::requestUpdate()
491 {
492 QMutexLocker locker(&mutex);
493
494 if (!available()) {
495 locker.unlock();
496 emit updateCompleted();
497 return;
498 }
499
500 // enumerate interfaces
501 WLAN_INTERFACE_INFO_LIST *interfaceList;
502 DWORD result = local_WlanEnumInterfaces(handle, 0, &interfaceList);
503 if (result != ERROR_SUCCESS) {
504 #ifdef BEARER_MANAGEMENT_DEBUG
505 qDebug("%s: WlanEnumInterfaces failed with error %ld\n", __FUNCTION__, result);
506 #endif
507
508 locker.unlock();
509 emit updateCompleted();
510
511 return;
512 }
513
514 bool requested = false;
515 for (unsigned int i = 0; i < interfaceList->dwNumberOfItems; ++i) {
516 result = local_WlanScan(handle, &interfaceList->InterfaceInfo[i].InterfaceGuid, 0, 0, 0);
517 if (result != ERROR_SUCCESS) {
518 #ifdef BEARER_MANAGEMENT_DEBUG
519 qDebug("%s: WlanScan failed with error %ld\n", __FUNCTION__, result);
520 #endif
521 } else {
522 requested = true;
523 }
524 }
525
526 local_WlanFreeMemory(interfaceList);
527
528 if (!requested) {
529 locker.unlock();
530 emit updateCompleted();
531 }
532 }
533
sessionStateForId(const QString & id)534 QNetworkSession::State QNativeWifiEngine::sessionStateForId(const QString &id)
535 {
536 QMutexLocker locker(&mutex);
537
538 QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(id);
539
540 if (!ptr)
541 return QNetworkSession::Invalid;
542
543 if (!ptr->isValid) {
544 return QNetworkSession::Invalid;
545 } else if ((ptr->state & QNetworkConfiguration::Active) == QNetworkConfiguration::Active) {
546 return QNetworkSession::Connected;
547 } else if ((ptr->state & QNetworkConfiguration::Discovered) ==
548 QNetworkConfiguration::Discovered) {
549 return QNetworkSession::Disconnected;
550 } else if ((ptr->state & QNetworkConfiguration::Defined) == QNetworkConfiguration::Defined) {
551 return QNetworkSession::NotAvailable;
552 } else if ((ptr->state & QNetworkConfiguration::Undefined) ==
553 QNetworkConfiguration::Undefined) {
554 return QNetworkSession::NotAvailable;
555 }
556
557 return QNetworkSession::Invalid;
558 }
559
capabilities() const560 QNetworkConfigurationManager::Capabilities QNativeWifiEngine::capabilities() const
561 {
562 return QNetworkConfigurationManager::ForcedRoaming |
563 QNetworkConfigurationManager::CanStartAndStopInterfaces;
564 }
565
createSessionBackend()566 QNetworkSessionPrivate *QNativeWifiEngine::createSessionBackend()
567 {
568 return new QNetworkSessionPrivateImpl;
569 }
570
defaultConfiguration()571 QNetworkConfigurationPrivatePointer QNativeWifiEngine::defaultConfiguration()
572 {
573 return QNetworkConfigurationPrivatePointer();
574 }
575
available()576 bool QNativeWifiEngine::available()
577 {
578 if (handle != INVALID_HANDLE_VALUE)
579 return true;
580
581 DWORD clientVersion;
582
583 DWORD result = local_WlanOpenHandle(1, 0, &clientVersion, &handle);
584 if (result != ERROR_SUCCESS) {
585 #ifdef BEARER_MANAGEMENT_DEBUG
586 if (result != ERROR_SERVICE_NOT_ACTIVE)
587 qDebug("%s: WlanOpenHandle failed with error %ld\n", __FUNCTION__, result);
588 #endif
589
590 return false;
591 }
592
593 result = local_WlanRegisterNotification(handle, WLAN_NOTIFICATION_SOURCE_ALL, true,
594 WLAN_NOTIFICATION_CALLBACK(qNotificationCallback),
595 this, 0, 0);
596 #ifdef BEARER_MANAGEMENT_DEBUG
597 if (result != ERROR_SUCCESS)
598 qDebug("%s: WlanRegisterNotification failed with error %ld\n", __FUNCTION__, result);
599 #endif
600
601 return handle != INVALID_HANDLE_VALUE;
602 }
603
closeHandle()604 void QNativeWifiEngine::closeHandle()
605 {
606 if (handle != INVALID_HANDLE_VALUE) {
607 local_WlanCloseHandle(handle, 0);
608 handle = INVALID_HANDLE_VALUE;
609 }
610 }
611
requiresPolling() const612 bool QNativeWifiEngine::requiresPolling() const
613 {
614 // On Windows XP SP2 and SP3 only connection and disconnection notifications are available.
615 // We need to poll for changes in available wireless networks.
616 return QOperatingSystemVersion::current()
617 <= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 5, 2);
618 }
619
620 QT_END_NAMESPACE
621
622 #endif // QT_NO_BEARERMANAGEMENT
623