source: trunk/src/plugins/bearer/nla/qnlaengine.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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