Reachability.m 9.31 KB
Newer Older
1
/*
2
     File: Reachability.m
3
 Abstract: Basic demonstration of how to use the SystemConfiguration Reachablity APIs.
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
  Version: 3.0

 Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple
 Inc. ("Apple") in consideration of your agreement to the following
 terms, and your use, installation, modification or redistribution of
 this Apple software constitutes acceptance of these terms.  If you do
 not agree with these terms, please do not use, install, modify or
 redistribute this Apple software.

 In consideration of your agreement to abide by the following terms, and
 subject to these terms, Apple grants you a personal, non-exclusive
 license, under Apple's copyrights in this original Apple software (the
 "Apple Software"), to use, reproduce, modify and redistribute the Apple
 Software, with or without modifications, in source and/or binary forms;
 provided that if you redistribute the Apple Software in its entirety and
 without modifications, you must retain this notice and the following
 text and disclaimers in all such redistributions of the Apple Software.
 Neither the name, trademarks, service marks or logos of Apple Inc. may
 be used to endorse or promote products derived from the Apple Software
 without specific prior written permission from Apple.  Except as
 expressly stated in this notice, no other rights or licenses, express or
 implied, are granted by Apple herein, including but not limited to any
 patent rights that may be infringed by your derivative works or by other
 works in which the Apple Software may be incorporated.

 The Apple Software is provided by Apple on an "AS IS" basis.  APPLE
 MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
 THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
 FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
 OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.

 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
 MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
 AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
 STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.

 Copyright (C) 2013 Apple Inc. All Rights Reserved.

 */
47 48 49 50

#import <arpa/inet.h>
#import <ifaddrs.h>
#import <netdb.h>
51
#import <sys/socket.h>
52 53 54 55 56

#import <CoreFoundation/CoreFoundation.h>

#import "Reachability.h"

57 58 59 60 61 62

NSString *kReachabilityChangedNotification = @"kNetworkReachabilityChangedNotification";


#pragma mark - Supporting functions

Felix Paul Kühne's avatar
Felix Paul Kühne committed
63
#ifndef NDEBUG
64
#define kShouldPrintReachabilityFlags 1
Felix Paul Kühne's avatar
Felix Paul Kühne committed
65 66 67
#else
#define kShouldPrintReachabilityFlags 0
#endif
68

69
static void PrintReachabilityFlags(SCNetworkReachabilityFlags flags, const char* comment)
70 71 72
{
#if kShouldPrintReachabilityFlags

73 74 75 76 77 78 79 80 81 82 83 84 85
    NSLog(@"Reachability Flag Status: %c%c %c%c%c%c%c%c%c %s\n",
          (flags & kSCNetworkReachabilityFlagsIsWWAN)				? 'W' : '-',
          (flags & kSCNetworkReachabilityFlagsReachable)            ? 'R' : '-',

          (flags & kSCNetworkReachabilityFlagsTransientConnection)  ? 't' : '-',
          (flags & kSCNetworkReachabilityFlagsConnectionRequired)   ? 'c' : '-',
          (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic)  ? 'C' : '-',
          (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-',
          (flags & kSCNetworkReachabilityFlagsConnectionOnDemand)   ? 'D' : '-',
          (flags & kSCNetworkReachabilityFlagsIsLocalAddress)       ? 'l' : '-',
          (flags & kSCNetworkReachabilityFlagsIsDirect)             ? 'd' : '-',
          comment
          );
86 87 88 89 90 91
#endif
}


static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info)
{
92 93 94
#pragma unused (target, flags)
	NSCAssert(info != NULL, @"info was NULL in ReachabilityCallback");
	NSCAssert([(__bridge NSObject*) info isKindOfClass: [Reachability class]], @"info was wrong class in ReachabilityCallback");
95

96 97 98
    Reachability* noteObject = (__bridge Reachability *)info;
    // Post a notification to notify the client that the network reachability changed.
    [[NSNotificationCenter defaultCenter] postNotificationName: kReachabilityChangedNotification object: noteObject];
99 100 101
}


102 103 104
#pragma mark - Reachability implementation

@implementation Reachability
105
{
106 107
	BOOL localWiFiRef;
	SCNetworkReachabilityRef reachabilityRef;
108 109
}

110 111

+ (instancetype)reachabilityWithHostName:(NSString *)hostName;
112
{
113
	Reachability* returnValue = NULL;
114
	SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]);
115
	if (reachability != NULL)
116
	{
117
		returnValue= [[Reachability alloc] init];
118
		if (returnValue != NULL)
119
		{
120 121
			returnValue->reachabilityRef = reachability;
			returnValue->localWiFiRef = NO;
122 123
		}
	}
124
	return returnValue;
125 126
}

127 128

+ (instancetype)reachabilityWithAddress:(const struct sockaddr_in *)hostAddress;
129
{
130 131 132 133 134
	SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)hostAddress);

	Reachability* returnValue = NULL;

	if (reachability != NULL)
135
	{
136
		returnValue = [[Reachability alloc] init];
137
		if (returnValue != NULL)
138
		{
139 140
			returnValue->reachabilityRef = reachability;
			returnValue->localWiFiRef = NO;
141 142
		}
	}
143
	return returnValue;
144 145
}

146 147 148


+ (instancetype)reachabilityForInternetConnection;
149 150 151 152 153
{
	struct sockaddr_in zeroAddress;
	bzero(&zeroAddress, sizeof(zeroAddress));
	zeroAddress.sin_len = sizeof(zeroAddress);
	zeroAddress.sin_family = AF_INET;
154 155

	return [self reachabilityWithAddress:&zeroAddress];
156 157
}

158 159

+ (instancetype)reachabilityForLocalWiFi;
160 161 162 163 164
{
	struct sockaddr_in localWifiAddress;
	bzero(&localWifiAddress, sizeof(localWifiAddress));
	localWifiAddress.sin_len = sizeof(localWifiAddress);
	localWifiAddress.sin_family = AF_INET;
165 166

	// IN_LINKLOCALNETNUM is defined in <netinet/in.h> as 169.254.0.0.
167
	localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM);
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210

	Reachability* returnValue = [self reachabilityWithAddress: &localWifiAddress];
	if (returnValue != NULL)
	{
		returnValue->localWiFiRef = YES;
	}

	return returnValue;
}


#pragma mark - Start and stop notifier

- (BOOL)startNotifier
{
	BOOL returnValue = NO;
	SCNetworkReachabilityContext context = {0, (__bridge void *)(self), NULL, NULL, NULL};

	if (SCNetworkReachabilitySetCallback(reachabilityRef, ReachabilityCallback, &context))
	{
		if (SCNetworkReachabilityScheduleWithRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode))
		{
			returnValue = YES;
		}
	}

	return returnValue;
}


- (void)stopNotifier
{
	if (reachabilityRef != NULL)
	{
		SCNetworkReachabilityUnscheduleFromRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
	}
}


- (void)dealloc
{
	[self stopNotifier];
	if (reachabilityRef != NULL)
211
	{
212
		CFRelease(reachabilityRef);
213 214 215 216
	}
}


217 218 219
#pragma mark - Network Flag Handling

- (NetworkStatus)localWiFiStatusForFlags:(SCNetworkReachabilityFlags)flags
220 221
{
	PrintReachabilityFlags(flags, "localWiFiStatusForFlags");
222
	BOOL returnValue = NotReachable;
223

224
	if ((flags & kSCNetworkReachabilityFlagsReachable) && (flags & kSCNetworkReachabilityFlagsIsDirect))
225
	{
226
		returnValue = ReachableViaWiFi;
227
	}
228 229

	return returnValue;
230 231
}

232 233

- (NetworkStatus)networkStatusForFlags:(SCNetworkReachabilityFlags)flags
234 235 236 237
{
	PrintReachabilityFlags(flags, "networkStatusForFlags");
	if ((flags & kSCNetworkReachabilityFlagsReachable) == 0)
	{
238
		// The target host is not reachable.
239 240 241
		return NotReachable;
	}

242
	BOOL returnValue = NotReachable;
243 244 245

	if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0)
	{
246 247 248 249
		/*
         If the target host is reachable and no connection is required then we'll assume (for now) that you're on Wi-Fi...
         */
		returnValue = ReachableViaWiFi;
250 251 252
	}

	if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) ||
253
        (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0))
254
	{
255 256 257 258 259 260 261 262 263 264 265 266
        /*
         ... and the connection is on-demand (or on-traffic) if the calling application is using the CFSocketStream or higher APIs...
         */

        if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0)
        {
            /*
             ... and no [user] intervention is needed...
             */
            returnValue = ReachableViaWiFi;
        }
    }
267 268 269

	if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN)
	{
270 271 272 273
		/*
         ... but WWAN connections are OK if the calling application is using the CFNetwork APIs.
         */
		returnValue = ReachableViaWWAN;
274
	}
275 276

	return returnValue;
277 278
}

279 280

- (BOOL)connectionRequired
281 282 283
{
	NSAssert(reachabilityRef != NULL, @"connectionRequired called with NULL reachabilityRef");
	SCNetworkReachabilityFlags flags;
284

285 286 287 288
	if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
	{
		return (flags & kSCNetworkReachabilityFlagsConnectionRequired);
	}
289 290

    return NO;
291 292
}

293 294

- (NetworkStatus)currentReachabilityStatus
295 296
{
	NSAssert(reachabilityRef != NULL, @"currentNetworkStatus called with NULL reachabilityRef");
297
	NetworkStatus returnValue = NotReachable;
298
	SCNetworkReachabilityFlags flags;
299

300 301
	if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
	{
302
		if (localWiFiRef)
303
		{
304
			returnValue = [self localWiFiStatusForFlags:flags];
305 306 307
		}
		else
		{
308
			returnValue = [self networkStatusForFlags:flags];
309 310
		}
	}
311 312

	return returnValue;
313
}
314 315


316
@end