diff options
author | Mårten Nordheim <marten.nordheim@qt.io> | 2019-04-12 15:46:35 +0200 |
---|---|---|
committer | Mårten Nordheim <marten.nordheim@qt.io> | 2019-07-08 12:12:45 +0200 |
commit | dc431b7e8548e97d19770285a6160ecfa8874815 (patch) | |
tree | bf494c7852e2efe997b0f9f452968ba1217575d7 /src/network/kernel | |
parent | 682e4795fe7a5ae64268212b23ff96706a0822d9 (diff) |
Introduce QNetwork(Connection|Status)Monitor to Windows
Currently not available for mingw because it's missing all the
interfaces. But should start working once it has them.
Change-Id: I231e8b69e008c5300a83087fe9cd071acd0687cb
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
Diffstat (limited to 'src/network/kernel')
-rw-r--r-- | src/network/kernel/kernel.pri | 2 | ||||
-rw-r--r-- | src/network/kernel/qnetconmonitor_win.cpp | 710 |
2 files changed, 712 insertions, 0 deletions
diff --git a/src/network/kernel/kernel.pri b/src/network/kernel/kernel.pri index 0e4cef5e74..a55648dbc7 100644 --- a/src/network/kernel/kernel.pri +++ b/src/network/kernel/kernel.pri @@ -77,6 +77,8 @@ macos | ios { kernel/qnetconmonitor_darwin.mm LIBS_PRIVATE += -framework SystemConfiguration +} else:qtConfig(netlistmgr) { + SOURCES += kernel/qnetconmonitor_win.cpp } else { SOURCES += kernel/qnetconmonitor_stub.cpp } diff --git a/src/network/kernel/qnetconmonitor_win.cpp b/src/network/kernel/qnetconmonitor_win.cpp new file mode 100644 index 0000000000..2eab0a062f --- /dev/null +++ b/src/network/kernel/qnetconmonitor_win.cpp @@ -0,0 +1,710 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module 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$ +** +****************************************************************************/ + +#include "qnetconmonitor_p.h" + +#include "private/qobject_p.h" + +#include <QtCore/quuid.h> +#include <QtCore/qmetaobject.h> + +#include <QtNetwork/qnetworkinterface.h> + +#include <objbase.h> +#include <netlistmgr.h> +#include <wrl/client.h> +#include <wrl/wrappers/corewrappers.h> +#include <comdef.h> +#include <iphlpapi.h> + +#include <algorithm> + +using namespace Microsoft::WRL; + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcNetMon, "qt.network.monitor"); + +namespace { +QString errorStringFromHResult(HRESULT hr) +{ + _com_error error(hr); + return QString::fromWCharArray(error.ErrorMessage()); +} + +template<typename T> +bool QueryInterfaceImpl(IUnknown *from, REFIID riid, void **ppvObject) +{ + if (riid == __uuidof(T)) { + *ppvObject = static_cast<T *>(from); + from->AddRef(); + return true; + } + return false; +} + +QNetworkInterface getInterfaceFromHostAddress(const QHostAddress &local) +{ + QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces(); + auto it = std::find_if( + interfaces.cbegin(), interfaces.cend(), [&local](const QNetworkInterface &iface) { + const auto &entries = iface.addressEntries(); + return std::any_of(entries.cbegin(), entries.cend(), + [&local](const QNetworkAddressEntry &entry) { + return entry.ip().isEqual(local, + QHostAddress::TolerantConversion); + }); + }); + if (it == interfaces.cend()) { + qCWarning(lcNetMon, "Could not find the interface for the local address."); + return {}; + } + return *it; +} +} // anonymous namespace + +class QNetworkConnectionEvents final : public INetworkConnectionEvents +{ +public: + QNetworkConnectionEvents(QNetworkConnectionMonitorPrivate *monitor); + ~QNetworkConnectionEvents(); + + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override; + + ULONG STDMETHODCALLTYPE AddRef() override { return ++ref; } + ULONG STDMETHODCALLTYPE Release() override + { + if (--ref == 0) { + delete this; + return 0; + } + return ref; + } + + HRESULT STDMETHODCALLTYPE + NetworkConnectionConnectivityChanged(GUID connectionId, NLM_CONNECTIVITY connectivity) override; + HRESULT STDMETHODCALLTYPE NetworkConnectionPropertyChanged( + GUID connectionId, NLM_CONNECTION_PROPERTY_CHANGE flags) override; + + Q_REQUIRED_RESULT + bool setTarget(const QNetworkInterface &iface); + Q_REQUIRED_RESULT + bool startMonitoring(); + Q_REQUIRED_RESULT + bool stopMonitoring(); + +private: + ComPtr<INetworkConnection> getNetworkConnectionFromAdapterGuid(QUuid guid); + + QUuid currentConnectionId{}; + + ComPtr<INetworkListManager> networkListManager; + ComPtr<IConnectionPoint> connectionPoint; + + QNetworkConnectionMonitorPrivate *monitor = nullptr; + + QAtomicInteger<ULONG> ref = 1; // start at 1 for our own initial reference + DWORD cookie = 0; +}; + +class QNetworkConnectionMonitorPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QNetworkConnectionMonitor); + +public: + QNetworkConnectionMonitorPrivate(); + ~QNetworkConnectionMonitorPrivate(); + + Q_REQUIRED_RESULT + bool setTargets(const QHostAddress &local, const QHostAddress &remote); + Q_REQUIRED_RESULT + bool startMonitoring(); + void stopMonitoring(); + + void setConnectivity(NLM_CONNECTIVITY newConnectivity); + +private: + ComPtr<QNetworkConnectionEvents> connectionEvents; + // We can assume we have access to internet/subnet when this class is created because + // connection has already been established to the peer: + NLM_CONNECTIVITY connectivity = + NLM_CONNECTIVITY(NLM_CONNECTIVITY_IPV4_INTERNET | NLM_CONNECTIVITY_IPV6_INTERNET + | NLM_CONNECTIVITY_IPV4_SUBNET | NLM_CONNECTIVITY_IPV6_SUBNET); + + bool sameSubnet = false; + bool monitoring = false; + bool comInitFailed = false; + bool remoteIsIPv6 = false; +}; + +QNetworkConnectionEvents::QNetworkConnectionEvents(QNetworkConnectionMonitorPrivate *monitor) + : monitor(monitor) +{ + auto hr = CoCreateInstance(CLSID_NetworkListManager, nullptr, CLSCTX_INPROC_SERVER, + IID_INetworkListManager, &networkListManager); + if (FAILED(hr)) { + qCWarning(lcNetMon) << "Could not get a NetworkListManager instance:" + << errorStringFromHResult(hr); + return; + } + + ComPtr<IConnectionPointContainer> connectionPointContainer; + hr = networkListManager.As(&connectionPointContainer); + if (SUCCEEDED(hr)) { + hr = connectionPointContainer->FindConnectionPoint(IID_INetworkConnectionEvents, + &connectionPoint); + } + if (FAILED(hr)) { + qCWarning(lcNetMon) << "Failed to get connection point for network events:" + << errorStringFromHResult(hr); + } +} + +QNetworkConnectionEvents::~QNetworkConnectionEvents() +{ + Q_ASSERT(ref == 0); +} + +ComPtr<INetworkConnection> QNetworkConnectionEvents::getNetworkConnectionFromAdapterGuid(QUuid guid) +{ + ComPtr<IEnumNetworkConnections> connections; + auto hr = networkListManager->GetNetworkConnections(connections.GetAddressOf()); + if (FAILED(hr)) { + qCWarning(lcNetMon) << "Failed to enumerate network connections:" + << errorStringFromHResult(hr); + return nullptr; + } + ComPtr<INetworkConnection> connection = nullptr; + do { + hr = connections->Next(1, connection.GetAddressOf(), nullptr); + if (FAILED(hr)) { + qCWarning(lcNetMon) << "Failed to get next network connection in enumeration:" + << errorStringFromHResult(hr); + break; + } + if (connection) { + GUID adapterId; + hr = connection->GetAdapterId(&adapterId); + if (FAILED(hr)) { + qCWarning(lcNetMon) << "Failed to get adapter ID from network connection:" + << errorStringFromHResult(hr); + continue; + } + if (guid == adapterId) + return connection; + } + } while (connection); + return nullptr; +} + +HRESULT STDMETHODCALLTYPE QNetworkConnectionEvents::QueryInterface(REFIID riid, void **ppvObject) +{ + if (!ppvObject) + return E_INVALIDARG; + + return QueryInterfaceImpl<IUnknown>(this, riid, ppvObject) + || QueryInterfaceImpl<INetworkConnectionEvents>(this, riid, ppvObject) + ? S_OK + : E_NOINTERFACE; +} + +HRESULT STDMETHODCALLTYPE QNetworkConnectionEvents::NetworkConnectionConnectivityChanged( + GUID connectionId, NLM_CONNECTIVITY newConnectivity) +{ + // This function is run on a different thread than 'monitor' is created on, so we need to run + // it on that thread + QMetaObject::invokeMethod(monitor->q_ptr, + [this, connectionId, newConnectivity, monitor = this->monitor]() { + if (connectionId == currentConnectionId) + monitor->setConnectivity(newConnectivity); + }, + Qt::QueuedConnection); + return S_OK; +} + +HRESULT STDMETHODCALLTYPE QNetworkConnectionEvents::NetworkConnectionPropertyChanged( + GUID connectionId, NLM_CONNECTION_PROPERTY_CHANGE flags) +{ + Q_UNUSED(connectionId); + Q_UNUSED(flags); + return E_NOTIMPL; +} + +bool QNetworkConnectionEvents::setTarget(const QNetworkInterface &iface) +{ + // Unset this in case it's already set to something + currentConnectionId = QUuid{}; + + NET_LUID luid; + if (ConvertInterfaceIndexToLuid(iface.index(), &luid) != NO_ERROR) { + qCWarning(lcNetMon, "Could not get the LUID for the interface."); + return false; + } + GUID guid; + if (ConvertInterfaceLuidToGuid(&luid, &guid) != NO_ERROR) { + qCWarning(lcNetMon, "Could not get the GUID for the interface."); + return false; + } + ComPtr<INetworkConnection> connection = getNetworkConnectionFromAdapterGuid(guid); + if (!connection) { + qCWarning(lcNetMon, "Could not get the INetworkConnection instance for the adapter GUID."); + return false; + } + auto hr = connection->GetConnectionId(&guid); + if (FAILED(hr)) { + qCWarning(lcNetMon) << "Failed to get the connection's GUID:" << errorStringFromHResult(hr); + return false; + } + currentConnectionId = guid; + + return true; +} + +bool QNetworkConnectionEvents::startMonitoring() +{ + if (currentConnectionId.isNull()) { + qCWarning(lcNetMon, "Can not start monitoring, set targets first"); + return false; + } + if (!connectionPoint) { + qCWarning(lcNetMon, + "We don't have the connection point, cannot start listening to events!"); + return false; + } + + auto hr = connectionPoint->Advise(this, &cookie); + if (FAILED(hr)) { + qCWarning(lcNetMon) << "Failed to subscribe to network connectivity events:" + << errorStringFromHResult(hr); + return false; + } + return true; +} + +bool QNetworkConnectionEvents::stopMonitoring() +{ + auto hr = connectionPoint->Unadvise(cookie); + if (FAILED(hr)) { + qCWarning(lcNetMon) << "Failed to unsubscribe from network connection events:" + << errorStringFromHResult(hr); + return false; + } + cookie = 0; + currentConnectionId = QUuid{}; + return true; +} + +QNetworkConnectionMonitorPrivate::QNetworkConnectionMonitorPrivate() +{ + auto hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED); + if (FAILED(hr)) { + qCWarning(lcNetMon) << "Failed to initialize COM:" << errorStringFromHResult(hr); + comInitFailed = true; + return; + } + + connectionEvents = new QNetworkConnectionEvents(this); +} + +QNetworkConnectionMonitorPrivate::~QNetworkConnectionMonitorPrivate() +{ + if (comInitFailed) + return; + connectionEvents.Reset(); + CoUninitialize(); +} + +bool QNetworkConnectionMonitorPrivate::setTargets(const QHostAddress &local, + const QHostAddress &remote) +{ + if (comInitFailed) + return false; + + QNetworkInterface iface = getInterfaceFromHostAddress(local); + if (!iface.isValid()) + return false; + const auto &addressEntries = iface.addressEntries(); + auto it = std::find_if( + addressEntries.cbegin(), addressEntries.cend(), + [&local](const QNetworkAddressEntry &entry) { return entry.ip() == local; }); + if (Q_UNLIKELY(it == addressEntries.cend())) { + qCWarning(lcNetMon, "The address entry we were working with disappeared"); + return false; + } + sameSubnet = remote.isInSubnet(local, it->prefixLength()); + remoteIsIPv6 = remote.protocol() == QAbstractSocket::IPv6Protocol; + + return connectionEvents->setTarget(iface); +} + +void QNetworkConnectionMonitorPrivate::setConnectivity(NLM_CONNECTIVITY newConnectivity) +{ + Q_Q(QNetworkConnectionMonitor); + const bool reachable = q->isReachable(); + connectivity = newConnectivity; + const bool newReachable = q->isReachable(); + if (reachable != newReachable) + emit q->reachabilityChanged(newReachable); +} + +bool QNetworkConnectionMonitorPrivate::startMonitoring() +{ + Q_ASSERT(connectionEvents); + Q_ASSERT(!monitoring); + if (connectionEvents->startMonitoring()) + monitoring = true; + return monitoring; +} + +void QNetworkConnectionMonitorPrivate::stopMonitoring() +{ + Q_ASSERT(connectionEvents); + Q_ASSERT(monitoring); + if (connectionEvents->stopMonitoring()) + monitoring = false; +} + +QNetworkConnectionMonitor::QNetworkConnectionMonitor() + : QObject(*new QNetworkConnectionMonitorPrivate) +{ +} + +QNetworkConnectionMonitor::QNetworkConnectionMonitor(const QHostAddress &local, + const QHostAddress &remote) + : QObject(*new QNetworkConnectionMonitorPrivate) +{ + setTargets(local, remote); +} + +QNetworkConnectionMonitor::~QNetworkConnectionMonitor() = default; + +bool QNetworkConnectionMonitor::setTargets(const QHostAddress &local, const QHostAddress &remote) +{ + if (isMonitoring()) { + qCWarning(lcNetMon, "Monitor is already active, call stopMonitoring() first"); + return false; + } + if (local.isNull()) { + qCWarning(lcNetMon, "Invalid (null) local address, cannot create a reachability target"); + return false; + } + // Silently return false for loopback addresses instead of printing warnings later + if (remote.isLoopback()) + return false; + + return d_func()->setTargets(local, remote); +} + +bool QNetworkConnectionMonitor::startMonitoring() +{ + Q_D(QNetworkConnectionMonitor); + if (isMonitoring()) { + qCWarning(lcNetMon, "Monitor is already active, call stopMonitoring() first"); + return false; + } + return d->startMonitoring(); +} + +bool QNetworkConnectionMonitor::isMonitoring() const +{ + return d_func()->monitoring; +} + +void QNetworkConnectionMonitor::stopMonitoring() +{ + Q_D(QNetworkConnectionMonitor); + if (!isMonitoring()) { + qCWarning(lcNetMon, "stopMonitoring was called when not monitoring!"); + return; + } + d->stopMonitoring(); +} + +bool QNetworkConnectionMonitor::isReachable() +{ + Q_D(QNetworkConnectionMonitor); + NLM_CONNECTIVITY required = d->sameSubnet + ? (d->remoteIsIPv6 ? NLM_CONNECTIVITY_IPV6_SUBNET : NLM_CONNECTIVITY_IPV4_SUBNET) + : (d->remoteIsIPv6 ? NLM_CONNECTIVITY_IPV6_INTERNET : NLM_CONNECTIVITY_IPV4_INTERNET); + return d_func()->connectivity & required; +} + +class QNetworkListManagerEvents final : public INetworkListManagerEvents +{ +public: + QNetworkListManagerEvents(QNetworkStatusMonitorPrivate *monitor); + ~QNetworkListManagerEvents(); + + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override; + + ULONG STDMETHODCALLTYPE AddRef() override { return ++ref; } + ULONG STDMETHODCALLTYPE Release() override + { + if (--ref == 0) { + delete this; + return 0; + } + return ref; + } + + HRESULT STDMETHODCALLTYPE ConnectivityChanged(NLM_CONNECTIVITY newConnectivity) override; + + Q_REQUIRED_RESULT + bool start(); + Q_REQUIRED_RESULT + bool stop(); + +private: + ComPtr<INetworkListManager> networkListManager = nullptr; + ComPtr<IConnectionPoint> connectionPoint = nullptr; + + QNetworkStatusMonitorPrivate *monitor = nullptr; + + QAtomicInteger<ULONG> ref = 1; // start at 1 for our own initial reference + DWORD cookie = 0; +}; + +class QNetworkStatusMonitorPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QNetworkStatusMonitor); + +public: + QNetworkStatusMonitorPrivate(); + ~QNetworkStatusMonitorPrivate(); + + Q_REQUIRED_RESULT + bool start(); + void stop(); + + void setConnectivity(NLM_CONNECTIVITY newConnectivity); + +private: + friend class QNetworkListManagerEvents; + + ComPtr<QNetworkListManagerEvents> managerEvents; + NLM_CONNECTIVITY connectivity = NLM_CONNECTIVITY_DISCONNECTED; + + bool monitoring = false; + bool comInitFailed = false; +}; + +QNetworkListManagerEvents::QNetworkListManagerEvents(QNetworkStatusMonitorPrivate *monitor) + : monitor(monitor) +{ + auto hr = CoCreateInstance(CLSID_NetworkListManager, nullptr, CLSCTX_INPROC_SERVER, + IID_INetworkListManager, &networkListManager); + if (FAILED(hr)) { + qCWarning(lcNetMon) << "Could not get a NetworkListManager instance:" + << errorStringFromHResult(hr); + return; + } + + // Set initial connectivity + hr = networkListManager->GetConnectivity(&monitor->connectivity); + if (FAILED(hr)) + qCWarning(lcNetMon) << "Could not get connectivity:" << errorStringFromHResult(hr); + + ComPtr<IConnectionPointContainer> connectionPointContainer; + hr = networkListManager.As(&connectionPointContainer); + if (SUCCEEDED(hr)) { + hr = connectionPointContainer->FindConnectionPoint(IID_INetworkListManagerEvents, + &connectionPoint); + } + if (FAILED(hr)) { + qCWarning(lcNetMon) << "Failed to get connection point for network list manager events:" + << errorStringFromHResult(hr); + } +} + +QNetworkListManagerEvents::~QNetworkListManagerEvents() +{ + Q_ASSERT(ref == 0); +} + +HRESULT STDMETHODCALLTYPE QNetworkListManagerEvents::QueryInterface(REFIID riid, void **ppvObject) +{ + if (!ppvObject) + return E_INVALIDARG; + + return QueryInterfaceImpl<IUnknown>(this, riid, ppvObject) + || QueryInterfaceImpl<INetworkListManagerEvents>(this, riid, ppvObject) + ? S_OK + : E_NOINTERFACE; +} + +HRESULT STDMETHODCALLTYPE +QNetworkListManagerEvents::ConnectivityChanged(NLM_CONNECTIVITY newConnectivity) +{ + // This function is run on a different thread than 'monitor' is created on, so we need to run + // it on that thread + QMetaObject::invokeMethod(monitor->q_ptr, + [newConnectivity, monitor = this->monitor]() { + monitor->setConnectivity(newConnectivity); + }, + Qt::QueuedConnection); + return S_OK; +} + +bool QNetworkListManagerEvents::start() +{ + if (!connectionPoint) { + qCWarning(lcNetMon, "Initialization failed, can't start!"); + return false; + } + auto hr = connectionPoint->Advise(this, &cookie); + if (FAILED(hr)) { + qCWarning(lcNetMon) << "Failed to subscribe to network connectivity events:" + << errorStringFromHResult(hr); + return false; + } + return true; +} + +bool QNetworkListManagerEvents::stop() +{ + Q_ASSERT(connectionPoint); + auto hr = connectionPoint->Unadvise(cookie); + if (FAILED(hr)) { + qCWarning(lcNetMon) << "Failed to unsubscribe from network connectivity events:" + << errorStringFromHResult(hr); + return false; + } + cookie = 0; + return true; +} + +QNetworkStatusMonitorPrivate::QNetworkStatusMonitorPrivate() +{ + auto hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED); + if (FAILED(hr)) { + qCWarning(lcNetMon) << "Failed to initialize COM:" << errorStringFromHResult(hr); + comInitFailed = true; + return; + } + managerEvents = new QNetworkListManagerEvents(this); +} + +QNetworkStatusMonitorPrivate::~QNetworkStatusMonitorPrivate() +{ + if (comInitFailed) + return; + if (monitoring) + stop(); + managerEvents.Reset(); + CoUninitialize(); +} + +void QNetworkStatusMonitorPrivate::setConnectivity(NLM_CONNECTIVITY newConnectivity) +{ + Q_Q(QNetworkStatusMonitor); + + const bool oldAccessibility = q->isNetworkAccesible(); + connectivity = newConnectivity; + const bool accessibility = q->isNetworkAccesible(); + if (oldAccessibility != accessibility) + emit q->onlineStateChanged(accessibility); +} + +bool QNetworkStatusMonitorPrivate::start() +{ + if (comInitFailed) + return false; + Q_ASSERT(managerEvents); + Q_ASSERT(!monitoring); + if (managerEvents->start()) + monitoring = true; + return monitoring; +} + +void QNetworkStatusMonitorPrivate::stop() +{ + Q_ASSERT(managerEvents); + Q_ASSERT(monitoring); + if (managerEvents->stop()) + monitoring = false; +} + +QNetworkStatusMonitor::QNetworkStatusMonitor() : QObject(*new QNetworkStatusMonitorPrivate) {} + +QNetworkStatusMonitor::~QNetworkStatusMonitor() {} + +bool QNetworkStatusMonitor::start() +{ + if (isMonitoring()) { + qCWarning(lcNetMon, "Monitor is already active, call stopMonitoring() first"); + return false; + } + + return d_func()->start(); +} + +void QNetworkStatusMonitor::stop() +{ + if (!isMonitoring()) { + qCWarning(lcNetMon, "stopMonitoring was called when not monitoring!"); + return; + } + + d_func()->stop(); +} + +bool QNetworkStatusMonitor::isMonitoring() const +{ + return d_func()->monitoring; +} + +bool QNetworkStatusMonitor::isNetworkAccesible() +{ + return d_func()->connectivity + & (NLM_CONNECTIVITY_IPV4_INTERNET | NLM_CONNECTIVITY_IPV6_INTERNET + | NLM_CONNECTIVITY_IPV4_SUBNET | NLM_CONNECTIVITY_IPV6_SUBNET); +} + +bool QNetworkStatusMonitor::isEnabled() +{ + return true; +} + +void QNetworkStatusMonitor::reachabilityChanged(bool online) +{ + Q_UNUSED(online); + Q_UNREACHABLE(); +} + +QT_END_NAMESPACE |