/**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the plugins of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL21$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** As a special exception, The Qt Company gives you certain additional ** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qgenericengine.h" #include "../qnetworksession_impl.h" #include #include #include #include #include #include #include #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) #include "../platformdefs_win.h" #endif #ifdef Q_OS_WINCE typedef ULONG NDIS_OID, *PNDIS_OID; # ifndef QT_NO_WINCE_NUIOUSER # include # endif #endif // Q_OS_WINCE #ifdef Q_OS_WINRT #include #include #include #include #include using namespace Microsoft::WRL; using namespace Microsoft::WRL::Wrappers; using namespace ABI::Windows::Foundation; using namespace ABI::Windows::Foundation::Collections; using namespace ABI::Windows::Networking; using namespace ABI::Windows::Networking::Connectivity; // needed as interface is used as parameter name in qGetInterfaceType #undef interface #endif // Q_OS_WINRT #ifdef Q_OS_LINUX #include #include #include #include #include #endif QT_BEGIN_NAMESPACE #ifndef QT_NO_NETWORKINTERFACE static QNetworkConfiguration::BearerType qGetInterfaceType(const QString &interface) { #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) DWORD bytesWritten; NDIS_MEDIUM medium; NDIS_PHYSICAL_MEDIUM physicalMedium; #if defined(Q_OS_WINCE) && !defined(QT_NO_WINCE_NUIOUSER) NDISUIO_QUERY_OID nicGetOid; HANDLE handle = CreateFile((PTCHAR)NDISUIO_DEVICE_NAME, 0, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); #else unsigned long oid; HANDLE handle = CreateFile((TCHAR *)QString::fromLatin1("\\\\.\\%1").arg(interface).utf16(), 0, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); #endif if (handle == INVALID_HANDLE_VALUE) return QNetworkConfiguration::BearerUnknown; bytesWritten = 0; #if defined(Q_OS_WINCE) && !defined(QT_NO_WINCE_NUIOUSER) ZeroMemory(&nicGetOid, sizeof(NDISUIO_QUERY_OID)); nicGetOid.Oid = OID_GEN_MEDIA_SUPPORTED; nicGetOid.ptcDeviceName = (PTCHAR)interface.utf16(); bool result = DeviceIoControl(handle, IOCTL_NDISUIO_QUERY_OID_VALUE, &nicGetOid, sizeof(nicGetOid), &nicGetOid, sizeof(nicGetOid), &bytesWritten, 0); #else oid = OID_GEN_MEDIA_SUPPORTED; bool result = DeviceIoControl(handle, IOCTL_NDIS_QUERY_GLOBAL_STATS, &oid, sizeof(oid), &medium, sizeof(medium), &bytesWritten, 0); #endif if (!result) { CloseHandle(handle); return QNetworkConfiguration::BearerUnknown; } bytesWritten = 0; #if defined(Q_OS_WINCE) && !defined(QT_NO_WINCE_NUIOUSER) medium = NDIS_MEDIUM( *(LPDWORD)nicGetOid.Data ); ZeroMemory(&nicGetOid, sizeof(NDISUIO_QUERY_OID)); nicGetOid.Oid = OID_GEN_PHYSICAL_MEDIUM; nicGetOid.ptcDeviceName = (PTCHAR)interface.utf16(); result = DeviceIoControl(handle, IOCTL_NDISUIO_QUERY_OID_VALUE, &nicGetOid, sizeof(nicGetOid), &nicGetOid, sizeof(nicGetOid), &bytesWritten, 0); physicalMedium = NDIS_PHYSICAL_MEDIUM( *(LPDWORD)nicGetOid.Data ); #else oid = OID_GEN_PHYSICAL_MEDIUM; result = DeviceIoControl(handle, IOCTL_NDIS_QUERY_GLOBAL_STATS, &oid, sizeof(oid), &physicalMedium, sizeof(physicalMedium), &bytesWritten, 0); #endif if (!result) { CloseHandle(handle); if (medium == NdisMedium802_3) return QNetworkConfiguration::BearerEthernet; else return QNetworkConfiguration::BearerUnknown; } CloseHandle(handle); if (medium == NdisMedium802_3) { switch (physicalMedium) { case NdisPhysicalMediumWirelessLan: return QNetworkConfiguration::BearerWLAN; case NdisPhysicalMediumBluetooth: return QNetworkConfiguration::BearerBluetooth; case NdisPhysicalMediumWiMax: return QNetworkConfiguration::BearerWiMAX; default: #ifdef BEARER_MANAGEMENT_DEBUG qDebug() << "Physical Medium" << physicalMedium; #endif return QNetworkConfiguration::BearerEthernet; } } #ifdef BEARER_MANAGEMENT_DEBUG qDebug() << medium << physicalMedium; #endif #elif defined(Q_OS_LINUX) int sock = socket(AF_INET, SOCK_DGRAM, 0); ifreq request; strncpy(request.ifr_name, interface.toLocal8Bit().data(), sizeof(request.ifr_name)); request.ifr_name[sizeof(request.ifr_name) - 1] = '\0'; int result = ioctl(sock, SIOCGIFHWADDR, &request); close(sock); if (result >= 0 && request.ifr_hwaddr.sa_family == ARPHRD_ETHER) return QNetworkConfiguration::BearerEthernet; #elif defined(Q_OS_WINRT) ComPtr networkInfoStatics; HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_Connectivity_NetworkInformation).Get(), &networkInfoStatics); Q_ASSERT_SUCCEEDED(hr); ComPtr> connectionProfiles; hr = networkInfoStatics->GetConnectionProfiles(&connectionProfiles); Q_ASSERT_SUCCEEDED(hr); if (!connectionProfiles) return QNetworkConfiguration::BearerUnknown; unsigned int size; hr = connectionProfiles->get_Size(&size); Q_ASSERT_SUCCEEDED(hr); for (unsigned int i = 0; i < size; ++i) { ComPtr profile; hr = connectionProfiles->GetAt(i, &profile); Q_ASSERT_SUCCEEDED(hr); ComPtr adapter; hr = profile->get_NetworkAdapter(&adapter); // Indicates that no internet connection is available/the device is in airplane mode if (hr == E_INVALIDARG) return QNetworkConfiguration::BearerUnknown; Q_ASSERT_SUCCEEDED(hr); GUID id; hr = adapter->get_NetworkAdapterId(&id); Q_ASSERT_SUCCEEDED(hr); OLECHAR adapterName[39]={0}; int length = StringFromGUID2(id, adapterName, 39); // "length - 1" as we have to remove the null terminator from it in order to compare if (!length || QString::fromRawData(reinterpret_cast(adapterName), length - 1) != interface) continue; ComPtr profile2; hr = profile.As(&profile2); Q_ASSERT_SUCCEEDED(hr); boolean isWLan; hr = profile2->get_IsWlanConnectionProfile(&isWLan); Q_ASSERT_SUCCEEDED(hr); if (isWLan) return QNetworkConfiguration::BearerWLAN; boolean isWWan; hr = profile2->get_IsWwanConnectionProfile(&isWWan); Q_ASSERT_SUCCEEDED(hr); if (isWWan) { ComPtr details; hr = profile2->get_WwanConnectionProfileDetails(&details); Q_ASSERT_SUCCEEDED(hr); WwanDataClass dataClass; hr = details->GetCurrentDataClass(&dataClass); Q_ASSERT_SUCCEEDED(hr); switch (dataClass) { case WwanDataClass_Edge: case WwanDataClass_Gprs: return QNetworkConfiguration::Bearer2G; case WwanDataClass_Umts: return QNetworkConfiguration::BearerWCDMA; case WwanDataClass_LteAdvanced: return QNetworkConfiguration::BearerLTE; case WwanDataClass_Hsdpa: case WwanDataClass_Hsupa: return QNetworkConfiguration::BearerHSPA; case WwanDataClass_Cdma1xRtt: case WwanDataClass_Cdma3xRtt: case WwanDataClass_CdmaUmb: return QNetworkConfiguration::BearerCDMA2000; case WwanDataClass_Cdma1xEvdv: case WwanDataClass_Cdma1xEvdo: case WwanDataClass_Cdma1xEvdoRevA: case WwanDataClass_Cdma1xEvdoRevB: return QNetworkConfiguration::BearerEVDO; case WwanDataClass_Custom: case WwanDataClass_None: default: return QNetworkConfiguration::BearerUnknown; } } return QNetworkConfiguration::BearerEthernet; } #else Q_UNUSED(interface); #endif return QNetworkConfiguration::BearerUnknown; } #endif QGenericEngine::QGenericEngine(QObject *parent) : QBearerEngineImpl(parent) { //workaround for deadlock in __cxa_guard_acquire with webkit on macos x //initialise the Q_GLOBAL_STATIC in same thread as the AtomicallyInitializedStatic (void)QNetworkInterface::interfaceFromIndex(0); } QGenericEngine::~QGenericEngine() { } QString QGenericEngine::getInterfaceFromId(const QString &id) { QMutexLocker locker(&mutex); return configurationInterface.value(id); } bool QGenericEngine::hasIdentifier(const QString &id) { QMutexLocker locker(&mutex); return configurationInterface.contains(id); } void QGenericEngine::connectToId(const QString &id) { emit connectionError(id, OperationNotSupported); } void QGenericEngine::disconnectFromId(const QString &id) { emit connectionError(id, OperationNotSupported); } void QGenericEngine::initialize() { doRequestUpdate(); } void QGenericEngine::requestUpdate() { doRequestUpdate(); } void QGenericEngine::doRequestUpdate() { #ifndef QT_NO_NETWORKINTERFACE QMutexLocker locker(&mutex); // Immediately after connecting with a wireless access point // QNetworkInterface::allInterfaces() will sometimes return an empty list. Calling it again a // second time results in a non-empty list. If we loose interfaces we will end up removing // network configurations which will break current sessions. QList interfaces = QNetworkInterface::allInterfaces(); if (interfaces.isEmpty()) interfaces = QNetworkInterface::allInterfaces(); QStringList previous = accessPointConfigurations.keys(); // create configuration for each interface while (!interfaces.isEmpty()) { QNetworkInterface interface = interfaces.takeFirst(); if (!interface.isValid()) continue; // ignore loopback interface if (interface.flags() & QNetworkInterface::IsLoopBack) continue; #ifndef Q_OS_WINRT // ignore WLAN interface handled in separate engine if (qGetInterfaceType(interface.name()) == QNetworkConfiguration::BearerWLAN) continue; #endif uint identifier; if (interface.index()) identifier = qHash(QLatin1String("generic:") + QString::number(interface.index())); else identifier = qHash(QLatin1String("generic:") + interface.hardwareAddress()); const QString id = QString::number(identifier); previous.removeAll(id); QString name = interface.humanReadableName(); if (name.isEmpty()) name = interface.name(); QNetworkConfiguration::StateFlags state = QNetworkConfiguration::Defined; if ((interface.flags() & QNetworkInterface::IsRunning) && !interface.addressEntries().isEmpty()) state |= QNetworkConfiguration::Active; if (accessPointConfigurations.contains(id)) { QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(id); bool changed = false; ptr->mutex.lock(); if (!ptr->isValid) { ptr->isValid = true; changed = true; } if (ptr->name != name) { ptr->name = name; changed = true; } if (ptr->id != id) { ptr->id = id; changed = true; } if (ptr->state != state) { ptr->state = state; changed = true; } ptr->mutex.unlock(); if (changed) { locker.unlock(); emit configurationChanged(ptr); locker.relock(); } } else { QNetworkConfigurationPrivatePointer ptr(new QNetworkConfigurationPrivate); ptr->name = name; ptr->isValid = true; ptr->id = id; ptr->state = state; ptr->type = QNetworkConfiguration::InternetAccessPoint; ptr->bearerType = qGetInterfaceType(interface.name()); accessPointConfigurations.insert(id, ptr); configurationInterface.insert(id, interface.name()); locker.unlock(); emit configurationAdded(ptr); locker.relock(); } } while (!previous.isEmpty()) { QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.take(previous.takeFirst()); configurationInterface.remove(ptr->id); locker.unlock(); emit configurationRemoved(ptr); locker.relock(); } locker.unlock(); #endif emit updateCompleted(); } QNetworkSession::State QGenericEngine::sessionStateForId(const QString &id) { QMutexLocker locker(&mutex); QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(id); if (!ptr) return QNetworkSession::Invalid; QMutexLocker configLocker(&ptr->mutex); if (!ptr->isValid) { return QNetworkSession::Invalid; } else if ((ptr->state & QNetworkConfiguration::Active) == QNetworkConfiguration::Active) { return QNetworkSession::Connected; } else if ((ptr->state & QNetworkConfiguration::Discovered) == QNetworkConfiguration::Discovered) { return QNetworkSession::Disconnected; } else if ((ptr->state & QNetworkConfiguration::Defined) == QNetworkConfiguration::Defined) { return QNetworkSession::NotAvailable; } else if ((ptr->state & QNetworkConfiguration::Undefined) == QNetworkConfiguration::Undefined) { return QNetworkSession::NotAvailable; } return QNetworkSession::Invalid; } QNetworkConfigurationManager::Capabilities QGenericEngine::capabilities() const { return QNetworkConfigurationManager::ForcedRoaming; } QNetworkSessionPrivate *QGenericEngine::createSessionBackend() { return new QNetworkSessionPrivateImpl; } QNetworkConfigurationPrivatePointer QGenericEngine::defaultConfiguration() { return QNetworkConfigurationPrivatePointer(); } bool QGenericEngine::requiresPolling() const { return true; } QT_END_NAMESPACE