/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the plugins of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** 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 https://www.qt.io/terms-conditions. For further ** information use the contact form at https://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 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ // see comment in ../platformdefs_win.h. #define WIN32_LEAN_AND_MEAN 1 #include "qgenericengine.h" #include "../qnetworksession_impl.h" #include #include #include #include #include #include #include #if defined(Q_OS_WIN32) // PMIB_TCPTABLE2 is only available since Vista #if _WIN32_WINNT < 0x0601 # undef _WIN32_WINNT # define _WIN32_WINNT 0x0601 #endif // _WIN32_WINNT < 0x0601 #include "../platformdefs_win.h" #include #endif #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; #endif // Q_OS_WINRT // needed as interface is used as parameter name in qGetInterfaceType #undef interface #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) // QNetworkInterface::name returns a more friendly name on Windows. That name is not // accepted as an identifier for CreateFile so we have to obtain the Luid. std::wstring buf = interface.toStdWString(); if (buf.size() == 0) return QNetworkConfiguration::BearerUnknown; NET_LUID luid; NETIO_STATUS status = ConvertInterfaceNameToLuidW(buf.c_str(), &luid); if (status != NO_ERROR) return QNetworkConfiguration::BearerUnknown; switch (luid.Info.IfType) { case IF_TYPE_ETHERNET_CSMACD: case IF_TYPE_ISO88025_TOKENRING: case IF_TYPE_PPP: case IF_TYPE_SOFTWARE_LOOPBACK: return QNetworkConfiguration::BearerEthernet; case IF_TYPE_IEEE80211: return QNetworkConfiguration::BearerWLAN; case IF_TYPE_ATM: case IF_TYPE_IEEE1394: case IF_TYPE_OTHER: case IF_TYPE_TUNNEL: return QNetworkConfiguration::BearerUnknown; default: #ifdef BEARER_MANAGEMENT_DEBUG qDebug() << "Interface Type" << luid.Info.IfType; #endif return QNetworkConfiguration::BearerUnknown; } return QNetworkConfiguration::BearerUnknown; #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) - 1); 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_WIN // 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