├── Example ├── iOS Example.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata └── iOS Example │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── LaunchImage.launchimage │ │ └── Contents.json │ ├── Info.plist │ ├── Prefix.pch │ ├── WebViewController.h │ ├── WebViewController.m │ ├── en.lproj │ └── InfoPlist.strings │ └── main.m ├── LICENSE.md ├── NSEtcHosts.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── NSEtcHosts ├── EtcHostsURLProtocol.h ├── EtcHostsURLProtocol.m └── NSEtcHosts.h └── README.md /Example/iOS Example.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | F8BC187118FE5C5C00684397 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8BC187018FE5C5C00684397 /* Foundation.framework */; }; 11 | F8BC187318FE5C5C00684397 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8BC187218FE5C5C00684397 /* CoreGraphics.framework */; }; 12 | F8BC187518FE5C5C00684397 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8BC187418FE5C5C00684397 /* UIKit.framework */; }; 13 | F8BC187B18FE5C5C00684397 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F8BC187918FE5C5C00684397 /* InfoPlist.strings */; }; 14 | F8BC187D18FE5C5C00684397 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F8BC187C18FE5C5C00684397 /* main.m */; }; 15 | F8BC188118FE5C5C00684397 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F8BC188018FE5C5C00684397 /* AppDelegate.m */; }; 16 | F8BC188A18FE5C5C00684397 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8BC188918FE5C5C00684397 /* XCTest.framework */; }; 17 | F8BC188B18FE5C5C00684397 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8BC187018FE5C5C00684397 /* Foundation.framework */; }; 18 | F8BC188C18FE5C5C00684397 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8BC187418FE5C5C00684397 /* UIKit.framework */; }; 19 | F8BC18A218FE5D3000684397 /* EtcHostsURLProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = F8BC18A118FE5D3000684397 /* EtcHostsURLProtocol.m */; }; 20 | F8BC18A618FE67F200684397 /* WebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F8BC18A518FE67F200684397 /* WebViewController.m */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXContainerItemProxy section */ 24 | F8BC188D18FE5C5C00684397 /* PBXContainerItemProxy */ = { 25 | isa = PBXContainerItemProxy; 26 | containerPortal = F8BC186518FE5C5B00684397 /* Project object */; 27 | proxyType = 1; 28 | remoteGlobalIDString = F8BC186C18FE5C5C00684397; 29 | remoteInfo = NSEtcHosts; 30 | }; 31 | /* End PBXContainerItemProxy section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | F8BC186D18FE5C5C00684397 /* NSEtcHosts.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NSEtcHosts.app; sourceTree = BUILT_PRODUCTS_DIR; }; 35 | F8BC187018FE5C5C00684397 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 36 | F8BC187218FE5C5C00684397 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 37 | F8BC187418FE5C5C00684397 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 38 | F8BC187818FE5C5C00684397 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 39 | F8BC187A18FE5C5C00684397 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 40 | F8BC187C18FE5C5C00684397 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 41 | F8BC187E18FE5C5C00684397 /* Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Prefix.pch; sourceTree = ""; }; 42 | F8BC187F18FE5C5C00684397 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 43 | F8BC188018FE5C5C00684397 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 44 | F8BC188818FE5C5C00684397 /* NSEtcHostsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NSEtcHostsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 45 | F8BC188918FE5C5C00684397 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 46 | F8BC18A018FE5D3000684397 /* EtcHostsURLProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EtcHostsURLProtocol.h; path = ../NSEtcHosts/EtcHostsURLProtocol.h; sourceTree = ""; }; 47 | F8BC18A118FE5D3000684397 /* EtcHostsURLProtocol.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = EtcHostsURLProtocol.m; path = ../NSEtcHosts/EtcHostsURLProtocol.m; sourceTree = ""; }; 48 | F8BC18A418FE67F200684397 /* WebViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebViewController.h; sourceTree = ""; }; 49 | F8BC18A518FE67F200684397 /* WebViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WebViewController.m; sourceTree = ""; }; 50 | F8BC18A718FE6FE300684397 /* NSEtcHosts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NSEtcHosts.h; path = ../NSEtcHosts/NSEtcHosts.h; sourceTree = ""; }; 51 | /* End PBXFileReference section */ 52 | 53 | /* Begin PBXFrameworksBuildPhase section */ 54 | F8BC186A18FE5C5C00684397 /* Frameworks */ = { 55 | isa = PBXFrameworksBuildPhase; 56 | buildActionMask = 2147483647; 57 | files = ( 58 | F8BC187318FE5C5C00684397 /* CoreGraphics.framework in Frameworks */, 59 | F8BC187518FE5C5C00684397 /* UIKit.framework in Frameworks */, 60 | F8BC187118FE5C5C00684397 /* Foundation.framework in Frameworks */, 61 | ); 62 | runOnlyForDeploymentPostprocessing = 0; 63 | }; 64 | F8BC188518FE5C5C00684397 /* Frameworks */ = { 65 | isa = PBXFrameworksBuildPhase; 66 | buildActionMask = 2147483647; 67 | files = ( 68 | F8BC188A18FE5C5C00684397 /* XCTest.framework in Frameworks */, 69 | F8BC188C18FE5C5C00684397 /* UIKit.framework in Frameworks */, 70 | F8BC188B18FE5C5C00684397 /* Foundation.framework in Frameworks */, 71 | ); 72 | runOnlyForDeploymentPostprocessing = 0; 73 | }; 74 | /* End PBXFrameworksBuildPhase section */ 75 | 76 | /* Begin PBXGroup section */ 77 | F8BC186418FE5C5B00684397 = { 78 | isa = PBXGroup; 79 | children = ( 80 | F8BC187618FE5C5C00684397 /* NSEtcHosts */, 81 | F8BC186F18FE5C5C00684397 /* Frameworks */, 82 | F8BC186E18FE5C5C00684397 /* Products */, 83 | F8BC189F18FE5CF400684397 /* Vendor */, 84 | ); 85 | sourceTree = ""; 86 | }; 87 | F8BC186E18FE5C5C00684397 /* Products */ = { 88 | isa = PBXGroup; 89 | children = ( 90 | F8BC186D18FE5C5C00684397 /* NSEtcHosts.app */, 91 | F8BC188818FE5C5C00684397 /* NSEtcHostsTests.xctest */, 92 | ); 93 | name = Products; 94 | sourceTree = ""; 95 | }; 96 | F8BC186F18FE5C5C00684397 /* Frameworks */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | F8BC187018FE5C5C00684397 /* Foundation.framework */, 100 | F8BC187218FE5C5C00684397 /* CoreGraphics.framework */, 101 | F8BC187418FE5C5C00684397 /* UIKit.framework */, 102 | F8BC188918FE5C5C00684397 /* XCTest.framework */, 103 | ); 104 | name = Frameworks; 105 | sourceTree = ""; 106 | }; 107 | F8BC187618FE5C5C00684397 /* NSEtcHosts */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | F8BC187F18FE5C5C00684397 /* AppDelegate.h */, 111 | F8BC188018FE5C5C00684397 /* AppDelegate.m */, 112 | F8BC18A418FE67F200684397 /* WebViewController.h */, 113 | F8BC18A518FE67F200684397 /* WebViewController.m */, 114 | F8BC187718FE5C5C00684397 /* Supporting Files */, 115 | ); 116 | name = NSEtcHosts; 117 | path = "iOS Example"; 118 | sourceTree = ""; 119 | }; 120 | F8BC187718FE5C5C00684397 /* Supporting Files */ = { 121 | isa = PBXGroup; 122 | children = ( 123 | F8BC187818FE5C5C00684397 /* Info.plist */, 124 | F8BC187918FE5C5C00684397 /* InfoPlist.strings */, 125 | F8BC187C18FE5C5C00684397 /* main.m */, 126 | F8BC187E18FE5C5C00684397 /* Prefix.pch */, 127 | ); 128 | name = "Supporting Files"; 129 | sourceTree = ""; 130 | }; 131 | F8BC189F18FE5CF400684397 /* Vendor */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | F8BC18A318FE5D3500684397 /* NSEtcHosts */, 135 | ); 136 | name = Vendor; 137 | sourceTree = ""; 138 | }; 139 | F8BC18A318FE5D3500684397 /* NSEtcHosts */ = { 140 | isa = PBXGroup; 141 | children = ( 142 | F8BC18A718FE6FE300684397 /* NSEtcHosts.h */, 143 | F8BC18A018FE5D3000684397 /* EtcHostsURLProtocol.h */, 144 | F8BC18A118FE5D3000684397 /* EtcHostsURLProtocol.m */, 145 | ); 146 | name = NSEtcHosts; 147 | sourceTree = ""; 148 | }; 149 | /* End PBXGroup section */ 150 | 151 | /* Begin PBXNativeTarget section */ 152 | F8BC186C18FE5C5C00684397 /* NSEtcHosts */ = { 153 | isa = PBXNativeTarget; 154 | buildConfigurationList = F8BC189918FE5C5C00684397 /* Build configuration list for PBXNativeTarget "NSEtcHosts" */; 155 | buildPhases = ( 156 | F8BC186918FE5C5C00684397 /* Sources */, 157 | F8BC186A18FE5C5C00684397 /* Frameworks */, 158 | F8BC186B18FE5C5C00684397 /* Resources */, 159 | ); 160 | buildRules = ( 161 | ); 162 | dependencies = ( 163 | ); 164 | name = NSEtcHosts; 165 | productName = NSEtcHosts; 166 | productReference = F8BC186D18FE5C5C00684397 /* NSEtcHosts.app */; 167 | productType = "com.apple.product-type.application"; 168 | }; 169 | F8BC188718FE5C5C00684397 /* NSEtcHostsTests */ = { 170 | isa = PBXNativeTarget; 171 | buildConfigurationList = F8BC189C18FE5C5C00684397 /* Build configuration list for PBXNativeTarget "NSEtcHostsTests" */; 172 | buildPhases = ( 173 | F8BC188418FE5C5C00684397 /* Sources */, 174 | F8BC188518FE5C5C00684397 /* Frameworks */, 175 | F8BC188618FE5C5C00684397 /* Resources */, 176 | ); 177 | buildRules = ( 178 | ); 179 | dependencies = ( 180 | F8BC188E18FE5C5C00684397 /* PBXTargetDependency */, 181 | ); 182 | name = NSEtcHostsTests; 183 | productName = NSEtcHostsTests; 184 | productReference = F8BC188818FE5C5C00684397 /* NSEtcHostsTests.xctest */; 185 | productType = "com.apple.product-type.bundle.unit-test"; 186 | }; 187 | /* End PBXNativeTarget section */ 188 | 189 | /* Begin PBXProject section */ 190 | F8BC186518FE5C5B00684397 /* Project object */ = { 191 | isa = PBXProject; 192 | attributes = { 193 | LastUpgradeCheck = 0510; 194 | ORGANIZATIONNAME = "Mattt"; 195 | TargetAttributes = { 196 | F8BC188718FE5C5C00684397 = { 197 | TestTargetID = F8BC186C18FE5C5C00684397; 198 | }; 199 | }; 200 | }; 201 | buildConfigurationList = F8BC186818FE5C5B00684397 /* Build configuration list for PBXProject "iOS Example" */; 202 | compatibilityVersion = "Xcode 3.2"; 203 | developmentRegion = English; 204 | hasScannedForEncodings = 0; 205 | knownRegions = ( 206 | English, 207 | en, 208 | ); 209 | mainGroup = F8BC186418FE5C5B00684397; 210 | productRefGroup = F8BC186E18FE5C5C00684397 /* Products */; 211 | projectDirPath = ""; 212 | projectRoot = ""; 213 | targets = ( 214 | F8BC186C18FE5C5C00684397 /* NSEtcHosts */, 215 | F8BC188718FE5C5C00684397 /* NSEtcHostsTests */, 216 | ); 217 | }; 218 | /* End PBXProject section */ 219 | 220 | /* Begin PBXResourcesBuildPhase section */ 221 | F8BC186B18FE5C5C00684397 /* Resources */ = { 222 | isa = PBXResourcesBuildPhase; 223 | buildActionMask = 2147483647; 224 | files = ( 225 | F8BC187B18FE5C5C00684397 /* InfoPlist.strings in Resources */, 226 | ); 227 | runOnlyForDeploymentPostprocessing = 0; 228 | }; 229 | F8BC188618FE5C5C00684397 /* Resources */ = { 230 | isa = PBXResourcesBuildPhase; 231 | buildActionMask = 2147483647; 232 | files = ( 233 | ); 234 | runOnlyForDeploymentPostprocessing = 0; 235 | }; 236 | /* End PBXResourcesBuildPhase section */ 237 | 238 | /* Begin PBXSourcesBuildPhase section */ 239 | F8BC186918FE5C5C00684397 /* Sources */ = { 240 | isa = PBXSourcesBuildPhase; 241 | buildActionMask = 2147483647; 242 | files = ( 243 | F8BC18A218FE5D3000684397 /* EtcHostsURLProtocol.m in Sources */, 244 | F8BC188118FE5C5C00684397 /* AppDelegate.m in Sources */, 245 | F8BC187D18FE5C5C00684397 /* main.m in Sources */, 246 | F8BC18A618FE67F200684397 /* WebViewController.m in Sources */, 247 | ); 248 | runOnlyForDeploymentPostprocessing = 0; 249 | }; 250 | F8BC188418FE5C5C00684397 /* Sources */ = { 251 | isa = PBXSourcesBuildPhase; 252 | buildActionMask = 2147483647; 253 | files = ( 254 | ); 255 | runOnlyForDeploymentPostprocessing = 0; 256 | }; 257 | /* End PBXSourcesBuildPhase section */ 258 | 259 | /* Begin PBXTargetDependency section */ 260 | F8BC188E18FE5C5C00684397 /* PBXTargetDependency */ = { 261 | isa = PBXTargetDependency; 262 | target = F8BC186C18FE5C5C00684397 /* NSEtcHosts */; 263 | targetProxy = F8BC188D18FE5C5C00684397 /* PBXContainerItemProxy */; 264 | }; 265 | /* End PBXTargetDependency section */ 266 | 267 | /* Begin PBXVariantGroup section */ 268 | F8BC187918FE5C5C00684397 /* InfoPlist.strings */ = { 269 | isa = PBXVariantGroup; 270 | children = ( 271 | F8BC187A18FE5C5C00684397 /* en */, 272 | ); 273 | name = InfoPlist.strings; 274 | sourceTree = ""; 275 | }; 276 | /* End PBXVariantGroup section */ 277 | 278 | /* Begin XCBuildConfiguration section */ 279 | F8BC189718FE5C5C00684397 /* Debug */ = { 280 | isa = XCBuildConfiguration; 281 | buildSettings = { 282 | ALWAYS_SEARCH_USER_PATHS = NO; 283 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 284 | CLANG_CXX_LIBRARY = "libc++"; 285 | CLANG_ENABLE_MODULES = YES; 286 | CLANG_ENABLE_OBJC_ARC = YES; 287 | CLANG_WARN_BOOL_CONVERSION = YES; 288 | CLANG_WARN_CONSTANT_CONVERSION = YES; 289 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 290 | CLANG_WARN_EMPTY_BODY = YES; 291 | CLANG_WARN_ENUM_CONVERSION = YES; 292 | CLANG_WARN_INT_CONVERSION = YES; 293 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 294 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 295 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 296 | COPY_PHASE_STRIP = NO; 297 | GCC_C_LANGUAGE_STANDARD = gnu99; 298 | GCC_DYNAMIC_NO_PIC = NO; 299 | GCC_OPTIMIZATION_LEVEL = 0; 300 | GCC_PREPROCESSOR_DEFINITIONS = ( 301 | "DEBUG=1", 302 | "$(inherited)", 303 | ); 304 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 305 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 306 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 307 | GCC_WARN_UNDECLARED_SELECTOR = YES; 308 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 309 | GCC_WARN_UNUSED_FUNCTION = YES; 310 | GCC_WARN_UNUSED_VARIABLE = YES; 311 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 312 | ONLY_ACTIVE_ARCH = YES; 313 | SDKROOT = iphoneos; 314 | }; 315 | name = Debug; 316 | }; 317 | F8BC189818FE5C5C00684397 /* Release */ = { 318 | isa = XCBuildConfiguration; 319 | buildSettings = { 320 | ALWAYS_SEARCH_USER_PATHS = NO; 321 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 322 | CLANG_CXX_LIBRARY = "libc++"; 323 | CLANG_ENABLE_MODULES = YES; 324 | CLANG_ENABLE_OBJC_ARC = YES; 325 | CLANG_WARN_BOOL_CONVERSION = YES; 326 | CLANG_WARN_CONSTANT_CONVERSION = YES; 327 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 328 | CLANG_WARN_EMPTY_BODY = YES; 329 | CLANG_WARN_ENUM_CONVERSION = YES; 330 | CLANG_WARN_INT_CONVERSION = YES; 331 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 332 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 333 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 334 | COPY_PHASE_STRIP = YES; 335 | ENABLE_NS_ASSERTIONS = NO; 336 | GCC_C_LANGUAGE_STANDARD = gnu99; 337 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 338 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 339 | GCC_WARN_UNDECLARED_SELECTOR = YES; 340 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 341 | GCC_WARN_UNUSED_FUNCTION = YES; 342 | GCC_WARN_UNUSED_VARIABLE = YES; 343 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 344 | SDKROOT = iphoneos; 345 | VALIDATE_PRODUCT = YES; 346 | }; 347 | name = Release; 348 | }; 349 | F8BC189A18FE5C5C00684397 /* Debug */ = { 350 | isa = XCBuildConfiguration; 351 | buildSettings = { 352 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 353 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 354 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 355 | GCC_PREFIX_HEADER = "iOS Example/Prefix.pch"; 356 | INFOPLIST_FILE = "$(SRCROOT)/iOS Example/Info.plist"; 357 | PRODUCT_NAME = "$(TARGET_NAME)"; 358 | WRAPPER_EXTENSION = app; 359 | }; 360 | name = Debug; 361 | }; 362 | F8BC189B18FE5C5C00684397 /* Release */ = { 363 | isa = XCBuildConfiguration; 364 | buildSettings = { 365 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 366 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 367 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 368 | GCC_PREFIX_HEADER = "iOS Example/Prefix.pch"; 369 | INFOPLIST_FILE = "$(SRCROOT)/iOS Example/Info.plist"; 370 | PRODUCT_NAME = "$(TARGET_NAME)"; 371 | WRAPPER_EXTENSION = app; 372 | }; 373 | name = Release; 374 | }; 375 | F8BC189D18FE5C5C00684397 /* Debug */ = { 376 | isa = XCBuildConfiguration; 377 | buildSettings = { 378 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/NSEtcHosts.app/NSEtcHosts"; 379 | FRAMEWORK_SEARCH_PATHS = ( 380 | "$(SDKROOT)/Developer/Library/Frameworks", 381 | "$(inherited)", 382 | "$(DEVELOPER_FRAMEWORKS_DIR)", 383 | ); 384 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 385 | GCC_PREFIX_HEADER = "NSEtcHosts/NSEtcHosts-Prefix.pch"; 386 | GCC_PREPROCESSOR_DEFINITIONS = ( 387 | "DEBUG=1", 388 | "$(inherited)", 389 | ); 390 | INFOPLIST_FILE = "NSEtcHostsTests/NSEtcHostsTests-Info.plist"; 391 | PRODUCT_NAME = "$(TARGET_NAME)"; 392 | TEST_HOST = "$(BUNDLE_LOADER)"; 393 | WRAPPER_EXTENSION = xctest; 394 | }; 395 | name = Debug; 396 | }; 397 | F8BC189E18FE5C5C00684397 /* Release */ = { 398 | isa = XCBuildConfiguration; 399 | buildSettings = { 400 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/NSEtcHosts.app/NSEtcHosts"; 401 | FRAMEWORK_SEARCH_PATHS = ( 402 | "$(SDKROOT)/Developer/Library/Frameworks", 403 | "$(inherited)", 404 | "$(DEVELOPER_FRAMEWORKS_DIR)", 405 | ); 406 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 407 | GCC_PREFIX_HEADER = "NSEtcHosts/NSEtcHosts-Prefix.pch"; 408 | INFOPLIST_FILE = "NSEtcHostsTests/NSEtcHostsTests-Info.plist"; 409 | PRODUCT_NAME = "$(TARGET_NAME)"; 410 | TEST_HOST = "$(BUNDLE_LOADER)"; 411 | WRAPPER_EXTENSION = xctest; 412 | }; 413 | name = Release; 414 | }; 415 | /* End XCBuildConfiguration section */ 416 | 417 | /* Begin XCConfigurationList section */ 418 | F8BC186818FE5C5B00684397 /* Build configuration list for PBXProject "iOS Example" */ = { 419 | isa = XCConfigurationList; 420 | buildConfigurations = ( 421 | F8BC189718FE5C5C00684397 /* Debug */, 422 | F8BC189818FE5C5C00684397 /* Release */, 423 | ); 424 | defaultConfigurationIsVisible = 0; 425 | defaultConfigurationName = Release; 426 | }; 427 | F8BC189918FE5C5C00684397 /* Build configuration list for PBXNativeTarget "NSEtcHosts" */ = { 428 | isa = XCConfigurationList; 429 | buildConfigurations = ( 430 | F8BC189A18FE5C5C00684397 /* Debug */, 431 | F8BC189B18FE5C5C00684397 /* Release */, 432 | ); 433 | defaultConfigurationIsVisible = 0; 434 | defaultConfigurationName = Release; 435 | }; 436 | F8BC189C18FE5C5C00684397 /* Build configuration list for PBXNativeTarget "NSEtcHostsTests" */ = { 437 | isa = XCConfigurationList; 438 | buildConfigurations = ( 439 | F8BC189D18FE5C5C00684397 /* Debug */, 440 | F8BC189E18FE5C5C00684397 /* Release */, 441 | ); 442 | defaultConfigurationIsVisible = 0; 443 | defaultConfigurationName = Release; 444 | }; 445 | /* End XCConfigurationList section */ 446 | }; 447 | rootObject = F8BC186518FE5C5B00684397 /* Project object */; 448 | } 449 | -------------------------------------------------------------------------------- /Example/iOS Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/iOS Example/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // AppDelegate.h 2 | // 3 | // Copyright (c) 2014 Mattt (https://mat.tt) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import 24 | 25 | @interface AppDelegate : UIResponder 26 | 27 | @property (strong, nonatomic) UIWindow *window; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /Example/iOS Example/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // AppDelegate.m 2 | // 3 | // Copyright (c) 2014 Mattt (https://mat.tt) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import "AppDelegate.h" 24 | 25 | #import "WebViewController.h" 26 | 27 | #import "NSEtcHosts.h" 28 | 29 | @implementation AppDelegate 30 | 31 | - (BOOL)application:(UIApplication *)application 32 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 33 | { 34 | [NSURLProtocol registerClass:[EtcHostsURLProtocol class]]; 35 | [EtcHostsURLProtocol configureHostsWithBlock:^(id configuration) { 36 | [configuration resolveHostName:@"google.com" toIPAddress:@"98.138.253.109"]; 37 | }]; 38 | 39 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 40 | self.window.backgroundColor = [UIColor whiteColor]; 41 | 42 | WebViewController *viewController = [[WebViewController alloc] init]; 43 | UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewController]; 44 | 45 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 46 | NSString *URLString = @"http://google.com"; 47 | viewController.navigationItem.prompt = URLString; 48 | [viewController.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:URLString]]]; 49 | }); 50 | 51 | self.window.rootViewController = navigationController; 52 | 53 | [self.window makeKeyAndVisible]; 54 | 55 | return YES; 56 | } 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /Example/iOS Example/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "60x60", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Example/iOS Example/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Example/iOS Example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.mattt.${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.0 25 | LSRequiresIPhoneOS 26 | 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Example/iOS Example/Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_3_0 10 | #warning "This project uses features only available in iOS SDK 3.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | #endif 17 | -------------------------------------------------------------------------------- /Example/iOS Example/WebViewController.h: -------------------------------------------------------------------------------- 1 | // WebViewController.h 2 | // 3 | // Copyright (c) 2014 Mattt (https://mat.tt) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import 24 | 25 | @interface WebViewController : UIViewController 26 | 27 | @property (nonatomic, strong) IBOutlet UIWebView *webView; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /Example/iOS Example/WebViewController.m: -------------------------------------------------------------------------------- 1 | // WebViewController.m 2 | // 3 | // Copyright (c) 2014 Mattt (https://mat.tt) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import "WebViewController.h" 24 | 25 | @interface WebViewController () 26 | @property (readwrite, nonatomic, strong) UIActivityIndicatorView *loadingActivityIndicatorView; 27 | @end 28 | 29 | @implementation WebViewController 30 | 31 | #pragma mark - UIViewController 32 | 33 | - (void)loadView { 34 | [super loadView]; 35 | 36 | self.webView = [[UIWebView alloc] initWithFrame:self.view.bounds]; 37 | self.webView.delegate = self; 38 | [self.view addSubview:self.webView]; 39 | 40 | self.loadingActivityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; 41 | self.loadingActivityIndicatorView.hidesWhenStopped = YES; 42 | } 43 | 44 | - (void)viewDidLoad { 45 | [super viewDidLoad]; 46 | 47 | self.title = NSLocalizedString(@"NSEtcHosts", nil); 48 | 49 | self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:self.loadingActivityIndicatorView]; 50 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction target:self action:@selector(showActionSheet:)]; 51 | self.navigationItem.rightBarButtonItem.enabled = NO; 52 | } 53 | 54 | #pragma mark - IBAction 55 | 56 | - (IBAction)showActionSheet:(id)sender { 57 | [[[UIActionSheet alloc] initWithTitle:[[self.webView.request URL] absoluteString] delegate:self cancelButtonTitle:NSLocalizedString(@"Cancel", nil) destructiveButtonTitle:nil otherButtonTitles:NSLocalizedString(@"Open in Safari", nil), nil] showInView:self.view]; 58 | } 59 | 60 | - (IBAction)openInSafari:(id)sender { 61 | [[UIApplication sharedApplication] openURL:[self.webView.request URL]]; 62 | } 63 | 64 | #pragma mark - UIWebViewDelegate 65 | 66 | - (void)webViewDidStartLoad:(UIWebView *)webView { 67 | self.navigationItem.rightBarButtonItem.enabled = YES; 68 | 69 | [self.loadingActivityIndicatorView startAnimating]; 70 | } 71 | 72 | - (void)webViewDidFinishLoad:(UIWebView *)webView { 73 | [self.loadingActivityIndicatorView stopAnimating]; 74 | } 75 | 76 | #pragma mark - UIActionSheetDelegate 77 | 78 | - (void)actionSheet:(UIActionSheet *)actionSheet 79 | clickedButtonAtIndex:(NSInteger)buttonIndex 80 | { 81 | if (buttonIndex != actionSheet.cancelButtonIndex) { 82 | [self openInSafari:actionSheet]; 83 | } 84 | } 85 | 86 | @end 87 | -------------------------------------------------------------------------------- /Example/iOS Example/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Example/iOS Example/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2014 Mattt (https://mat.tt) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | #import 23 | 24 | #import "AppDelegate.h" 25 | 26 | int main(int argc, char *argv[]) { 27 | @autoreleasepool { 28 | return UIApplicationMain(argc, argv, nil, 29 | NSStringFromClass([AppDelegate class])); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Mattt (https://mat.tt) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /NSEtcHosts.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 9 | 10 | 12 | 13 | 15 | 16 | 17 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /NSEtcHosts.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /NSEtcHosts/EtcHostsURLProtocol.h: -------------------------------------------------------------------------------- 1 | // EtcHostsURLProtocol.h 2 | // 3 | // Copyright (c) 2014 Mattt (https://mat.tt) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import 24 | 25 | @protocol EtcHostsConfiguration; 26 | 27 | /** 28 | 29 | */ 30 | @interface EtcHostsURLProtocol : NSURLProtocol 31 | 32 | /** 33 | 34 | */ 35 | + (void)configureHostsWithBlock:(void (^)(id configuration))block; 36 | 37 | @end 38 | 39 | #pragma mark - 40 | 41 | /** 42 | 43 | */ 44 | @protocol EtcHostsConfiguration 45 | 46 | /** 47 | 48 | */ 49 | - (void)resolveHostName:(NSString *)hostName 50 | toIPAddress:(NSString *)IPAddress; 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /NSEtcHosts/EtcHostsURLProtocol.m: -------------------------------------------------------------------------------- 1 | // EtcHostsURLProtocol.m 2 | // 3 | // Copyright (c) 2014 Mattt (https://mat.tt) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import "EtcHostsURLProtocol.h" 24 | 25 | static NSString * const EtcHostModifiedPropertyKey = @"EtcHostModifiedProperty"; 26 | 27 | @interface EtcHostsConfiguration : NSObject 28 | - (NSString *)IPAddressForHostName:(NSString *)hostName; 29 | @end 30 | 31 | #pragma mark - 32 | 33 | @interface EtcHostsURLProtocol () 34 | @property (readwrite, nonatomic, strong) NSURLConnection *connection; 35 | @end 36 | 37 | @implementation EtcHostsURLProtocol 38 | 39 | + (EtcHostsConfiguration *)sharedConfiguration { 40 | static EtcHostsConfiguration * _sharedConfiguration = nil; 41 | static dispatch_once_t onceToken; 42 | dispatch_once(&onceToken, ^{ 43 | _sharedConfiguration = [[EtcHostsConfiguration alloc] init]; 44 | }); 45 | 46 | return _sharedConfiguration; 47 | } 48 | 49 | + (void)configureHostsWithBlock:(void (^)(id configuration))block { 50 | if (block) { 51 | block([self sharedConfiguration]); 52 | } 53 | } 54 | 55 | #pragma mark - NSURLProtocol 56 | 57 | + (BOOL)canInitWithRequest:(NSURLRequest *)request { 58 | return [[self sharedConfiguration] IPAddressForHostName:[[request URL] host]] && ([[[request URL] scheme] caseInsensitiveCompare:@"http"] == NSOrderedSame || [[[request URL] scheme] caseInsensitiveCompare:@"https"] == NSOrderedSame); 59 | } 60 | 61 | + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request { 62 | NSMutableURLRequest *mutableRequest = [request mutableCopy]; 63 | 64 | NSURLComponents *URLComponents = [NSURLComponents componentsWithString:[[mutableRequest URL] absoluteString]]; 65 | URLComponents.scheme = @"http"; 66 | URLComponents.host = [[[self class] sharedConfiguration] IPAddressForHostName:URLComponents.host]; 67 | 68 | mutableRequest.URL = [URLComponents URL]; 69 | 70 | [NSURLProtocol setProperty:@(YES) forKey:EtcHostModifiedPropertyKey inRequest:mutableRequest]; 71 | 72 | return mutableRequest; 73 | } 74 | 75 | - (void)startLoading { 76 | self.connection = [[NSURLConnection alloc] initWithRequest:[[self class] canonicalRequestForRequest:self.request] delegate:self startImmediately:YES]; 77 | } 78 | 79 | - (void)stopLoading { 80 | [self.connection cancel]; 81 | } 82 | 83 | #pragma mark - NSURLConnectionDelegate 84 | 85 | - (void)connection:(NSURLConnection *)connection 86 | didFailWithError:(NSError *)error 87 | { 88 | [[self client] URLProtocol:self didFailWithError:error]; 89 | } 90 | 91 | - (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection { 92 | return YES; 93 | } 94 | 95 | - (void)connection:(NSURLConnection *)connection 96 | didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { 97 | [[self client] URLProtocol:self didReceiveAuthenticationChallenge:challenge]; 98 | } 99 | 100 | - (void)connection:(NSURLConnection *)connection 101 | didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { 102 | [[self client] URLProtocol:self didCancelAuthenticationChallenge:challenge]; 103 | } 104 | 105 | #pragma mark - NSURLConnectionDataDelegate 106 | 107 | - (void)connection:(NSURLConnection *)connection 108 | didReceiveResponse:(NSURLResponse *)response 109 | { 110 | [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:[[self request] cachePolicy]]; 111 | } 112 | 113 | - (void)connection:(NSURLConnection *)connection 114 | didReceiveData:(NSData *)data 115 | { 116 | [[self client] URLProtocol:self didLoadData:data]; 117 | } 118 | 119 | - (NSCachedURLResponse *)connection:(NSURLConnection *)connection 120 | willCacheResponse:(NSCachedURLResponse *)cachedResponse 121 | { 122 | return cachedResponse; 123 | } 124 | 125 | - (void)connectionDidFinishLoading:(NSURLConnection *)connection { 126 | [[self client] URLProtocolDidFinishLoading:self]; 127 | } 128 | 129 | @end 130 | 131 | #pragma mark - 132 | 133 | @interface EtcHostsConfiguration () 134 | @property (readwrite, nonatomic, strong) NSMutableDictionary *mutableIPAddressesByHostName; 135 | @end 136 | 137 | @implementation EtcHostsConfiguration 138 | 139 | - (instancetype)init { 140 | self = [super init]; 141 | if (!self) { 142 | return nil; 143 | } 144 | 145 | self.mutableIPAddressesByHostName = [[NSMutableDictionary alloc] init]; 146 | 147 | return self; 148 | } 149 | 150 | - (NSString *)IPAddressForHostName:(NSString *)hostName { 151 | return self.mutableIPAddressesByHostName[hostName]; 152 | } 153 | 154 | #pragma mark - EtcHostsConfiguration 155 | 156 | - (void)resolveHostName:(NSString *)hostName 157 | toIPAddress:(NSString *)IPAddress 158 | { 159 | NSParameterAssert(hostName); 160 | NSParameterAssert(IPAddress); 161 | 162 | self.mutableIPAddressesByHostName[[hostName lowercaseString]] = IPAddress; 163 | } 164 | 165 | @end 166 | -------------------------------------------------------------------------------- /NSEtcHosts/NSEtcHosts.h: -------------------------------------------------------------------------------- 1 | // NSEtcHosts.h 2 | // 3 | // Copyright (c) 2014 Mattt (https://mat.tt) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #ifndef _NSETCHOSTS_ 24 | #define _NSETCHOSTS_ 25 | #import "EtcHostsURLProtocol.h" 26 | #endif 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NSEtcHosts 2 | 3 | > This project was a proof-of-concept that relied on a combination of factors unique to the time of creation 4 | > that have since been remediated by, among other things, 5 | > the ubiquitous adoption of HTTPS, iOS App Transport Security, and `NSURLSession`. 6 | > Insofar as this approach should ever have been used (it shouldn't) 7 | > the aforementioned improvements to the networking stack render this project inviable. 8 | > Suffice to say, this project is no longer maintained. 9 | 10 | NSEtcHosts uses `NSURLProtocol` to intercept requests for specified host names, 11 | resolving them instead to a different IP address, 12 | similar to an [`/etc/hosts` file](http://en.wikipedia.org/wiki/Hosts_%28file%29) on a Unix system. 13 | (This does not actually affect the _actual_ hosts file used by iOS, 14 | nor does it affect routing behavior outside of the application process) 15 | 16 | ## Usage 17 | 18 | ```objective-c 19 | [NSURLProtocol registerClass:[EtcHostsURLProtocol class]]; 20 | [EtcHostsURLProtocol configureHostsWithBlock:^(id configuration) { 21 | [configuration resolveHostName:@"google.com" toIPAddress:@"98.138.253.109"]; 22 | }]; 23 | ``` 24 | 25 | ## Contact 26 | 27 | [Mattt](https://twitter.com/mattt) 28 | 29 | ## License 30 | 31 | NSEtcHosts is available under the MIT license. 32 | See the LICENSE file for more info. 33 | --------------------------------------------------------------------------------