├── .gitignore ├── FakeReachability.xcodeproj └── project.pbxproj ├── FakeReachability ├── AppDelegate.h ├── AppDelegate.m ├── FakeReachability-Info.plist ├── FakeReachability-Prefix.pch ├── GCDAsyncSocket.h ├── GCDAsyncSocket.m ├── ReachabilityFaker.h ├── ReachabilityFaker.m ├── en.lproj │ ├── Credits.rtf │ ├── InfoPlist.strings │ └── MainMenu.xib └── main.m ├── README.md ├── Reachability.h └── Reachability.m /.gitignore: -------------------------------------------------------------------------------- 1 | DerivedData 2 | .DS_Store 3 | project.xcworkspace 4 | xcuserdata -------------------------------------------------------------------------------- /FakeReachability.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 972EC332148AC754006875E5 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 972EC331148AC754006875E5 /* Cocoa.framework */; }; 11 | 972EC33C148AC754006875E5 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 972EC33A148AC754006875E5 /* InfoPlist.strings */; }; 12 | 972EC33E148AC754006875E5 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 972EC33D148AC754006875E5 /* main.m */; }; 13 | 972EC342148AC754006875E5 /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 972EC340148AC754006875E5 /* Credits.rtf */; }; 14 | 972EC345148AC754006875E5 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 972EC344148AC754006875E5 /* AppDelegate.m */; }; 15 | 972EC348148AC755006875E5 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 972EC346148AC755006875E5 /* MainMenu.xib */; }; 16 | 972EC350148AC7E1006875E5 /* ReachabilityFaker.m in Sources */ = {isa = PBXBuildFile; fileRef = 972EC34F148AC7E1006875E5 /* ReachabilityFaker.m */; }; 17 | 972EC353148AC803006875E5 /* GCDAsyncSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 972EC352148AC803006875E5 /* GCDAsyncSocket.m */; }; 18 | 972EC355148AC9CF006875E5 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 972EC354148AC9CF006875E5 /* Security.framework */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXFileReference section */ 22 | 972EC32D148AC754006875E5 /* FakeReachability.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FakeReachability.app; sourceTree = BUILT_PRODUCTS_DIR; }; 23 | 972EC331148AC754006875E5 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 24 | 972EC334148AC754006875E5 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; 25 | 972EC335148AC754006875E5 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; 26 | 972EC336148AC754006875E5 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 27 | 972EC339148AC754006875E5 /* FakeReachability-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "FakeReachability-Info.plist"; sourceTree = ""; }; 28 | 972EC33B148AC754006875E5 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 29 | 972EC33D148AC754006875E5 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 30 | 972EC33F148AC754006875E5 /* FakeReachability-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FakeReachability-Prefix.pch"; sourceTree = ""; }; 31 | 972EC341148AC754006875E5 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = en.lproj/Credits.rtf; sourceTree = ""; }; 32 | 972EC343148AC754006875E5 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 33 | 972EC344148AC754006875E5 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 34 | 972EC347148AC755006875E5 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainMenu.xib; sourceTree = ""; }; 35 | 972EC34E148AC7E1006875E5 /* ReachabilityFaker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReachabilityFaker.h; sourceTree = ""; }; 36 | 972EC34F148AC7E1006875E5 /* ReachabilityFaker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ReachabilityFaker.m; sourceTree = ""; }; 37 | 972EC351148AC803006875E5 /* GCDAsyncSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDAsyncSocket.h; sourceTree = ""; }; 38 | 972EC352148AC803006875E5 /* GCDAsyncSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDAsyncSocket.m; sourceTree = ""; }; 39 | 972EC354148AC9CF006875E5 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 40 | /* End PBXFileReference section */ 41 | 42 | /* Begin PBXFrameworksBuildPhase section */ 43 | 972EC32A148AC754006875E5 /* Frameworks */ = { 44 | isa = PBXFrameworksBuildPhase; 45 | buildActionMask = 2147483647; 46 | files = ( 47 | 972EC355148AC9CF006875E5 /* Security.framework in Frameworks */, 48 | 972EC332148AC754006875E5 /* Cocoa.framework in Frameworks */, 49 | ); 50 | runOnlyForDeploymentPostprocessing = 0; 51 | }; 52 | /* End PBXFrameworksBuildPhase section */ 53 | 54 | /* Begin PBXGroup section */ 55 | 972EC322148AC754006875E5 = { 56 | isa = PBXGroup; 57 | children = ( 58 | 972EC354148AC9CF006875E5 /* Security.framework */, 59 | 972EC337148AC754006875E5 /* FakeReachability */, 60 | 972EC330148AC754006875E5 /* Frameworks */, 61 | 972EC32E148AC754006875E5 /* Products */, 62 | ); 63 | sourceTree = ""; 64 | }; 65 | 972EC32E148AC754006875E5 /* Products */ = { 66 | isa = PBXGroup; 67 | children = ( 68 | 972EC32D148AC754006875E5 /* FakeReachability.app */, 69 | ); 70 | name = Products; 71 | sourceTree = ""; 72 | }; 73 | 972EC330148AC754006875E5 /* Frameworks */ = { 74 | isa = PBXGroup; 75 | children = ( 76 | 972EC331148AC754006875E5 /* Cocoa.framework */, 77 | 972EC333148AC754006875E5 /* Other Frameworks */, 78 | ); 79 | name = Frameworks; 80 | sourceTree = ""; 81 | }; 82 | 972EC333148AC754006875E5 /* Other Frameworks */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | 972EC334148AC754006875E5 /* AppKit.framework */, 86 | 972EC335148AC754006875E5 /* CoreData.framework */, 87 | 972EC336148AC754006875E5 /* Foundation.framework */, 88 | ); 89 | name = "Other Frameworks"; 90 | sourceTree = ""; 91 | }; 92 | 972EC337148AC754006875E5 /* FakeReachability */ = { 93 | isa = PBXGroup; 94 | children = ( 95 | 972EC351148AC803006875E5 /* GCDAsyncSocket.h */, 96 | 972EC352148AC803006875E5 /* GCDAsyncSocket.m */, 97 | 972EC343148AC754006875E5 /* AppDelegate.h */, 98 | 972EC344148AC754006875E5 /* AppDelegate.m */, 99 | 972EC346148AC755006875E5 /* MainMenu.xib */, 100 | 972EC338148AC754006875E5 /* Supporting Files */, 101 | 972EC34E148AC7E1006875E5 /* ReachabilityFaker.h */, 102 | 972EC34F148AC7E1006875E5 /* ReachabilityFaker.m */, 103 | ); 104 | path = FakeReachability; 105 | sourceTree = ""; 106 | }; 107 | 972EC338148AC754006875E5 /* Supporting Files */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | 972EC339148AC754006875E5 /* FakeReachability-Info.plist */, 111 | 972EC33A148AC754006875E5 /* InfoPlist.strings */, 112 | 972EC33D148AC754006875E5 /* main.m */, 113 | 972EC33F148AC754006875E5 /* FakeReachability-Prefix.pch */, 114 | 972EC340148AC754006875E5 /* Credits.rtf */, 115 | ); 116 | name = "Supporting Files"; 117 | sourceTree = ""; 118 | }; 119 | /* End PBXGroup section */ 120 | 121 | /* Begin PBXNativeTarget section */ 122 | 972EC32C148AC754006875E5 /* FakeReachability */ = { 123 | isa = PBXNativeTarget; 124 | buildConfigurationList = 972EC34B148AC755006875E5 /* Build configuration list for PBXNativeTarget "FakeReachability" */; 125 | buildPhases = ( 126 | 972EC329148AC754006875E5 /* Sources */, 127 | 972EC32A148AC754006875E5 /* Frameworks */, 128 | 972EC32B148AC754006875E5 /* Resources */, 129 | ); 130 | buildRules = ( 131 | ); 132 | dependencies = ( 133 | ); 134 | name = FakeReachability; 135 | productName = FakeReachability; 136 | productReference = 972EC32D148AC754006875E5 /* FakeReachability.app */; 137 | productType = "com.apple.product-type.application"; 138 | }; 139 | /* End PBXNativeTarget section */ 140 | 141 | /* Begin PBXProject section */ 142 | 972EC324148AC754006875E5 /* Project object */ = { 143 | isa = PBXProject; 144 | attributes = { 145 | LastUpgradeCheck = 0420; 146 | }; 147 | buildConfigurationList = 972EC327148AC754006875E5 /* Build configuration list for PBXProject "FakeReachability" */; 148 | compatibilityVersion = "Xcode 3.2"; 149 | developmentRegion = English; 150 | hasScannedForEncodings = 0; 151 | knownRegions = ( 152 | en, 153 | ); 154 | mainGroup = 972EC322148AC754006875E5; 155 | productRefGroup = 972EC32E148AC754006875E5 /* Products */; 156 | projectDirPath = ""; 157 | projectRoot = ""; 158 | targets = ( 159 | 972EC32C148AC754006875E5 /* FakeReachability */, 160 | ); 161 | }; 162 | /* End PBXProject section */ 163 | 164 | /* Begin PBXResourcesBuildPhase section */ 165 | 972EC32B148AC754006875E5 /* Resources */ = { 166 | isa = PBXResourcesBuildPhase; 167 | buildActionMask = 2147483647; 168 | files = ( 169 | 972EC33C148AC754006875E5 /* InfoPlist.strings in Resources */, 170 | 972EC342148AC754006875E5 /* Credits.rtf in Resources */, 171 | 972EC348148AC755006875E5 /* MainMenu.xib in Resources */, 172 | ); 173 | runOnlyForDeploymentPostprocessing = 0; 174 | }; 175 | /* End PBXResourcesBuildPhase section */ 176 | 177 | /* Begin PBXSourcesBuildPhase section */ 178 | 972EC329148AC754006875E5 /* Sources */ = { 179 | isa = PBXSourcesBuildPhase; 180 | buildActionMask = 2147483647; 181 | files = ( 182 | 972EC33E148AC754006875E5 /* main.m in Sources */, 183 | 972EC345148AC754006875E5 /* AppDelegate.m in Sources */, 184 | 972EC350148AC7E1006875E5 /* ReachabilityFaker.m in Sources */, 185 | 972EC353148AC803006875E5 /* GCDAsyncSocket.m in Sources */, 186 | ); 187 | runOnlyForDeploymentPostprocessing = 0; 188 | }; 189 | /* End PBXSourcesBuildPhase section */ 190 | 191 | /* Begin PBXVariantGroup section */ 192 | 972EC33A148AC754006875E5 /* InfoPlist.strings */ = { 193 | isa = PBXVariantGroup; 194 | children = ( 195 | 972EC33B148AC754006875E5 /* en */, 196 | ); 197 | name = InfoPlist.strings; 198 | sourceTree = ""; 199 | }; 200 | 972EC340148AC754006875E5 /* Credits.rtf */ = { 201 | isa = PBXVariantGroup; 202 | children = ( 203 | 972EC341148AC754006875E5 /* en */, 204 | ); 205 | name = Credits.rtf; 206 | sourceTree = ""; 207 | }; 208 | 972EC346148AC755006875E5 /* MainMenu.xib */ = { 209 | isa = PBXVariantGroup; 210 | children = ( 211 | 972EC347148AC755006875E5 /* en */, 212 | ); 213 | name = MainMenu.xib; 214 | sourceTree = ""; 215 | }; 216 | /* End PBXVariantGroup section */ 217 | 218 | /* Begin XCBuildConfiguration section */ 219 | 972EC349148AC755006875E5 /* Debug */ = { 220 | isa = XCBuildConfiguration; 221 | buildSettings = { 222 | ALWAYS_SEARCH_USER_PATHS = NO; 223 | ARCHS = "$(ARCHS_STANDARD_64_BIT)"; 224 | COPY_PHASE_STRIP = NO; 225 | GCC_C_LANGUAGE_STANDARD = gnu99; 226 | GCC_DYNAMIC_NO_PIC = NO; 227 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 228 | GCC_OPTIMIZATION_LEVEL = 0; 229 | GCC_PREPROCESSOR_DEFINITIONS = ( 230 | "DEBUG=1", 231 | "$(inherited)", 232 | ); 233 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 234 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 235 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 236 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 237 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 238 | GCC_WARN_UNUSED_VARIABLE = YES; 239 | MACOSX_DEPLOYMENT_TARGET = 10.7; 240 | ONLY_ACTIVE_ARCH = YES; 241 | SDKROOT = macosx; 242 | }; 243 | name = Debug; 244 | }; 245 | 972EC34A148AC755006875E5 /* Release */ = { 246 | isa = XCBuildConfiguration; 247 | buildSettings = { 248 | ALWAYS_SEARCH_USER_PATHS = NO; 249 | ARCHS = "$(ARCHS_STANDARD_64_BIT)"; 250 | COPY_PHASE_STRIP = YES; 251 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 252 | GCC_C_LANGUAGE_STANDARD = gnu99; 253 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 254 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 255 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 256 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 257 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 258 | GCC_WARN_UNUSED_VARIABLE = YES; 259 | MACOSX_DEPLOYMENT_TARGET = 10.7; 260 | SDKROOT = macosx; 261 | }; 262 | name = Release; 263 | }; 264 | 972EC34C148AC755006875E5 /* Debug */ = { 265 | isa = XCBuildConfiguration; 266 | buildSettings = { 267 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 268 | GCC_PREFIX_HEADER = "FakeReachability/FakeReachability-Prefix.pch"; 269 | INFOPLIST_FILE = "FakeReachability/FakeReachability-Info.plist"; 270 | PRODUCT_NAME = "$(TARGET_NAME)"; 271 | WRAPPER_EXTENSION = app; 272 | }; 273 | name = Debug; 274 | }; 275 | 972EC34D148AC755006875E5 /* Release */ = { 276 | isa = XCBuildConfiguration; 277 | buildSettings = { 278 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 279 | GCC_PREFIX_HEADER = "FakeReachability/FakeReachability-Prefix.pch"; 280 | INFOPLIST_FILE = "FakeReachability/FakeReachability-Info.plist"; 281 | PRODUCT_NAME = "$(TARGET_NAME)"; 282 | WRAPPER_EXTENSION = app; 283 | }; 284 | name = Release; 285 | }; 286 | /* End XCBuildConfiguration section */ 287 | 288 | /* Begin XCConfigurationList section */ 289 | 972EC327148AC754006875E5 /* Build configuration list for PBXProject "FakeReachability" */ = { 290 | isa = XCConfigurationList; 291 | buildConfigurations = ( 292 | 972EC349148AC755006875E5 /* Debug */, 293 | 972EC34A148AC755006875E5 /* Release */, 294 | ); 295 | defaultConfigurationIsVisible = 0; 296 | defaultConfigurationName = Release; 297 | }; 298 | 972EC34B148AC755006875E5 /* Build configuration list for PBXNativeTarget "FakeReachability" */ = { 299 | isa = XCConfigurationList; 300 | buildConfigurations = ( 301 | 972EC34C148AC755006875E5 /* Debug */, 302 | 972EC34D148AC755006875E5 /* Release */, 303 | ); 304 | defaultConfigurationIsVisible = 0; 305 | }; 306 | /* End XCConfigurationList section */ 307 | }; 308 | rootObject = 972EC324148AC754006875E5 /* Project object */; 309 | } 310 | -------------------------------------------------------------------------------- /FakeReachability/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // FakeReachability 4 | // 5 | // Created by Moritz Haarmann on 03.12.11. 6 | // Copyright (c) 2011 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : NSObject 12 | 13 | @property (assign) IBOutlet NSWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /FakeReachability/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // FakeReachability 4 | // 5 | // Created by Moritz Haarmann on 03.12.11. 6 | // Copyright (c) 2011 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @implementation AppDelegate 12 | 13 | @synthesize window = _window; 14 | 15 | - (void)dealloc 16 | { 17 | [super dealloc]; 18 | } 19 | 20 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification 21 | { 22 | // Insert code here to initialize your application 23 | } 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /FakeReachability/FakeReachability-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | com.moritzhaarmann.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSMinimumSystemVersion 26 | ${MACOSX_DEPLOYMENT_TARGET} 27 | NSHumanReadableCopyright 28 | Copyright © 2011 __MyCompanyName__. All rights reserved. 29 | NSMainNibFile 30 | MainMenu 31 | NSPrincipalClass 32 | NSApplication 33 | 34 | 35 | -------------------------------------------------------------------------------- /FakeReachability/FakeReachability-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'FakeReachability' target in the 'FakeReachability' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /FakeReachability/GCDAsyncSocket.h: -------------------------------------------------------------------------------- 1 | // 2 | // GCDAsyncSocket.h 3 | // 4 | // This class is in the public domain. 5 | // Originally created by Robbie Hanson in Q3 2010. 6 | // Updated and maintained by Deusty LLC and the Mac development community. 7 | // 8 | // http://code.google.com/p/cocoaasyncsocket/ 9 | // 10 | 11 | #import 12 | #import 13 | #import 14 | 15 | @class GCDAsyncReadPacket; 16 | @class GCDAsyncWritePacket; 17 | 18 | extern NSString *const GCDAsyncSocketException; 19 | extern NSString *const GCDAsyncSocketErrorDomain; 20 | 21 | #if !TARGET_OS_IPHONE 22 | extern NSString *const GCDAsyncSocketSSLCipherSuites; 23 | extern NSString *const GCDAsyncSocketSSLDiffieHellmanParameters; 24 | #endif 25 | 26 | enum GCDAsyncSocketError 27 | { 28 | GCDAsyncSocketNoError = 0, // Never used 29 | GCDAsyncSocketBadConfigError, // Invalid configuration 30 | GCDAsyncSocketBadParamError, // Invalid parameter was passed 31 | GCDAsyncSocketConnectTimeoutError, // A connect operation timed out 32 | GCDAsyncSocketReadTimeoutError, // A read operation timed out 33 | GCDAsyncSocketWriteTimeoutError, // A write operation timed out 34 | GCDAsyncSocketReadMaxedOutError, // Reached set maxLength without completing 35 | GCDAsyncSocketClosedError, // The remote peer closed the connection 36 | GCDAsyncSocketOtherError, // Description provided in userInfo 37 | }; 38 | typedef enum GCDAsyncSocketError GCDAsyncSocketError; 39 | 40 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 41 | #pragma mark - 42 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 43 | 44 | @interface GCDAsyncSocket : NSObject 45 | { 46 | uint32_t flags; 47 | uint16_t config; 48 | 49 | id delegate; 50 | dispatch_queue_t delegateQueue; 51 | 52 | int socket4FD; 53 | int socket6FD; 54 | int connectIndex; 55 | NSData * connectInterface4; 56 | NSData * connectInterface6; 57 | 58 | dispatch_queue_t socketQueue; 59 | 60 | dispatch_source_t accept4Source; 61 | dispatch_source_t accept6Source; 62 | dispatch_source_t connectTimer; 63 | dispatch_source_t readSource; 64 | dispatch_source_t writeSource; 65 | dispatch_source_t readTimer; 66 | dispatch_source_t writeTimer; 67 | 68 | NSMutableArray *readQueue; 69 | NSMutableArray *writeQueue; 70 | 71 | GCDAsyncReadPacket *currentRead; 72 | GCDAsyncWritePacket *currentWrite; 73 | 74 | unsigned long socketFDBytesAvailable; 75 | 76 | NSMutableData *partialReadBuffer; 77 | 78 | #if TARGET_OS_IPHONE 79 | CFStreamClientContext streamContext; 80 | CFReadStreamRef readStream; 81 | CFWriteStreamRef writeStream; 82 | #else 83 | SSLContextRef sslContext; 84 | NSMutableData *sslReadBuffer; 85 | size_t sslWriteCachedLength; 86 | #endif 87 | 88 | id userData; 89 | } 90 | 91 | /** 92 | * GCDAsyncSocket uses the standard delegate paradigm, 93 | * but executes all delegate callbacks on a given delegate dispatch queue. 94 | * This allows for maximum concurrency, while at the same time providing easy thread safety. 95 | * 96 | * You MUST set a delegate AND delegate dispatch queue before attempting to 97 | * use the socket, or you will get an error. 98 | * 99 | * The socket queue is optional. 100 | * If you pass NULL, GCDAsyncSocket will automatically create it's own socket queue. 101 | * If you choose to provide a socket queue, the socket queue must not be a concurrent queue. 102 | * 103 | * The delegate queue and socket queue can optionally be the same. 104 | **/ 105 | - (id)init; 106 | - (id)initWithSocketQueue:(dispatch_queue_t)sq; 107 | - (id)initWithDelegate:(id)aDelegate delegateQueue:(dispatch_queue_t)dq; 108 | - (id)initWithDelegate:(id)aDelegate delegateQueue:(dispatch_queue_t)dq socketQueue:(dispatch_queue_t)sq; 109 | 110 | #pragma mark Configuration 111 | 112 | - (id)delegate; 113 | - (void)setDelegate:(id)delegate; 114 | - (void)synchronouslySetDelegate:(id)delegate; 115 | 116 | - (dispatch_queue_t)delegateQueue; 117 | - (void)setDelegateQueue:(dispatch_queue_t)delegateQueue; 118 | - (void)synchronouslySetDelegateQueue:(dispatch_queue_t)delegateQueue; 119 | 120 | - (void)getDelegate:(id *)delegatePtr delegateQueue:(dispatch_queue_t *)delegateQueuePtr; 121 | - (void)setDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue; 122 | - (void)synchronouslySetDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue; 123 | 124 | /** 125 | * Traditionally sockets are not closed until the conversation is over. 126 | * However, it is technically possible for the remote enpoint to close its write stream. 127 | * Our socket would then be notified that there is no more data to be read, 128 | * but our socket would still be writeable and the remote endpoint could continue to receive our data. 129 | * 130 | * The argument for this confusing functionality stems from the idea that a client could shut down its 131 | * write stream after sending a request to the server, thus notifying the server there are to be no further requests. 132 | * In practice, however, this technique did little to help server developers. 133 | * 134 | * To make matters worse, from a TCP perspective there is no way to tell the difference from a read stream close 135 | * and a full socket close. They both result in the TCP stack receiving a FIN packet. The only way to tell 136 | * is by continuing to write to the socket. If it was only a read stream close, then writes will continue to work. 137 | * Otherwise an error will be occur shortly (when the remote end sends us a RST packet). 138 | * 139 | * In addition to the technical challenges and confusion, many high level socket/stream API's provide 140 | * no support for dealing with the problem. If the read stream is closed, the API immediately declares the 141 | * socket to be closed, and shuts down the write stream as well. In fact, this is what Apple's CFStream API does. 142 | * It might sound like poor design at first, but in fact it simplifies development. 143 | * 144 | * The vast majority of the time if the read stream is closed it's because the remote endpoint closed its socket. 145 | * Thus it actually makes sense to close the socket at this point. 146 | * And in fact this is what most networking developers want and expect to happen. 147 | * However, if you are writing a server that interacts with a plethora of clients, 148 | * you might encounter a client that uses the discouraged technique of shutting down its write stream. 149 | * If this is the case, you can set this property to NO, 150 | * and make use of the socketDidCloseReadStream delegate method. 151 | * 152 | * The default value is YES. 153 | **/ 154 | - (BOOL)autoDisconnectOnClosedReadStream; 155 | - (void)setAutoDisconnectOnClosedReadStream:(BOOL)flag; 156 | 157 | /** 158 | * By default, both IPv4 and IPv6 are enabled. 159 | * 160 | * For accepting incoming connections, this means GCDAsyncSocket automatically supports both protocols, 161 | * and can simulataneously accept incoming connections on either protocol. 162 | * 163 | * For outgoing connections, this means GCDAsyncSocket can connect to remote hosts running either protocol. 164 | * If a DNS lookup returns only IPv4 results, GCDAsyncSocket will automatically use IPv4. 165 | * If a DNS lookup returns only IPv6 results, GCDAsyncSocket will automatically use IPv6. 166 | * If a DNS lookup returns both IPv4 and IPv6 results, the preferred protocol will be chosen. 167 | * By default, the preferred protocol is IPv4, but may be configured as desired. 168 | **/ 169 | - (BOOL)isIPv4Enabled; 170 | - (void)setIPv4Enabled:(BOOL)flag; 171 | 172 | - (BOOL)isIPv6Enabled; 173 | - (void)setIPv6Enabled:(BOOL)flag; 174 | 175 | - (BOOL)isIPv4PreferredOverIPv6; 176 | - (void)setPreferIPv4OverIPv6:(BOOL)flag; 177 | 178 | /** 179 | * User data allows you to associate arbitrary information with the socket. 180 | * This data is not used internally by socket in any way. 181 | **/ 182 | - (id)userData; 183 | - (void)setUserData:(id)arbitraryUserData; 184 | 185 | #pragma mark Accepting 186 | 187 | /** 188 | * Tells the socket to begin listening and accepting connections on the given port. 189 | * When a connection is accepted, a new instance of GCDAsyncSocket will be spawned to handle it, 190 | * and the socket:didAcceptNewSocket: delegate method will be invoked. 191 | * 192 | * The socket will listen on all available interfaces (e.g. wifi, ethernet, etc) 193 | **/ 194 | - (BOOL)acceptOnPort:(uint16_t)port error:(NSError **)errPtr; 195 | 196 | /** 197 | * This method is the same as acceptOnPort:error: with the 198 | * additional option of specifying which interface to listen on. 199 | * 200 | * For example, you could specify that the socket should only accept connections over ethernet, 201 | * and not other interfaces such as wifi. 202 | * 203 | * The interface may be specified by name (e.g. "en1" or "lo0") or by IP address (e.g. "192.168.4.34"). 204 | * You may also use the special strings "localhost" or "loopback" to specify that 205 | * the socket only accept connections from the local machine. 206 | * 207 | * You can see the list of interfaces via the command line utility "ifconfig", 208 | * or programmatically via the getifaddrs() function. 209 | * 210 | * To accept connections on any interface pass nil, or simply use the acceptOnPort:error: method. 211 | **/ 212 | - (BOOL)acceptOnInterface:(NSString *)interface port:(uint16_t)port error:(NSError **)errPtr; 213 | 214 | #pragma mark Connecting 215 | 216 | /** 217 | * Connects to the given host and port. 218 | * 219 | * This method invokes connectToHost:onPort:viaInterface:withTimeout:error: 220 | * and uses the default interface, and no timeout. 221 | **/ 222 | - (BOOL)connectToHost:(NSString *)host onPort:(uint16_t)port error:(NSError **)errPtr; 223 | 224 | /** 225 | * Connects to the given host and port with an optional timeout. 226 | * 227 | * This method invokes connectToHost:onPort:viaInterface:withTimeout:error: and uses the default interface. 228 | **/ 229 | - (BOOL)connectToHost:(NSString *)host 230 | onPort:(uint16_t)port 231 | withTimeout:(NSTimeInterval)timeout 232 | error:(NSError **)errPtr; 233 | 234 | /** 235 | * Connects to the given host & port, via the optional interface, with an optional timeout. 236 | * 237 | * The host may be a domain name (e.g. "deusty.com") or an IP address string (e.g. "192.168.0.2"). 238 | * The host may also be the special strings "localhost" or "loopback" to specify connecting 239 | * to a service on the local machine. 240 | * 241 | * The interface may be a name (e.g. "en1" or "lo0") or the corresponding IP address (e.g. "192.168.4.35"). 242 | * The interface may also be used to specify the local port (see below). 243 | * 244 | * To not time out use a negative time interval. 245 | * 246 | * This method will return NO if an error is detected, and set the error pointer (if one was given). 247 | * Possible errors would be a nil host, invalid interface, or socket is already connected. 248 | * 249 | * If no errors are detected, this method will start a background connect operation and immediately return YES. 250 | * The delegate callbacks are used to notify you when the socket connects, or if the host was unreachable. 251 | * 252 | * Since this class supports queued reads and writes, you can immediately start reading and/or writing. 253 | * All read/write operations will be queued, and upon socket connection, 254 | * the operations will be dequeued and processed in order. 255 | * 256 | * The interface may optionally contain a port number at the end of the string, separated by a colon. 257 | * This allows you to specify the local port that should be used for the outgoing connection. (read paragraph to end) 258 | * To specify both interface and local port: "en1:8082" or "192.168.4.35:2424". 259 | * To specify only local port: ":8082". 260 | * Please note this is an advanced feature, and is somewhat hidden on purpose. 261 | * You should understand that 99.999% of the time you should NOT specify the local port for an outgoing connection. 262 | * If you think you need to, there is a very good chance you have a fundamental misunderstanding somewhere. 263 | * Local ports do NOT need to match remote ports. In fact, they almost never do. 264 | * This feature is here for networking professionals using very advanced techniques. 265 | **/ 266 | - (BOOL)connectToHost:(NSString *)host 267 | onPort:(uint16_t)port 268 | viaInterface:(NSString *)interface 269 | withTimeout:(NSTimeInterval)timeout 270 | error:(NSError **)errPtr; 271 | 272 | /** 273 | * Connects to the given address, specified as a sockaddr structure wrapped in a NSData object. 274 | * For example, a NSData object returned from NSNetservice's addresses method. 275 | * 276 | * If you have an existing struct sockaddr you can convert it to a NSData object like so: 277 | * struct sockaddr sa -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len]; 278 | * struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len]; 279 | * 280 | * This method invokes connectToAdd 281 | **/ 282 | - (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr; 283 | 284 | /** 285 | * This method is the same as connectToAddress:error: with an additional timeout option. 286 | * To not time out use a negative time interval, or simply use the connectToAddress:error: method. 287 | **/ 288 | - (BOOL)connectToAddress:(NSData *)remoteAddr withTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr; 289 | 290 | /** 291 | * Connects to the given address, using the specified interface and timeout. 292 | * 293 | * The address is specified as a sockaddr structure wrapped in a NSData object. 294 | * For example, a NSData object returned from NSNetservice's addresses method. 295 | * 296 | * If you have an existing struct sockaddr you can convert it to a NSData object like so: 297 | * struct sockaddr sa -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len]; 298 | * struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len]; 299 | * 300 | * The interface may be a name (e.g. "en1" or "lo0") or the corresponding IP address (e.g. "192.168.4.35"). 301 | * The interface may also be used to specify the local port (see below). 302 | * 303 | * The timeout is optional. To not time out use a negative time interval. 304 | * 305 | * This method will return NO if an error is detected, and set the error pointer (if one was given). 306 | * Possible errors would be a nil host, invalid interface, or socket is already connected. 307 | * 308 | * If no errors are detected, this method will start a background connect operation and immediately return YES. 309 | * The delegate callbacks are used to notify you when the socket connects, or if the host was unreachable. 310 | * 311 | * Since this class supports queued reads and writes, you can immediately start reading and/or writing. 312 | * All read/write operations will be queued, and upon socket connection, 313 | * the operations will be dequeued and processed in order. 314 | * 315 | * The interface may optionally contain a port number at the end of the string, separated by a colon. 316 | * This allows you to specify the local port that should be used for the outgoing connection. (read paragraph to end) 317 | * To specify both interface and local port: "en1:8082" or "192.168.4.35:2424". 318 | * To specify only local port: ":8082". 319 | * Please note this is an advanced feature, and is somewhat hidden on purpose. 320 | * You should understand that 99.999% of the time you should NOT specify the local port for an outgoing connection. 321 | * If you think you need to, there is a very good chance you have a fundamental misunderstanding somewhere. 322 | * Local ports do NOT need to match remote ports. In fact, they almost never do. 323 | * This feature is here for networking professionals using very advanced techniques. 324 | **/ 325 | - (BOOL)connectToAddress:(NSData *)remoteAddr 326 | viaInterface:(NSString *)interface 327 | withTimeout:(NSTimeInterval)timeout 328 | error:(NSError **)errPtr; 329 | 330 | #pragma mark Disconnecting 331 | 332 | /** 333 | * Disconnects immediately (synchronously). Any pending reads or writes are dropped. 334 | * 335 | * If the socket is not already disconnected, an invocation to the socketDidDisconnect:withError: delegate method 336 | * will be queued onto the delegateQueue asynchronously (behind any previously queued delegate methods). 337 | * In other words, the disconnected delegate method will be invoked sometime shortly after this method returns. 338 | * 339 | * Please note the recommended way of releasing a GCDAsyncSocket instance (e.g. in a dealloc method) 340 | * [asyncSocket setDelegate:nil]; 341 | * [asyncSocket disconnect]; 342 | * [asyncSocket release]; 343 | * 344 | * If you plan on disconnecting the socket, and then immediately asking it to connect again, 345 | * you'll likely want to do so like this: 346 | * [asyncSocket setDelegate:nil]; 347 | * [asyncSocket disconnect]; 348 | * [asyncSocket setDelegate:self]; 349 | * [asyncSocket connect...]; 350 | **/ 351 | - (void)disconnect; 352 | 353 | /** 354 | * Disconnects after all pending reads have completed. 355 | * After calling this, the read and write methods will do nothing. 356 | * The socket will disconnect even if there are still pending writes. 357 | **/ 358 | - (void)disconnectAfterReading; 359 | 360 | /** 361 | * Disconnects after all pending writes have completed. 362 | * After calling this, the read and write methods will do nothing. 363 | * The socket will disconnect even if there are still pending reads. 364 | **/ 365 | - (void)disconnectAfterWriting; 366 | 367 | /** 368 | * Disconnects after all pending reads and writes have completed. 369 | * After calling this, the read and write methods will do nothing. 370 | **/ 371 | - (void)disconnectAfterReadingAndWriting; 372 | 373 | #pragma mark Diagnostics 374 | 375 | /** 376 | * Returns whether the socket is disconnected or connected. 377 | * 378 | * A disconnected socket may be recycled. 379 | * That is, it can used again for connecting or listening. 380 | * 381 | * If a socket is in the process of connecting, it may be neither disconnected nor connected. 382 | **/ 383 | - (BOOL)isDisconnected; 384 | - (BOOL)isConnected; 385 | 386 | /** 387 | * Returns the local or remote host and port to which this socket is connected, or nil and 0 if not connected. 388 | * The host will be an IP address. 389 | **/ 390 | - (NSString *)connectedHost; 391 | - (uint16_t)connectedPort; 392 | 393 | - (NSString *)localHost; 394 | - (uint16_t)localPort; 395 | 396 | /** 397 | * Returns the local or remote address to which this socket is connected, 398 | * specified as a sockaddr structure wrapped in a NSData object. 399 | * 400 | * See also the connectedHost, connectedPort, localHost and localPort methods. 401 | **/ 402 | - (NSData *)connectedAddress; 403 | - (NSData *)localAddress; 404 | 405 | /** 406 | * Returns whether the socket is IPv4 or IPv6. 407 | * An accepting socket may be both. 408 | **/ 409 | - (BOOL)isIPv4; 410 | - (BOOL)isIPv6; 411 | 412 | /** 413 | * Returns whether or not the socket has been secured via SSL/TLS. 414 | * 415 | * See also the startTLS method. 416 | **/ 417 | - (BOOL)isSecure; 418 | 419 | #pragma mark Reading 420 | 421 | // The readData and writeData methods won't block (they are asynchronous). 422 | // 423 | // When a read is complete the socket:didReadData:withTag: delegate method is dispatched on the delegateQueue. 424 | // When a write is complete the socket:didWriteDataWithTag: delegate method is dispatched on the delegateQueue. 425 | // 426 | // You may optionally set a timeout for any read/write operation. (To not timeout, use a negative time interval.) 427 | // If a read/write opertion times out, the corresponding "socket:shouldTimeout..." delegate method 428 | // is called to optionally allow you to extend the timeout. 429 | // Upon a timeout, the "socket:didDisconnectWithError:" method is called 430 | // 431 | // The tag is for your convenience. 432 | // You can use it as an array index, step number, state id, pointer, etc. 433 | 434 | /** 435 | * Reads the first available bytes that become available on the socket. 436 | * 437 | * If the timeout value is negative, the read operation will not use a timeout. 438 | **/ 439 | - (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag; 440 | 441 | /** 442 | * Reads the first available bytes that become available on the socket. 443 | * The bytes will be appended to the given byte buffer starting at the given offset. 444 | * The given buffer will automatically be increased in size if needed. 445 | * 446 | * If the timeout value is negative, the read operation will not use a timeout. 447 | * If the buffer if nil, the socket will create a buffer for you. 448 | * 449 | * If the bufferOffset is greater than the length of the given buffer, 450 | * the method will do nothing, and the delegate will not be called. 451 | * 452 | * If you pass a buffer, you must not alter it in any way while AsyncSocket is using it. 453 | * After completion, the data returned in socket:didReadData:withTag: will be a subset of the given buffer. 454 | * That is, it will reference the bytes that were appended to the given buffer. 455 | **/ 456 | - (void)readDataWithTimeout:(NSTimeInterval)timeout 457 | buffer:(NSMutableData *)buffer 458 | bufferOffset:(NSUInteger)offset 459 | tag:(long)tag; 460 | 461 | /** 462 | * Reads the first available bytes that become available on the socket. 463 | * The bytes will be appended to the given byte buffer starting at the given offset. 464 | * The given buffer will automatically be increased in size if needed. 465 | * A maximum of length bytes will be read. 466 | * 467 | * If the timeout value is negative, the read operation will not use a timeout. 468 | * If the buffer if nil, a buffer will automatically be created for you. 469 | * If maxLength is zero, no length restriction is enforced. 470 | * 471 | * If the bufferOffset is greater than the length of the given buffer, 472 | * the method will do nothing, and the delegate will not be called. 473 | * 474 | * If you pass a buffer, you must not alter it in any way while AsyncSocket is using it. 475 | * After completion, the data returned in socket:didReadData:withTag: will be a subset of the given buffer. 476 | * That is, it will reference the bytes that were appended to the given buffer. 477 | **/ 478 | - (void)readDataWithTimeout:(NSTimeInterval)timeout 479 | buffer:(NSMutableData *)buffer 480 | bufferOffset:(NSUInteger)offset 481 | maxLength:(NSUInteger)length 482 | tag:(long)tag; 483 | 484 | /** 485 | * Reads the given number of bytes. 486 | * 487 | * If the timeout value is negative, the read operation will not use a timeout. 488 | * 489 | * If the length is 0, this method does nothing and the delegate is not called. 490 | **/ 491 | - (void)readDataToLength:(NSUInteger)length withTimeout:(NSTimeInterval)timeout tag:(long)tag; 492 | 493 | /** 494 | * Reads the given number of bytes. 495 | * The bytes will be appended to the given byte buffer starting at the given offset. 496 | * The given buffer will automatically be increased in size if needed. 497 | * 498 | * If the timeout value is negative, the read operation will not use a timeout. 499 | * If the buffer if nil, a buffer will automatically be created for you. 500 | * 501 | * If the length is 0, this method does nothing and the delegate is not called. 502 | * If the bufferOffset is greater than the length of the given buffer, 503 | * the method will do nothing, and the delegate will not be called. 504 | * 505 | * If you pass a buffer, you must not alter it in any way while AsyncSocket is using it. 506 | * After completion, the data returned in socket:didReadData:withTag: will be a subset of the given buffer. 507 | * That is, it will reference the bytes that were appended to the given buffer. 508 | **/ 509 | - (void)readDataToLength:(NSUInteger)length 510 | withTimeout:(NSTimeInterval)timeout 511 | buffer:(NSMutableData *)buffer 512 | bufferOffset:(NSUInteger)offset 513 | tag:(long)tag; 514 | 515 | /** 516 | * Reads bytes until (and including) the passed "data" parameter, which acts as a separator. 517 | * 518 | * If the timeout value is negative, the read operation will not use a timeout. 519 | * 520 | * If you pass nil or zero-length data as the "data" parameter, 521 | * the method will do nothing, and the delegate will not be called. 522 | * 523 | * To read a line from the socket, use the line separator (e.g. CRLF for HTTP, see below) as the "data" parameter. 524 | * Note that this method is not character-set aware, so if a separator can occur naturally as part of the encoding for 525 | * a character, the read will prematurely end. 526 | **/ 527 | - (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag; 528 | 529 | /** 530 | * Reads bytes until (and including) the passed "data" parameter, which acts as a separator. 531 | * The bytes will be appended to the given byte buffer starting at the given offset. 532 | * The given buffer will automatically be increased in size if needed. 533 | * 534 | * If the timeout value is negative, the read operation will not use a timeout. 535 | * If the buffer if nil, a buffer will automatically be created for you. 536 | * 537 | * If the bufferOffset is greater than the length of the given buffer, 538 | * the method will do nothing, and the delegate will not be called. 539 | * 540 | * If you pass a buffer, you must not alter it in any way while AsyncSocket is using it. 541 | * After completion, the data returned in socket:didReadData:withTag: will be a subset of the given buffer. 542 | * That is, it will reference the bytes that were appended to the given buffer. 543 | * 544 | * To read a line from the socket, use the line separator (e.g. CRLF for HTTP, see below) as the "data" parameter. 545 | * Note that this method is not character-set aware, so if a separator can occur naturally as part of the encoding for 546 | * a character, the read will prematurely end. 547 | **/ 548 | - (void)readDataToData:(NSData *)data 549 | withTimeout:(NSTimeInterval)timeout 550 | buffer:(NSMutableData *)buffer 551 | bufferOffset:(NSUInteger)offset 552 | tag:(long)tag; 553 | 554 | /** 555 | * Reads bytes until (and including) the passed "data" parameter, which acts as a separator. 556 | * 557 | * If the timeout value is negative, the read operation will not use a timeout. 558 | * 559 | * If maxLength is zero, no length restriction is enforced. 560 | * Otherwise if maxLength bytes are read without completing the read, 561 | * it is treated similarly to a timeout - the socket is closed with a GCDAsyncSocketReadMaxedOutError. 562 | * The read will complete successfully if exactly maxLength bytes are read and the given data is found at the end. 563 | * 564 | * If you pass nil or zero-length data as the "data" parameter, 565 | * the method will do nothing, and the delegate will not be called. 566 | * If you pass a maxLength parameter that is less than the length of the data parameter, 567 | * the method will do nothing, and the delegate will not be called. 568 | * 569 | * To read a line from the socket, use the line separator (e.g. CRLF for HTTP, see below) as the "data" parameter. 570 | * Note that this method is not character-set aware, so if a separator can occur naturally as part of the encoding for 571 | * a character, the read will prematurely end. 572 | **/ 573 | - (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout maxLength:(NSUInteger)length tag:(long)tag; 574 | 575 | /** 576 | * Reads bytes until (and including) the passed "data" parameter, which acts as a separator. 577 | * The bytes will be appended to the given byte buffer starting at the given offset. 578 | * The given buffer will automatically be increased in size if needed. 579 | * 580 | * If the timeout value is negative, the read operation will not use a timeout. 581 | * If the buffer if nil, a buffer will automatically be created for you. 582 | * 583 | * If maxLength is zero, no length restriction is enforced. 584 | * Otherwise if maxLength bytes are read without completing the read, 585 | * it is treated similarly to a timeout - the socket is closed with a GCDAsyncSocketReadMaxedOutError. 586 | * The read will complete successfully if exactly maxLength bytes are read and the given data is found at the end. 587 | * 588 | * If you pass a maxLength parameter that is less than the length of the data parameter, 589 | * the method will do nothing, and the delegate will not be called. 590 | * If the bufferOffset is greater than the length of the given buffer, 591 | * the method will do nothing, and the delegate will not be called. 592 | * 593 | * If you pass a buffer, you must not alter it in any way while AsyncSocket is using it. 594 | * After completion, the data returned in socket:didReadData:withTag: will be a subset of the given buffer. 595 | * That is, it will reference the bytes that were appended to the given buffer. 596 | * 597 | * To read a line from the socket, use the line separator (e.g. CRLF for HTTP, see below) as the "data" parameter. 598 | * Note that this method is not character-set aware, so if a separator can occur naturally as part of the encoding for 599 | * a character, the read will prematurely end. 600 | **/ 601 | - (void)readDataToData:(NSData *)data 602 | withTimeout:(NSTimeInterval)timeout 603 | buffer:(NSMutableData *)buffer 604 | bufferOffset:(NSUInteger)offset 605 | maxLength:(NSUInteger)length 606 | tag:(long)tag; 607 | 608 | #pragma mark Writing 609 | 610 | /** 611 | * Writes data to the socket, and calls the delegate when finished. 612 | * 613 | * If you pass in nil or zero-length data, this method does nothing and the delegate will not be called. 614 | * If the timeout value is negative, the write operation will not use a timeout. 615 | **/ 616 | - (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag; 617 | 618 | #pragma mark Security 619 | 620 | /** 621 | * Secures the connection using SSL/TLS. 622 | * 623 | * This method may be called at any time, and the TLS handshake will occur after all pending reads and writes 624 | * are finished. This allows one the option of sending a protocol dependent StartTLS message, and queuing 625 | * the upgrade to TLS at the same time, without having to wait for the write to finish. 626 | * Any reads or writes scheduled after this method is called will occur over the secured connection. 627 | * 628 | * The possible keys and values for the TLS settings are well documented. 629 | * Some possible keys are: 630 | * - kCFStreamSSLLevel 631 | * - kCFStreamSSLAllowsExpiredCertificates 632 | * - kCFStreamSSLAllowsExpiredRoots 633 | * - kCFStreamSSLAllowsAnyRoot 634 | * - kCFStreamSSLValidatesCertificateChain 635 | * - kCFStreamSSLPeerName 636 | * - kCFStreamSSLCertificates 637 | * - kCFStreamSSLIsServer 638 | * 639 | * Please refer to Apple's documentation for associated values, as well as other possible keys. 640 | * 641 | * If you pass in nil or an empty dictionary, the default settings will be used. 642 | * 643 | * The default settings will check to make sure the remote party's certificate is signed by a 644 | * trusted 3rd party certificate agency (e.g. verisign) and that the certificate is not expired. 645 | * However it will not verify the name on the certificate unless you 646 | * give it a name to verify against via the kCFStreamSSLPeerName key. 647 | * The security implications of this are important to understand. 648 | * Imagine you are attempting to create a secure connection to MySecureServer.com, 649 | * but your socket gets directed to MaliciousServer.com because of a hacked DNS server. 650 | * If you simply use the default settings, and MaliciousServer.com has a valid certificate, 651 | * the default settings will not detect any problems since the certificate is valid. 652 | * To properly secure your connection in this particular scenario you 653 | * should set the kCFStreamSSLPeerName property to "MySecureServer.com". 654 | * If you do not know the peer name of the remote host in advance (for example, you're not sure 655 | * if it will be "domain.com" or "www.domain.com"), then you can use the default settings to validate the 656 | * certificate, and then use the X509Certificate class to verify the issuer after the socket has been secured. 657 | * The X509Certificate class is part of the CocoaAsyncSocket open source project. 658 | **/ 659 | - (void)startTLS:(NSDictionary *)tlsSettings; 660 | 661 | #pragma mark Advanced 662 | 663 | /** 664 | * It's not thread-safe to access certain variables from outside the socket's internal queue. 665 | * 666 | * For example, the socket file descriptor. 667 | * File descriptors are simply integers which reference an index in the per-process file table. 668 | * However, when one requests a new file descriptor (by opening a file or socket), 669 | * the file descriptor returned is guaranteed to be the lowest numbered unused descriptor. 670 | * So if we're not careful, the following could be possible: 671 | * 672 | * - Thread A invokes a method which returns the socket's file descriptor. 673 | * - The socket is closed via the socket's internal queue on thread B. 674 | * - Thread C opens a file, and subsequently receives the file descriptor that was previously the socket's FD. 675 | * - Thread A is now accessing/altering the file instead of the socket. 676 | * 677 | * In addition to this, other variables are not actually objects, 678 | * and thus cannot be retained/released or even autoreleased. 679 | * An example is the sslContext, of type SSLContextRef, which is actually a malloc'd struct. 680 | * 681 | * Although there are internal variables that make it difficult to maintain thread-safety, 682 | * it is important to provide access to these variables 683 | * to ensure this class can be used in a wide array of environments. 684 | * This method helps to accomplish this by invoking the current block on the socket's internal queue. 685 | * The methods below can be invoked from within the block to access 686 | * those generally thread-unsafe internal variables in a thread-safe manner. 687 | * The given block will be invoked synchronously on the socket's internal queue. 688 | * 689 | * If you save references to any protected variables and use them outside the block, you do so at your own peril. 690 | **/ 691 | - (void)performBlock:(dispatch_block_t)block; 692 | 693 | /** 694 | * These methods are only available from within the context of a performBlock: invocation. 695 | * See the documentation for the performBlock: method above. 696 | * 697 | * Provides access to the socket's file descriptor(s). 698 | * If the socket is a server socket (is accepting incoming connections), 699 | * it might actually have multiple internal socket file descriptors - one for IPv4 and one for IPv6. 700 | **/ 701 | - (int)socketFD; 702 | - (int)socket4FD; 703 | - (int)socket6FD; 704 | 705 | #if TARGET_OS_IPHONE 706 | 707 | /** 708 | * These methods are only available from within the context of a performBlock: invocation. 709 | * See the documentation for the performBlock: method above. 710 | * 711 | * Provides access to the socket's internal CFReadStream/CFWriteStream. 712 | * 713 | * These streams are only used as workarounds for specific iOS shortcomings: 714 | * 715 | * - Apple has decided to keep the SecureTransport framework private is iOS. 716 | * This means the only supplied way to do SSL/TLS is via CFStream or some other API layered on top of it. 717 | * Thus, in order to provide SSL/TLS support on iOS we are forced to rely on CFStream, 718 | * instead of the preferred and faster and more powerful SecureTransport. 719 | * 720 | * - If a socket doesn't have backgrounding enabled, and that socket is closed while the app is backgrounded, 721 | * Apple only bothers to notify us via the CFStream API. 722 | * The faster and more powerful GCD API isn't notified properly in this case. 723 | * 724 | * See also: (BOOL)enableBackgroundingOnSocket 725 | **/ 726 | - (CFReadStreamRef)readStream; 727 | - (CFWriteStreamRef)writeStream; 728 | 729 | /** 730 | * This method is only available from within the context of a performBlock: invocation. 731 | * See the documentation for the performBlock: method above. 732 | * 733 | * Configures the socket to allow it to operate when the iOS application has been backgrounded. 734 | * In other words, this method creates a read & write stream, and invokes: 735 | * 736 | * CFReadStreamSetProperty(readStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); 737 | * CFWriteStreamSetProperty(writeStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); 738 | * 739 | * Returns YES if successful, NO otherwise. 740 | * 741 | * Note: Apple does not officially support backgrounding server sockets. 742 | * That is, if your socket is accepting incoming connections, Apple does not officially support 743 | * allowing iOS applications to accept incoming connections while an app is backgrounded. 744 | * 745 | * Example usage: 746 | * 747 | * - (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port 748 | * { 749 | * [asyncSocket performBlock:^{ 750 | * [asyncSocket enableBackgroundingOnSocket]; 751 | * }]; 752 | * } 753 | **/ 754 | - (BOOL)enableBackgroundingOnSocket; 755 | 756 | #else 757 | 758 | /** 759 | * This method is only available from within the context of a performBlock: invocation. 760 | * See the documentation for the performBlock: method above. 761 | * 762 | * Provides access to the socket's SSLContext, if SSL/TLS has been started on the socket. 763 | **/ 764 | - (SSLContextRef)sslContext; 765 | 766 | #endif 767 | 768 | #pragma mark Utilities 769 | 770 | /** 771 | * Extracting host and port information from raw address data. 772 | **/ 773 | + (NSString *)hostFromAddress:(NSData *)address; 774 | + (uint16_t)portFromAddress:(NSData *)address; 775 | + (BOOL)getHost:(NSString **)hostPtr port:(uint16_t *)portPtr fromAddress:(NSData *)address; 776 | 777 | /** 778 | * A few common line separators, for use with the readDataToData:... methods. 779 | **/ 780 | + (NSData *)CRLFData; // 0x0D0A 781 | + (NSData *)CRData; // 0x0D 782 | + (NSData *)LFData; // 0x0A 783 | + (NSData *)ZeroData; // 0x00 784 | 785 | @end 786 | 787 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 788 | #pragma mark - 789 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 790 | 791 | @protocol GCDAsyncSocketDelegate 792 | @optional 793 | 794 | /** 795 | * This method is called immediately prior to socket:didAcceptNewSocket:. 796 | * It optionally allows a listening socket to specify the socketQueue for a new accepted socket. 797 | * If this method is not implemented, or returns NULL, the new accepted socket will create its own default queue. 798 | * 799 | * Since you cannot autorelease a dispatch_queue, 800 | * this method uses the "new" prefix in its name to specify that the returned queue has been retained. 801 | * 802 | * Thus you could do something like this in the implementation: 803 | * return dispatch_queue_create("MyQueue", NULL); 804 | * 805 | * If you are placing multiple sockets on the same queue, 806 | * then care should be taken to increment the retain count each time this method is invoked. 807 | * 808 | * For example, your implementation might look something like this: 809 | * dispatch_retain(myExistingQueue); 810 | * return myExistingQueue; 811 | **/ 812 | - (dispatch_queue_t)newSocketQueueForConnectionFromAddress:(NSData *)address onSocket:(GCDAsyncSocket *)sock; 813 | 814 | /** 815 | * Called when a socket accepts a connection. 816 | * Another socket is automatically spawned to handle it. 817 | * 818 | * You must retain the newSocket if you wish to handle the connection. 819 | * Otherwise the newSocket instance will be released and the spawned connection will be closed. 820 | * 821 | * By default the new socket will have the same delegate and delegateQueue. 822 | * You may, of course, change this at any time. 823 | **/ 824 | - (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket; 825 | 826 | /** 827 | * Called when a socket connects and is ready for reading and writing. 828 | * The host parameter will be an IP address, not a DNS name. 829 | **/ 830 | - (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port; 831 | 832 | /** 833 | * Called when a socket has completed reading the requested data into memory. 834 | * Not called if there is an error. 835 | **/ 836 | - (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag; 837 | 838 | /** 839 | * Called when a socket has read in data, but has not yet completed the read. 840 | * This would occur if using readToData: or readToLength: methods. 841 | * It may be used to for things such as updating progress bars. 842 | **/ 843 | - (void)socket:(GCDAsyncSocket *)sock didReadPartialDataOfLength:(NSUInteger)partialLength tag:(long)tag; 844 | 845 | /** 846 | * Called when a socket has completed writing the requested data. Not called if there is an error. 847 | **/ 848 | - (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag; 849 | 850 | /** 851 | * Called when a socket has written some data, but has not yet completed the entire write. 852 | * It may be used to for things such as updating progress bars. 853 | **/ 854 | - (void)socket:(GCDAsyncSocket *)sock didWritePartialDataOfLength:(NSUInteger)partialLength tag:(long)tag; 855 | 856 | /** 857 | * Called if a read operation has reached its timeout without completing. 858 | * This method allows you to optionally extend the timeout. 859 | * If you return a positive time interval (> 0) the read's timeout will be extended by the given amount. 860 | * If you don't implement this method, or return a non-positive time interval (<= 0) the read will timeout as usual. 861 | * 862 | * The elapsed parameter is the sum of the original timeout, plus any additions previously added via this method. 863 | * The length parameter is the number of bytes that have been read so far for the read operation. 864 | * 865 | * Note that this method may be called multiple times for a single read if you return positive numbers. 866 | **/ 867 | - (NSTimeInterval)socket:(GCDAsyncSocket *)sock shouldTimeoutReadWithTag:(long)tag 868 | elapsed:(NSTimeInterval)elapsed 869 | bytesDone:(NSUInteger)length; 870 | 871 | /** 872 | * Called if a write operation has reached its timeout without completing. 873 | * This method allows you to optionally extend the timeout. 874 | * If you return a positive time interval (> 0) the write's timeout will be extended by the given amount. 875 | * If you don't implement this method, or return a non-positive time interval (<= 0) the write will timeout as usual. 876 | * 877 | * The elapsed parameter is the sum of the original timeout, plus any additions previously added via this method. 878 | * The length parameter is the number of bytes that have been written so far for the write operation. 879 | * 880 | * Note that this method may be called multiple times for a single write if you return positive numbers. 881 | **/ 882 | - (NSTimeInterval)socket:(GCDAsyncSocket *)sock shouldTimeoutWriteWithTag:(long)tag 883 | elapsed:(NSTimeInterval)elapsed 884 | bytesDone:(NSUInteger)length; 885 | 886 | /** 887 | * Conditionally called if the read stream closes, but the write stream may still be writeable. 888 | * 889 | * This delegate method is only called if autoDisconnectOnClosedReadStream has been set to NO. 890 | * See the discussion on the autoDisconnectOnClosedReadStream method for more information. 891 | **/ 892 | - (void)socketDidCloseReadStream:(GCDAsyncSocket *)sock; 893 | 894 | /** 895 | * Called when a socket disconnects with or without error. 896 | * 897 | * If you call the disconnect method, and the socket wasn't already disconnected, 898 | * this delegate method will be called before the disconnect method returns. 899 | **/ 900 | - (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err; 901 | 902 | /** 903 | * Called after the socket has successfully completed SSL/TLS negotiation. 904 | * This method is not called unless you use the provided startTLS method. 905 | * 906 | * If a SSL/TLS negotiation fails (invalid certificate, etc) then the socket will immediately close, 907 | * and the socketDidDisconnect:withError: delegate method will be called with the specific SSL error code. 908 | **/ 909 | - (void)socketDidSecure:(GCDAsyncSocket *)sock; 910 | 911 | @end 912 | -------------------------------------------------------------------------------- /FakeReachability/ReachabilityFaker.h: -------------------------------------------------------------------------------- 1 | // 2 | // ReachabilityFaker.h 3 | // FakeReachability 4 | // 5 | // Created by Moritz Haarmann on 03.12.11. 6 | // Copyright (c) 2011 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GCDAsyncSocket.h" 11 | 12 | @interface ReachabilityFaker : NSObject 13 | 14 | @property (nonatomic,retain) GCDAsyncSocket* serverSocket; 15 | @property (nonatomic) BOOL offline; 16 | @property (nonatomic,retain) NSNetService* service; 17 | 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /FakeReachability/ReachabilityFaker.m: -------------------------------------------------------------------------------- 1 | // 2 | // ReachabilityFaker.m 3 | // FakeReachability 4 | // 5 | // Created by Moritz Haarmann on 03.12.11. 6 | // Copyright (c) 2011 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "ReachabilityFaker.h" 10 | 11 | 12 | @interface ReachabilityFaker(Private) 13 | -(void)updateInternalState:(BOOL)isNowOffline; 14 | @end 15 | 16 | @implementation ReachabilityFaker 17 | 18 | @synthesize serverSocket; 19 | @synthesize offline; 20 | @synthesize service; 21 | 22 | -(id)init { 23 | self = [super init]; 24 | if ( self ){ 25 | [self updateInternalState:NO]; 26 | self.offline = NO; 27 | } 28 | return self; 29 | } 30 | 31 | -(void)updateInternalState:(BOOL)isNowOffline { 32 | if ( isNowOffline != self.offline){ 33 | if ( isNowOffline ){ 34 | self.service = [[[NSNetService alloc] initWithDomain:@"local." type:@"_fakeoffline._tcp" name:@"Fake Reachability" port:2131] autorelease]; 35 | [self.service publish]; 36 | } else { 37 | NSLog(@"Removing the service"); 38 | if ( self.service ){ 39 | [self.service stop]; 40 | 41 | self.service.delegate = nil; 42 | 43 | self.service = nil; 44 | 45 | } 46 | } 47 | self.offline = isNowOffline; 48 | } 49 | } 50 | 51 | 52 | -(IBAction)buttonChangedAction:(NSSegmentedControl*)sender { 53 | BOOL isOffline = !( sender.selectedSegment == 0 ); 54 | [self updateInternalState:isOffline]; 55 | } 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /FakeReachability/en.lproj/Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf0\ansi{\fonttbl\f0\fswiss Helvetica;} 2 | {\colortbl;\red255\green255\blue255;} 3 | \paperw9840\paperh8400 4 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural 5 | 6 | \f0\b\fs24 \cf0 Engineering: 7 | \b0 \ 8 | Some people\ 9 | \ 10 | 11 | \b Human Interface Design: 12 | \b0 \ 13 | Some other people\ 14 | \ 15 | 16 | \b Testing: 17 | \b0 \ 18 | Hopefully not nobody\ 19 | \ 20 | 21 | \b Documentation: 22 | \b0 \ 23 | Whoever\ 24 | \ 25 | 26 | \b With special thanks to: 27 | \b0 \ 28 | Mom\ 29 | } 30 | -------------------------------------------------------------------------------- /FakeReachability/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /FakeReachability/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // FakeReachability 4 | // 5 | // Created by Moritz Haarmann on 03.12.11. 6 | // Copyright (c) 2011 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, char *argv[]) 12 | { 13 | return NSApplicationMain(argc, (const char **)argv); 14 | } 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fake Reachability for iOS Development 2 | 3 | You certainly know the pain when debugging online/offline functionality by turning airport-mode on and off all the time? This tiny package here might help you solve this problem. 4 | 5 | It consists of a simple Mac-App and a tuned Reachability.[hm] that observes the Mac Apps desired Online-Offline state. It then acts like the regular Reachability, online that you are in charge and don't need to change your real connectivity on the Device or the Simulator. 6 | 7 | Warning: This may explode at any time. Feel free to improve it. 8 | 9 | Licensed under the DWTFYWPL, Do What The Fuck You Want To Public License ( http://sam.zoy.org/wtfpl/COPYING ). 10 | 11 | ## Install 12 | Simply drop Reachability.[hm] in your project and set the DEBUG flag. If the DEBUG flag is not set it will behave exactly like the common Reachability. 13 | 14 | The default state is online, and you need the mac app to manually set the state to offline. If you want to hack yourself, you just need to publish a Bonjour-Service called "_fakeoffline._tcp". Simple, huh? -------------------------------------------------------------------------------- /Reachability.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | File: Reachability.h 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 | 49 | #import 50 | #import 51 | 52 | typedef enum { 53 | NotReachable = 0, 54 | ReachableViaWiFi, 55 | ReachableViaWWAN 56 | } NetworkStatus; 57 | #define kReachabilityChangedNotification @"kNetworkReachabilityChangedNotification" 58 | 59 | @interface Reachability: NSObject 60 | { 61 | BOOL localWiFiRef; 62 | SCNetworkReachabilityRef reachabilityRef; 63 | } 64 | 65 | //reachabilityWithHostName- Use to check the reachability of a particular host name. 66 | + (Reachability*) reachabilityWithHostName: (NSString*) hostName; 67 | 68 | //reachabilityWithAddress- Use to check the reachability of a particular IP address. 69 | + (Reachability*) reachabilityWithAddress: (const struct sockaddr_in*) hostAddress; 70 | 71 | //reachabilityForInternetConnection- checks whether the default route is available. 72 | // Should be used by applications that do not connect to a particular host 73 | + (Reachability*) reachabilityForInternetConnection; 74 | 75 | //reachabilityForLocalWiFi- checks whether a local wifi connection is available. 76 | + (Reachability*) reachabilityForLocalWiFi; 77 | 78 | //Start listening for reachability notifications on the current run loop 79 | - (BOOL) startNotifier; 80 | - (void) stopNotifier; 81 | 82 | - (NetworkStatus) currentReachabilityStatus; 83 | //WWAN may be available, but not active until a connection has been established. 84 | //WiFi may require a connection for VPN on Demand. 85 | - (BOOL) connectionRequired; 86 | #ifdef DEBUG 87 | @property (nonatomic) BOOL isFakeOffline; 88 | @property (nonatomic,retain) NSNetServiceBrowser* fakeBrowser; 89 | #endif 90 | @end 91 | 92 | 93 | -------------------------------------------------------------------------------- /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 49 | #import 50 | #import 51 | #import 52 | #import 53 | #import 54 | #import "GCDAsyncSocket.h" 55 | 56 | #import 57 | #import 58 | #import 59 | #import "Reachability.h" 60 | 61 | #define kShouldPrintReachabilityFlags 0 62 | 63 | 64 | 65 | static void PrintReachabilityFlags(SCNetworkReachabilityFlags flags, const char* comment) 66 | { 67 | #if kShouldPrintReachabilityFlags 68 | 69 | NSLog(@"Reachability Flag Status: %c%c %c%c%c%c%c%c%c %s\n", 70 | (flags & kSCNetworkReachabilityFlagsIsWWAN) ? 'W' : '-', 71 | (flags & kSCNetworkReachabilityFlagsReachable) ? 'R' : '-', 72 | 73 | (flags & kSCNetworkReachabilityFlagsTransientConnection) ? 't' : '-', 74 | (flags & kSCNetworkReachabilityFlagsConnectionRequired) ? 'c' : '-', 75 | (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) ? 'C' : '-', 76 | (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-', 77 | (flags & kSCNetworkReachabilityFlagsConnectionOnDemand) ? 'D' : '-', 78 | (flags & kSCNetworkReachabilityFlagsIsLocalAddress) ? 'l' : '-', 79 | (flags & kSCNetworkReachabilityFlagsIsDirect) ? 'd' : '-', 80 | comment 81 | ); 82 | #endif 83 | } 84 | 85 | 86 | @implementation Reachability 87 | 88 | #ifdef DEBUG 89 | @synthesize isFakeOffline; 90 | @synthesize fakeBrowser; 91 | 92 | 93 | #endif 94 | 95 | 96 | static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info) 97 | { 98 | #pragma unused (target, flags) 99 | NSCAssert(info != NULL, @"info was NULL in ReachabilityCallback"); 100 | NSCAssert([(NSObject*) info isKindOfClass: [Reachability class]], @"info was wrong class in ReachabilityCallback"); 101 | 102 | //We're on the main RunLoop, so an NSAutoreleasePool is not necessary, but is added defensively 103 | // in case someon uses the Reachablity object in a different thread. 104 | NSAutoreleasePool* myPool = [[NSAutoreleasePool alloc] init]; 105 | 106 | Reachability* noteObject = (Reachability*) info; 107 | // Post a notification to notify the client that the network reachability changed. 108 | [[NSNotificationCenter defaultCenter] postNotificationName: kReachabilityChangedNotification object: noteObject]; 109 | 110 | [myPool release]; 111 | } 112 | 113 | - (BOOL) startNotifier 114 | { 115 | #ifndef DEBUG 116 | BOOL retVal = NO; 117 | SCNetworkReachabilityContext context = {0, self, NULL, NULL, NULL}; 118 | if(SCNetworkReachabilitySetCallback(reachabilityRef, ReachabilityCallback, &context)) 119 | { 120 | if(SCNetworkReachabilityScheduleWithRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) 121 | { 122 | retVal = YES; 123 | } 124 | } 125 | return retVal; 126 | #elsif 127 | return YES 128 | #endif 129 | } 130 | 131 | - (void) stopNotifier 132 | { 133 | #ifndef DEBUG 134 | if(reachabilityRef!= NULL) 135 | { 136 | SCNetworkReachabilityUnscheduleFromRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); 137 | } 138 | #endif 139 | } 140 | 141 | - (void) dealloc 142 | { 143 | [self stopNotifier]; 144 | if(reachabilityRef!= NULL) 145 | { 146 | CFRelease(reachabilityRef); 147 | } 148 | [super dealloc]; 149 | } 150 | 151 | + (Reachability*) reachabilityWithHostName: (NSString*) hostName; 152 | { 153 | Reachability* retVal = NULL; 154 | SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]); 155 | if(reachability!= NULL) 156 | { 157 | retVal= [[[self alloc] init] autorelease]; 158 | if(retVal!= NULL) 159 | { 160 | retVal->reachabilityRef = reachability; 161 | retVal->localWiFiRef = NO; 162 | } 163 | } 164 | return retVal; 165 | 166 | } 167 | 168 | + (Reachability*) reachabilityWithAddress: (const struct sockaddr_in*) hostAddress; 169 | { 170 | SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)hostAddress); 171 | Reachability* retVal = NULL; 172 | if(reachability!= NULL) 173 | { 174 | retVal= [[[self alloc] init] autorelease]; 175 | if(retVal!= NULL) 176 | { 177 | retVal->reachabilityRef = reachability; 178 | retVal->localWiFiRef = NO; 179 | } 180 | } 181 | return retVal; 182 | } 183 | 184 | + (Reachability*) reachabilityForInternetConnection; 185 | { 186 | struct sockaddr_in zeroAddress; 187 | bzero(&zeroAddress, sizeof(zeroAddress)); 188 | zeroAddress.sin_len = sizeof(zeroAddress); 189 | zeroAddress.sin_family = AF_INET; 190 | return [self reachabilityWithAddress: &zeroAddress]; 191 | } 192 | 193 | + (Reachability*) reachabilityForLocalWiFi; 194 | { 195 | struct sockaddr_in localWifiAddress; 196 | bzero(&localWifiAddress, sizeof(localWifiAddress)); 197 | localWifiAddress.sin_len = sizeof(localWifiAddress); 198 | localWifiAddress.sin_family = AF_INET; 199 | // IN_LINKLOCALNETNUM is defined in as 169.254.0.0 200 | localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM); 201 | Reachability* retVal = [self reachabilityWithAddress: &localWifiAddress]; 202 | if(retVal!= NULL) 203 | { 204 | retVal->localWiFiRef = YES; 205 | } 206 | return retVal; 207 | } 208 | 209 | #pragma mark Network Flag Handling 210 | 211 | - (NetworkStatus) localWiFiStatusForFlags: (SCNetworkReachabilityFlags) flags 212 | { 213 | PrintReachabilityFlags(flags, "localWiFiStatusForFlags"); 214 | 215 | BOOL retVal = NotReachable; 216 | if((flags & kSCNetworkReachabilityFlagsReachable) && (flags & kSCNetworkReachabilityFlagsIsDirect)) 217 | { 218 | retVal = ReachableViaWiFi; 219 | } 220 | return retVal; 221 | } 222 | 223 | - (NetworkStatus) networkStatusForFlags: (SCNetworkReachabilityFlags) flags 224 | { 225 | PrintReachabilityFlags(flags, "networkStatusForFlags"); 226 | if ((flags & kSCNetworkReachabilityFlagsReachable) == 0) 227 | { 228 | // if target host is not reachable 229 | return NotReachable; 230 | } 231 | 232 | BOOL retVal = NotReachable; 233 | 234 | if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0) 235 | { 236 | // if target host is reachable and no connection is required 237 | // then we'll assume (for now) that your on Wi-Fi 238 | retVal = ReachableViaWiFi; 239 | } 240 | 241 | 242 | if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || 243 | (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0)) 244 | { 245 | // ... and the connection is on-demand (or on-traffic) if the 246 | // calling application is using the CFSocketStream or higher APIs 247 | 248 | if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0) 249 | { 250 | // ... and no [user] intervention is needed 251 | retVal = ReachableViaWiFi; 252 | } 253 | } 254 | 255 | if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN) 256 | { 257 | // ... but WWAN connections are OK if the calling application 258 | // is using the CFNetwork (CFSocketStream?) APIs. 259 | retVal = ReachableViaWWAN; 260 | } 261 | return retVal; 262 | } 263 | 264 | - (BOOL) connectionRequired; 265 | { 266 | NSAssert(reachabilityRef != NULL, @"connectionRequired called with NULL reachabilityRef"); 267 | SCNetworkReachabilityFlags flags; 268 | if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) 269 | { 270 | return (flags & kSCNetworkReachabilityFlagsConnectionRequired); 271 | } 272 | return NO; 273 | } 274 | 275 | - (NetworkStatus) currentReachabilityStatus 276 | { 277 | #ifndef DEBUG 278 | NSAssert(reachabilityRef != NULL, @"currentNetworkStatus called with NULL reachabilityRef"); 279 | NetworkStatus retVal = NotReachable; 280 | SCNetworkReachabilityFlags flags; 281 | if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) 282 | { 283 | if(localWiFiRef) 284 | { 285 | retVal = [self localWiFiStatusForFlags: flags]; 286 | } 287 | else 288 | { 289 | retVal = [self networkStatusForFlags: flags]; 290 | } 291 | } 292 | return retVal; 293 | #elsif 294 | if ( self.isFakeOffline ){ 295 | return NotReachable; 296 | } else { 297 | return ReachableViaWiFi; 298 | } 299 | 300 | #endif 301 | } 302 | 303 | #pragma mark - Hacky hacky fake reachability 304 | 305 | #ifdef DEBUG 306 | 307 | /** 308 | * Overriden, need to create the bonjour browser 309 | */ 310 | -(id)init { 311 | self = [super init]; 312 | if ( self ){ 313 | self.fakeBrowser = [[[NSNetServiceBrowser alloc] init] autorelease]; 314 | self.fakeBrowser.delegate = self; 315 | [self.fakeBrowser searchForServicesOfType:@"_fakeoffline._tcp" inDomain:@"local."]; 316 | self.isFakeOffline = NO; 317 | 318 | } 319 | return self; 320 | } 321 | 322 | -(void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didFindService:(NSNetService *)aNetService moreComing:(BOOL)moreComing { 323 | // Discovered a "Fake Offline" Service. Put this to offline if not already, and post a notification. 324 | if ( !self.isFakeOffline ){ 325 | self.isFakeOffline = YES; 326 | [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:kReachabilityChangedNotification object:self]]; 327 | } 328 | } 329 | 330 | -(void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didRemoveService:(NSNetService *)aNetService moreComing:(BOOL)moreComing { 331 | // The fake beacon disappeared, which means we are online again. 332 | if ( self.isFakeOffline ){ 333 | self.isFakeOffline = NO; 334 | [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:kReachabilityChangedNotification object:self]]; 335 | 336 | } 337 | } 338 | 339 | #endif 340 | 341 | @end 342 | --------------------------------------------------------------------------------