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, &parameters, 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