Forráskód Böngészése

Update Reachability code

We want to ensure IPv6 compatibilty, so we're updating the code we use from Apple's current sample code.

Also creating a README with a link to the sample code, for easier finding in the future.
Adam Pritchard 9 éve
szülő
commit
4089f0b199

+ 5 - 0
MobileLibrary/iOS/PsiphonTunnel/PsiphonTunnel/Reachability/README.md

@@ -0,0 +1,5 @@
+# Reachability
+
+This code is from: 
+
+https://developer.apple.com/library/content/samplecode/Reachability/Introduction/Intro.html

+ 40 - 78
MobileLibrary/iOS/PsiphonTunnel/PsiphonTunnel/Reachability/Reachability.h

@@ -1,102 +1,64 @@
 /*
- Copyright (c) 2011, Tony Million.
- All rights reserved.
+ Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ See LICENSE.txt for this sample’s licensing information
  
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
- 
- 1. Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
- 
- 2. Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
- 
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- POSSIBILITY OF SUCH DAMAGE.
+ Abstract:
+ Basic demonstration of how to use the SystemConfiguration Reachablity APIs.
  */
 
 #import <Foundation/Foundation.h>
 #import <SystemConfiguration/SystemConfiguration.h>
+#import <netinet/in.h>
 
-//! Project version number for MacOSReachability.
-FOUNDATION_EXPORT double ReachabilityVersionNumber;
-
-//! Project version string for MacOSReachability.
-FOUNDATION_EXPORT const unsigned char ReachabilityVersionString[];
 
-/**
- * Create NS_ENUM macro if it does not exist on the targeted version of iOS or OS X.
- *
- * @see http://nshipster.com/ns_enum-ns_options/
- **/
-#ifndef NS_ENUM
-#define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type
-#endif
+typedef enum : NSInteger {
+	NotReachable = 0,
+	ReachableViaWiFi,
+	ReachableViaWWAN
+} NetworkStatus;
 
-extern NSString *const kReachabilityChangedNotification;
+#pragma mark IPv6 Support
+//Reachability fully support IPv6.  For full details, see ReadMe.md.
 
-typedef NS_ENUM(NSInteger, NetworkStatus) {
-    // Apple NetworkStatus Compatible Names.
-    NotReachable = 0,
-    ReachableViaWiFi = 2,
-    ReachableViaWWAN = 1
-};
 
-@class Reachability;
-
-typedef void (^NetworkReachable)(Reachability * reachability);
-typedef void (^NetworkUnreachable)(Reachability * reachability);
-typedef void (^NetworkReachability)(Reachability * reachability, SCNetworkConnectionFlags flags);
+extern NSString *kReachabilityChangedNotification;
 
 
 @interface Reachability : NSObject
 
-@property (nonatomic, copy) NetworkReachable    reachableBlock;
-@property (nonatomic, copy) NetworkUnreachable  unreachableBlock;
-@property (nonatomic, copy) NetworkReachability reachabilityBlock;
-
-@property (nonatomic, assign) BOOL reachableOnWWAN;
+/*!
+ * Use to check the reachability of a given host name.
+ */
++ (instancetype)reachabilityWithHostName:(NSString *)hostName;
 
+/*!
+ * Use to check the reachability of a given IP address.
+ */
++ (instancetype)reachabilityWithAddress:(const struct sockaddr *)hostAddress;
 
-+(instancetype)reachabilityWithHostname:(NSString*)hostname;
-// This is identical to the function above, but is here to maintain
-//compatibility with Apples original code. (see .m)
-+(instancetype)reachabilityWithHostName:(NSString*)hostname;
-+(instancetype)reachabilityForInternetConnection;
-+(instancetype)reachabilityWithAddress:(void *)hostAddress;
-+(instancetype)reachabilityForLocalWiFi;
+/*!
+ * Checks whether the default route is available. Should be used by applications that do not connect to a particular host.
+ */
++ (instancetype)reachabilityForInternetConnection;
 
--(instancetype)initWithReachabilityRef:(SCNetworkReachabilityRef)ref;
 
--(BOOL)startNotifier;
--(void)stopNotifier;
+#pragma mark reachabilityForLocalWiFi
+//reachabilityForLocalWiFi has been removed from the sample.  See ReadMe.md for more information.
+//+ (instancetype)reachabilityForLocalWiFi;
 
--(BOOL)isReachable;
--(BOOL)isReachableViaWWAN;
--(BOOL)isReachableViaWiFi;
+/*!
+ * Start listening for reachability notifications on the current run loop.
+ */
+- (BOOL)startNotifier;
+- (void)stopNotifier;
 
-// WWAN may be available, but not active until a connection has been established.
-// WiFi may require a connection for VPN on Demand.
--(BOOL)isConnectionRequired; // Identical DDG variant.
--(BOOL)connectionRequired; // Apple's routine.
-// Dynamic, on demand connection?
--(BOOL)isConnectionOnDemand;
-// Is user intervention required?
--(BOOL)isInterventionRequired;
+- (NetworkStatus)currentReachabilityStatus;
 
--(NetworkStatus)currentReachabilityStatus;
--(SCNetworkReachabilityFlags)reachabilityFlags;
--(NSString*)currentReachabilityString;
--(NSString*)currentReachabilityFlags;
+/*!
+ * WWAN may be available, but not active until a connection has been established. WiFi may require a connection for VPN on Demand.
+ */
+- (BOOL)connectionRequired;
 
 @end
+
+

+ 160 - 399
MobileLibrary/iOS/PsiphonTunnel/PsiphonTunnel/Reachability/Reachability.m

@@ -1,481 +1,242 @@
 /*
- Copyright (c) 2011, Tony Million.
- All rights reserved.
+ Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ See LICENSE.txt for this sample’s licensing information
  
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
- 
- 1. Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
- 
- 2. Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
- 
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- POSSIBILITY OF SUCH DAMAGE.
+ Abstract:
+ Basic demonstration of how to use the SystemConfiguration Reachablity APIs.
  */
 
-#import "Reachability.h"
-
-#import <sys/socket.h>
-#import <netinet/in.h>
-#import <netinet6/in6.h>
 #import <arpa/inet.h>
 #import <ifaddrs.h>
 #import <netdb.h>
+#import <sys/socket.h>
+#import <netinet/in.h>
 
+#import <CoreFoundation/CoreFoundation.h>
 
-NSString *const kReachabilityChangedNotification = @"kReachabilityChangedNotification";
+#import "Reachability.h"
 
+#pragma mark IPv6 Support
+//Reachability fully support IPv6.  For full details, see ReadMe.md.
 
-@interface Reachability ()
 
-@property (nonatomic, assign) SCNetworkReachabilityRef  reachabilityRef;
-@property (nonatomic, strong) dispatch_queue_t          reachabilitySerialQueue;
-@property (nonatomic, strong) id                        reachabilityObject;
+NSString *kReachabilityChangedNotification = @"kNetworkReachabilityChangedNotification";
 
--(void)reachabilityChanged:(SCNetworkReachabilityFlags)flags;
--(BOOL)isReachableWithFlags:(SCNetworkReachabilityFlags)flags;
 
-@end
+#pragma mark - Supporting functions
 
+#define kShouldPrintReachabilityFlags 1
 
-static NSString *reachabilityFlags(SCNetworkReachabilityFlags flags)
+static void PrintReachabilityFlags(SCNetworkReachabilityFlags flags, const char* comment)
 {
-    return [NSString stringWithFormat:@"%c%c %c%c%c%c%c%c%c",
-#if	TARGET_OS_IPHONE
-            (flags & kSCNetworkReachabilityFlagsIsWWAN)               ? 'W' : '-',
-#else
-            'X',
-#endif
-            (flags & kSCNetworkReachabilityFlagsReachable)            ? 'R' : '-',
-            (flags & kSCNetworkReachabilityFlagsConnectionRequired)   ? 'c' : '-',
-            (flags & kSCNetworkReachabilityFlagsTransientConnection)  ? 't' : '-',
-            (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-',
-            (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic)  ? 'C' : '-',
-            (flags & kSCNetworkReachabilityFlagsConnectionOnDemand)   ? 'D' : '-',
-            (flags & kSCNetworkReachabilityFlagsIsLocalAddress)       ? 'l' : '-',
-            (flags & kSCNetworkReachabilityFlagsIsDirect)             ? 'd' : '-'];
-}
+#if kShouldPrintReachabilityFlags
 
-// Start listening for reachability notifications on the current run loop
-static void TMReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info)
-{
-#pragma unused (target)
-    
-    Reachability *reachability = ((__bridge Reachability*)info);
-    
-    // We probably don't need an autoreleasepool here, as GCD docs state each queue has its own autorelease pool,
-    // but what the heck eh?
-    @autoreleasepool
-    {
-        [reachability reachabilityChanged:flags];
-    }
-}
+    NSLog(@"Reachability Flag Status: %c%c %c%c%c%c%c%c%c %s\n",
+          (flags & kSCNetworkReachabilityFlagsIsWWAN)				? 'W' : '-',
+          (flags & kSCNetworkReachabilityFlagsReachable)            ? 'R' : '-',
 
-
-@implementation Reachability
-
-#pragma mark - Class Constructor Methods
-
-+(instancetype)reachabilityWithHostName:(NSString*)hostname
-{
-    return [Reachability reachabilityWithHostname:hostname];
+          (flags & kSCNetworkReachabilityFlagsTransientConnection)  ? 't' : '-',
+          (flags & kSCNetworkReachabilityFlagsConnectionRequired)   ? 'c' : '-',
+          (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic)  ? 'C' : '-',
+          (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-',
+          (flags & kSCNetworkReachabilityFlagsConnectionOnDemand)   ? 'D' : '-',
+          (flags & kSCNetworkReachabilityFlagsIsLocalAddress)       ? 'l' : '-',
+          (flags & kSCNetworkReachabilityFlagsIsDirect)             ? 'd' : '-',
+          comment
+          );
+#endif
 }
 
-+(instancetype)reachabilityWithHostname:(NSString*)hostname
-{
-    SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithName(NULL, [hostname UTF8String]);
-    if (ref)
-    {
-        id reachability = [[self alloc] initWithReachabilityRef:ref];
-        
-        CFRelease(ref);
-        
-        return reachability;
-    }
-    
-    return nil;
-}
 
-+(instancetype)reachabilityWithAddress:(void *)hostAddress
+static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info)
 {
-    SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)hostAddress);
-    if (ref)
-    {
-        id reachability = [[self alloc] initWithReachabilityRef:ref];
-        
-        CFRelease(ref);
-        
-        return reachability;
-    }
-    
-    return nil;
-}
+#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");
 
-+(instancetype)reachabilityForInternetConnection
-{
-    struct sockaddr_in zeroAddress;
-    bzero(&zeroAddress, sizeof(zeroAddress));
-    zeroAddress.sin_len = sizeof(zeroAddress);
-    zeroAddress.sin_family = AF_INET;
-    
-    return [self reachabilityWithAddress:&zeroAddress];
-}
-
-+(instancetype)reachabilityForLocalWiFi
-{
-    struct sockaddr_in localWifiAddress;
-    bzero(&localWifiAddress, sizeof(localWifiAddress));
-    localWifiAddress.sin_len            = sizeof(localWifiAddress);
-    localWifiAddress.sin_family         = AF_INET;
-    // IN_LINKLOCALNETNUM is defined in <netinet/in.h> as 169.254.0.0
-    localWifiAddress.sin_addr.s_addr    = htonl(IN_LINKLOCALNETNUM);
-    
-    return [self reachabilityWithAddress:&localWifiAddress];
+    Reachability* noteObject = (__bridge Reachability *)info;
+    // Post a notification to notify the client that the network reachability changed.
+    [[NSNotificationCenter defaultCenter] postNotificationName: kReachabilityChangedNotification object: noteObject];
 }
 
 
-// Initialization methods
+#pragma mark - Reachability implementation
 
--(instancetype)initWithReachabilityRef:(SCNetworkReachabilityRef)ref
+@implementation Reachability
 {
-    self = [super init];
-    if (self != nil)
-    {
-        self.reachableOnWWAN = YES;
-        self.reachabilityRef = ref;
-        
-        CFRetain(self.reachabilityRef);
-        
-        // We need to create a serial queue.
-        // We allocate this once for the lifetime of the notifier.
-        
-        self.reachabilitySerialQueue = dispatch_queue_create("com.tonymillion.reachability", NULL);
-    }
-    
-    return self;
+	SCNetworkReachabilityRef _reachabilityRef;
 }
 
--(void)dealloc
++ (instancetype)reachabilityWithHostName:(NSString *)hostName
 {
-    [self stopNotifier];
-    
-    if(self.reachabilityRef)
-    {
-        CFRelease(self.reachabilityRef);
-        self.reachabilityRef = nil;
-    }
-    
-    self.reachableBlock          = nil;
-    self.unreachableBlock        = nil;
-    self.reachabilityBlock       = nil;
-    self.reachabilitySerialQueue = nil;
+	Reachability* returnValue = NULL;
+	SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]);
+	if (reachability != NULL)
+	{
+		returnValue= [[self alloc] init];
+		if (returnValue != NULL)
+		{
+			returnValue->_reachabilityRef = reachability;
+		}
+        else {
+            CFRelease(reachability);
+        }
+	}
+	return returnValue;
 }
 
-#pragma mark - Notifier Methods
 
-// Notifier
-// NOTE: This uses GCD to trigger the blocks - they *WILL NOT* be called on THE MAIN THREAD
-// - In other words DO NOT DO ANY UI UPDATES IN THE BLOCKS.
-//   INSTEAD USE dispatch_async(dispatch_get_main_queue(), ^{UISTUFF}) (or dispatch_sync if you want)
-
--(BOOL)startNotifier
++ (instancetype)reachabilityWithAddress:(const struct sockaddr *)hostAddress
 {
-    // allow start notifier to be called multiple times
-    if(self.reachabilityObject && (self.reachabilityObject == self))
-    {
-        return YES;
-    }
-    
-    
-    SCNetworkReachabilityContext    context = { 0, NULL, NULL, NULL, NULL };
-    context.info = (__bridge void *)self;
-    
-    if(SCNetworkReachabilitySetCallback(self.reachabilityRef, TMReachabilityCallback, &context))
-    {
-        // Set it as our reachability queue, which will retain the queue
-        if(SCNetworkReachabilitySetDispatchQueue(self.reachabilityRef, self.reachabilitySerialQueue))
-        {
-            // this should do a retain on ourself, so as long as we're in notifier mode we shouldn't disappear out from under ourselves
-            // woah
-            self.reachabilityObject = self;
-            return YES;
-        }
-        else
-        {
-#ifdef DEBUG
-            NSLog(@"SCNetworkReachabilitySetDispatchQueue() failed: %s", SCErrorString(SCError()));
-#endif
-            
-            // UH OH - FAILURE - stop any callbacks!
-            SCNetworkReachabilitySetCallback(self.reachabilityRef, NULL, NULL);
+	SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, hostAddress);
+
+	Reachability* returnValue = NULL;
+
+	if (reachability != NULL)
+	{
+		returnValue = [[self alloc] init];
+		if (returnValue != NULL)
+		{
+			returnValue->_reachabilityRef = reachability;
+		}
+        else {
+            CFRelease(reachability);
         }
-    }
-    else
-    {
-#ifdef DEBUG
-        NSLog(@"SCNetworkReachabilitySetCallback() failed: %s", SCErrorString(SCError()));
-#endif
-    }
-    
-    // if we get here we fail at the internet
-    self.reachabilityObject = nil;
-    return NO;
+	}
+	return returnValue;
 }
 
--(void)stopNotifier
+
++ (instancetype)reachabilityForInternetConnection
 {
-    // First stop, any callbacks!
-    SCNetworkReachabilitySetCallback(self.reachabilityRef, NULL, NULL);
+	struct sockaddr_in zeroAddress;
+	bzero(&zeroAddress, sizeof(zeroAddress));
+	zeroAddress.sin_len = sizeof(zeroAddress);
+	zeroAddress.sin_family = AF_INET;
     
-    // Unregister target from the GCD serial dispatch queue.
-    SCNetworkReachabilitySetDispatchQueue(self.reachabilityRef, NULL);
-    
-    self.reachabilityObject = nil;
+    return [self reachabilityWithAddress: (const struct sockaddr *) &zeroAddress];
 }
 
-#pragma mark - reachability tests
+#pragma mark reachabilityForLocalWiFi
+//reachabilityForLocalWiFi has been removed from the sample.  See ReadMe.md for more information.
+//+ (instancetype)reachabilityForLocalWiFi
 
-// This is for the case where you flick the airplane mode;
-// you end up getting something like this:
-//Reachability: WR ct-----
-//Reachability: -- -------
-//Reachability: WR ct-----
-//Reachability: -- -------
-// We treat this as 4 UNREACHABLE triggers - really apple should do better than this
 
-#define testcase (kSCNetworkReachabilityFlagsConnectionRequired | kSCNetworkReachabilityFlagsTransientConnection)
 
--(BOOL)isReachableWithFlags:(SCNetworkReachabilityFlags)flags
-{
-    BOOL connectionUP = YES;
-    
-    if(!(flags & kSCNetworkReachabilityFlagsReachable))
-        connectionUP = NO;
-    
-    if( (flags & testcase) == testcase )
-        connectionUP = NO;
-    
-#if	TARGET_OS_IPHONE
-    if(flags & kSCNetworkReachabilityFlagsIsWWAN)
-    {
-        // We're on 3G.
-        if(!self.reachableOnWWAN)
-        {
-            // We don't want to connect when on 3G.
-            connectionUP = NO;
-        }
-    }
-#endif
-    
-    return connectionUP;
-}
+#pragma mark - Start and stop notifier
 
--(BOOL)isReachable
+- (BOOL)startNotifier
 {
-    SCNetworkReachabilityFlags flags;
-    
-    if(!SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
-        return NO;
-    
-    return [self isReachableWithFlags:flags];
-}
+	BOOL returnValue = NO;
+	SCNetworkReachabilityContext context = {0, (__bridge void *)(self), NULL, NULL, NULL};
 
--(BOOL)isReachableViaWWAN
-{
-#if	TARGET_OS_IPHONE
+	if (SCNetworkReachabilitySetCallback(_reachabilityRef, ReachabilityCallback, &context))
+	{
+		if (SCNetworkReachabilityScheduleWithRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode))
+		{
+			returnValue = YES;
+		}
+	}
     
-    SCNetworkReachabilityFlags flags = 0;
-    
-    if(SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
-    {
-        // Check we're REACHABLE
-        if(flags & kSCNetworkReachabilityFlagsReachable)
-        {
-            // Now, check we're on WWAN
-            if(flags & kSCNetworkReachabilityFlagsIsWWAN)
-            {
-                return YES;
-            }
-        }
-    }
-#endif
-    
-    return NO;
+	return returnValue;
 }
 
--(BOOL)isReachableViaWiFi
+
+- (void)stopNotifier
 {
-    SCNetworkReachabilityFlags flags = 0;
-    
-    if(SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
-    {
-        // Check we're reachable
-        if((flags & kSCNetworkReachabilityFlagsReachable))
-        {
-#if	TARGET_OS_IPHONE
-            // Check we're NOT on WWAN
-            if((flags & kSCNetworkReachabilityFlagsIsWWAN))
-            {
-                return NO;
-            }
-#endif
-            return YES;
-        }
-    }
-    
-    return NO;
+	if (_reachabilityRef != NULL)
+	{
+		SCNetworkReachabilityUnscheduleFromRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+	}
 }
 
 
-// WWAN may be available, but not active until a connection has been established.
-// WiFi may require a connection for VPN on Demand.
--(BOOL)isConnectionRequired
+- (void)dealloc
 {
-    return [self connectionRequired];
+	[self stopNotifier];
+	if (_reachabilityRef != NULL)
+	{
+		CFRelease(_reachabilityRef);
+	}
 }
 
--(BOOL)connectionRequired
-{
-    SCNetworkReachabilityFlags flags;
-    
-    if(SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
-    {
-        return (flags & kSCNetworkReachabilityFlagsConnectionRequired);
-    }
-    
-    return NO;
-}
 
-// Dynamic, on demand connection?
--(BOOL)isConnectionOnDemand
-{
-    SCNetworkReachabilityFlags flags;
-    
-    if (SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
-    {
-        return ((flags & kSCNetworkReachabilityFlagsConnectionRequired) &&
-                (flags & (kSCNetworkReachabilityFlagsConnectionOnTraffic | kSCNetworkReachabilityFlagsConnectionOnDemand)));
-    }
-    
-    return NO;
-}
+#pragma mark - Network Flag Handling
 
-// Is user intervention required?
--(BOOL)isInterventionRequired
+- (NetworkStatus)networkStatusForFlags:(SCNetworkReachabilityFlags)flags
 {
-    SCNetworkReachabilityFlags flags;
-    
-    if (SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
-    {
-        return ((flags & kSCNetworkReachabilityFlagsConnectionRequired) &&
-                (flags & kSCNetworkReachabilityFlagsInterventionRequired));
-    }
-    
-    return NO;
-}
+	PrintReachabilityFlags(flags, "networkStatusForFlags");
+	if ((flags & kSCNetworkReachabilityFlagsReachable) == 0)
+	{
+		// The target host is not reachable.
+		return NotReachable;
+	}
 
+    NetworkStatus returnValue = NotReachable;
 
-#pragma mark - reachability status stuff
+	if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0)
+	{
+		/*
+         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;
+	}
 
--(NetworkStatus)currentReachabilityStatus
-{
-    if([self isReachable])
-    {
-        if([self isReachableViaWiFi])
-            return ReachableViaWiFi;
-        
-#if	TARGET_OS_IPHONE
-        return ReachableViaWWAN;
-#endif
-    }
-    
-    return NotReachable;
-}
+	if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) ||
+        (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0))
+	{
+        /*
+         ... and the connection is on-demand (or on-traffic) if the calling application is using the CFSocketStream or higher APIs...
+         */
 
--(SCNetworkReachabilityFlags)reachabilityFlags
-{
-    SCNetworkReachabilityFlags flags = 0;
-    
-    if(SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
-    {
-        return flags;
+        if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0)
+        {
+            /*
+             ... and no [user] intervention is needed...
+             */
+            returnValue = ReachableViaWiFi;
+        }
     }
-    
-    return 0;
-}
 
--(NSString*)currentReachabilityString
-{
-    NetworkStatus temp = [self currentReachabilityStatus];
+	if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN)
+	{
+		/*
+         ... but WWAN connections are OK if the calling application is using the CFNetwork APIs.
+         */
+		returnValue = ReachableViaWWAN;
+	}
     
-    if(temp == ReachableViaWWAN)
-    {
-        // Updated for the fact that we have CDMA phones now!
-        return NSLocalizedString(@"Cellular", @"");
-    }
-    if (temp == ReachableViaWiFi)
-    {
-        return NSLocalizedString(@"WiFi", @"");
-    }
-    
-    return NSLocalizedString(@"No Connection", @"");
+	return returnValue;
 }
 
--(NSString*)currentReachabilityFlags
+
+- (BOOL)connectionRequired
 {
-    return reachabilityFlags([self reachabilityFlags]);
+	NSAssert(_reachabilityRef != NULL, @"connectionRequired called with NULL reachabilityRef");
+	SCNetworkReachabilityFlags flags;
+
+	if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags))
+	{
+		return (flags & kSCNetworkReachabilityFlagsConnectionRequired);
+	}
+
+    return NO;
 }
 
-#pragma mark - Callback function calls this method
 
--(void)reachabilityChanged:(SCNetworkReachabilityFlags)flags
+- (NetworkStatus)currentReachabilityStatus
 {
-    if([self isReachableWithFlags:flags])
-    {
-        if(self.reachableBlock)
-        {
-            self.reachableBlock(self);
-        }
-    }
-    else
-    {
-        if(self.unreachableBlock)
-        {
-            self.unreachableBlock(self);
-        }
-    }
+	NSAssert(_reachabilityRef != NULL, @"currentNetworkStatus called with NULL SCNetworkReachabilityRef");
+	NetworkStatus returnValue = NotReachable;
+	SCNetworkReachabilityFlags flags;
     
-    if(self.reachabilityBlock)
-    {
-        self.reachabilityBlock(self, flags);
-    }
+	if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags))
+	{
+        returnValue = [self networkStatusForFlags:flags];
+	}
     
-    // this makes sure the change notification happens on the MAIN THREAD
-    dispatch_async(dispatch_get_main_queue(), ^{
-        [[NSNotificationCenter defaultCenter] postNotificationName:kReachabilityChangedNotification
-                                                            object:self];
-    });
+	return returnValue;
 }
 
-#pragma mark - Debug Description
-
-- (NSString *) description
-{
-    NSString *description = [NSString stringWithFormat:@"<%@: %#x (%@)>",
-                             NSStringFromClass([self class]), (unsigned int) self, [self currentReachabilityFlags]];
-    return description;
-}
 
 @end