7#include "private/qobject_p.h"
9#include <QtCore/quuid.h>
10#include <QtCore/qmetaobject.h>
12#include <QtCore/private/qfunctions_win_p.h>
13#include <QtCore/private/qsystemerror_p.h>
15#include <QtNetwork/qnetworkinterface.h>
18#include <netlistmgr.h>
19#include <QtCore/private/qcomptr_p.h>
20#include <wrl/wrappers/corewrappers.h>
25using namespace Microsoft::WRL;
33bool QueryInterfaceImpl(IUnknown *from, REFIID riid,
void **ppvObject)
35 if (riid == __uuidof(T)) {
36 *ppvObject =
static_cast<T *>(from);
43QNetworkInterface getInterfaceFromHostAddress(
const QHostAddress &local)
45 QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces();
46 auto it =
std::find_if(
47 interfaces.cbegin(), interfaces.cend(), [&local](
const QNetworkInterface &iface) {
48 const auto &entries = iface.addressEntries();
49 return std::any_of(entries.cbegin(), entries.cend(),
50 [&local](
const QNetworkAddressEntry &entry) {
51 return entry.ip().isEqual(local,
52 QHostAddress::TolerantConversion);
55 if (it == interfaces.cend()) {
56 qCDebug(lcNetMon,
"Could not find the interface for the local address.");
94 ComPtr<INetworkConnection> getNetworkConnectionFromAdapterGuid(QUuid guid);
96 QUuid currentConnectionId{};
98 ComPtr<INetworkListManager> networkListManager;
99 ComPtr<IConnectionPoint> connectionPoint;
103 QAtomicInteger<ULONG> ref = 0;
109 Q_DECLARE_PUBLIC(QNetworkConnectionMonitor);
116 bool setTargets(
const QHostAddress &local,
const QHostAddress &remote);
124 QComHelper comHelper;
126 ComPtr<QNetworkConnectionEvents> connectionEvents;
135 bool sameSubnet =
false;
136 bool isLinkLocal =
false;
137 bool monitoring =
false;
138 bool remoteIsIPv6 =
false;
144 auto hr = CoCreateInstance(CLSID_NetworkListManager,
nullptr, CLSCTX_INPROC_SERVER,
145 IID_INetworkListManager, &networkListManager);
147 qCDebug(lcNetMon) <<
"Could not get a NetworkListManager instance:"
148 << QSystemError::windowsComString(hr);
152 ComPtr<IConnectionPointContainer> connectionPointContainer;
153 hr = networkListManager.As(&connectionPointContainer);
155 hr = connectionPointContainer->FindConnectionPoint(IID_INetworkConnectionEvents,
159 qCDebug(lcNetMon) <<
"Failed to get connection point for network events:"
160 << QSystemError::windowsComString(hr);
171 if (!networkListManager) {
172 qCDebug(lcNetMon) <<
"Failed to enumerate network connections:"
173 <<
"NetworkListManager was not instantiated";
177 ComPtr<IEnumNetworkConnections> connections;
178 auto hr = networkListManager->GetNetworkConnections(connections.GetAddressOf());
180 qCDebug(lcNetMon) <<
"Failed to enumerate network connections:"
181 << QSystemError::windowsComString(hr);
184 ComPtr<INetworkConnection> connection =
nullptr;
186 hr = connections->Next(1, connection.GetAddressOf(),
nullptr);
188 qCDebug(lcNetMon) <<
"Failed to get next network connection in enumeration:"
189 << QSystemError::windowsComString(hr);
194 hr = connection->GetAdapterId(&adapterId);
196 qCDebug(lcNetMon) <<
"Failed to get adapter ID from network connection:"
197 << QSystemError::windowsComString(hr);
200 if (guid == adapterId)
203 }
while (connection);
207HRESULT STDMETHODCALLTYPE QNetworkConnectionEvents::QueryInterface(REFIID riid,
void **ppvObject)
212 return QueryInterfaceImpl<IUnknown>(
this, riid, ppvObject)
213 || QueryInterfaceImpl<INetworkConnectionEvents>(
this, riid, ppvObject)
218HRESULT STDMETHODCALLTYPE QNetworkConnectionEvents::NetworkConnectionConnectivityChanged(
219 GUID connectionId, NLM_CONNECTIVITY newConnectivity)
223 QMetaObject::invokeMethod(monitor->q_ptr,
224 [
this, connectionId, newConnectivity, monitor =
this->monitor]() {
225 if (connectionId == currentConnectionId)
226 monitor->setConnectivity(newConnectivity);
228 Qt::QueuedConnection);
232HRESULT STDMETHODCALLTYPE QNetworkConnectionEvents::NetworkConnectionPropertyChanged(
233 GUID connectionId, NLM_CONNECTION_PROPERTY_CHANGE flags)
235 Q_UNUSED(connectionId);
243 currentConnectionId = QUuid{};
246 if (ConvertInterfaceIndexToLuid(iface.index(), &luid) != NO_ERROR) {
247 qCDebug(lcNetMon,
"Could not get the LUID for the interface.");
251 if (ConvertInterfaceLuidToGuid(&luid, &guid) != NO_ERROR) {
252 qCDebug(lcNetMon,
"Could not get the GUID for the interface.");
255 ComPtr<INetworkConnection> connection = getNetworkConnectionFromAdapterGuid(guid);
257 qCDebug(lcNetMon,
"Could not get the INetworkConnection instance for the adapter GUID.");
260 auto hr = connection->GetConnectionId(&guid);
262 qCDebug(lcNetMon) <<
"Failed to get the connection's GUID:"
263 << QSystemError::windowsComString(hr);
266 currentConnectionId = guid;
273 if (currentConnectionId.isNull()) {
274 qCDebug(lcNetMon,
"Can not start monitoring, set targets first");
277 if (!connectionPoint) {
279 "We don't have the connection point, cannot start listening to events!");
283 auto hr = connectionPoint->Advise(
this, &cookie);
285 qCDebug(lcNetMon) <<
"Failed to subscribe to network connectivity events:"
286 << QSystemError::windowsComString(hr);
294 auto hr = connectionPoint->Unadvise(cookie);
296 qCDebug(lcNetMon) <<
"Failed to unsubscribe from network connection events:"
297 << QSystemError::windowsComString(hr);
301 currentConnectionId = QUuid{};
307 if (!comHelper.isValid())
310 connectionEvents =
new QNetworkConnectionEvents(
this);
315 if (!comHelper.isValid())
319 connectionEvents.Reset();
323 const QHostAddress &remote)
325 if (!comHelper.isValid())
328 QNetworkInterface iface = getInterfaceFromHostAddress(local);
329 if (!iface.isValid())
331 const auto &addressEntries = iface.addressEntries();
332 auto it =
std::find_if(
333 addressEntries.cbegin(), addressEntries.cend(),
334 [&local](
const QNetworkAddressEntry &entry) {
return entry.ip() == local; });
335 if (Q_UNLIKELY(it == addressEntries.cend())) {
336 qCDebug(lcNetMon,
"The address entry we were working with disappeared");
339 sameSubnet = remote.isInSubnet(local, it->prefixLength());
340 isLinkLocal = remote.isLinkLocal() && local.isLinkLocal();
341 remoteIsIPv6 = remote.protocol() == QAbstractSocket::IPv6Protocol;
343 return connectionEvents->setTarget(iface);
348 Q_Q(QNetworkConnectionMonitor);
349 const bool reachable = q->isReachable();
350 connectivity = newConnectivity;
351 const bool newReachable = q->isReachable();
352 if (reachable != newReachable)
353 emit q->reachabilityChanged(newReachable);
358 Q_ASSERT(connectionEvents);
359 Q_ASSERT(!monitoring);
360 if (connectionEvents->startMonitoring())
367 Q_ASSERT(connectionEvents);
368 Q_ASSERT(monitoring);
369 if (connectionEvents->stopMonitoring())
373QNetworkConnectionMonitor::QNetworkConnectionMonitor()
374 : QObject(*
new QNetworkConnectionMonitorPrivate)
378QNetworkConnectionMonitor::QNetworkConnectionMonitor(
const QHostAddress &local,
379 const QHostAddress &remote)
380 : QObject(*
new QNetworkConnectionMonitorPrivate)
382 setTargets(local, remote);
385QNetworkConnectionMonitor::~QNetworkConnectionMonitor() =
default;
387bool QNetworkConnectionMonitor::setTargets(
const QHostAddress &local,
const QHostAddress &remote)
389 if (isMonitoring()) {
390 qCDebug(lcNetMon,
"Monitor is already active, call stopMonitoring() first");
393 if (local.isNull()) {
394 qCDebug(lcNetMon,
"Invalid (null) local address, cannot create a reachability target");
398 if (remote.isLoopback())
401 return d_func()->setTargets(local, remote);
404bool QNetworkConnectionMonitor::startMonitoring()
406 Q_D(QNetworkConnectionMonitor);
407 if (isMonitoring()) {
408 qCDebug(lcNetMon,
"Monitor is already active, call stopMonitoring() first");
411 return d->startMonitoring();
414bool QNetworkConnectionMonitor::isMonitoring()
const
416 return d_func()->monitoring;
419void QNetworkConnectionMonitor::stopMonitoring()
421 Q_D(QNetworkConnectionMonitor);
422 if (!isMonitoring()) {
423 qCDebug(lcNetMon,
"stopMonitoring was called when not monitoring!");
429bool QNetworkConnectionMonitor::isReachable()
431 Q_D(QNetworkConnectionMonitor);
433 const NLM_CONNECTIVITY RequiredSameSubnetIPv6 =
434 NLM_CONNECTIVITY(NLM_CONNECTIVITY_IPV6_SUBNET | NLM_CONNECTIVITY_IPV6_LOCALNETWORK
435 | NLM_CONNECTIVITY_IPV6_INTERNET);
436 const NLM_CONNECTIVITY RequiredSameSubnetIPv4 =
437 NLM_CONNECTIVITY(NLM_CONNECTIVITY_IPV4_SUBNET | NLM_CONNECTIVITY_IPV4_LOCALNETWORK
438 | NLM_CONNECTIVITY_IPV4_INTERNET);
440 NLM_CONNECTIVITY required;
441 if (d->isLinkLocal) {
442 required = NLM_CONNECTIVITY(
443 d->remoteIsIPv6 ? NLM_CONNECTIVITY_IPV6_NOTRAFFIC | RequiredSameSubnetIPv6
444 : NLM_CONNECTIVITY_IPV4_NOTRAFFIC | RequiredSameSubnetIPv4);
445 }
else if (d->sameSubnet) {
447 NLM_CONNECTIVITY(d->remoteIsIPv6 ? RequiredSameSubnetIPv6 : RequiredSameSubnetIPv4);
450 required = NLM_CONNECTIVITY(d->remoteIsIPv6 ? NLM_CONNECTIVITY_IPV6_INTERNET
451 : NLM_CONNECTIVITY_IPV4_INTERNET);
454 return d_func()->connectivity & required;
457bool QNetworkConnectionMonitor::isEnabled()
virtual ~QNetworkConnectionEvents()
bool setTarget(const QNetworkInterface &iface)
QNetworkConnectionEvents(QNetworkConnectionMonitorPrivate *monitor)
bool setTargets(const QHostAddress &local, const QHostAddress &remote)
~QNetworkConnectionMonitorPrivate()
QNetworkConnectionMonitorPrivate()
void setConnectivity(NLM_CONNECTIVITY newConnectivity)
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")