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 "qnlaengine.h"
41 #include "../qnetworksession_impl.h"
42
43 #include <QtNetwork/private/qnetworkconfiguration_p.h>
44
45 #include <QtCore/qthread.h>
46 #include <QtCore/qmutex.h>
47 #include <QtCore/qcoreapplication.h>
48 #include <QtCore/qstringlist.h>
49
50 #include <QtCore/qdebug.h>
51
52 #include "../platformdefs_win.h"
53
54 QT_BEGIN_NAMESPACE
55
QWindowsSockInit2()56 QWindowsSockInit2::QWindowsSockInit2()
57 : version(0)
58 {
59 //### should we try for 2.2 on all platforms ??
60 WSAData wsadata;
61
62 // IPv6 requires Winsock v2.0 or better.
63 if (WSAStartup(MAKEWORD(2,0), &wsadata) != 0) {
64 qWarning("QBearerManagementAPI: WinSock v2.0 initialization failed.");
65 } else {
66 version = 0x20;
67 }
68 }
69
~QWindowsSockInit2()70 QWindowsSockInit2::~QWindowsSockInit2()
71 {
72 WSACleanup();
73 }
74
75 #ifdef BEARER_MANAGEMENT_DEBUG
printBlob(NLA_BLOB * blob)76 static void printBlob(NLA_BLOB *blob)
77 {
78 qDebug() << "==== BEGIN NLA_BLOB ====" << Qt::endl
79
80 << "type:" << blob->header.type << Qt::endl
81 << "size:" << blob->header.dwSize << Qt::endl
82 << "next offset:" << blob->header.nextOffset;
83
84 switch (blob->header.type) {
85 case NLA_RAW_DATA:
86 qDebug() << "Raw Data" << Qt::endl
87 << '\t' << blob->data.rawData;
88 break;
89 case NLA_INTERFACE:
90 qDebug() << "Interface" << Qt::endl
91 << "\ttype:" << blob->data.interfaceData.dwType << Qt::endl
92 << "\tspeed:" << blob->data.interfaceData.dwSpeed << Qt::endl
93 << "\tadapter:" << blob->data.interfaceData.adapterName;
94 break;
95 case NLA_802_1X_LOCATION:
96 qDebug() << "802.1x Location" << Qt::endl
97 << '\t' << blob->data.locationData.information;
98 break;
99 case NLA_CONNECTIVITY:
100 qDebug() << "Connectivity" << Qt::endl
101 << "\ttype:" << blob->data.connectivity.type << Qt::endl
102 << "\tinternet:" << blob->data.connectivity.internet;
103 break;
104 case NLA_ICS:
105 qDebug() << "ICS" << Qt::endl
106 << "\tspeed:" << blob->data.ICS.remote.speed << Qt::endl
107 << "\ttype:" << blob->data.ICS.remote.type << Qt::endl
108 << "\tstate:" << blob->data.ICS.remote.state << Qt::endl
109 << "\tmachine name:" << blob->data.ICS.remote.machineName << Qt::endl
110 << "\tshared adapter name:" << blob->data.ICS.remote.sharedAdapterName;
111 break;
112 default:
113 qDebug("UNKNOWN BLOB TYPE");
114 }
115
116 qDebug("===== END NLA_BLOB =====");
117 }
118 #endif
119
qGetInterfaceType(const QString & interface)120 static QNetworkConfiguration::BearerType qGetInterfaceType(const QString &interface)
121 {
122 unsigned long oid;
123 DWORD bytesWritten;
124
125 NDIS_MEDIUM medium;
126 NDIS_PHYSICAL_MEDIUM physicalMedium;
127
128 HANDLE handle = CreateFile((TCHAR *)QString::fromLatin1("\\\\.\\%1").arg(interface).utf16(), 0,
129 FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
130 if (handle == INVALID_HANDLE_VALUE)
131 return QNetworkConfiguration::BearerUnknown;
132
133 oid = OID_GEN_MEDIA_SUPPORTED;
134 bytesWritten = 0;
135 bool result = DeviceIoControl(handle, IOCTL_NDIS_QUERY_GLOBAL_STATS, &oid, sizeof(oid),
136 &medium, sizeof(medium), &bytesWritten, 0);
137 if (!result) {
138 CloseHandle(handle);
139 return QNetworkConfiguration::BearerUnknown;
140 }
141
142 oid = OID_GEN_PHYSICAL_MEDIUM;
143 bytesWritten = 0;
144 result = DeviceIoControl(handle, IOCTL_NDIS_QUERY_GLOBAL_STATS, &oid, sizeof(oid),
145 &physicalMedium, sizeof(physicalMedium), &bytesWritten, 0);
146 if (!result) {
147 CloseHandle(handle);
148
149 if (medium == NdisMedium802_3)
150 return QNetworkConfiguration::BearerEthernet;
151 else
152 return QNetworkConfiguration::BearerUnknown;
153 }
154
155 CloseHandle(handle);
156
157 if (medium == NdisMedium802_3) {
158 switch (physicalMedium) {
159 case NdisPhysicalMediumWirelessLan:
160 return QNetworkConfiguration::BearerWLAN;
161 case NdisPhysicalMediumBluetooth:
162 return QNetworkConfiguration::BearerBluetooth;
163 case NdisPhysicalMediumWiMax:
164 return QNetworkConfiguration::BearerWiMAX;
165 default:
166 #ifdef BEARER_MANAGEMENT_DEBUG
167 qDebug() << "Physical Medium" << physicalMedium;
168 #endif
169 return QNetworkConfiguration::BearerEthernet;
170 }
171 }
172
173 #ifdef BEARER_MANAGEMENT_DEBUG
174 qDebug() << medium << physicalMedium;
175 #endif
176
177 return QNetworkConfiguration::BearerUnknown;
178 }
179
180 class QNlaThread : public QThread
181 {
182 Q_OBJECT
183
184 public:
185 QNlaThread(QNlaEngine *parent = 0);
186 ~QNlaThread();
187
188 QList<QNetworkConfigurationPrivate *> getConfigurations();
189
190 void forceUpdate();
191
192 protected:
193 virtual void run();
194
195 private:
196 void updateConfigurations(QList<QNetworkConfigurationPrivate *> &configs);
197 DWORD parseBlob(NLA_BLOB *blob, QNetworkConfigurationPrivate *cpPriv) const;
198 QNetworkConfigurationPrivate *parseQuerySet(const WSAQUERYSET *querySet) const;
199 void fetchConfigurations();
200
201 signals:
202 void networksChanged();
203
204 private:
205 QMutex mutex;
206 HANDLE handle;
207 bool done;
208 QList<QNetworkConfigurationPrivate *> fetchedConfigurations;
209 };
210
QNlaThread(QNlaEngine * parent)211 QNlaThread::QNlaThread(QNlaEngine *parent)
212 : QThread(parent), handle(0), done(false)
213 {
214 }
215
~QNlaThread()216 QNlaThread::~QNlaThread()
217 {
218 mutex.lock();
219
220 done = true;
221
222 if (handle) {
223 /* cancel completion event */
224 if (WSALookupServiceEnd(handle) == SOCKET_ERROR) {
225 #ifdef BEARER_MANAGEMENT_DEBUG
226 qDebug("WSALookupServiceEnd error %d", WSAGetLastError());
227 #endif
228 }
229 }
230 mutex.unlock();
231
232 wait();
233 }
234
getConfigurations()235 QList<QNetworkConfigurationPrivate *> QNlaThread::getConfigurations()
236 {
237 QMutexLocker locker(&mutex);
238
239 QList<QNetworkConfigurationPrivate *> foundConfigurations = fetchedConfigurations;
240 fetchedConfigurations.clear();
241
242 return foundConfigurations;
243 }
244
forceUpdate()245 void QNlaThread::forceUpdate()
246 {
247 mutex.lock();
248
249 if (handle) {
250 /* cancel completion event */
251 if (WSALookupServiceEnd(handle) == SOCKET_ERROR) {
252 #ifdef BEARER_MANAGEMENT_DEBUG
253 qDebug("WSALookupServiceEnd error %d", WSAGetLastError());
254 #endif
255 }
256 handle = 0;
257 }
258 mutex.unlock();
259 }
260
run()261 void QNlaThread::run()
262 {
263 WSAEVENT changeEvent = WSACreateEvent();
264 if (changeEvent == WSA_INVALID_EVENT)
265 return;
266
267 while (true) {
268 fetchConfigurations();
269
270 WSAQUERYSET qsRestrictions;
271
272 memset(&qsRestrictions, 0, sizeof(qsRestrictions));
273 qsRestrictions.dwSize = sizeof(qsRestrictions);
274 qsRestrictions.dwNameSpace = NS_NLA;
275
276 mutex.lock();
277 if (done) {
278 mutex.unlock();
279 break;
280 }
281 int result = WSALookupServiceBegin(&qsRestrictions, LUP_RETURN_ALL, &handle);
282 mutex.unlock();
283
284 if (result == SOCKET_ERROR)
285 break;
286
287 WSACOMPLETION completion;
288 WSAOVERLAPPED overlapped;
289
290 memset(&overlapped, 0, sizeof(overlapped));
291 overlapped.hEvent = changeEvent;
292
293 memset(&completion, 0, sizeof(completion));
294 completion.Type = NSP_NOTIFY_EVENT;
295 completion.Parameters.Event.lpOverlapped = &overlapped;
296
297 DWORD bytesReturned = 0;
298 result = WSANSPIoctl(handle, SIO_NSP_NOTIFY_CHANGE, 0, 0, 0, 0,
299 &bytesReturned, &completion);
300 if (result == SOCKET_ERROR) {
301 if (WSAGetLastError() != WSA_IO_PENDING)
302 break;
303 }
304
305 // Not interested in unrelated IO completion events
306 // although we also don't want to block them
307 while (WaitForSingleObjectEx(changeEvent, WSA_INFINITE, true) != WAIT_IO_COMPLETION &&
308 handle)
309 {
310 }
311
312 mutex.lock();
313 if (handle) {
314 result = WSALookupServiceEnd(handle);
315 if (result == SOCKET_ERROR) {
316 mutex.unlock();
317 break;
318 }
319 handle = 0;
320 }
321 mutex.unlock();
322 }
323
324 WSACloseEvent(changeEvent);
325 }
326
updateConfigurations(QList<QNetworkConfigurationPrivate * > & configs)327 void QNlaThread::updateConfigurations(QList<QNetworkConfigurationPrivate *> &configs)
328 {
329 mutex.lock();
330
331 while (!fetchedConfigurations.isEmpty())
332 delete fetchedConfigurations.takeFirst();
333
334 fetchedConfigurations = configs;
335
336 mutex.unlock();
337
338 emit networksChanged();
339 }
340
parseBlob(NLA_BLOB * blob,QNetworkConfigurationPrivate * cpPriv) const341 DWORD QNlaThread::parseBlob(NLA_BLOB *blob, QNetworkConfigurationPrivate *cpPriv) const
342 {
343 #ifdef BEARER_MANAGEMENT_DEBUG
344 printBlob(blob);
345 #endif
346
347 switch (blob->header.type) {
348 case NLA_RAW_DATA:
349 #ifdef BEARER_MANAGEMENT_DEBUG
350 qDebug("%s: unhandled header type NLA_RAW_DATA", __FUNCTION__);
351 #endif
352 break;
353 case NLA_INTERFACE:
354 cpPriv->state = QNetworkConfiguration::Active;
355 if (QNlaEngine *engine = qobject_cast<QNlaEngine *>(parent())) {
356 engine->configurationInterface[cpPriv->id.toUInt()] =
357 QString::fromLatin1(blob->data.interfaceData.adapterName);
358 }
359 break;
360 case NLA_802_1X_LOCATION:
361 #ifdef BEARER_MANAGEMENT_DEBUG
362 qDebug("%s: unhandled header type NLA_802_1X_LOCATION", __FUNCTION__);
363 #endif
364 break;
365 case NLA_CONNECTIVITY:
366 #ifdef BEARER_MANAGEMENT_DEBUG
367 qDebug("%s: unhandled header type NLA_CONNECTIVITY", __FUNCTION__);
368 #endif
369 break;
370 case NLA_ICS:
371 #ifdef BEARER_MANAGEMENT_DEBUG
372 qDebug("%s: unhandled header type NLA_ICS", __FUNCTION__);
373 #endif
374 break;
375 default:
376 #ifdef BEARER_MANAGEMENT_DEBUG
377 qDebug("%s: unhandled header type %d", __FUNCTION__, blob->header.type);
378 #endif
379 ;
380 }
381
382 return blob->header.nextOffset;
383 }
384
parseQuerySet(const WSAQUERYSET * querySet) const385 QNetworkConfigurationPrivate *QNlaThread::parseQuerySet(const WSAQUERYSET *querySet) const
386 {
387 QNetworkConfigurationPrivate *cpPriv = new QNetworkConfigurationPrivate;
388
389 cpPriv->name = QString::fromWCharArray(querySet->lpszServiceInstanceName);
390 cpPriv->isValid = true;
391 cpPriv->id = QString::number(qHash(QLatin1String("NLA:") + cpPriv->name));
392 cpPriv->state = QNetworkConfiguration::Defined;
393 cpPriv->type = QNetworkConfiguration::InternetAccessPoint;
394
395 #ifdef BEARER_MANAGEMENT_DEBUG
396 qDebug() << "size:" << querySet->dwSize;
397 qDebug() << "service instance name:" << QString::fromUtf16(querySet->lpszServiceInstanceName);
398 qDebug() << "service class id:" << querySet->lpServiceClassId;
399 qDebug() << "version:" << querySet->lpVersion;
400 qDebug() << "comment:" << QString::fromUtf16(querySet->lpszComment);
401 qDebug() << "namespace:" << querySet->dwNameSpace;
402 qDebug() << "namespace provider id:" << querySet->lpNSProviderId;
403 qDebug() << "context:" << QString::fromUtf16(querySet->lpszContext);
404 qDebug() << "number of protocols:" << querySet->dwNumberOfProtocols;
405 qDebug() << "protocols:" << querySet->lpafpProtocols;
406 qDebug() << "query string:" << QString::fromUtf16(querySet->lpszQueryString);
407 qDebug() << "number of cs addresses:" << querySet->dwNumberOfCsAddrs;
408 qDebug() << "cs addresses:" << querySet->lpcsaBuffer;
409 qDebug() << "output flags:" << querySet->dwOutputFlags;
410 #endif
411
412 if (querySet->lpBlob) {
413 #ifdef BEARER_MANAGEMENT_DEBUG
414 qDebug() << "blob size:" << querySet->lpBlob->cbSize;
415 qDebug() << "blob data:" << querySet->lpBlob->pBlobData;
416 #endif
417
418 DWORD offset = 0;
419 do {
420 NLA_BLOB *blob = reinterpret_cast<NLA_BLOB *>(querySet->lpBlob->pBlobData + offset);
421 DWORD nextOffset = parseBlob(blob, cpPriv);
422 if (nextOffset == offset)
423 break;
424 else
425 offset = nextOffset;
426 } while (offset != 0 && offset < querySet->lpBlob->cbSize);
427 }
428
429 if (QNlaEngine *engine = qobject_cast<QNlaEngine *>(parent())) {
430 const QString interface = engine->getInterfaceFromId(cpPriv->id);
431 cpPriv->bearerType = qGetInterfaceType(interface);
432 }
433
434 return cpPriv;
435 }
436
fetchConfigurations()437 void QNlaThread::fetchConfigurations()
438 {
439 QList<QNetworkConfigurationPrivate *> foundConfigurations;
440
441 WSAQUERYSET qsRestrictions;
442 HANDLE hLookup = 0;
443
444 memset(&qsRestrictions, 0, sizeof(qsRestrictions));
445 qsRestrictions.dwSize = sizeof(qsRestrictions);
446 qsRestrictions.dwNameSpace = NS_NLA;
447
448 int result = WSALookupServiceBegin(&qsRestrictions, LUP_RETURN_ALL | LUP_DEEP, &hLookup);
449 if (result == SOCKET_ERROR) {
450 mutex.lock();
451 fetchedConfigurations.clear();
452 mutex.unlock();
453 }
454
455 char buffer[0x10000];
456 while (result == 0) {
457 DWORD bufferLength = sizeof(buffer);
458 result = WSALookupServiceNext(hLookup, LUP_RETURN_ALL,
459 &bufferLength, reinterpret_cast<WSAQUERYSET *>(buffer));
460
461 if (result == SOCKET_ERROR)
462 break;
463
464 QNetworkConfigurationPrivate *cpPriv =
465 parseQuerySet(reinterpret_cast<WSAQUERYSET *>(buffer));
466
467 foundConfigurations.append(cpPriv);
468 }
469
470 if (hLookup) {
471 result = WSALookupServiceEnd(hLookup);
472 if (result == SOCKET_ERROR) {
473 #ifdef BEARER_MANAGEMENT_DEBUG
474 qDebug("WSALookupServiceEnd error %d", WSAGetLastError());
475 #endif
476 }
477 }
478
479 updateConfigurations(foundConfigurations);
480 }
481
QNlaEngine(QObject * parent)482 QNlaEngine::QNlaEngine(QObject *parent)
483 : QBearerEngineImpl(parent), nlaThread(0)
484 {
485 nlaThread = new QNlaThread(this);
486 connect(nlaThread, SIGNAL(networksChanged()),
487 this, SLOT(networksChanged()));
488 nlaThread->start();
489
490 qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
491 }
492
~QNlaEngine()493 QNlaEngine::~QNlaEngine()
494 {
495 delete nlaThread;
496 }
497
networksChanged()498 void QNlaEngine::networksChanged()
499 {
500 QMutexLocker locker(&mutex);
501
502 QStringList previous = accessPointConfigurations.keys();
503
504 QList<QNetworkConfigurationPrivate *> foundConfigurations = nlaThread->getConfigurations();
505 while (!foundConfigurations.isEmpty()) {
506 QNetworkConfigurationPrivate *cpPriv = foundConfigurations.takeFirst();
507
508 previous.removeAll(cpPriv->id);
509
510 if (accessPointConfigurations.contains(cpPriv->id)) {
511 QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(cpPriv->id);
512
513 bool changed = false;
514
515 ptr->mutex.lock();
516
517 if (ptr->isValid != cpPriv->isValid) {
518 ptr->isValid = cpPriv->isValid;
519 changed = true;
520 }
521
522 if (ptr->name != cpPriv->name) {
523 ptr->name = cpPriv->name;
524 changed = true;
525 }
526
527 if (ptr->state != cpPriv->state) {
528 ptr->state = cpPriv->state;
529 changed = true;
530 }
531
532 ptr->mutex.unlock();
533
534 if (changed) {
535 locker.unlock();
536 emit configurationChanged(ptr);
537 locker.relock();
538 }
539
540 delete cpPriv;
541 } else {
542 QNetworkConfigurationPrivatePointer ptr(cpPriv);
543
544 accessPointConfigurations.insert(ptr->id, ptr);
545
546 locker.unlock();
547 emit configurationAdded(ptr);
548 locker.relock();
549 }
550 }
551
552 while (!previous.isEmpty()) {
553 QNetworkConfigurationPrivatePointer ptr =
554 accessPointConfigurations.take(previous.takeFirst());
555
556 locker.unlock();
557 emit configurationRemoved(ptr);
558 locker.relock();
559 }
560
561 locker.unlock();
562 emit updateCompleted();
563 }
564
getInterfaceFromId(const QString & id)565 QString QNlaEngine::getInterfaceFromId(const QString &id)
566 {
567 QMutexLocker locker(&mutex);
568
569 return configurationInterface.value(id.toUInt());
570 }
571
hasIdentifier(const QString & id)572 bool QNlaEngine::hasIdentifier(const QString &id)
573 {
574 QMutexLocker locker(&mutex);
575
576 return configurationInterface.contains(id.toUInt());
577 }
578
connectToId(const QString & id)579 void QNlaEngine::connectToId(const QString &id)
580 {
581 emit connectionError(id, OperationNotSupported);
582 }
583
disconnectFromId(const QString & id)584 void QNlaEngine::disconnectFromId(const QString &id)
585 {
586 emit connectionError(id, OperationNotSupported);
587 }
588
requestUpdate()589 void QNlaEngine::requestUpdate()
590 {
591 QMutexLocker locker(&mutex);
592
593 nlaThread->forceUpdate();
594 }
595
sessionStateForId(const QString & id)596 QNetworkSession::State QNlaEngine::sessionStateForId(const QString &id)
597 {
598 QMutexLocker locker(&mutex);
599
600 QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(id);
601
602 if (!ptr)
603 return QNetworkSession::Invalid;
604
605 if (!ptr->isValid) {
606 return QNetworkSession::Invalid;
607 } else if ((ptr->state & QNetworkConfiguration::Active) == QNetworkConfiguration::Active) {
608 return QNetworkSession::Connected;
609 } else if ((ptr->state & QNetworkConfiguration::Discovered) ==
610 QNetworkConfiguration::Discovered) {
611 return QNetworkSession::Disconnected;
612 } else if ((ptr->state & QNetworkConfiguration::Defined) == QNetworkConfiguration::Defined) {
613 return QNetworkSession::NotAvailable;
614 } else if ((ptr->state & QNetworkConfiguration::Undefined) ==
615 QNetworkConfiguration::Undefined) {
616 return QNetworkSession::NotAvailable;
617 }
618
619 return QNetworkSession::Invalid;
620 }
621
capabilities() const622 QNetworkConfigurationManager::Capabilities QNlaEngine::capabilities() const
623 {
624 return QNetworkConfigurationManager::ForcedRoaming;
625 }
626
createSessionBackend()627 QNetworkSessionPrivate *QNlaEngine::createSessionBackend()
628 {
629 return new QNetworkSessionPrivateImpl;
630 }
631
defaultConfiguration()632 QNetworkConfigurationPrivatePointer QNlaEngine::defaultConfiguration()
633 {
634 return QNetworkConfigurationPrivatePointer();
635 }
636
637 #include "qnlaengine.moc"
638 QT_END_NAMESPACE
639
640