OpenVPN 3 Core Library
Loading...
Searching...
No Matches
reachable.hpp
Go to the documentation of this file.
1// OpenVPN -- An application to securely tunnel IP networks
2// over a single port, with support for SSL/TLS-based
3// session authentication and key exchange,
4// packet encryption, packet authentication, and
5// packet compression.
6//
7// Copyright (C) 2012- OpenVPN Inc.
8//
9// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
10//
11//
12// This code is derived from the Apple sample Reachability.m under
13// the following license.
14//
15// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
16// Inc. ("Apple") in consideration of your agreement to the following
17// terms, and your use, installation, modification or redistribution of
18// this Apple software constitutes acceptance of these terms. If you do
19// not agree with these terms, please do not use, install, modify or
20// redistribute this Apple software.
21//
22// In consideration of your agreement to abide by the following terms, and
23// subject to these terms, Apple grants you a personal, non-exclusive
24// license, under Apple's copyrights in this original Apple software (the
25// "Apple Software"), to use, reproduce, modify and redistribute the Apple
26// Software, with or without modifications, in source and/or binary forms;
27// provided that if you redistribute the Apple Software in its entirety and
28// without modifications, you must retain this notice and the following
29// text and disclaimers in all such redistributions of the Apple Software.
30// Neither the name, trademarks, service marks or logos of Apple Inc. may
31// be used to endorse or promote products derived from the Apple Software
32// without specific prior written permission from Apple. Except as
33// expressly stated in this notice, no other rights or licenses, express or
34// implied, are granted by Apple herein, including but not limited to any
35// patent rights that may be infringed by your derivative works or by other
36// works in which the Apple Software may be incorporated.
37//
38// The Apple Software is provided by Apple on an "AS IS" basis. APPLE
39// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
40// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
41// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
42// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
43//
44// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
45// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
46// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
47// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
48// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
49// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
50// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
51// POSSIBILITY OF SUCH DAMAGE.
52//
53// Copyright (C) 2013 Apple Inc. All Rights Reserved.
54
55// Wrapper for Apple SCNetworkReachability methods.
56
57#ifndef OPENVPN_APPLECRYPTO_UTIL_REACHABLE_H
58#define OPENVPN_APPLECRYPTO_UTIL_REACHABLE_H
59
60#import "TargetConditionals.h"
61
62#include <netinet/in.h>
63#include <SystemConfiguration/SCNetworkReachability.h>
64
65#include <string>
66#include <sstream>
67#include <memory>
68
72
73namespace openvpn {
74#if defined(__APPLE__)
75#pragma clang diagnostic push
76#pragma clang diagnostic ignored "-Wdeprecated-declarations"
77#endif
78namespace CF {
79OPENVPN_CF_WRAP(NetworkReachability, network_reachability_cast, SCNetworkReachabilityRef, SCNetworkReachabilityGetTypeID);
80}
81
82#if defined(__APPLE__)
83#pragma clang diagnostic pop
84#endif
85
87{
88 public:
90
91 enum Type
92 {
95 };
96
97 std::string to_string() const
98 {
99 return to_string(flags());
100 }
101
102 std::string to_string(const SCNetworkReachabilityFlags f) const
103 {
104 const Status s = vstatus(f);
105 const Type t = vtype();
106
107 std::string ret;
108 ret += render_type(t);
109 ret += ':';
110 ret += render_status(s);
111 ret += '/';
112 ret += render_flags(f);
113 return ret;
114 }
115
117 {
118 return vstatus(flags());
119 }
120
121#if defined(__APPLE__)
122#pragma clang diagnostic push
123#pragma clang diagnostic ignored "-Wdeprecated-declarations"
124#endif
125 SCNetworkReachabilityFlags flags() const
126 {
127 SCNetworkReachabilityFlags f = 0;
128 if (SCNetworkReachabilityGetFlags(reach(), &f) == TRUE)
129 return f;
130 else
131 return 0;
132 }
133#if defined(__APPLE__)
134#pragma clang diagnostic pop
135#endif
136
137 static std::string render_type(Type type)
138 {
139 switch (type)
140 {
141 case Internet:
142 return "Internet";
143 case WiFi:
144 return "WiFi";
145 default:
146 return "Type???";
147 }
148 }
149
150 static std::string render_status(const Status status)
151 {
152 switch (status)
153 {
155 return "NotReachable";
157 return "ReachableViaWiFi";
159 return "ReachableViaWWAN";
160 default:
161 return "ReachableVia???";
162 }
163 }
164
165 static std::string render_flags(const SCNetworkReachabilityFlags flags)
166 {
167 std::string ret;
168#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR // Mac OS X doesn't define WWAN flags
169 if (flags & kSCNetworkReachabilityFlagsIsWWAN)
170 ret += 'W';
171 else
172#endif
173 ret += '-';
174 if (flags & kSCNetworkReachabilityFlagsReachable)
175 ret += 'R';
176 else
177 ret += '-';
178 ret += ' ';
179 if (flags & kSCNetworkReachabilityFlagsTransientConnection)
180 ret += 't';
181 else
182 ret += '-';
183 if (flags & kSCNetworkReachabilityFlagsConnectionRequired)
184 ret += 'c';
185 else
186 ret += '-';
187 if (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic)
188 ret += 'C';
189 else
190 ret += '-';
191 if (flags & kSCNetworkReachabilityFlagsInterventionRequired)
192 ret += 'i';
193 else
194 ret += '-';
195 if (flags & kSCNetworkReachabilityFlagsConnectionOnDemand)
196 ret += 'D';
197 else
198 ret += '-';
199 if (flags & kSCNetworkReachabilityFlagsIsLocalAddress)
200 ret += 'l';
201 else
202 ret += '-';
203 if (flags & kSCNetworkReachabilityFlagsIsDirect)
204 ret += 'd';
205 else
206 ret += '-';
207 return ret;
208 }
209
210 virtual Type vtype() const = 0;
211 virtual Status vstatus(const SCNetworkReachabilityFlags flags) const = 0;
212
213 virtual ~ReachabilityBase() = default;
214
215 CF::NetworkReachability reach;
216};
217
219{
220 public:
221#if defined(__APPLE__)
222#pragma clang diagnostic push
223#pragma clang diagnostic ignored "-Wdeprecated-declarations"
224#endif
226 {
227 struct sockaddr_in addr;
228 bzero(&addr, sizeof(addr));
229 addr.sin_len = sizeof(addr);
230 addr.sin_family = AF_INET;
231 reach.reset(SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (struct sockaddr *)&addr));
232 }
233#if defined(__APPLE__)
234#pragma clang diagnostic pop
235#endif
236
237 Type vtype() const override
238 {
239 return Internet;
240 }
241
242 Status vstatus(const SCNetworkReachabilityFlags flags) const override
243 {
244 return status_from_flags(flags);
245 }
246
247 static Status status_from_flags(const SCNetworkReachabilityFlags flags)
248 {
249 if ((flags & kSCNetworkReachabilityFlagsReachable) == 0)
250 {
251 // The target host is not reachable.
253 }
254
256
257 if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0)
258 {
259 // If the target host is reachable and no connection is required then
260 // we'll assume (for now) that you're on Wi-Fi...
262 }
263
264#if 0 // don't contaminate result by considering on-demand viability
265 if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) ||
266 (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0))
267 {
268 // ... and the connection is on-demand (or on-traffic) if the
269 // calling application is using the CFSocketStream or higher APIs...
270
271 if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0)
272 {
273 // ... and no [user] intervention is needed...
275 }
276 }
277#endif
278
279#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR // Mac OS X doesn't define WWAN flags
280 if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN)
281 {
282 // ... but WWAN connections are OK if the calling application
283 // is using the CFNetwork APIs.
285 }
286#endif
287
288 return ret;
289 }
290};
291
293{
294 public:
296 {
297 struct sockaddr_in addr;
298 bzero(&addr, sizeof(addr));
299 addr.sin_len = sizeof(addr);
300 addr.sin_family = AF_INET;
301 addr.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM); // 169.254.0.0.
302#if defined(__APPLE__)
303#pragma clang diagnostic push
304#pragma clang diagnostic ignored "-Wdeprecated-declarations"
305#endif
306 reach.reset(SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (struct sockaddr *)&addr));
307#if defined(__APPLE__)
308#pragma clang diagnostic pop
309#endif
310 }
311
312 Type vtype() const override
313 {
314 return WiFi;
315 }
316
317 Status vstatus(const SCNetworkReachabilityFlags flags) const override
318 {
319 return status_from_flags(flags);
320 }
321
322 static Status status_from_flags(const SCNetworkReachabilityFlags flags)
323 {
325 if ((flags & kSCNetworkReachabilityFlagsReachable) && (flags & kSCNetworkReachabilityFlagsIsDirect))
327 return ret;
328 }
329};
330
332{
333 public:
334 Reachability(const bool enable_internet, const bool enable_wifi)
335 {
336 if (enable_internet)
338 if (enable_wifi)
339 wifi.reset(new ReachabilityViaWiFi);
340 }
341
342 bool reachableViaWiFi() const
343 {
344 if (internet)
345 {
346 if (wifi)
347 return internet->status() == ReachableViaWiFi && wifi->status() == ReachableViaWiFi;
348 else
349 return internet->status() == ReachableViaWiFi;
350 }
351 else
352 {
353 if (wifi)
354 return wifi->status() == ReachableViaWiFi;
355 else
356 return false;
357 }
358 }
359
361 {
362 if (internet)
363 return internet->status() == ReachableViaWWAN;
364 else
365 return false;
366 }
367
368 Status reachable() const override
369 {
370 if (reachableViaWiFi())
371 return ReachableViaWiFi;
372 else if (reachableViaCellular())
373 return ReachableViaWWAN;
374 else
375 return NotReachable;
376 }
377
378 bool reachableVia(const std::string &net_type) const override
379 {
380 if (net_type == "cellular")
381 return reachableViaCellular();
382 else if (net_type == "wifi")
383 return reachableViaWiFi();
384 else
386 }
387
388 std::string to_string() const override
389 {
390 std::string ret;
391 if (internet)
392 ret += internet->to_string();
393 if (internet && wifi)
394 ret += ' ';
395 if (wifi)
396 ret += wifi->to_string();
397 return ret;
398 }
399
400 std::unique_ptr<ReachabilityViaInternet> internet;
401 std::unique_ptr<ReachabilityViaWiFi> wifi;
402};
403
405{
406 public:
407 ReachabilityTracker(const bool enable_internet, const bool enable_wifi)
408 : reachability(enable_internet, enable_wifi),
409 scheduled(false)
410 {
411 }
412
424
426 {
427 if (scheduled)
428 {
431 if (reachability.wifi)
433 scheduled = false;
434 }
435 }
436
437 virtual void reachability_tracker_event(const ReachabilityBase &rb, SCNetworkReachabilityFlags flags) = 0;
438
443
444 private:
445 bool schedule(ReachabilityBase &rb, SCNetworkReachabilityCallBack cb)
446 {
447 SCNetworkReachabilityContext context = {0, this, nullptr, nullptr, nullptr};
448 if (rb.reach.defined())
449 {
450#if defined(__APPLE__)
451#pragma clang diagnostic push
452#pragma clang diagnostic ignored "-Wdeprecated-declarations"
453#endif
454 if (SCNetworkReachabilitySetCallback(rb.reach(),
455 cb,
456 &context)
457 == FALSE)
458 return false;
459 if (SCNetworkReachabilityScheduleWithRunLoop(rb.reach(),
460 CFRunLoopGetCurrent(),
461 kCFRunLoopCommonModes)
462 == FALSE)
463 return false;
464 return true;
465#if defined(__APPLE__)
466#pragma clang diagnostic pop
467#endif
468 }
469 else
470 return false;
471 }
472
473#if defined(__APPLE__)
474#pragma clang diagnostic push
475#pragma clang diagnostic ignored "-Wdeprecated-declarations"
476#endif
478 {
479 if (rb.reach.defined())
480 SCNetworkReachabilityUnscheduleFromRunLoop(rb.reach(), CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
481 }
482#if defined(__APPLE__)
483#pragma clang diagnostic pop
484#endif
485
486 static void internet_callback_static(SCNetworkReachabilityRef target,
487 SCNetworkReachabilityFlags flags,
488 void *info)
489 {
492 }
493
494 static void wifi_callback_static(SCNetworkReachabilityRef target,
495 SCNetworkReachabilityFlags flags,
496 void *info)
497 {
500 }
501
504};
505} // namespace openvpn
506
507#endif
#define OPENVPN_CF_WRAP(cls, castmeth, cftype, idmeth)
Definition cf.hpp:26
virtual Status vstatus(const SCNetworkReachabilityFlags flags) const =0
static std::string render_type(Type type)
std::string to_string() const
Definition reachable.hpp:97
SCNetworkReachabilityFlags flags() const
static std::string render_flags(const SCNetworkReachabilityFlags flags)
CF::NetworkReachability reach
std::string to_string(const SCNetworkReachabilityFlags f) const
virtual ~ReachabilityBase()=default
virtual Type vtype() const =0
ReachabilityInterface::Status Status
Definition reachable.hpp:89
static std::string render_status(const Status status)
bool schedule(ReachabilityBase &rb, SCNetworkReachabilityCallBack cb)
void cancel(ReachabilityBase &rb)
ReachabilityTracker(const bool enable_internet, const bool enable_wifi)
virtual void reachability_tracker_event(const ReachabilityBase &rb, SCNetworkReachabilityFlags flags)=0
static void internet_callback_static(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info)
static void wifi_callback_static(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info)
static Status status_from_flags(const SCNetworkReachabilityFlags flags)
Status vstatus(const SCNetworkReachabilityFlags flags) const override
Type vtype() const override
Type vtype() const override
Status vstatus(const SCNetworkReachabilityFlags flags) const override
static Status status_from_flags(const SCNetworkReachabilityFlags flags)
bool reachableViaCellular() const
std::unique_ptr< ReachabilityViaWiFi > wifi
std::unique_ptr< ReachabilityViaInternet > internet
std::string to_string() const override
bool reachableViaWiFi() const
Status reachable() const override
Reachability(const bool enable_internet, const bool enable_wifi)
bool reachableVia(const std::string &net_type) const override
reroute_gw flags
std::string ret
auto f(const Thing1 t)