Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qnetconmonitor_darwin.mm
Go to the documentation of this file.
1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "private/qnativesocketengine_p_p.h"
5#include "private/qnetconmonitor_p.h"
6
7#include "private/qobject_p.h"
8
9#include <SystemConfiguration/SystemConfiguration.h>
10#include <CoreFoundation/CoreFoundation.h>
11
12#include <netinet/in.h>
13
14#include <cstring>
15
17
18Q_LOGGING_CATEGORY(lcNetMon, "qt.network.monitor");
19
20namespace {
21
22class ReachabilityDispatchQueue
23{
24public:
25 ReachabilityDispatchQueue()
26 {
27 queue = dispatch_queue_create("qt-network-reachability-queue", nullptr);
28 if (!queue)
29 qCWarning(lcNetMon, "Failed to create a dispatch queue for reachability probes");
30 }
31
32 ~ReachabilityDispatchQueue()
33 {
34 if (queue)
35 dispatch_release(queue);
36 }
37
38 dispatch_queue_t data() const
39 {
40 return queue;
41 }
42
43private:
44 dispatch_queue_t queue = nullptr;
45
46 Q_DISABLE_COPY_MOVE(ReachabilityDispatchQueue)
47};
48
49dispatch_queue_t qt_reachability_queue()
50{
51 static const ReachabilityDispatchQueue reachabilityQueue;
52 return reachabilityQueue.data();
53}
54
55qt_sockaddr qt_hostaddress_to_sockaddr(const QHostAddress &src)
56{
57 if (src.isNull())
58 return {};
59
61 if (src.protocol() == QAbstractSocket::IPv4Protocol) {
62 dst.a4 = sockaddr_in{};
63 dst.a4.sin_family = AF_INET;
64 dst.a4.sin_addr.s_addr = htonl(src.toIPv4Address());
65 dst.a4.sin_len = sizeof(sockaddr_in);
66 } else if (src.protocol() == QAbstractSocket::IPv6Protocol) {
67 dst.a6 = sockaddr_in6{};
68 dst.a6.sin6_family = AF_INET6;
69 dst.a6.sin6_len = sizeof(sockaddr_in6);
70 const Q_IPV6ADDR ipv6 = src.toIPv6Address();
71 std::memcpy(&dst.a6.sin6_addr, &ipv6, sizeof ipv6);
72 } else {
73 Q_UNREACHABLE();
74 }
75
76 return dst;
77}
78
79} // unnamed namespace
80
82{
83public:
84 SCNetworkReachabilityRef probe = nullptr;
85 SCNetworkReachabilityFlags state = kSCNetworkReachabilityFlagsIsLocalAddress;
86 bool scheduled = false;
87
88 void updateState(SCNetworkReachabilityFlags newState);
89 void reset();
90 bool isReachable() const;
91#ifdef QT_PLATFORM_UIKIT
92 bool isWwan() const;
93#endif
94
95 static void probeCallback(SCNetworkReachabilityRef probe, SCNetworkReachabilityFlags flags, void *info);
96
97 Q_DECLARE_PUBLIC(QNetworkConnectionMonitor)
98};
99
101{
102 // To be executed only on the reachability queue.
104
105 // NETMONTODO: for now, 'online' for us means kSCNetworkReachabilityFlagsReachable
106 // is set. There are more possible flags that require more tests/some special
107 // setup. So in future this part and related can change/be extended.
108 const bool wasReachable = isReachable();
109
110#ifdef QT_PLATFORM_UIKIT
111 const bool hadWwan = isWwan();
112#endif
113
114 state = newState;
115 if (wasReachable != isReachable())
116 emit q->reachabilityChanged(isReachable());
117
118#ifdef QT_PLATFORM_UIKIT
119 if (hadWwan != isWwan())
120 emit q->isWwanChanged(isWwan());
121#endif
122}
123
125{
126 if (probe) {
127 CFRelease(probe);
128 probe = nullptr;
129 }
130
131 state = kSCNetworkReachabilityFlagsIsLocalAddress;
132 scheduled = false;
133}
134
136{
137 return !!(state & kSCNetworkReachabilityFlagsReachable);
138}
139
140#ifdef QT_PLATFORM_UIKIT // The IsWWAN flag is not available on macOS
141bool QNetworkConnectionMonitorPrivate::isWwan() const
142{
143 return !!(state & kSCNetworkReachabilityFlagsIsWWAN);
144}
145#endif
146
147void QNetworkConnectionMonitorPrivate::probeCallback(SCNetworkReachabilityRef probe, SCNetworkReachabilityFlags flags, void *info)
148{
149 // To be executed only on the reachability queue.
151
152 auto monitorPrivate = static_cast<QNetworkConnectionMonitorPrivate *>(info);
153 Q_ASSERT(monitorPrivate);
154 monitorPrivate->updateState(flags);
155}
156
161
167
175
177{
179
180 if (isMonitoring()) {
181 qCWarning(lcNetMon, "Monitor is already active, call stopMonitoring() first");
182 return false;
183 }
184
185 if (local.isNull()) {
186 qCWarning(lcNetMon, "Invalid (null) local address, cannot create a reachability target");
187 return false;
188 }
189
190 // Clear the old target if needed:
191 d->reset();
192
193 qt_sockaddr client = qt_hostaddress_to_sockaddr(local);
194 if (remote.isNull()) {
195 // That's a special case our QNetworkInformation backend is using (AnyIpv4/6 address to check an overall status).
196 d->probe = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, reinterpret_cast<sockaddr *>(&client));
197 } else {
198 qt_sockaddr target = qt_hostaddress_to_sockaddr(remote);
199 d->probe = SCNetworkReachabilityCreateWithAddressPair(kCFAllocatorDefault,
200 reinterpret_cast<sockaddr *>(&client),
201 reinterpret_cast<sockaddr *>(&target));
202 }
203
204 if (d->probe) {
205 // Let's read the initial state so that callback coming later can
206 // see a difference. Ignore errors though.
207 SCNetworkReachabilityGetFlags(d->probe, &d->state);
208 }else {
209 qCWarning(lcNetMon, "Failed to create network reachability probe");
210 return false;
211 }
212
213 return true;
214}
215
217{
219
220 if (isMonitoring()) {
221 qCWarning(lcNetMon, "Monitor is already active, call stopMonitoring() first");
222 return false;
223 }
224
225 if (!d->probe) {
226 qCWarning(lcNetMon, "Can not start monitoring, set targets first");
227 return false;
228 }
229
230 auto queue = qt_reachability_queue();
231 if (!queue) {
232 qWarning(lcNetMon, "Failed to create a dispatch queue to schedule a probe on");
233 return false;
234 }
235
236 SCNetworkReachabilityContext context = {};
237 context.info = d;
238 if (!SCNetworkReachabilitySetCallback(d->probe, QNetworkConnectionMonitorPrivate::probeCallback, &context)) {
239 qWarning(lcNetMon, "Failed to set a reachability callback");
240 return false;
241 }
242
243
244 if (!SCNetworkReachabilitySetDispatchQueue(d->probe, queue)) {
245 qWarning(lcNetMon, "Failed to schedule a reachability callback on a queue");
246 return false;
247 }
248
249 return d->scheduled = true;
250}
251
253{
254 Q_D(const QNetworkConnectionMonitor);
255
256 return d->scheduled;
257}
258
260{
262
263 if (d->scheduled) {
264 Q_ASSERT(d->probe);
265 SCNetworkReachabilitySetDispatchQueue(d->probe, nullptr);
266 SCNetworkReachabilitySetCallback(d->probe, nullptr, nullptr);
267 d->scheduled = false;
268 }
269}
270
272{
274
275 if (isMonitoring()) {
276 qCWarning(lcNetMon, "Calling isReachable() is unsafe after the monitoring started");
277 return false;
278 }
279
280 if (!d->probe) {
281 qCWarning(lcNetMon, "Reachability is unknown, set the target first");
282 return false;
283 }
284
285 return d->isReachable();
286}
287
288#ifdef QT_PLATFORM_UIKIT
289bool QNetworkConnectionMonitor::isWwan() const
290{
291 Q_D(const QNetworkConnectionMonitor);
292
293 if (isMonitoring()) {
294 qCWarning(lcNetMon, "Calling isWwan() is unsafe after the monitoring started");
295 return false;
296 }
297
298 if (!d->probe) {
299 qCWarning(lcNetMon, "Medium is unknown, set the target first");
300 return false;
301 }
302
303 return d->isWwan();
304}
305#endif
306
308{
309 return true;
310}
311
static constexpr auto IPv4Protocol
static constexpr auto IPv6Protocol
The QHostAddress class provides an IP address.
bool isNull() const
Returns true if this host address is not valid for any host or interface.
void updateState(SCNetworkReachabilityFlags newState)
static void probeCallback(SCNetworkReachabilityRef probe, SCNetworkReachabilityFlags flags, void *info)
bool setTargets(const QHostAddress &local, const QHostAddress &remote)
\inmodule QtCore
Definition qobject.h:103
void newState(QList< State > &states, const char *token, const char *lexem, bool pre)
Combined button and popup list for selecting options.
static void * context
QIPv6Address Q_IPV6ADDR
#define qWarning
Definition qlogging.h:166
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
#define AF_INET6
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum src
GLenum GLenum dst
GLenum target
GLbitfield flags
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define emit
#define Q_UNUSED(x)
QQueue< int > queue
[0]
QHostInfo info
[0]