FreeRDP
Reachability.m
1 /*
2 
3  File: Reachability.m
4  Abstract: Basic demonstration of how to use the SystemConfiguration Reachablity APIs.
5 
6  Version: 2.2
7 
8  Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc.
9  ("Apple") in consideration of your agreement to the following terms, and your
10  use, installation, modification or redistribution of this Apple software
11  constitutes acceptance of these terms. If you do not agree with these terms,
12  please do not use, install, modify or redistribute this Apple software.
13 
14  In consideration of your agreement to abide by the following terms, and subject
15  to these terms, Apple grants you a personal, non-exclusive license, under
16  Apple's copyrights in this original Apple software (the "Apple Software"), to
17  use, reproduce, modify and redistribute the Apple Software, with or without
18  modifications, in source and/or binary forms; provided that if you redistribute
19  the Apple Software in its entirety and without modifications, you must retain
20  this notice and the following text and disclaimers in all such redistributions
21  of the Apple Software.
22  Neither the name, trademarks, service marks or logos of Apple Inc. may be used
23  to endorse or promote products derived from the Apple Software without specific
24  prior written permission from Apple. Except as expressly stated in this notice,
25  no other rights or licenses, express or implied, are granted by Apple herein,
26  including but not limited to any patent rights that may be infringed by your
27  derivative works or by other works in which the Apple Software may be
28  incorporated.
29 
30  The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
31  WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
32  WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
33  PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
34  COMBINATION WITH YOUR PRODUCTS.
35 
36  IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
37  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
38  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39  ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR
40  DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF
41  CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF
42  APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43 
44  Copyright (C) 2010 Apple Inc. All Rights Reserved.
45 
46 */
47 
48 #import <sys/socket.h>
49 #import <netinet/in.h>
50 #import <netinet6/in6.h>
51 #import <arpa/inet.h>
52 #import <ifaddrs.h>
53 #import <netdb.h>
54 
55 #import <CoreFoundation/CoreFoundation.h>
56 
57 #import "Reachability.h"
58 
59 #define kShouldPrintReachabilityFlags 1
60 
61 static void PrintReachabilityFlags(SCNetworkReachabilityFlags flags, const char *comment)
62 {
63 #if kShouldPrintReachabilityFlags
64 
65  NSLog(@"Reachability Flag Status: %c%c %c%c%c%c%c%c%c %s\n",
66  (flags & kSCNetworkReachabilityFlagsIsWWAN) ? 'W' : '-',
67  (flags & kSCNetworkReachabilityFlagsReachable) ? 'R' : '-',
68 
69  (flags & kSCNetworkReachabilityFlagsTransientConnection) ? 't' : '-',
70  (flags & kSCNetworkReachabilityFlagsConnectionRequired) ? 'c' : '-',
71  (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) ? 'C' : '-',
72  (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-',
73  (flags & kSCNetworkReachabilityFlagsConnectionOnDemand) ? 'D' : '-',
74  (flags & kSCNetworkReachabilityFlagsIsLocalAddress) ? 'l' : '-',
75  (flags & kSCNetworkReachabilityFlagsIsDirect) ? 'd' : '-', comment);
76 #endif
77 }
78 
79 @implementation Reachability
80 static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags,
81  void *info)
82 {
83 #pragma unused(target, flags)
84  NSCAssert(info != NULL, @"info was NULL in ReachabilityCallback");
85  NSCAssert([(NSObject *)info isKindOfClass:[Reachability class]],
86  @"info was wrong class in ReachabilityCallback");
87 
88  // We're on the main RunLoop, so an NSAutoreleasePool is not necessary, but is added defensively
89  // in case someon uses the Reachablity object in a different thread.
90  NSAutoreleasePool *myPool = [[NSAutoreleasePool alloc] init];
91 
92  Reachability *noteObject = (Reachability *)info;
93  // Post a notification to notify the client that the network reachability changed.
94  [[NSNotificationCenter defaultCenter] postNotificationName:kReachabilityChangedNotification
95  object:noteObject];
96 
97  [myPool release];
98 }
99 
100 - (BOOL)startNotifier
101 {
102  BOOL retVal = NO;
103  SCNetworkReachabilityContext context = { 0, self, NULL, NULL, NULL };
104  if (SCNetworkReachabilitySetCallback(reachabilityRef, ReachabilityCallback, &context))
105  {
106  if (SCNetworkReachabilityScheduleWithRunLoop(reachabilityRef, CFRunLoopGetCurrent(),
107  kCFRunLoopDefaultMode))
108  {
109  retVal = YES;
110  }
111  }
112  return retVal;
113 }
114 
115 - (void)stopNotifier
116 {
117  if (reachabilityRef != NULL)
118  {
119  SCNetworkReachabilityUnscheduleFromRunLoop(reachabilityRef, CFRunLoopGetCurrent(),
120  kCFRunLoopDefaultMode);
121  }
122 }
123 
124 - (void)dealloc
125 {
126  [self stopNotifier];
127  if (reachabilityRef != NULL)
128  {
129  CFRelease(reachabilityRef);
130  }
131  [super dealloc];
132 }
133 
134 + (Reachability *)reachabilityWithHostName:(NSString *)hostName;
135 {
136  Reachability *retVal = NULL;
137  SCNetworkReachabilityRef reachability =
138  SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]);
139  if (reachability != NULL)
140  {
141  retVal = [[[self alloc] init] autorelease];
142  if (retVal != NULL)
143  {
144  retVal->reachabilityRef = reachability;
145  retVal->localWiFiRef = NO;
146  }
147  }
148  return retVal;
149 }
150 
151 + (Reachability *)reachabilityWithAddress:(const struct sockaddr_in *)hostAddress;
152 {
153  SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(
154  kCFAllocatorDefault, (const struct sockaddr *)hostAddress);
155  Reachability *retVal = NULL;
156  if (reachability != NULL)
157  {
158  retVal = [[[self alloc] init] autorelease];
159  if (retVal != NULL)
160  {
161  retVal->reachabilityRef = reachability;
162  retVal->localWiFiRef = NO;
163  }
164  }
165  return retVal;
166 }
167 
168 + (Reachability *)reachabilityForInternetConnection;
169 {
170  struct sockaddr_in zeroAddress;
171  bzero(&zeroAddress, sizeof(zeroAddress));
172  zeroAddress.sin_len = sizeof(zeroAddress);
173  zeroAddress.sin_family = AF_INET;
174  return [self reachabilityWithAddress:&zeroAddress];
175 }
176 
177 + (Reachability *)reachabilityForLocalWiFi;
178 {
179  struct sockaddr_in localWifiAddress;
180  bzero(&localWifiAddress, sizeof(localWifiAddress));
181  localWifiAddress.sin_len = sizeof(localWifiAddress);
182  localWifiAddress.sin_family = AF_INET;
183  // IN_LINKLOCALNETNUM is defined in <netinet/in.h> as 169.254.0.0
184  localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM);
185  Reachability *retVal = [self reachabilityWithAddress:&localWifiAddress];
186  if (retVal != NULL)
187  {
188  retVal->localWiFiRef = YES;
189  }
190  return retVal;
191 }
192 
193 #pragma mark Network Flag Handling
194 
195 - (NetworkStatus)localWiFiStatusForFlags:(SCNetworkReachabilityFlags)flags
196 {
197  PrintReachabilityFlags(flags, "localWiFiStatusForFlags");
198 
199  BOOL retVal = NotReachable;
200  if ((flags & kSCNetworkReachabilityFlagsReachable) &&
201  (flags & kSCNetworkReachabilityFlagsIsDirect))
202  {
203  retVal = ReachableViaWiFi;
204  }
205  return retVal;
206 }
207 
208 - (NetworkStatus)networkStatusForFlags:(SCNetworkReachabilityFlags)flags
209 {
210  PrintReachabilityFlags(flags, "networkStatusForFlags");
211  if ((flags & kSCNetworkReachabilityFlagsReachable) == 0)
212  {
213  // if target host is not reachable
214  return NotReachable;
215  }
216 
217  BOOL retVal = NotReachable;
218 
219  if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0)
220  {
221  // if target host is reachable and no connection is required
222  // then we'll assume (for now) that your on Wi-Fi
223  retVal = ReachableViaWiFi;
224  }
225 
226  if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand) != 0) ||
227  (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0))
228  {
229  // ... and the connection is on-demand (or on-traffic) if the
230  // calling application is using the CFSocketStream or higher APIs
231 
232  if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0)
233  {
234  // ... and no [user] intervention is needed
235  retVal = ReachableViaWiFi;
236  }
237  }
238 
239  if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN)
240  {
241  // ... but WWAN connections are OK if the calling application
242  // is using the CFNetwork (CFSocketStream?) APIs.
243  retVal = ReachableViaWWAN;
244  }
245  return retVal;
246 }
247 
248 - (BOOL)connectionRequired;
249 {
250  NSAssert(reachabilityRef != NULL, @"connectionRequired called with NULL reachabilityRef");
251  SCNetworkReachabilityFlags flags;
252  if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
253  {
254  return (flags & kSCNetworkReachabilityFlagsConnectionRequired);
255  }
256  return NO;
257 }
258 
259 - (NetworkStatus)currentReachabilityStatus
260 {
261  NSAssert(reachabilityRef != NULL, @"currentNetworkStatus called with NULL reachabilityRef");
262  NetworkStatus retVal = NotReachable;
263  SCNetworkReachabilityFlags flags;
264  if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
265  {
266  if (localWiFiRef)
267  {
268  retVal = [self localWiFiStatusForFlags:flags];
269  }
270  else
271  {
272  retVal = [self networkStatusForFlags:flags];
273  }
274  }
275  return retVal;
276 }
277 @end