├── .bundle └── config ├── .gitignore ├── .travis.yml ├── Example ├── KIDDNS.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── KIDDNS-Example.xcscheme ├── KIDDNS.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── KIDDNS │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── KIDDNS-Info.plist │ ├── KIDDNS-Prefix.pch │ ├── VKAppDelegate.h │ ├── VKAppDelegate.m │ ├── VKViewController.h │ ├── VKViewController.m │ ├── en.lproj │ │ └── InfoPlist.strings │ └── main.m ├── Podfile ├── Podfile.lock └── Tests │ ├── Tests-Info.plist │ ├── Tests-Prefix.pch │ ├── Tests.m │ └── en.lproj │ └── InfoPlist.strings ├── Gemfile ├── Gemfile.lock ├── KIDDNS.podspec ├── KIDDNS ├── Assets │ ├── .gitkeep │ └── meta.json └── Classes │ ├── .gitkeep │ ├── DNS │ ├── DNSCenter.h │ ├── DNSCenter.m │ ├── DNSCenter_internal.h │ ├── DNSConfigureProxie.h │ ├── DNSConfigureProxie.m │ ├── KIDAuthChallenger.h │ ├── KIDAuthChallenger.m │ ├── KIDDNSLogger.h │ └── KIDDNSLogger.m │ └── URLProtocol │ ├── HBHTTPURLProtocol.h │ ├── HBHTTPURLProtocol.m │ ├── HBMutableArray.h │ ├── HBMutableArray.m │ ├── HBURLSessionDemux.h │ ├── HBURLSessionDemux.m │ ├── HBURLSessionMap.h │ ├── HBURLSessionMap.m │ ├── NSURLSession+URLProtocol.h │ ├── NSURLSession+URLProtocol.m │ ├── RxHTTPDNSURLProtocol.h │ ├── RxHTTPDNSURLProtocol.m │ ├── RxSNIURLProtocol.h │ ├── RxSNIURLProtocol.m │ ├── URLProtocolLog.h │ └── URLProtocolLog.m ├── LICENSE ├── README.md └── _Pods.xcodeproj /.bundle/config: -------------------------------------------------------------------------------- 1 | --- 2 | BUNDLE_PATH: "vendor/bundle" 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata/ 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | # Bundler 23 | # .bundle 24 | vendor/bundle 25 | 26 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 27 | # Carthage/Checkouts 28 | 29 | Carthage/Build 30 | 31 | # We recommend against adding the Pods directory to your .gitignore. However 32 | # you should judge for yourself, the pros and cons are mentioned at: 33 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 34 | # 35 | # Note: if you ignore the Pods directory, make sure to uncomment 36 | # `pod install` in .travis.yml 37 | # 38 | Pods/ 39 | 40 | # Fastlane 41 | fastlane/products 42 | fastlane/binary 43 | fastlane/report.xml 44 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # references: 2 | # * https://www.objc.io/issues/6-build-tools/travis-ci/ 3 | # * https://github.com/supermarin/xcpretty#usage 4 | 5 | osx_image: xcode7.3 6 | language: objective-c 7 | # cache: cocoapods 8 | # podfile: Example/Podfile 9 | # before_install: 10 | # - gem install cocoapods # Since Travis is not always on latest version 11 | # - pod install --project-directory=Example 12 | script: 13 | - set -o pipefail && xcodebuild test -enableCodeCoverage YES -workspace Example/KIDDNS.xcworkspace -scheme KIDDNS-Example -sdk iphonesimulator9.3 ONLY_ACTIVE_ARCH=NO | xcpretty 14 | - pod lib lint 15 | -------------------------------------------------------------------------------- /Example/KIDDNS.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2222188C19F95E570B652AF8 /* libPods-KIDDNS_Tests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E0362B4736D0F73AA0BB22D8 /* libPods-KIDDNS_Tests.a */; }; 11 | 6003F58E195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 12 | 6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58F195388D20070C39A /* CoreGraphics.framework */; }; 13 | 6003F592195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; 14 | 6003F598195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F596195388D20070C39A /* InfoPlist.strings */; }; 15 | 6003F59A195388D20070C39A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F599195388D20070C39A /* main.m */; }; 16 | 6003F59E195388D20070C39A /* VKAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F59D195388D20070C39A /* VKAppDelegate.m */; }; 17 | 6003F5A7195388D20070C39A /* VKViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F5A6195388D20070C39A /* VKViewController.m */; }; 18 | 6003F5A9195388D20070C39A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5A8195388D20070C39A /* Images.xcassets */; }; 19 | 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F5AF195388D20070C39A /* XCTest.framework */; }; 20 | 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 21 | 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; 22 | 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5B8195388D20070C39A /* InfoPlist.strings */; }; 23 | 6003F5BC195388D20070C39A /* Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F5BB195388D20070C39A /* Tests.m */; }; 24 | 71719F9F1E33DC2100824A3D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */; }; 25 | 873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */; }; 26 | FE58D597A752BCE3EFAEB946 /* libPods-KIDDNS_Example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 754998D813E6CAB63DC444C2 /* libPods-KIDDNS_Example.a */; }; 27 | /* End PBXBuildFile section */ 28 | 29 | /* Begin PBXContainerItemProxy section */ 30 | 6003F5B3195388D20070C39A /* PBXContainerItemProxy */ = { 31 | isa = PBXContainerItemProxy; 32 | containerPortal = 6003F582195388D10070C39A /* Project object */; 33 | proxyType = 1; 34 | remoteGlobalIDString = 6003F589195388D20070C39A; 35 | remoteInfo = KIDDNS; 36 | }; 37 | /* End PBXContainerItemProxy section */ 38 | 39 | /* Begin PBXFileReference section */ 40 | 3B5826388ED46228BC1FFE18 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; 41 | 3EEC44FBAE31F1B90AB67328 /* KIDDNS.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = KIDDNS.podspec; path = ../KIDDNS.podspec; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 42 | 6003F58A195388D20070C39A /* KIDDNS_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = KIDDNS_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 43 | 6003F58D195388D20070C39A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 44 | 6003F58F195388D20070C39A /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 45 | 6003F591195388D20070C39A /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 46 | 6003F595195388D20070C39A /* KIDDNS-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "KIDDNS-Info.plist"; sourceTree = ""; }; 47 | 6003F597195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 48 | 6003F599195388D20070C39A /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 49 | 6003F59B195388D20070C39A /* KIDDNS-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "KIDDNS-Prefix.pch"; sourceTree = ""; }; 50 | 6003F59C195388D20070C39A /* VKAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VKAppDelegate.h; sourceTree = ""; }; 51 | 6003F59D195388D20070C39A /* VKAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VKAppDelegate.m; sourceTree = ""; }; 52 | 6003F5A5195388D20070C39A /* VKViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VKViewController.h; sourceTree = ""; }; 53 | 6003F5A6195388D20070C39A /* VKViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VKViewController.m; sourceTree = ""; }; 54 | 6003F5A8195388D20070C39A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 55 | 6003F5AE195388D20070C39A /* KIDDNS_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = KIDDNS_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 56 | 6003F5AF195388D20070C39A /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 57 | 6003F5B7195388D20070C39A /* Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Tests-Info.plist"; sourceTree = ""; }; 58 | 6003F5B9195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 59 | 6003F5BB195388D20070C39A /* Tests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Tests.m; sourceTree = ""; }; 60 | 606FC2411953D9B200FFA9A0 /* Tests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Tests-Prefix.pch"; sourceTree = ""; }; 61 | 6EE342A59C272FA0E8FAF01B /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 62 | 71719F9E1E33DC2100824A3D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 63 | 71FDFC59B26C062B2788676E /* Pods-KIDDNS_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-KIDDNS_Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-KIDDNS_Example/Pods-KIDDNS_Example.debug.xcconfig"; sourceTree = ""; }; 64 | 754998D813E6CAB63DC444C2 /* libPods-KIDDNS_Example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-KIDDNS_Example.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 65 | 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Main.storyboard; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 66 | C8E2456AA3C8F912D518764C /* Pods-KIDDNS_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-KIDDNS_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-KIDDNS_Tests/Pods-KIDDNS_Tests.release.xcconfig"; sourceTree = ""; }; 67 | D6AFF7AF72BEAB2D2768E890 /* Pods-KIDDNS_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-KIDDNS_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-KIDDNS_Tests/Pods-KIDDNS_Tests.debug.xcconfig"; sourceTree = ""; }; 68 | DA094D770172EA30CEF523E7 /* Pods-KIDDNS_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-KIDDNS_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-KIDDNS_Example/Pods-KIDDNS_Example.release.xcconfig"; sourceTree = ""; }; 69 | E0362B4736D0F73AA0BB22D8 /* libPods-KIDDNS_Tests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-KIDDNS_Tests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 70 | /* End PBXFileReference section */ 71 | 72 | /* Begin PBXFrameworksBuildPhase section */ 73 | 6003F587195388D20070C39A /* Frameworks */ = { 74 | isa = PBXFrameworksBuildPhase; 75 | buildActionMask = 2147483647; 76 | files = ( 77 | 6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */, 78 | 6003F592195388D20070C39A /* UIKit.framework in Frameworks */, 79 | 6003F58E195388D20070C39A /* Foundation.framework in Frameworks */, 80 | FE58D597A752BCE3EFAEB946 /* libPods-KIDDNS_Example.a in Frameworks */, 81 | ); 82 | runOnlyForDeploymentPostprocessing = 0; 83 | }; 84 | 6003F5AB195388D20070C39A /* Frameworks */ = { 85 | isa = PBXFrameworksBuildPhase; 86 | buildActionMask = 2147483647; 87 | files = ( 88 | 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */, 89 | 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */, 90 | 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */, 91 | 2222188C19F95E570B652AF8 /* libPods-KIDDNS_Tests.a in Frameworks */, 92 | ); 93 | runOnlyForDeploymentPostprocessing = 0; 94 | }; 95 | /* End PBXFrameworksBuildPhase section */ 96 | 97 | /* Begin PBXGroup section */ 98 | 55F97F373CD4A6E602D91BFF /* Pods */ = { 99 | isa = PBXGroup; 100 | children = ( 101 | 71FDFC59B26C062B2788676E /* Pods-KIDDNS_Example.debug.xcconfig */, 102 | DA094D770172EA30CEF523E7 /* Pods-KIDDNS_Example.release.xcconfig */, 103 | D6AFF7AF72BEAB2D2768E890 /* Pods-KIDDNS_Tests.debug.xcconfig */, 104 | C8E2456AA3C8F912D518764C /* Pods-KIDDNS_Tests.release.xcconfig */, 105 | ); 106 | name = Pods; 107 | sourceTree = ""; 108 | }; 109 | 6003F581195388D10070C39A = { 110 | isa = PBXGroup; 111 | children = ( 112 | 60FF7A9C1954A5C5007DD14C /* Podspec Metadata */, 113 | 6003F593195388D20070C39A /* Example for KIDDNS */, 114 | 6003F5B5195388D20070C39A /* Tests */, 115 | 6003F58C195388D20070C39A /* Frameworks */, 116 | 6003F58B195388D20070C39A /* Products */, 117 | 55F97F373CD4A6E602D91BFF /* Pods */, 118 | ); 119 | sourceTree = ""; 120 | }; 121 | 6003F58B195388D20070C39A /* Products */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | 6003F58A195388D20070C39A /* KIDDNS_Example.app */, 125 | 6003F5AE195388D20070C39A /* KIDDNS_Tests.xctest */, 126 | ); 127 | name = Products; 128 | sourceTree = ""; 129 | }; 130 | 6003F58C195388D20070C39A /* Frameworks */ = { 131 | isa = PBXGroup; 132 | children = ( 133 | 6003F58D195388D20070C39A /* Foundation.framework */, 134 | 6003F58F195388D20070C39A /* CoreGraphics.framework */, 135 | 6003F591195388D20070C39A /* UIKit.framework */, 136 | 6003F5AF195388D20070C39A /* XCTest.framework */, 137 | 754998D813E6CAB63DC444C2 /* libPods-KIDDNS_Example.a */, 138 | E0362B4736D0F73AA0BB22D8 /* libPods-KIDDNS_Tests.a */, 139 | ); 140 | name = Frameworks; 141 | sourceTree = ""; 142 | }; 143 | 6003F593195388D20070C39A /* Example for KIDDNS */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | 6003F59C195388D20070C39A /* VKAppDelegate.h */, 147 | 6003F59D195388D20070C39A /* VKAppDelegate.m */, 148 | 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */, 149 | 6003F5A5195388D20070C39A /* VKViewController.h */, 150 | 6003F5A6195388D20070C39A /* VKViewController.m */, 151 | 71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */, 152 | 6003F5A8195388D20070C39A /* Images.xcassets */, 153 | 6003F594195388D20070C39A /* Supporting Files */, 154 | ); 155 | name = "Example for KIDDNS"; 156 | path = KIDDNS; 157 | sourceTree = ""; 158 | }; 159 | 6003F594195388D20070C39A /* Supporting Files */ = { 160 | isa = PBXGroup; 161 | children = ( 162 | 6003F595195388D20070C39A /* KIDDNS-Info.plist */, 163 | 6003F596195388D20070C39A /* InfoPlist.strings */, 164 | 6003F599195388D20070C39A /* main.m */, 165 | 6003F59B195388D20070C39A /* KIDDNS-Prefix.pch */, 166 | ); 167 | name = "Supporting Files"; 168 | sourceTree = ""; 169 | }; 170 | 6003F5B5195388D20070C39A /* Tests */ = { 171 | isa = PBXGroup; 172 | children = ( 173 | 6003F5BB195388D20070C39A /* Tests.m */, 174 | 6003F5B6195388D20070C39A /* Supporting Files */, 175 | ); 176 | path = Tests; 177 | sourceTree = ""; 178 | }; 179 | 6003F5B6195388D20070C39A /* Supporting Files */ = { 180 | isa = PBXGroup; 181 | children = ( 182 | 6003F5B7195388D20070C39A /* Tests-Info.plist */, 183 | 6003F5B8195388D20070C39A /* InfoPlist.strings */, 184 | 606FC2411953D9B200FFA9A0 /* Tests-Prefix.pch */, 185 | ); 186 | name = "Supporting Files"; 187 | sourceTree = ""; 188 | }; 189 | 60FF7A9C1954A5C5007DD14C /* Podspec Metadata */ = { 190 | isa = PBXGroup; 191 | children = ( 192 | 3EEC44FBAE31F1B90AB67328 /* KIDDNS.podspec */, 193 | 6EE342A59C272FA0E8FAF01B /* README.md */, 194 | 3B5826388ED46228BC1FFE18 /* LICENSE */, 195 | ); 196 | name = "Podspec Metadata"; 197 | sourceTree = ""; 198 | }; 199 | /* End PBXGroup section */ 200 | 201 | /* Begin PBXNativeTarget section */ 202 | 6003F589195388D20070C39A /* KIDDNS_Example */ = { 203 | isa = PBXNativeTarget; 204 | buildConfigurationList = 6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "KIDDNS_Example" */; 205 | buildPhases = ( 206 | 48247F5F63E7729D4C0E3E56 /* [CP] Check Pods Manifest.lock */, 207 | 6003F586195388D20070C39A /* Sources */, 208 | 6003F587195388D20070C39A /* Frameworks */, 209 | 6003F588195388D20070C39A /* Resources */, 210 | ); 211 | buildRules = ( 212 | ); 213 | dependencies = ( 214 | ); 215 | name = KIDDNS_Example; 216 | productName = KIDDNS; 217 | productReference = 6003F58A195388D20070C39A /* KIDDNS_Example.app */; 218 | productType = "com.apple.product-type.application"; 219 | }; 220 | 6003F5AD195388D20070C39A /* KIDDNS_Tests */ = { 221 | isa = PBXNativeTarget; 222 | buildConfigurationList = 6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "KIDDNS_Tests" */; 223 | buildPhases = ( 224 | C1F47C3C1115EA2790838D31 /* [CP] Check Pods Manifest.lock */, 225 | 6003F5AA195388D20070C39A /* Sources */, 226 | 6003F5AB195388D20070C39A /* Frameworks */, 227 | 6003F5AC195388D20070C39A /* Resources */, 228 | ); 229 | buildRules = ( 230 | ); 231 | dependencies = ( 232 | 6003F5B4195388D20070C39A /* PBXTargetDependency */, 233 | ); 234 | name = KIDDNS_Tests; 235 | productName = KIDDNSTests; 236 | productReference = 6003F5AE195388D20070C39A /* KIDDNS_Tests.xctest */; 237 | productType = "com.apple.product-type.bundle.unit-test"; 238 | }; 239 | /* End PBXNativeTarget section */ 240 | 241 | /* Begin PBXProject section */ 242 | 6003F582195388D10070C39A /* Project object */ = { 243 | isa = PBXProject; 244 | attributes = { 245 | CLASSPREFIX = VK; 246 | LastUpgradeCheck = 0720; 247 | ORGANIZATIONNAME = yiyangest; 248 | TargetAttributes = { 249 | 6003F589195388D20070C39A = { 250 | DevelopmentTeam = 9KTNHF45DM; 251 | ProvisioningStyle = Manual; 252 | }; 253 | 6003F5AD195388D20070C39A = { 254 | TestTargetID = 6003F589195388D20070C39A; 255 | }; 256 | }; 257 | }; 258 | buildConfigurationList = 6003F585195388D10070C39A /* Build configuration list for PBXProject "KIDDNS" */; 259 | compatibilityVersion = "Xcode 3.2"; 260 | developmentRegion = English; 261 | hasScannedForEncodings = 0; 262 | knownRegions = ( 263 | en, 264 | Base, 265 | ); 266 | mainGroup = 6003F581195388D10070C39A; 267 | productRefGroup = 6003F58B195388D20070C39A /* Products */; 268 | projectDirPath = ""; 269 | projectRoot = ""; 270 | targets = ( 271 | 6003F589195388D20070C39A /* KIDDNS_Example */, 272 | 6003F5AD195388D20070C39A /* KIDDNS_Tests */, 273 | ); 274 | }; 275 | /* End PBXProject section */ 276 | 277 | /* Begin PBXResourcesBuildPhase section */ 278 | 6003F588195388D20070C39A /* Resources */ = { 279 | isa = PBXResourcesBuildPhase; 280 | buildActionMask = 2147483647; 281 | files = ( 282 | 873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */, 283 | 71719F9F1E33DC2100824A3D /* LaunchScreen.storyboard in Resources */, 284 | 6003F5A9195388D20070C39A /* Images.xcassets in Resources */, 285 | 6003F598195388D20070C39A /* InfoPlist.strings in Resources */, 286 | ); 287 | runOnlyForDeploymentPostprocessing = 0; 288 | }; 289 | 6003F5AC195388D20070C39A /* Resources */ = { 290 | isa = PBXResourcesBuildPhase; 291 | buildActionMask = 2147483647; 292 | files = ( 293 | 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */, 294 | ); 295 | runOnlyForDeploymentPostprocessing = 0; 296 | }; 297 | /* End PBXResourcesBuildPhase section */ 298 | 299 | /* Begin PBXShellScriptBuildPhase section */ 300 | 48247F5F63E7729D4C0E3E56 /* [CP] Check Pods Manifest.lock */ = { 301 | isa = PBXShellScriptBuildPhase; 302 | buildActionMask = 2147483647; 303 | files = ( 304 | ); 305 | inputPaths = ( 306 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 307 | "${PODS_ROOT}/Manifest.lock", 308 | ); 309 | name = "[CP] Check Pods Manifest.lock"; 310 | outputPaths = ( 311 | "$(DERIVED_FILE_DIR)/Pods-KIDDNS_Example-checkManifestLockResult.txt", 312 | ); 313 | runOnlyForDeploymentPostprocessing = 0; 314 | shellPath = /bin/sh; 315 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 316 | showEnvVarsInLog = 0; 317 | }; 318 | C1F47C3C1115EA2790838D31 /* [CP] Check Pods Manifest.lock */ = { 319 | isa = PBXShellScriptBuildPhase; 320 | buildActionMask = 2147483647; 321 | files = ( 322 | ); 323 | inputPaths = ( 324 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 325 | "${PODS_ROOT}/Manifest.lock", 326 | ); 327 | name = "[CP] Check Pods Manifest.lock"; 328 | outputPaths = ( 329 | "$(DERIVED_FILE_DIR)/Pods-KIDDNS_Tests-checkManifestLockResult.txt", 330 | ); 331 | runOnlyForDeploymentPostprocessing = 0; 332 | shellPath = /bin/sh; 333 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 334 | showEnvVarsInLog = 0; 335 | }; 336 | /* End PBXShellScriptBuildPhase section */ 337 | 338 | /* Begin PBXSourcesBuildPhase section */ 339 | 6003F586195388D20070C39A /* Sources */ = { 340 | isa = PBXSourcesBuildPhase; 341 | buildActionMask = 2147483647; 342 | files = ( 343 | 6003F59E195388D20070C39A /* VKAppDelegate.m in Sources */, 344 | 6003F5A7195388D20070C39A /* VKViewController.m in Sources */, 345 | 6003F59A195388D20070C39A /* main.m in Sources */, 346 | ); 347 | runOnlyForDeploymentPostprocessing = 0; 348 | }; 349 | 6003F5AA195388D20070C39A /* Sources */ = { 350 | isa = PBXSourcesBuildPhase; 351 | buildActionMask = 2147483647; 352 | files = ( 353 | 6003F5BC195388D20070C39A /* Tests.m in Sources */, 354 | ); 355 | runOnlyForDeploymentPostprocessing = 0; 356 | }; 357 | /* End PBXSourcesBuildPhase section */ 358 | 359 | /* Begin PBXTargetDependency section */ 360 | 6003F5B4195388D20070C39A /* PBXTargetDependency */ = { 361 | isa = PBXTargetDependency; 362 | target = 6003F589195388D20070C39A /* KIDDNS_Example */; 363 | targetProxy = 6003F5B3195388D20070C39A /* PBXContainerItemProxy */; 364 | }; 365 | /* End PBXTargetDependency section */ 366 | 367 | /* Begin PBXVariantGroup section */ 368 | 6003F596195388D20070C39A /* InfoPlist.strings */ = { 369 | isa = PBXVariantGroup; 370 | children = ( 371 | 6003F597195388D20070C39A /* en */, 372 | ); 373 | name = InfoPlist.strings; 374 | sourceTree = ""; 375 | }; 376 | 6003F5B8195388D20070C39A /* InfoPlist.strings */ = { 377 | isa = PBXVariantGroup; 378 | children = ( 379 | 6003F5B9195388D20070C39A /* en */, 380 | ); 381 | name = InfoPlist.strings; 382 | sourceTree = ""; 383 | }; 384 | 71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */ = { 385 | isa = PBXVariantGroup; 386 | children = ( 387 | 71719F9E1E33DC2100824A3D /* Base */, 388 | ); 389 | name = LaunchScreen.storyboard; 390 | sourceTree = ""; 391 | }; 392 | /* End PBXVariantGroup section */ 393 | 394 | /* Begin XCBuildConfiguration section */ 395 | 6003F5BD195388D20070C39A /* Debug */ = { 396 | isa = XCBuildConfiguration; 397 | buildSettings = { 398 | ALWAYS_SEARCH_USER_PATHS = NO; 399 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 400 | CLANG_CXX_LIBRARY = "libc++"; 401 | CLANG_ENABLE_MODULES = YES; 402 | CLANG_ENABLE_OBJC_ARC = YES; 403 | CLANG_WARN_BOOL_CONVERSION = YES; 404 | CLANG_WARN_CONSTANT_CONVERSION = YES; 405 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 406 | CLANG_WARN_EMPTY_BODY = YES; 407 | CLANG_WARN_ENUM_CONVERSION = YES; 408 | CLANG_WARN_INT_CONVERSION = YES; 409 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 410 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 411 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 412 | COPY_PHASE_STRIP = NO; 413 | ENABLE_TESTABILITY = YES; 414 | GCC_C_LANGUAGE_STANDARD = gnu99; 415 | GCC_DYNAMIC_NO_PIC = NO; 416 | GCC_OPTIMIZATION_LEVEL = 0; 417 | GCC_PREPROCESSOR_DEFINITIONS = ( 418 | "DEBUG=1", 419 | "$(inherited)", 420 | ); 421 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 422 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 423 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 424 | GCC_WARN_UNDECLARED_SELECTOR = YES; 425 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 426 | GCC_WARN_UNUSED_FUNCTION = YES; 427 | GCC_WARN_UNUSED_VARIABLE = YES; 428 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 429 | ONLY_ACTIVE_ARCH = YES; 430 | SDKROOT = iphoneos; 431 | TARGETED_DEVICE_FAMILY = "1,2"; 432 | }; 433 | name = Debug; 434 | }; 435 | 6003F5BE195388D20070C39A /* Release */ = { 436 | isa = XCBuildConfiguration; 437 | buildSettings = { 438 | ALWAYS_SEARCH_USER_PATHS = NO; 439 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 440 | CLANG_CXX_LIBRARY = "libc++"; 441 | CLANG_ENABLE_MODULES = YES; 442 | CLANG_ENABLE_OBJC_ARC = YES; 443 | CLANG_WARN_BOOL_CONVERSION = YES; 444 | CLANG_WARN_CONSTANT_CONVERSION = YES; 445 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 446 | CLANG_WARN_EMPTY_BODY = YES; 447 | CLANG_WARN_ENUM_CONVERSION = YES; 448 | CLANG_WARN_INT_CONVERSION = YES; 449 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 450 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 451 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 452 | COPY_PHASE_STRIP = YES; 453 | ENABLE_NS_ASSERTIONS = NO; 454 | GCC_C_LANGUAGE_STANDARD = gnu99; 455 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 456 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 457 | GCC_WARN_UNDECLARED_SELECTOR = YES; 458 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 459 | GCC_WARN_UNUSED_FUNCTION = YES; 460 | GCC_WARN_UNUSED_VARIABLE = YES; 461 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 462 | SDKROOT = iphoneos; 463 | TARGETED_DEVICE_FAMILY = "1,2"; 464 | VALIDATE_PRODUCT = YES; 465 | }; 466 | name = Release; 467 | }; 468 | 6003F5C0195388D20070C39A /* Debug */ = { 469 | isa = XCBuildConfiguration; 470 | baseConfigurationReference = 71FDFC59B26C062B2788676E /* Pods-KIDDNS_Example.debug.xcconfig */; 471 | buildSettings = { 472 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 473 | CODE_SIGN_STYLE = Manual; 474 | DEVELOPMENT_TEAM = 9KTNHF45DM; 475 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 476 | GCC_PREFIX_HEADER = "KIDDNS/KIDDNS-Prefix.pch"; 477 | INFOPLIST_FILE = "KIDDNS/KIDDNS-Info.plist"; 478 | MODULE_NAME = ExampleApp; 479 | PRODUCT_BUNDLE_IDENTIFIER = "cn.com.vipkid.automatic.demo.KIDDNS-Example"; 480 | PRODUCT_NAME = "$(TARGET_NAME)"; 481 | PROVISIONING_PROFILE = "70c5cf05-8c19-4224-841b-e5eb38eb9b15"; 482 | PROVISIONING_PROFILE_SPECIFIER = automaticDemo_development_enterprise; 483 | WRAPPER_EXTENSION = app; 484 | }; 485 | name = Debug; 486 | }; 487 | 6003F5C1195388D20070C39A /* Release */ = { 488 | isa = XCBuildConfiguration; 489 | baseConfigurationReference = DA094D770172EA30CEF523E7 /* Pods-KIDDNS_Example.release.xcconfig */; 490 | buildSettings = { 491 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 492 | CODE_SIGN_STYLE = Manual; 493 | DEVELOPMENT_TEAM = ""; 494 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 495 | GCC_PREFIX_HEADER = "KIDDNS/KIDDNS-Prefix.pch"; 496 | INFOPLIST_FILE = "KIDDNS/KIDDNS-Info.plist"; 497 | MODULE_NAME = ExampleApp; 498 | PRODUCT_BUNDLE_IDENTIFIER = "cn.com.vipkid.automatic.demo.KIDDNS-Example"; 499 | PRODUCT_NAME = "$(TARGET_NAME)"; 500 | PROVISIONING_PROFILE_SPECIFIER = ""; 501 | WRAPPER_EXTENSION = app; 502 | }; 503 | name = Release; 504 | }; 505 | 6003F5C3195388D20070C39A /* Debug */ = { 506 | isa = XCBuildConfiguration; 507 | baseConfigurationReference = D6AFF7AF72BEAB2D2768E890 /* Pods-KIDDNS_Tests.debug.xcconfig */; 508 | buildSettings = { 509 | BUNDLE_LOADER = "$(TEST_HOST)"; 510 | FRAMEWORK_SEARCH_PATHS = ( 511 | "$(SDKROOT)/Developer/Library/Frameworks", 512 | "$(inherited)", 513 | "$(DEVELOPER_FRAMEWORKS_DIR)", 514 | ); 515 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 516 | GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch"; 517 | GCC_PREPROCESSOR_DEFINITIONS = ( 518 | "DEBUG=1", 519 | "$(inherited)", 520 | ); 521 | INFOPLIST_FILE = "Tests/Tests-Info.plist"; 522 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; 523 | PRODUCT_NAME = "$(TARGET_NAME)"; 524 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/KIDDNS_Example.app/KIDDNS_Example"; 525 | WRAPPER_EXTENSION = xctest; 526 | }; 527 | name = Debug; 528 | }; 529 | 6003F5C4195388D20070C39A /* Release */ = { 530 | isa = XCBuildConfiguration; 531 | baseConfigurationReference = C8E2456AA3C8F912D518764C /* Pods-KIDDNS_Tests.release.xcconfig */; 532 | buildSettings = { 533 | BUNDLE_LOADER = "$(TEST_HOST)"; 534 | FRAMEWORK_SEARCH_PATHS = ( 535 | "$(SDKROOT)/Developer/Library/Frameworks", 536 | "$(inherited)", 537 | "$(DEVELOPER_FRAMEWORKS_DIR)", 538 | ); 539 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 540 | GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch"; 541 | INFOPLIST_FILE = "Tests/Tests-Info.plist"; 542 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; 543 | PRODUCT_NAME = "$(TARGET_NAME)"; 544 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/KIDDNS_Example.app/KIDDNS_Example"; 545 | WRAPPER_EXTENSION = xctest; 546 | }; 547 | name = Release; 548 | }; 549 | /* End XCBuildConfiguration section */ 550 | 551 | /* Begin XCConfigurationList section */ 552 | 6003F585195388D10070C39A /* Build configuration list for PBXProject "KIDDNS" */ = { 553 | isa = XCConfigurationList; 554 | buildConfigurations = ( 555 | 6003F5BD195388D20070C39A /* Debug */, 556 | 6003F5BE195388D20070C39A /* Release */, 557 | ); 558 | defaultConfigurationIsVisible = 0; 559 | defaultConfigurationName = Release; 560 | }; 561 | 6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "KIDDNS_Example" */ = { 562 | isa = XCConfigurationList; 563 | buildConfigurations = ( 564 | 6003F5C0195388D20070C39A /* Debug */, 565 | 6003F5C1195388D20070C39A /* Release */, 566 | ); 567 | defaultConfigurationIsVisible = 0; 568 | defaultConfigurationName = Release; 569 | }; 570 | 6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "KIDDNS_Tests" */ = { 571 | isa = XCConfigurationList; 572 | buildConfigurations = ( 573 | 6003F5C3195388D20070C39A /* Debug */, 574 | 6003F5C4195388D20070C39A /* Release */, 575 | ); 576 | defaultConfigurationIsVisible = 0; 577 | defaultConfigurationName = Release; 578 | }; 579 | /* End XCConfigurationList section */ 580 | }; 581 | rootObject = 6003F582195388D10070C39A /* Project object */; 582 | } 583 | -------------------------------------------------------------------------------- /Example/KIDDNS.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/KIDDNS.xcodeproj/xcshareddata/xcschemes/KIDDNS-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /Example/KIDDNS.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/KIDDNS.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/KIDDNS/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Example/KIDDNS/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Example/KIDDNS/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Example/KIDDNS/KIDDNS-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 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 | NSAppTransportSecurity 28 | 29 | NSAllowsArbitraryLoads 30 | 31 | 32 | UILaunchStoryboardName 33 | LaunchScreen 34 | UIMainStoryboardFile 35 | Main 36 | UIRequiredDeviceCapabilities 37 | 38 | armv7 39 | 40 | UISupportedInterfaceOrientations 41 | 42 | UIInterfaceOrientationPortrait 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | UISupportedInterfaceOrientations~ipad 47 | 48 | UIInterfaceOrientationPortrait 49 | UIInterfaceOrientationPortraitUpsideDown 50 | UIInterfaceOrientationLandscapeLeft 51 | UIInterfaceOrientationLandscapeRight 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /Example/KIDDNS/KIDDNS-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_5_0 10 | #warning "This project uses features only available in iOS SDK 5.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | @import UIKit; 15 | @import Foundation; 16 | #endif 17 | -------------------------------------------------------------------------------- /Example/KIDDNS/VKAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // VKAppDelegate.h 3 | // KIDDNS 4 | // 5 | // Created by yiyangest on 09/14/2018. 6 | // Copyright (c) 2018 yiyangest. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | 11 | @interface VKAppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Example/KIDDNS/VKAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // VKAppDelegate.m 3 | // KIDDNS 4 | // 5 | // Created by yiyangest on 09/14/2018. 6 | // Copyright (c) 2018 yiyangest. All rights reserved. 7 | // 8 | 9 | #import "VKAppDelegate.h" 10 | #import 11 | 12 | 13 | @interface KIDLogger : NSObject 14 | 15 | @end 16 | 17 | @implementation KIDLogger 18 | 19 | - (void)logLevel:(KIDDNSLogLevel)level result:(KIDDNSResult *)result 20 | { 21 | NSLog(@"[DNSLog][%@] result: %@", @(level), result); 22 | } 23 | 24 | @end 25 | 26 | @implementation VKAppDelegate 27 | 28 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 29 | { 30 | // Override point for customization after application launch. 31 | KIDDNSConfig *config = [KIDDNSConfig new]; 32 | config.accountId = 1291; 33 | config.key = @"your key"; 34 | config.presolvedHosts = @[@"httpbin.org"]; 35 | [[DNSCenter defaultCenter] initializeDNSServiceWithConfig:config]; 36 | [[DNSCenter defaultCenter] addLogger:[KIDLogger new]]; 37 | [DNSCenter defaultCenter].automaticDNS = YES; 38 | [DNSCenter defaultCenter].whiteList = @[@"httpbin.org"]; 39 | return YES; 40 | } 41 | 42 | - (void)applicationWillResignActive:(UIApplication *)application 43 | { 44 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 45 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 46 | } 47 | 48 | - (void)applicationDidEnterBackground:(UIApplication *)application 49 | { 50 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 51 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 52 | } 53 | 54 | - (void)applicationWillEnterForeground:(UIApplication *)application 55 | { 56 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 57 | } 58 | 59 | - (void)applicationDidBecomeActive:(UIApplication *)application 60 | { 61 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 62 | } 63 | 64 | - (void)applicationWillTerminate:(UIApplication *)application 65 | { 66 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 67 | } 68 | 69 | @end 70 | 71 | -------------------------------------------------------------------------------- /Example/KIDDNS/VKViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // VKViewController.h 3 | // KIDDNS 4 | // 5 | // Created by yiyangest on 09/14/2018. 6 | // Copyright (c) 2018 yiyangest. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | 11 | @interface VKViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/KIDDNS/VKViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // VKViewController.m 3 | // KIDDNS 4 | // 5 | // Created by yiyangest on 09/14/2018. 6 | // Copyright (c) 2018 yiyangest. All rights reserved. 7 | // 8 | 9 | #import "VKViewController.h" 10 | #import 11 | 12 | @interface VKViewController () 13 | 14 | @end 15 | 16 | @implementation VKViewController 17 | 18 | - (void)viewDidLoad 19 | { 20 | [super viewDidLoad]; 21 | // Do any additional setup after loading the view, typically from a nib. 22 | [[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@"https://httpbin.org/get"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { 23 | if (error) { 24 | NSLog(@"[VC] error: %@", error); 25 | } 26 | NSLog(@"[VC] completed"); 27 | }] resume]; 28 | } 29 | 30 | - (void)didReceiveMemoryWarning 31 | { 32 | [super didReceiveMemoryWarning]; 33 | // Dispose of any resources that can be recreated. 34 | } 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /Example/KIDDNS/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Example/KIDDNS/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // KIDDNS 4 | // 5 | // Created by yiyangest on 09/14/2018. 6 | // Copyright (c) 2018 yiyangest. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | #import "VKAppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) 13 | { 14 | @autoreleasepool { 15 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([VKAppDelegate class])); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '8.0' 2 | 3 | source 'https://github.com/CocoaPods/Specs' 4 | source 'https://github.com/aliyun/aliyun-specs' 5 | 6 | 7 | target 'KIDDNS_Example' do 8 | pod 'KIDDNS', :path => '../' 9 | 10 | target 'KIDDNS_Tests' do 11 | inherit! :search_paths 12 | 13 | 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - AlicloudBeacon (1.1.0): 3 | - AlicloudUTDID 4 | - AlicloudHTTPDNS (1.6.18): 5 | - AlicloudBeacon 6 | - AlicloudUT 7 | - AlicloudUtils 8 | - AlicloudUT (5.2.0.5): 9 | - AlicloudUTDID 10 | - AlicloudUTDID (1.1.0.19) 11 | - AlicloudUtils (1.3.5): 12 | - AlicloudUTDID 13 | - KIDDNS (0.1.3): 14 | - AlicloudHTTPDNS 15 | 16 | DEPENDENCIES: 17 | - KIDDNS (from `../`) 18 | 19 | SPEC REPOS: 20 | https://github.com/aliyun/aliyun-specs.git: 21 | - AlicloudBeacon 22 | - AlicloudHTTPDNS 23 | - AlicloudUT 24 | - AlicloudUTDID 25 | - AlicloudUtils 26 | 27 | EXTERNAL SOURCES: 28 | KIDDNS: 29 | :path: "../" 30 | 31 | SPEC CHECKSUMS: 32 | AlicloudBeacon: 784b88ccbe7939660dc2d6736ef90ad47bcbb2c3 33 | AlicloudHTTPDNS: 7876dee3783255cebe9a32ca68f22c606ecb07ba 34 | AlicloudUT: 1b52965b97c5fc0b630901ce184391fbe1ef8479 35 | AlicloudUTDID: 14b582e41b39bc66e5a3420b61dafd0567ef4603 36 | AlicloudUtils: 36bd836fdd5c040ee0c9c06b0e1e1096a8c59c44 37 | KIDDNS: 5723de0c512f1038ddf96d2512a9d23d367c91f9 38 | 39 | PODFILE CHECKSUM: d81fc102acbd9808040e346b715130feb04f8dc1 40 | 41 | COCOAPODS: 1.5.3 42 | -------------------------------------------------------------------------------- /Example/Tests/Tests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Example/Tests/Tests-Prefix.pch: -------------------------------------------------------------------------------- 1 | // The contents of this file are implicitly included at the beginning of every test case source file. 2 | 3 | #ifdef __OBJC__ 4 | 5 | 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /Example/Tests/Tests.m: -------------------------------------------------------------------------------- 1 | // 2 | // KIDDNSTests.m 3 | // KIDDNSTests 4 | // 5 | // Created by yiyangest on 09/14/2018. 6 | // Copyright (c) 2018 yiyangest. All rights reserved. 7 | // 8 | 9 | @import XCTest; 10 | 11 | @interface Tests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation Tests 16 | 17 | - (void)setUp 18 | { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown 24 | { 25 | // Put teardown code here. This method is called after the invocation of each test method in the class. 26 | [super tearDown]; 27 | } 28 | 29 | - (void)testExample 30 | { 31 | XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); 32 | } 33 | 34 | @end 35 | 36 | -------------------------------------------------------------------------------- /Example/Tests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem "fastlane" 4 | gem "cocoapods" 5 | 6 | plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') 7 | eval_gemfile(plugins_path) if File.exist?(plugins_path) -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | CFPropertyList (3.0.0) 5 | activesupport (4.2.10) 6 | i18n (~> 0.7) 7 | minitest (~> 5.1) 8 | thread_safe (~> 0.3, >= 0.3.4) 9 | tzinfo (~> 1.1) 10 | addressable (2.5.2) 11 | public_suffix (>= 2.0.2, < 4.0) 12 | atomos (0.1.3) 13 | babosa (1.0.2) 14 | claide (1.0.2) 15 | cocoapods (1.5.3) 16 | activesupport (>= 4.0.2, < 5) 17 | claide (>= 1.0.2, < 2.0) 18 | cocoapods-core (= 1.5.3) 19 | cocoapods-deintegrate (>= 1.0.2, < 2.0) 20 | cocoapods-downloader (>= 1.2.0, < 2.0) 21 | cocoapods-plugins (>= 1.0.0, < 2.0) 22 | cocoapods-search (>= 1.0.0, < 2.0) 23 | cocoapods-stats (>= 1.0.0, < 2.0) 24 | cocoapods-trunk (>= 1.3.0, < 2.0) 25 | cocoapods-try (>= 1.1.0, < 2.0) 26 | colored2 (~> 3.1) 27 | escape (~> 0.0.4) 28 | fourflusher (~> 2.0.1) 29 | gh_inspector (~> 1.0) 30 | molinillo (~> 0.6.5) 31 | nap (~> 1.0) 32 | ruby-macho (~> 1.1) 33 | xcodeproj (>= 1.5.7, < 2.0) 34 | cocoapods-core (1.5.3) 35 | activesupport (>= 4.0.2, < 6) 36 | fuzzy_match (~> 2.0.4) 37 | nap (~> 1.0) 38 | cocoapods-deintegrate (1.0.2) 39 | cocoapods-downloader (1.2.2) 40 | cocoapods-plugins (1.0.0) 41 | nap 42 | cocoapods-search (1.0.0) 43 | cocoapods-stats (1.0.0) 44 | cocoapods-trunk (1.3.1) 45 | nap (>= 0.8, < 2.0) 46 | netrc (~> 0.11) 47 | cocoapods-try (1.1.0) 48 | colored (1.2) 49 | colored2 (3.1.2) 50 | commander-fastlane (4.4.6) 51 | highline (~> 1.7.2) 52 | concurrent-ruby (1.0.5) 53 | declarative (0.0.10) 54 | declarative-option (0.1.0) 55 | domain_name (0.5.20180417) 56 | unf (>= 0.0.5, < 1.0.0) 57 | dotenv (2.5.0) 58 | emoji_regex (0.1.1) 59 | escape (0.0.4) 60 | excon (0.62.0) 61 | faraday (0.15.3) 62 | multipart-post (>= 1.2, < 3) 63 | faraday-cookie_jar (0.0.6) 64 | faraday (>= 0.7.4) 65 | http-cookie (~> 1.0.0) 66 | faraday_middleware (0.12.2) 67 | faraday (>= 0.7.4, < 1.0) 68 | fastimage (2.1.4) 69 | fastlane (2.107.0) 70 | CFPropertyList (>= 2.3, < 4.0.0) 71 | addressable (>= 2.3, < 3.0.0) 72 | babosa (>= 1.0.2, < 2.0.0) 73 | bundler (>= 1.12.0, < 2.0.0) 74 | colored 75 | commander-fastlane (>= 4.4.6, < 5.0.0) 76 | dotenv (>= 2.1.1, < 3.0.0) 77 | emoji_regex (~> 0.1) 78 | excon (>= 0.45.0, < 1.0.0) 79 | faraday (~> 0.9) 80 | faraday-cookie_jar (~> 0.0.6) 81 | faraday_middleware (~> 0.9) 82 | fastimage (>= 2.1.0, < 3.0.0) 83 | gh_inspector (>= 1.1.2, < 2.0.0) 84 | google-api-client (>= 0.21.2, < 0.24.0) 85 | highline (>= 1.7.2, < 2.0.0) 86 | json (< 3.0.0) 87 | mini_magick (~> 4.5.1) 88 | multi_json 89 | multi_xml (~> 0.5) 90 | multipart-post (~> 2.0.0) 91 | plist (>= 3.1.0, < 4.0.0) 92 | public_suffix (~> 2.0.0) 93 | rubyzip (>= 1.2.2, < 2.0.0) 94 | security (= 0.1.3) 95 | simctl (~> 1.6.3) 96 | slack-notifier (>= 2.0.0, < 3.0.0) 97 | terminal-notifier (>= 1.6.2, < 2.0.0) 98 | terminal-table (>= 1.4.5, < 2.0.0) 99 | tty-screen (>= 0.6.3, < 1.0.0) 100 | tty-spinner (>= 0.8.0, < 1.0.0) 101 | word_wrap (~> 1.0.0) 102 | xcodeproj (>= 1.6.0, < 2.0.0) 103 | xcpretty (~> 0.3.0) 104 | xcpretty-travis-formatter (>= 0.0.3) 105 | fourflusher (2.0.1) 106 | fuzzy_match (2.0.4) 107 | gh_inspector (1.1.3) 108 | google-api-client (0.23.9) 109 | addressable (~> 2.5, >= 2.5.1) 110 | googleauth (>= 0.5, < 0.7.0) 111 | httpclient (>= 2.8.1, < 3.0) 112 | mime-types (~> 3.0) 113 | representable (~> 3.0) 114 | retriable (>= 2.0, < 4.0) 115 | signet (~> 0.9) 116 | googleauth (0.6.7) 117 | faraday (~> 0.12) 118 | jwt (>= 1.4, < 3.0) 119 | memoist (~> 0.16) 120 | multi_json (~> 1.11) 121 | os (>= 0.9, < 2.0) 122 | signet (~> 0.7) 123 | highline (1.7.10) 124 | http-cookie (1.0.3) 125 | domain_name (~> 0.5) 126 | httpclient (2.8.3) 127 | i18n (0.9.5) 128 | concurrent-ruby (~> 1.0) 129 | json (2.1.0) 130 | jwt (2.1.0) 131 | memoist (0.16.0) 132 | mime-types (3.2.2) 133 | mime-types-data (~> 3.2015) 134 | mime-types-data (3.2018.0812) 135 | mini_magick (4.5.1) 136 | minitest (5.11.3) 137 | molinillo (0.6.6) 138 | multi_json (1.13.1) 139 | multi_xml (0.6.0) 140 | multipart-post (2.0.0) 141 | nanaimo (0.2.6) 142 | nap (1.1.0) 143 | naturally (2.2.0) 144 | netrc (0.11.0) 145 | os (1.0.0) 146 | plist (3.4.0) 147 | public_suffix (2.0.5) 148 | representable (3.0.4) 149 | declarative (< 0.1.0) 150 | declarative-option (< 0.2.0) 151 | uber (< 0.2.0) 152 | retriable (3.1.2) 153 | rouge (2.0.7) 154 | ruby-macho (1.3.1) 155 | rubyzip (1.2.2) 156 | security (0.1.3) 157 | signet (0.11.0) 158 | addressable (~> 2.3) 159 | faraday (~> 0.9) 160 | jwt (>= 1.5, < 3.0) 161 | multi_json (~> 1.10) 162 | simctl (1.6.5) 163 | CFPropertyList 164 | naturally 165 | slack-notifier (2.3.2) 166 | terminal-notifier (1.8.0) 167 | terminal-table (1.8.0) 168 | unicode-display_width (~> 1.1, >= 1.1.1) 169 | thread_safe (0.3.6) 170 | tty-cursor (0.6.0) 171 | tty-screen (0.6.5) 172 | tty-spinner (0.8.0) 173 | tty-cursor (>= 0.5.0) 174 | tzinfo (1.2.5) 175 | thread_safe (~> 0.1) 176 | uber (0.1.0) 177 | unf (0.1.4) 178 | unf_ext 179 | unf_ext (0.0.7.5) 180 | unicode-display_width (1.4.0) 181 | word_wrap (1.0.0) 182 | xcodeproj (1.7.0) 183 | CFPropertyList (>= 2.3.3, < 4.0) 184 | atomos (~> 0.1.3) 185 | claide (>= 1.0.2, < 2.0) 186 | colored2 (~> 3.1) 187 | nanaimo (~> 0.2.6) 188 | xcpretty (0.3.0) 189 | rouge (~> 2.0.7) 190 | xcpretty-travis-formatter (1.0.0) 191 | xcpretty (~> 0.2, >= 0.0.7) 192 | 193 | PLATFORMS 194 | ruby 195 | 196 | DEPENDENCIES 197 | cocoapods 198 | fastlane 199 | 200 | BUNDLED WITH 201 | 1.16.2 202 | -------------------------------------------------------------------------------- /KIDDNS.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint KIDDNS.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = 'KIDDNS' 11 | s.version = '1.0.0' 12 | s.summary = 'Using HTTPDNS over your network' 13 | s.static_framework = true 14 | 15 | # This description is used to generate tags and improve search results. 16 | # * Think: What does it do? Why did you write it? What is the focus? 17 | # * Try to keep it short, snappy and to the point. 18 | # * Write the description between the DESC delimiters below. 19 | # * Finally, don't worry about the indent, CocoaPods strips it! 20 | 21 | s.description = <<-DESC 22 | Using HTTPDNS over your network request based on NSURLProtocol, dealt with both normal http and https SNI scenario. 23 | DESC 24 | 25 | s.homepage = 'https://github.com/VIPKID-OpenSource/KIDDNS.git' 26 | # s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2' 27 | s.license = { :type => 'MIT', :file => 'LICENSE' } 28 | s.author = { 'yiyangest' => 'y31210@gmail.com' } 29 | s.source = { :git => 'https://github.com/VIPKID-OpenSource/KIDDNS.git', :tag => s.version.to_s } 30 | 31 | s.ios.deployment_target = '8.0' 32 | 33 | s.source_files = 'KIDDNS/Classes/**/*' 34 | s.public_header_files = 'KIDDNS/Classes/DNS/DNSCenter.h','KIDDNS/Classes/DNS/KIDDNSLogger.h' 35 | 36 | # s.public_header_files = 'KIDDNS/Classes/**/*.h' 37 | # s.frameworks = 'UIKit', 'MapKit' 38 | # s.dependency 'AFNetworking', '~> 2.3' 39 | s.dependency 'AlicloudHTTPDNS' 40 | end 41 | -------------------------------------------------------------------------------- /KIDDNS/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VIPKID-OpenSource/KIDDNS-iOS/748ac83e9a4c55a4abfb1877a53fcb12df8dc81f/KIDDNS/Assets/.gitkeep -------------------------------------------------------------------------------- /KIDDNS/Assets/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "BusVersion": "~> 1.0", 3 | "PluginVersion": "0.1.1", 4 | "MainClass": "HybridServer", 5 | "PluginHost": "hybrid", 6 | "PluginId": "hybrid", 7 | "Permissions": "", 8 | "PluginUri": "http" 9 | } 10 | -------------------------------------------------------------------------------- /KIDDNS/Classes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VIPKID-OpenSource/KIDDNS-iOS/748ac83e9a4c55a4abfb1877a53fcb12df8dc81f/KIDDNS/Classes/.gitkeep -------------------------------------------------------------------------------- /KIDDNS/Classes/DNS/DNSCenter.h: -------------------------------------------------------------------------------- 1 | // 2 | // DNSCenter.h 3 | // KIDDNS 4 | // 5 | // Created by yiyang on 2018/9/14. 6 | // 7 | 8 | #import 9 | #import "KIDDNSLogger.h" 10 | 11 | 12 | /** 13 | DNS配置项 14 | */ 15 | @interface KIDDNSConfig : NSObject 16 | 17 | /** 18 | 阿里云HTTPDNS应用的account id 19 | */ 20 | @property (nonatomic, assign) int accountId; 21 | 22 | /** 23 | 阿里云HTTPDNS应用的app key 24 | */ 25 | @property (nonatomic, copy) NSString *key; 26 | 27 | /** 28 | 需要预解析的域名列表 29 | */ 30 | @property (nonatomic, copy) NSArray *presolvedHosts; 31 | 32 | @end 33 | 34 | /** 35 | DNS中心服务,通过此对象来开启或关闭HTTPDNS服务 36 | */ 37 | @interface DNSCenter : NSObject 38 | 39 | /** 40 | 白名单列表,支持正则匹配,如果设置了白名单,只有在白名单之内的域名才会进行HTTPDNS,如果没有设置白名单,则只要不在黑名单内,都视为在白名单内 41 | */ 42 | @property (nonatomic, copy) NSArray *whiteList; 43 | 44 | /** 45 | 黑名单列表,支持正则匹配,如果设置了黑名单,则黑名单内的域名都不会进行HTTPDNS,黑名单的优先级高于白名单. 46 | */ 47 | @property (nonatomic, copy) NSArray *blackList; 48 | 49 | /** 50 | 是否开启自动HTTPDNS服务 51 | */ 52 | @property (nonatomic, assign) BOOL automaticDNS; 53 | 54 | /** 55 | 获取默认的DNS中心服务 56 | 57 | @return DNSCenter 58 | */ 59 | + (instancetype)defaultCenter; 60 | 61 | - (instancetype)init NS_UNAVAILABLE; 62 | 63 | /** 64 | 使用配置项初始化HTTPDNS服务 65 | 66 | @param config HTTPDNS服务配置项 67 | */ 68 | - (void)initializeDNSServiceWithConfig:(KIDDNSConfig *)config; 69 | 70 | /** 71 | 添加日志记录对象 72 | 73 | @param logger 遵循KIDDNSLogger协议的日志对象 74 | */ 75 | - (void)addLogger:(id)logger; 76 | 77 | /** 78 | 移除之前添加到DNSCenter的日志记录对象 79 | 80 | @param logger 之前添加到DNSCenter的日志对象 81 | */ 82 | - (void)removeLogger:(id)logger; 83 | 84 | 85 | /** 86 | 返回URL对应的HTTPDNS网址。 87 | 88 | @param url 需要DNS解析的完整URL 89 | @return 返回解析过后的结果,完整URL。如果解析失败或者解析之后和原url一致,则返回nil 90 | */ 91 | - (NSString *)dnsResultForURL:(NSString *)url; 92 | 93 | @end 94 | -------------------------------------------------------------------------------- /KIDDNS/Classes/DNS/DNSCenter.m: -------------------------------------------------------------------------------- 1 | // 2 | // DNSCenter.m 3 | // KIDDNS 4 | // 5 | // Created by yiyang on 2018/9/14. 6 | // 7 | 8 | #import "DNSCenter.h" 9 | #import "DNSConfigureProxie.h" 10 | #import "RxHTTPDNSURLProtocol.h" 11 | #import "RxSNIURLProtocol.h" 12 | #import 13 | 14 | #import 15 | 16 | static NSMutableArray *newProtocolClasses = nil; 17 | 18 | @implementation KIDDNSConfig 19 | 20 | @end 21 | 22 | @interface DNSCenter () 23 | 24 | @property (nonatomic, strong) HttpDnsService *dnsService; 25 | @property (nonatomic, copy) NSString *key; 26 | @property (nonatomic, assign) int accountID; 27 | 28 | @property (nonatomic, strong) NSMutableArray *loggers; 29 | 30 | @end 31 | 32 | @implementation DNSCenter 33 | 34 | + (instancetype)defaultCenter 35 | { 36 | static DNSCenter *instance = nil; 37 | static dispatch_once_t onceToken; 38 | dispatch_once(&onceToken, ^{ 39 | newProtocolClasses = [NSMutableArray new]; 40 | [self swizzleProtocolClasses]; 41 | 42 | instance = [[DNSCenter alloc] initWithAccountID:0 andKey:nil]; 43 | 44 | }); 45 | return instance; 46 | } 47 | 48 | - (instancetype)initWithAccountID:(int)accountID andKey:(NSString *)key 49 | { 50 | if (self = [super init]) { 51 | self.automaticDNS = YES; 52 | self.key = key; 53 | self.accountID = accountID; 54 | } 55 | return self; 56 | } 57 | 58 | - (void)initializeDNSService 59 | { 60 | 61 | self.dnsService = [[HttpDnsService alloc] initWithAccountID:self.accountID secretKey:self.key]; 62 | [self.dnsService setCachedIPEnabled:YES]; 63 | [self.dnsService setExpiredIPEnabled:YES]; 64 | [self.dnsService setHTTPSRequestEnabled:YES]; 65 | self.dnsService.delegate = self; 66 | 67 | #if DEBUG 68 | [self.dnsService setLogEnabled:YES]; 69 | #else 70 | [self.dnsService setLogEnabled:NO]; 71 | #endif 72 | } 73 | 74 | - (void)initializeDNSServiceWithConfig:(KIDDNSConfig *)config 75 | { 76 | self.accountID = config.accountId; 77 | self.key = config.key; 78 | 79 | [self initializeDNSServiceWithPresolvedHost:config.presolvedHosts]; 80 | } 81 | 82 | - (void)initializeDNSServiceWithPresolvedHost:(NSArray *)hosts 83 | { 84 | [self initializeDNSService]; 85 | [self.dnsService setPreResolveHosts:hosts]; 86 | } 87 | 88 | - (NSString *)dnsResultForURL:(NSString *)url 89 | { 90 | NSURL *originalURL = [NSURL URLWithString:url]; 91 | if (originalURL == nil) { 92 | return nil; 93 | } 94 | if ([DNSConfigureProxie configureProxies]) { 95 | return nil; 96 | } 97 | NSString *originalHost = originalURL.host; 98 | if ([self isIP:originalHost]) { 99 | return nil; 100 | } 101 | NSString *dnsResultIP = [self.dnsService getIpByHostAsyncInURLFormat:originalHost]; 102 | 103 | if (dnsResultIP.length == 0) { 104 | return nil; 105 | } 106 | if ([dnsResultIP isEqualToString:originalHost]) { 107 | return nil; 108 | } 109 | NSURLComponents *originalComponents = [NSURLComponents componentsWithURL:originalURL resolvingAgainstBaseURL:NO]; 110 | originalComponents.host = dnsResultIP; 111 | 112 | return originalComponents.URL.absoluteString; 113 | } 114 | 115 | - (void)automaticEnableDidChange:(BOOL)automatic 116 | { 117 | if (automatic) { 118 | [[self class] registerURLProtocol:[RxHTTPDNSURLProtocol class]]; 119 | [[self class] registerURLProtocol:[RxSNIURLProtocol class]]; 120 | } else { 121 | [[self class] unregisterURLProtocol:[RxHTTPDNSURLProtocol class]]; 122 | [[self class] unregisterURLProtocol:[RxSNIURLProtocol class]]; 123 | } 124 | } 125 | 126 | + (void)registerURLProtocol:(Class)protocolClass 127 | { 128 | [NSURLProtocol registerClass:protocolClass]; 129 | [newProtocolClasses addObject:protocolClass]; 130 | } 131 | 132 | + (void)unregisterURLProtocol:(Class)protocolClass 133 | { 134 | [NSURLProtocol unregisterClass:protocolClass]; 135 | [newProtocolClasses removeObject:protocolClass]; 136 | } 137 | 138 | + (void)swizzleProtocolClasses 139 | { 140 | NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; 141 | Class configClass = object_getClass(config); 142 | Method method1 = class_getInstanceMethod(configClass, @selector(protocolClasses)); 143 | Method method2 = class_getInstanceMethod([NSURLSessionConfiguration class], @selector(fake_protocolClasses)); 144 | method_exchangeImplementations(method1, method2); 145 | } 146 | 147 | #pragma mark - Loggers 148 | 149 | - (void)addLogger:(id)logger 150 | { 151 | [self.loggers addObject:logger]; 152 | } 153 | 154 | - (void)removeLogger:(id)logger 155 | { 156 | [self.loggers removeObject:logger]; 157 | } 158 | 159 | #pragma mark - Getter & Setter 160 | 161 | - (void)setAutomaticDNS:(BOOL)automaticDNS 162 | { 163 | if (_automaticDNS != automaticDNS) { 164 | _automaticDNS = automaticDNS; 165 | [self automaticEnableDidChange:_automaticDNS]; 166 | } 167 | } 168 | 169 | - (NSMutableArray *)loggers 170 | { 171 | if (_loggers == nil) { 172 | _loggers = [NSMutableArray new]; 173 | } 174 | return _loggers; 175 | } 176 | 177 | - (BOOL)isIP:(NSString *)ip 178 | { 179 | NSString *ipv4 = @"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"; 180 | NSString *ipv6 = @"([a-f0-9]{1,4}(:[a-f0-9]{1,4}){7}|[a-f0-9]{1,4}(:[a-f0-9]{1,4}){0,7}::[a-f0-9]{0,4}(:[a-f0-9]{1,4}){0,7})"; 181 | NSPredicate *predicateIPV4 = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", ipv4]; 182 | NSPredicate *predicateIPV6 = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", ipv6]; 183 | if ([predicateIPV4 evaluateWithObject:ip] || [predicateIPV6 evaluateWithObject:ip]) { 184 | return YES; 185 | } 186 | return NO; 187 | } 188 | 189 | #pragma mark - HttpDNSDegradationDelegate 190 | 191 | - (BOOL)shouldDegradeHTTPDNS:(NSString *)hostName 192 | { 193 | if (hostName.length == 0) { 194 | return YES; 195 | } 196 | BOOL shouldDegrade = NO; 197 | NSArray *blackList = [self.blackList copy]; 198 | for (NSString *item in blackList) { 199 | if ([hostName rangeOfString:item options:NSRegularExpressionSearch].location != NSNotFound) { 200 | shouldDegrade = YES; 201 | break; 202 | } 203 | } 204 | if (shouldDegrade == NO) { 205 | shouldDegrade = YES; 206 | NSArray *whiteList = [self.whiteList copy]; 207 | for (NSString *item in whiteList) { 208 | if ([hostName rangeOfString:item options:NSRegularExpressionSearch].location != NSNotFound) { 209 | shouldDegrade = NO; 210 | break; 211 | } 212 | } 213 | // 如果没有设置白名单,则视为都在白名单内 214 | if (whiteList.count == 0) { 215 | shouldDegrade = NO; 216 | } 217 | } 218 | return shouldDegrade; 219 | } 220 | 221 | @end 222 | 223 | @interface NSURLSessionConfiguration(protocol) 224 | @end 225 | 226 | @implementation NSURLSessionConfiguration(protocol) 227 | 228 | - (NSArray *)fake_protocolClasses 229 | { 230 | NSArray *clazzes = [self fake_protocolClasses]; 231 | if (clazzes.count == 0) { 232 | return @[]; 233 | } 234 | NSMutableArray *resultProtocols = [NSMutableArray arrayWithArray:clazzes]; 235 | NSArray *newProtocols = [newProtocolClasses copy]; 236 | if (newProtocols.count == 0) { 237 | return clazzes; 238 | } 239 | [newProtocols enumerateObjectsUsingBlock:^(Class _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 240 | if ([resultProtocols containsObject:obj] == NO) { 241 | [resultProtocols insertObject:obj atIndex:0]; 242 | } 243 | }]; 244 | 245 | return resultProtocols; 246 | } 247 | 248 | @end 249 | -------------------------------------------------------------------------------- /KIDDNS/Classes/DNS/DNSCenter_internal.h: -------------------------------------------------------------------------------- 1 | // 2 | // DNSCenter_internal.h 3 | // KIDDNS 4 | // 5 | // Created by yiyang on 2018/9/28. 6 | // 7 | 8 | #ifndef DNSCenter_internal_h 9 | #define DNSCenter_internal_h 10 | 11 | #import "DNSCenter.h" 12 | 13 | @interface DNSCenter (Private) 14 | 15 | - (NSMutableArray *)loggers; 16 | 17 | @end 18 | 19 | 20 | #endif /* DNSCenter_internal_h */ 21 | -------------------------------------------------------------------------------- /KIDDNS/Classes/DNS/DNSConfigureProxie.h: -------------------------------------------------------------------------------- 1 | // 2 | // DNSConfigureProxie.h 3 | // RxData 4 | // 5 | // Created by caotianyuan on 2018/9/3. 6 | // 7 | 8 | #import 9 | 10 | @interface DNSConfigureProxie : NSObject 11 | 12 | /** 13 | 是否在设备上开启了代理 14 | 15 | @return YES: 开启了代理, NO: 没有开启代理 16 | */ 17 | + (BOOL) configureProxies; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /KIDDNS/Classes/DNS/DNSConfigureProxie.m: -------------------------------------------------------------------------------- 1 | // 2 | // DNSConfigureProxie.m 3 | // RxData 4 | // 5 | // Created by caotianyuan on 2018/9/3. 6 | // 7 | 8 | #import "DNSConfigureProxie.h" 9 | 10 | @implementation DNSConfigureProxie 11 | 12 | + (BOOL) configureProxies:(NSString *)urlString 13 | { 14 | NSDictionary *proxySettings = CFBridgingRelease(CFNetworkCopySystemProxySettings()); 15 | 16 | NSArray *proxies = nil; 17 | NSURL *url = [[NSURL alloc] initWithString:urlString]; 18 | 19 | proxies = CFBridgingRelease(CFNetworkCopyProxiesForURL((__bridge CFURLRef)url, 20 | (__bridge CFDictionaryRef)proxySettings)); 21 | if (proxies.count > 0) 22 | { 23 | NSDictionary *settings = [proxies objectAtIndex:0]; 24 | NSString *host = [settings objectForKey:(NSString *)kCFProxyHostNameKey]; 25 | NSString *port = [settings objectForKey:(NSString *)kCFProxyPortNumberKey]; 26 | 27 | if (host || port) 28 | { 29 | return YES; 30 | } 31 | } 32 | return NO; 33 | } 34 | 35 | + (BOOL) configureProxies { 36 | CFDictionaryRef dicRef = CFNetworkCopySystemProxySettings(); 37 | const CFStringRef proxyCFstr = (const CFStringRef)CFDictionaryGetValue(dicRef, (const void*)kCFNetworkProxiesHTTPProxy); 38 | NSString* proxy = (__bridge NSString *)proxyCFstr; 39 | CFRelease(dicRef); 40 | if (proxy) { 41 | return YES; 42 | } else { 43 | return NO; 44 | } 45 | } 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /KIDDNS/Classes/DNS/KIDAuthChallenger.h: -------------------------------------------------------------------------------- 1 | // 2 | // KIDAuthChallenger.h 3 | // KIDDNS 4 | // 5 | // Created by yiyang on 2018/9/14. 6 | // 7 | 8 | #import 9 | 10 | @interface KIDAuthChallenger : NSObject 11 | 12 | /** 13 | 处理证书校验的逻辑 14 | 15 | @param host 证书对应的域名 16 | @param session 请求对应的NSURLSession 17 | @param challenge 证书challenge 18 | @param completionHandler 校验的结果回调 19 | */ 20 | + (void)didReceiveChallengeOnHost:(NSString *)host session:(NSURLSession *)session challenge:(NSURLAuthenticationChallenge *)challenge completion:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /KIDDNS/Classes/DNS/KIDAuthChallenger.m: -------------------------------------------------------------------------------- 1 | // 2 | // KIDAuthChallenger.m 3 | // KIDDNS 4 | // 5 | // Created by yiyang on 2018/9/14. 6 | // 7 | 8 | #import "KIDAuthChallenger.h" 9 | 10 | @implementation KIDAuthChallenger 11 | 12 | + (void)didReceiveChallengeOnHost:(NSString *)host session:(NSURLSession *)session challenge:(NSURLAuthenticationChallenge *)challenge completion:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler 13 | { 14 | if (!challenge) { 15 | return; 16 | } 17 | NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling; 18 | NSURLCredential *credential = nil; 19 | 20 | if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { 21 | if ([self evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:host]) { 22 | disposition = NSURLSessionAuthChallengeUseCredential; 23 | credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; 24 | } else { 25 | disposition = NSURLSessionAuthChallengePerformDefaultHandling; 26 | } 27 | } else { 28 | disposition = NSURLSessionAuthChallengePerformDefaultHandling; 29 | } 30 | // 对于其他的challenges直接使用默认的验证方案 31 | completionHandler(disposition, credential); 32 | } 33 | 34 | + (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust 35 | forDomain:(NSString *)domain { 36 | /* 37 | * 创建证书校验策略 38 | */ 39 | NSMutableArray *policies = [NSMutableArray array]; 40 | if (domain) { 41 | [policies addObject:(__bridge_transfer id) SecPolicyCreateSSL(true, (__bridge CFStringRef) domain)]; 42 | } else { 43 | [policies addObject:(__bridge_transfer id) SecPolicyCreateBasicX509()]; 44 | } 45 | /* 46 | * 绑定校验策略到服务端的证书上 47 | */ 48 | SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef) policies); 49 | /* 50 | * 评估当前serverTrust是否可信任, 51 | * 官方建议在result = kSecTrustResultUnspecified 或 kSecTrustResultProceed 52 | * 的情况下serverTrust可以被验证通过,https://developer.apple.com/library/ios/technotes/tn2232/_index.html 53 | * 关于SecTrustResultType的详细信息请参考SecTrust.h 54 | */ 55 | SecTrustResultType result; 56 | SecTrustEvaluate(serverTrust, &result); 57 | return (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed); 58 | } 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /KIDDNS/Classes/DNS/KIDDNSLogger.h: -------------------------------------------------------------------------------- 1 | // 2 | // KIDDNSLogger.h 3 | // KIDDNS 4 | // 5 | // Created by yiyang on 2018/9/28. 6 | // 7 | 8 | #import 9 | 10 | typedef NS_ENUM(NSUInteger, KIDDNSLogLevel) { 11 | KIDDNSLogLevelVerbose, 12 | KIDDNSLogLevelInfo, 13 | KIDDNSLogLevelWarn, 14 | KIDDNSLogLevelError 15 | }; 16 | 17 | @interface KIDDNSResult : NSObject 18 | 19 | /** 20 | 请求的URL 21 | */ 22 | @property (nonatomic, copy) NSString *url; 23 | 24 | /** 25 | 请求所走的NSURLProtocoln 类名 26 | */ 27 | @property (nonatomic, copy) NSString *URLProtocolName; 28 | 29 | /** 30 | 请求生命周期内是否使用过HTTPDNS服务 31 | */ 32 | @property (nonatomic, assign) BOOL useHTTPDNS; 33 | 34 | /** 35 | 请求生命周期内是否使用过默认的LocalDNS服务 36 | */ 37 | @property (nonatomic, assign) BOOL useLocalDNS; 38 | 39 | /** 40 | 请求是否通过HTTPDNS服务而成功完成 41 | */ 42 | @property (nonatomic, assign) BOOL successByHTTPDNS; 43 | 44 | /** 45 | 请求最终是否成功完成 46 | */ 47 | @property (nonatomic, assign) BOOL successAfterAll; 48 | 49 | @end 50 | 51 | 52 | 53 | NS_ASSUME_NONNULL_BEGIN 54 | 55 | @protocol KIDDNSLogger 56 | 57 | /** 58 | 记录HTTPDNS的结果 59 | 60 | @param level 日志级别 61 | @param result HTTPDNS的结果 62 | */ 63 | - (void)logLevel:(KIDDNSLogLevel)level result:(KIDDNSResult *)result; 64 | 65 | @end 66 | 67 | NS_ASSUME_NONNULL_END 68 | -------------------------------------------------------------------------------- /KIDDNS/Classes/DNS/KIDDNSLogger.m: -------------------------------------------------------------------------------- 1 | // 2 | // KIDDNSLogger.m 3 | // KIDDNS 4 | // 5 | // Created by yiyang on 2018/9/28. 6 | // 7 | 8 | #import 9 | #import "KIDDNSLogger.h" 10 | 11 | 12 | @implementation KIDDNSResult 13 | 14 | - (instancetype)init 15 | { 16 | if (self = [super init]) { 17 | self.successAfterAll = NO; 18 | self.successByHTTPDNS = NO; 19 | self.useHTTPDNS = NO; 20 | self.useLocalDNS = NO; 21 | } 22 | return self; 23 | } 24 | 25 | - (NSString *)description 26 | { 27 | NSMutableDictionary *dict = [NSMutableDictionary new]; 28 | dict[@"successAfterAll"] = @(self.successAfterAll); 29 | dict[@"successByHTTPDNS"] = @(self.successByHTTPDNS); 30 | dict[@"useHTTPDNS"] = @(self.useHTTPDNS); 31 | dict[@"useLocalDNS"] = @(self.useLocalDNS); 32 | dict[@"url"] = self.url; 33 | dict[@"URLProtocolName"] = self.URLProtocolName; 34 | 35 | NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dict options:0 error:nil]; 36 | NSString *desc = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; 37 | return desc; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /KIDDNS/Classes/URLProtocol/HBHTTPURLProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // VKCacheFileInterceptor.h 3 | // Pods 4 | // 5 | // Created by yiyang on 2017/9/9. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @class KIDDNSResult; 12 | 13 | @interface HBHTTPURLProtocol : NSURLProtocol 14 | 15 | @property (atomic, strong, readwrite) NSURLSessionTask * currentTask; ///< The NSURLSession task for that request; client thread only. 16 | @property (nonatomic, strong) KIDDNSResult *dnsResult; 17 | 18 | + (void)start; 19 | 20 | + (void)registerInterceptor:(Class)interceptor; 21 | + (void)unregisterInterceptor:(Class)interceptor; 22 | 23 | + (void)markRequestAsIgnored:(NSMutableURLRequest *)request; 24 | + (void)unmarkRequestAsIgnored:(NSMutableURLRequest *)request; 25 | 26 | + (BOOL)shouldInterceptRequest:(NSURLRequest *)request; 27 | + (void)rewriteRequest:(NSMutableURLRequest *)request; 28 | 29 | - (void)startRemoteRequest:(NSURLRequest *)request; 30 | 31 | - (NSURLSessionTask *)taskWithRequest:(NSURLRequest *)request; 32 | 33 | - (void)logDNSResult; 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /KIDDNS/Classes/URLProtocol/HBHTTPURLProtocol.m: -------------------------------------------------------------------------------- 1 | // 2 | // VKCacheFileInterceptor.m 3 | // Pods 4 | // 5 | // Created by yiyang on 2017/9/9. 6 | // 7 | // 8 | 9 | #import "HBHTTPURLProtocol.h" 10 | #import "HBURLSessionDemux.h" 11 | #import "HBMutableArray.m" 12 | #import "URLProtocolLog.h" 13 | #import "HBURLSessionMap.h" 14 | #import 15 | #import 16 | #import "DNSCenter_internal.h" 17 | 18 | static HBMutableArray *sRegisters; 19 | 20 | @interface HBHTTPURLProtocol () 21 | 22 | @property (atomic, strong, readwrite) NSThread * clientThread; ///< The thread on which we should call the client. 23 | 24 | /*! The run loop modes in which to call the client. 25 | * \details The concurrency control here is complex. It's set up on the client 26 | * thread in -startLoading and then never modified. It is, however, read by code 27 | * running on other threads (specifically the main thread), so we deallocate it in 28 | * -dealloc rather than in -stopLoading. We can be sure that it's not read before 29 | * it's set up because the main thread code that reads it can only be called after 30 | * -startLoading has started the connection running. 31 | */ 32 | 33 | @property (atomic, copy, readwrite) NSArray * modes; 34 | @property (atomic, assign, readwrite) NSTimeInterval startTime; ///< The start time of the request; written by client thread only; read by any thread. 35 | 36 | @property (nonatomic, strong) NSURLSessionTask *originalTask; 37 | 38 | 39 | @end 40 | 41 | @implementation HBHTTPURLProtocol 42 | 43 | + (HBURLSessionDemux *)sharedDemux 44 | { 45 | static HBURLSessionDemux *demux; 46 | static dispatch_once_t onceToken; 47 | dispatch_once(&onceToken, ^{ 48 | NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; 49 | demux = [[HBURLSessionDemux alloc] initWithConfiguration:sessionConfiguration]; 50 | }); 51 | 52 | return demux; 53 | } 54 | 55 | + (void)start 56 | { 57 | [URLProtocolLog logWithPrefix:@"URLProtocol" format:@"start URLProtocol"]; 58 | [NSURLProtocol registerClass:self]; 59 | } 60 | 61 | #pragma mark - Public Methods Only For HBCacheFileInterceptor 62 | 63 | + (void)registerInterceptor:(Class)interceptor 64 | { 65 | if (![interceptor isSubclassOfClass:self]) { 66 | return; 67 | } 68 | NSString *key = NSStringFromClass(interceptor); 69 | if (key.length == 0) { 70 | return; 71 | } 72 | if (![self isInterceptorRegistered:interceptor]) { 73 | [sRegisters addObject:key]; 74 | [NSURLProtocol registerClass:interceptor]; 75 | } 76 | } 77 | 78 | + (void)unregisterInterceptor:(Class)interceptor 79 | { 80 | if (![interceptor isSubclassOfClass:self]) { 81 | return; 82 | } 83 | if (NSStringFromClass(interceptor).length == 0) { 84 | return; 85 | } 86 | if ([self isInterceptorRegistered:interceptor]) { 87 | [sRegisters removeObject:NSStringFromClass(interceptor)]; 88 | [NSURLProtocol unregisterClass:interceptor]; 89 | } 90 | } 91 | 92 | #pragma mark - Public Methods For SubClass & Non-override 93 | 94 | + (void)markRequestAsIgnored:(NSMutableURLRequest *)request 95 | { 96 | [NSURLProtocol setProperty:@YES forKey:kOurRecursiveRequestFlagProperty inRequest:request]; 97 | } 98 | 99 | + (void)unmarkRequestAsIgnored:(NSMutableURLRequest *)request 100 | { 101 | [NSURLProtocol removePropertyForKey:kOurRecursiveRequestFlagProperty inRequest:request]; 102 | } 103 | 104 | + (BOOL)isRequestIgnored:(NSURLRequest *)request 105 | { 106 | if ([NSURLProtocol propertyForKey:kOurRecursiveRequestFlagProperty inRequest:request]) { 107 | return YES; 108 | } 109 | return NO; 110 | } 111 | 112 | #pragma mark - NSURLProtocol methods 113 | /*! Used to mark our recursive requests so that we don't try to handle them (and thereby 114 | * suffer an infinite recursive death). 115 | */ 116 | 117 | static NSString * kOurRecursiveRequestFlagProperty = @"com.hybooster.cachefileInterceptor"; 118 | 119 | + (BOOL)canInitWithRequest:(NSURLRequest *)request 120 | { 121 | NSString *scheme = request.URL.scheme; 122 | 123 | if (!([scheme isEqualToString:@"http"] || [scheme isEqualToString:@"https"])) { 124 | return NO; 125 | } 126 | if ([self isRequestIgnored:request]) { 127 | return NO; 128 | } 129 | 130 | return [self shouldInterceptRequest:request]; 131 | } 132 | 133 | + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request 134 | { 135 | return request; 136 | } 137 | 138 | - (instancetype)initWithTask:(NSURLSessionTask *)task cachedResponse:(NSCachedURLResponse *)cachedResponse client:(id)client 139 | { 140 | if (self = [super initWithTask:task cachedResponse:cachedResponse client:client]) { 141 | self.originalTask = task; 142 | KIDDNSResult *result = [KIDDNSResult new]; 143 | result.url = task.currentRequest.URL.absoluteString; 144 | result.URLProtocolName = NSStringFromClass([self class]); 145 | self.dnsResult = result; 146 | } 147 | return self; 148 | } 149 | 150 | - (void)startLoading 151 | { 152 | NSMutableURLRequest *newQuest = nil; 153 | if ([self.request isKindOfClass:[NSMutableURLRequest class]]) { 154 | newQuest = (NSMutableURLRequest *)self.request; 155 | } else { 156 | newQuest = [self.request mutableCopy]; 157 | } 158 | 159 | [[self class] markRequestAsIgnored:newQuest]; 160 | 161 | [[self class] rewriteRequest:newQuest]; 162 | 163 | [self startRemoteRequest:newQuest]; 164 | [URLProtocolLog logWithPrefix:@"URLProtocol" format:@"%@ -> %@:%@", NSStringFromSelector(_cmd), newQuest.URL, newQuest.HTTPMethod]; 165 | } 166 | 167 | - (void)stopLoading 168 | { 169 | [URLProtocolLog logWithPrefix:@"URLProtocol" format:@"%@", NSStringFromSelector(_cmd)]; 170 | if (self.currentTask != nil) { 171 | [self.currentTask cancel]; 172 | self.currentTask = nil; 173 | } 174 | } 175 | 176 | #pragma mark - NSURLSessionDataDelegate 177 | 178 | - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler 179 | { 180 | [URLProtocolLog logWithPrefix:@"URLProtocol" format:@"%@", NSStringFromSelector(_cmd)]; 181 | if (self.currentTask == task) { 182 | NSMutableURLRequest *redirectRequest = [request mutableCopy]; 183 | [[self class] unmarkRequestAsIgnored:redirectRequest]; 184 | 185 | [[self client] URLProtocol:self wasRedirectedToRequest:redirectRequest redirectResponse:response]; 186 | 187 | [self.currentTask cancel]; 188 | 189 | [[self client] URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:nil]]; 190 | } 191 | } 192 | 193 | - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data 194 | { 195 | 196 | [URLProtocolLog logWithPrefix:@"URLProtocol" format:@"%@", NSStringFromSelector(_cmd)]; 197 | [self.client URLProtocol:self didLoadData:data]; 198 | } 199 | 200 | - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend 201 | { 202 | if (task == self.originalTask) { 203 | return; 204 | } 205 | NSURLSession *originalSession = [HBURLSessionMap fetchSessionOfTask:self.originalTask]; 206 | if (originalSession.delegate && [originalSession.delegate respondsToSelector:@selector(URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:)]) { 207 | [originalSession.delegateQueue addOperationWithBlock:^{ 208 | [(id)originalSession.delegate URLSession:originalSession task:self.originalTask didSendBodyData:bytesSent totalBytesSent:totalBytesSent totalBytesExpectedToSend:totalBytesExpectedToSend]; 209 | }]; 210 | } 211 | } 212 | 213 | - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler 214 | { 215 | 216 | [URLProtocolLog logWithPrefix:@"URLProtocol" format:@"%@", NSStringFromSelector(_cmd)]; 217 | [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; 218 | completionHandler(NSURLSessionResponseAllow); 219 | } 220 | 221 | - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error 222 | { 223 | [URLProtocolLog logWithPrefix:@"URLProtocol" format:@"%@", NSStringFromSelector(_cmd)]; 224 | if (error == nil) { 225 | [self.client URLProtocolDidFinishLoading:self]; 226 | } else { 227 | if ([[error domain] isEqual:NSURLErrorDomain] && error.code == NSURLErrorCancelled) { 228 | // Do nothing. This happens in two cases: 229 | // 230 | // o during a redirect, in which case the redirect code has already told the client about 231 | // the failure 232 | // 233 | // o if the request is cancelled by a call to -stopLoading, in which case the client doesn't 234 | // want to know about the failure 235 | } else { 236 | 237 | [self.client URLProtocol:self didFailWithError:error]; 238 | } 239 | } 240 | } 241 | 242 | #pragma mark - Private Maintain Interceptors Methods 243 | 244 | + (BOOL)isInterceptorRegistered:(Class)interceptor 245 | { 246 | NSString *key = NSStringFromClass(interceptor); 247 | if (key.length == 0) { 248 | return NO; 249 | } 250 | if (!sRegisters) { 251 | sRegisters = [HBMutableArray new]; 252 | return NO; 253 | } 254 | return [sRegisters containsObject:key]; 255 | } 256 | 257 | #pragma mark - Private Request Handler methods 258 | 259 | - (NSURLSessionTask *)taskWithRequest:(NSURLRequest *)request 260 | { 261 | NSMutableArray *modes = [NSMutableArray new]; 262 | [modes addObject:NSDefaultRunLoopMode]; 263 | 264 | NSString *currentMode = [[NSRunLoop currentRunLoop] currentMode]; 265 | 266 | if (currentMode != nil && ![currentMode isEqualToString:NSDefaultRunLoopMode]) { 267 | [modes addObject:currentMode]; 268 | } 269 | self.modes = modes; 270 | 271 | NSURLSessionTask *dataTask = [[[self class] sharedDemux] dataTaskWithRequest:request delegate:self modes:self.modes]; 272 | return dataTask; 273 | } 274 | 275 | - (void)startRemoteRequest:(NSURLRequest *)request 276 | { 277 | NSURLSessionTask *task = [self taskWithRequest:request]; 278 | self.currentTask = task; 279 | [task resume]; 280 | } 281 | 282 | - (void)logDNSResult 283 | { 284 | [[[DNSCenter defaultCenter] loggers] enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 285 | [obj logLevel:KIDDNSLogLevelInfo result:self.dnsResult]; 286 | }]; 287 | } 288 | 289 | #pragma mark - SubClass Override Methods 290 | 291 | + (BOOL)shouldInterceptRequest:(NSURLRequest *)request 292 | { 293 | return NO; 294 | } 295 | 296 | + (void)rewriteRequest:(NSMutableURLRequest *)request 297 | { 298 | 299 | } 300 | @end 301 | -------------------------------------------------------------------------------- /KIDDNS/Classes/URLProtocol/HBMutableArray.h: -------------------------------------------------------------------------------- 1 | // 2 | // HBMutableArray.h 3 | // RxData 4 | // 5 | // Created by yiyang on 2018/8/29. 6 | // 7 | 8 | #import 9 | 10 | /** 11 | 线程安全的可读写的数组 12 | */ 13 | @interface HBMutableArray : NSObject 14 | 15 | - (BOOL)containsObject:(id)object; 16 | - (void)addObject:(id)object; 17 | - (void)removeObject:(id)object; 18 | - (id)objectAtIndex:(NSUInteger)index; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /KIDDNS/Classes/URLProtocol/HBMutableArray.m: -------------------------------------------------------------------------------- 1 | // 2 | // HBMutableArray.m 3 | // RxData 4 | // 5 | // Created by yiyang on 2018/8/29. 6 | // 7 | 8 | #import "HBMutableArray.h" 9 | 10 | @interface HBMutableArray() 11 | @property (nonatomic, strong) NSMutableArray *array; 12 | @property (nonatomic, strong) dispatch_queue_t queue; 13 | @end 14 | 15 | @implementation HBMutableArray 16 | 17 | - (instancetype)init { 18 | if (self = [super init]) { 19 | _array = [NSMutableArray new]; 20 | _queue = dispatch_queue_create("rxdata.hbarray.queue", DISPATCH_QUEUE_CONCURRENT); 21 | } 22 | return self; 23 | } 24 | 25 | - (BOOL)containsObject:(id)object 26 | { 27 | __block BOOL result = NO; 28 | dispatch_sync(_queue, ^{ 29 | result = [self.array containsObject:object]; 30 | }); 31 | return result; 32 | } 33 | 34 | - (id)objectAtIndex:(NSUInteger)index 35 | { 36 | __block id result = nil; 37 | dispatch_sync(_queue, ^{ 38 | result = [self.array objectAtIndex:index]; 39 | }); 40 | return result; 41 | } 42 | 43 | - (void)addObject:(id)object 44 | { 45 | dispatch_barrier_async(_queue, ^{ 46 | [self.array addObject:object]; 47 | }); 48 | } 49 | 50 | - (void)removeObject:(id)object 51 | { 52 | dispatch_barrier_async(_queue, ^{ 53 | [self.array removeObject:object]; 54 | }); 55 | } 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /KIDDNS/Classes/URLProtocol/HBURLSessionDemux.h: -------------------------------------------------------------------------------- 1 | // 2 | // VKURLSessionDemux.h 3 | // Pods 4 | // 5 | // Created by yiyang on 2017/9/9. 6 | // 7 | // 8 | 9 | #import 10 | 11 | /*! A simple class for demultiplexing NSURLSession delegate callbacks to a per-task delegate object. 12 | 13 | You initialise the class with a session configuration. After that you can create data tasks 14 | within that session by calling -dataTaskWithRequest:delegate:modes:. Any delegate callbacks 15 | for that data task are redirected to the delegate on the thread that created the task in 16 | one of the specified run loop modes. That thread must run its run loop in order to get 17 | these callbacks. 18 | */ 19 | 20 | @interface HBURLSessionDemux : NSObject 21 | 22 | /*! Create a demultiplex for the specified session configuration. 23 | * \param configuration The session configuration to use; if nil, a default session is created. 24 | * \returns An initialised instance. 25 | */ 26 | 27 | - (instancetype)initWithConfiguration:(NSURLSessionConfiguration *)configuration; 28 | 29 | @property (atomic, copy, readonly ) NSURLSessionConfiguration * configuration; ///< A copy of the configuration passed to -initWithConfiguration:. 30 | @property (atomic, strong, readonly ) NSURLSession * session; ///< The session created from the configuration passed to -initWithConfiguration:. 31 | 32 | /*! Creates a new data task whose delegate callbacks are routed to the supplied delegate. 33 | * \details The callbacks are run on the current thread (that is, the thread that called this 34 | * method) in the specified modes. 35 | * 36 | * The delegate is retained until the task completes, that is, until after your 37 | * -URLSession:task:didCompleteWithError: delegate callback returns. 38 | * 39 | * The returned task is suspend. You must resume the returned task for the task to 40 | * make progress. Furthermore, it's not safe to simply discard the returned task 41 | * because in that case the task's delegate is never released. 42 | * 43 | * \param request The request that the data task executes; must not be nil. 44 | * \param delegate The delegate to receive the data task's delegate callbacks; must not be nil. 45 | * \param modes The run loop modes in which to run the data task's delegate callbacks; if nil or 46 | * empty, the default run loop mode (NSDefaultRunLoopMode is used). 47 | * \returns A suspended data task that you must resume. 48 | */ 49 | 50 | - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request delegate:(id)delegate modes:(NSArray *)modes; 51 | 52 | @end 53 | 54 | -------------------------------------------------------------------------------- /KIDDNS/Classes/URLProtocol/HBURLSessionDemux.m: -------------------------------------------------------------------------------- 1 | // 2 | // VKURLSessionDemux.m 3 | // Pods 4 | // 5 | // Created by yiyang on 2017/9/9. 6 | // 7 | // 8 | 9 | #import "HBURLSessionDemux.h" 10 | 11 | @interface HBURLSessionDemuxTaskInfo : NSObject 12 | 13 | - (instancetype)initWithTask:(NSURLSessionDataTask *)task delegate:(id)delegate modes:(NSArray *)modes; 14 | 15 | @property (atomic, strong, readonly ) NSURLSessionDataTask * task; 16 | @property (atomic, strong, readonly ) id delegate; 17 | @property (atomic, strong, readonly ) NSThread * thread; 18 | @property (atomic, copy, readonly ) NSArray * modes; 19 | 20 | - (void)performBlock:(dispatch_block_t)block; 21 | 22 | - (void)invalidate; 23 | 24 | @end 25 | 26 | @interface HBURLSessionDemuxTaskInfo () 27 | 28 | @property (atomic, strong, readwrite) id delegate; 29 | @property (atomic, strong, readwrite) NSThread * thread; 30 | 31 | @end 32 | 33 | @implementation HBURLSessionDemuxTaskInfo 34 | 35 | - (instancetype)initWithTask:(NSURLSessionDataTask *)task delegate:(id)delegate modes:(NSArray *)modes 36 | { 37 | assert(task != nil); 38 | assert(delegate != nil); 39 | assert(modes != nil); 40 | 41 | self = [super init]; 42 | if (self != nil) { 43 | self->_task = task; 44 | self->_delegate = delegate; 45 | self->_thread = [NSThread currentThread]; 46 | self->_modes = [modes copy]; 47 | } 48 | return self; 49 | } 50 | 51 | - (void)performBlock:(dispatch_block_t)block 52 | { 53 | assert(self.delegate != nil); 54 | assert(self.thread != nil); 55 | [self performSelector:@selector(performBlockOnClientThread:) onThread:self.thread withObject:[block copy] waitUntilDone:NO modes:self.modes]; 56 | } 57 | 58 | - (void)performBlockOnClientThread:(dispatch_block_t)block 59 | { 60 | assert([NSThread currentThread] == self.thread); 61 | block(); 62 | } 63 | 64 | - (void)invalidate 65 | { 66 | self.delegate = nil; 67 | self.thread = nil; 68 | } 69 | 70 | @end 71 | 72 | @interface HBURLSessionDemux () 73 | 74 | @property (atomic, strong, readonly ) NSMutableDictionary * taskInfoByTaskID; // keys NSURLSessionTask taskIdentifier, values are SessionManager 75 | @property (atomic, strong, readonly ) NSOperationQueue * sessionDelegateQueue; 76 | 77 | @end 78 | 79 | @implementation HBURLSessionDemux 80 | 81 | - (instancetype)init 82 | { 83 | return [self initWithConfiguration:nil]; 84 | } 85 | 86 | - (instancetype)initWithConfiguration:(NSURLSessionConfiguration *)configuration 87 | { 88 | // configuration may be nil 89 | self = [super init]; 90 | if (self != nil) { 91 | if (configuration == nil) { 92 | configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; 93 | } 94 | self->_configuration = [configuration copy]; 95 | 96 | self->_taskInfoByTaskID = [[NSMutableDictionary alloc] init]; 97 | 98 | self->_sessionDelegateQueue = [[NSOperationQueue alloc] init]; 99 | [self->_sessionDelegateQueue setMaxConcurrentOperationCount:1]; 100 | [self->_sessionDelegateQueue setName:@"HBURLSessionDemux"]; 101 | 102 | self->_session = [NSURLSession sessionWithConfiguration:self->_configuration delegate:self delegateQueue:self->_sessionDelegateQueue]; 103 | self->_session.sessionDescription = @"HBURLSessionDemux"; 104 | } 105 | return self; 106 | } 107 | 108 | - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request delegate:(id)delegate modes:(NSArray *)modes 109 | { 110 | NSURLSessionDataTask * task; 111 | HBURLSessionDemuxTaskInfo * taskInfo; 112 | 113 | assert(request != nil); 114 | assert(delegate != nil); 115 | // modes may be nil 116 | 117 | if ([modes count] == 0) { 118 | modes = @[ NSDefaultRunLoopMode ]; 119 | } 120 | 121 | task = [self.session dataTaskWithRequest:request]; 122 | assert(task != nil); 123 | 124 | taskInfo = [[HBURLSessionDemuxTaskInfo alloc] initWithTask:task delegate:delegate modes:modes]; 125 | 126 | @synchronized (self) { 127 | self.taskInfoByTaskID[@(task.taskIdentifier)] = taskInfo; 128 | } 129 | 130 | return task; 131 | } 132 | 133 | - (HBURLSessionDemuxTaskInfo *)taskInfoForTask:(NSURLSessionTask *)task 134 | { 135 | HBURLSessionDemuxTaskInfo * result; 136 | 137 | assert(task != nil); 138 | 139 | @synchronized (self) { 140 | result = self.taskInfoByTaskID[@(task.taskIdentifier)]; 141 | assert(result != nil); 142 | } 143 | return result; 144 | } 145 | 146 | - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)newRequest completionHandler:(void (^)(NSURLRequest *))completionHandler 147 | { 148 | HBURLSessionDemuxTaskInfo * taskInfo; 149 | 150 | taskInfo = [self taskInfoForTask:task]; 151 | if ([taskInfo.delegate respondsToSelector:@selector(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:)]) { 152 | [taskInfo performBlock:^{ 153 | [taskInfo.delegate URLSession:session task:task willPerformHTTPRedirection:response newRequest:newRequest completionHandler:completionHandler]; 154 | }]; 155 | } else { 156 | completionHandler(newRequest); 157 | } 158 | } 159 | 160 | - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler 161 | { 162 | HBURLSessionDemuxTaskInfo * taskInfo; 163 | 164 | taskInfo = [self taskInfoForTask:task]; 165 | if ([taskInfo.delegate respondsToSelector:@selector(URLSession:task:didReceiveChallenge:completionHandler:)]) { 166 | [taskInfo performBlock:^{ 167 | [taskInfo.delegate URLSession:session task:task didReceiveChallenge:challenge completionHandler:completionHandler]; 168 | }]; 169 | } else { 170 | completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil); 171 | } 172 | } 173 | 174 | - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler 175 | { 176 | HBURLSessionDemuxTaskInfo * taskInfo; 177 | 178 | taskInfo = [self taskInfoForTask:task]; 179 | if ([taskInfo.delegate respondsToSelector:@selector(URLSession:task:needNewBodyStream:)]) { 180 | [taskInfo performBlock:^{ 181 | [taskInfo.delegate URLSession:session task:task needNewBodyStream:completionHandler]; 182 | }]; 183 | } else { 184 | completionHandler(nil); 185 | } 186 | } 187 | 188 | - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend 189 | { 190 | HBURLSessionDemuxTaskInfo * taskInfo; 191 | 192 | taskInfo = [self taskInfoForTask:task]; 193 | if ([taskInfo.delegate respondsToSelector:@selector(URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:)]) { 194 | [taskInfo performBlock:^{ 195 | [taskInfo.delegate URLSession:session task:task didSendBodyData:bytesSent totalBytesSent:totalBytesSent totalBytesExpectedToSend:totalBytesExpectedToSend]; 196 | }]; 197 | } 198 | } 199 | 200 | - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error 201 | { 202 | HBURLSessionDemuxTaskInfo * taskInfo; 203 | 204 | taskInfo = [self taskInfoForTask:task]; 205 | 206 | // This is our last delegate callback so we remove our task info record. 207 | 208 | @synchronized (self) { 209 | [self.taskInfoByTaskID removeObjectForKey:@(taskInfo.task.taskIdentifier)]; 210 | } 211 | 212 | // Call the delegate if required. In that case we invalidate the task info on the client thread 213 | // after calling the delegate, otherwise the client thread side of the -performBlock: code can 214 | // find itself with an invalidated task info. 215 | 216 | if ([taskInfo.delegate respondsToSelector:@selector(URLSession:task:didCompleteWithError:)]) { 217 | [taskInfo performBlock:^{ 218 | [taskInfo.delegate URLSession:session task:task didCompleteWithError:error]; 219 | [taskInfo invalidate]; 220 | }]; 221 | } else { 222 | [taskInfo invalidate]; 223 | } 224 | } 225 | 226 | - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler 227 | { 228 | HBURLSessionDemuxTaskInfo * taskInfo; 229 | 230 | taskInfo = [self taskInfoForTask:dataTask]; 231 | if ([taskInfo.delegate respondsToSelector:@selector(URLSession:dataTask:didReceiveResponse:completionHandler:)]) { 232 | [taskInfo performBlock:^{ 233 | [taskInfo.delegate URLSession:session dataTask:dataTask didReceiveResponse:response completionHandler:completionHandler]; 234 | }]; 235 | } else { 236 | completionHandler(NSURLSessionResponseAllow); 237 | } 238 | } 239 | 240 | - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask 241 | { 242 | HBURLSessionDemuxTaskInfo * taskInfo; 243 | 244 | taskInfo = [self taskInfoForTask:dataTask]; 245 | if ([taskInfo.delegate respondsToSelector:@selector(URLSession:dataTask:didBecomeDownloadTask:)]) { 246 | [taskInfo performBlock:^{ 247 | [taskInfo.delegate URLSession:session dataTask:dataTask didBecomeDownloadTask:downloadTask]; 248 | }]; 249 | } 250 | } 251 | 252 | - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data 253 | { 254 | HBURLSessionDemuxTaskInfo * taskInfo; 255 | 256 | taskInfo = [self taskInfoForTask:dataTask]; 257 | if ([taskInfo.delegate respondsToSelector:@selector(URLSession:dataTask:didReceiveData:)]) { 258 | [taskInfo performBlock:^{ 259 | [taskInfo.delegate URLSession:session dataTask:dataTask didReceiveData:data]; 260 | }]; 261 | } 262 | } 263 | 264 | - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask willCacheResponse:(NSCachedURLResponse *)proposedResponse completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler 265 | { 266 | HBURLSessionDemuxTaskInfo * taskInfo; 267 | 268 | taskInfo = [self taskInfoForTask:dataTask]; 269 | if ([taskInfo.delegate respondsToSelector:@selector(URLSession:dataTask:willCacheResponse:completionHandler:)]) { 270 | [taskInfo performBlock:^{ 271 | [taskInfo.delegate URLSession:session dataTask:dataTask willCacheResponse:proposedResponse completionHandler:completionHandler]; 272 | }]; 273 | } else { 274 | completionHandler(proposedResponse); 275 | } 276 | } 277 | 278 | @end 279 | 280 | -------------------------------------------------------------------------------- /KIDDNS/Classes/URLProtocol/HBURLSessionMap.h: -------------------------------------------------------------------------------- 1 | // 2 | // HBURLSessionMap.h 3 | // RxData 4 | // 5 | // Created by yiyang on 2018/8/31. 6 | // 7 | 8 | #import 9 | 10 | @interface HBURLSessionMap : NSObject 11 | 12 | + (NSURLSession *)fetchSessionOfTask:(NSURLSessionTask *)task; 13 | + (void)recordSessionTask:(NSURLSessionTask *)task ofSession:(NSURLSession *)session; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /KIDDNS/Classes/URLProtocol/HBURLSessionMap.m: -------------------------------------------------------------------------------- 1 | // 2 | // HBURLSessionMap.m 3 | // RxData 4 | // 5 | // Created by yiyang on 2018/8/31. 6 | // 7 | 8 | #import "HBURLSessionMap.h" 9 | 10 | @interface HBURLSessionMap () { 11 | NSMapTable * _map; 12 | dispatch_queue_t _queue; 13 | } 14 | @end 15 | 16 | @implementation HBURLSessionMap 17 | 18 | + (instancetype)sharedInstance { 19 | static HBURLSessionMap *instance = nil; 20 | static dispatch_once_t onceToken; 21 | dispatch_once(&onceToken, ^{ 22 | instance = [HBURLSessionMap new]; 23 | }); 24 | return instance; 25 | } 26 | 27 | - (instancetype)init 28 | { 29 | if (self = [super init]) { 30 | _map = [NSMapTable weakToWeakObjectsMapTable]; 31 | _queue = dispatch_queue_create("com.rxdata.urlprotocol.sessionmap", DISPATCH_QUEUE_CONCURRENT); 32 | } 33 | return self; 34 | } 35 | 36 | - (NSURLSession *)fetchSessionOfTask:(NSURLSessionTask *)task 37 | { 38 | __block NSURLSession *session = nil; 39 | dispatch_sync(_queue, ^{ 40 | session = [self->_map objectForKey:task]; 41 | }); 42 | return session; 43 | } 44 | 45 | - (void)recordSessionTask:(NSURLSessionTask *)task ofSession:(NSURLSession *)session 46 | { 47 | dispatch_barrier_async(_queue, ^{ 48 | [self->_map setObject:session forKey:task]; 49 | }); 50 | } 51 | 52 | + (NSURLSession *)fetchSessionOfTask:(NSURLSessionTask *)task 53 | { 54 | return [[self sharedInstance] fetchSessionOfTask:task]; 55 | } 56 | 57 | + (void)recordSessionTask:(NSURLSessionTask *)task ofSession:(NSURLSession *)session 58 | { 59 | [[self sharedInstance] recordSessionTask:task ofSession:session]; 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /KIDDNS/Classes/URLProtocol/NSURLSession+URLProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSURLSession+URLProtocol.h 3 | // RxData 4 | // 5 | // Created by yiyang on 2018/8/30. 6 | // 7 | 8 | #import 9 | 10 | @interface NSURLSession (URLProtocol) 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /KIDDNS/Classes/URLProtocol/NSURLSession+URLProtocol.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSURLSession+URLProtocol.m 3 | // RxData 4 | // 5 | // Created by yiyang on 2018/8/30. 6 | // 7 | 8 | #import "NSURLSession+URLProtocol.h" 9 | #import 10 | #import "HBURLSessionMap.h" 11 | 12 | @implementation NSURLSession (URLProtocol) 13 | 14 | + (void)load 15 | { 16 | [self swizzleMethods]; 17 | } 18 | 19 | + (void)swizzleMethods 20 | { 21 | NSArray *selectors = @[@"dataTaskWithRequest:", @"dataTaskWithURL:",@"uploadTaskWithRequest:fromFile:",@"uploadTaskWithRequest:fromData:",@"uploadTaskWithStreamedRequest:",@"downloadTaskWithRequest:",@"downloadTaskWithURL:",@"downloadTaskWithResumeData:"]; 22 | [selectors enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 23 | Method originalMethod = class_getInstanceMethod([NSURLSession class],NSSelectorFromString(obj)); 24 | NSString *fakeSelector = [NSString stringWithFormat:@"fake_%@", obj]; 25 | Method fakeMethod = class_getInstanceMethod([NSURLSession class], NSSelectorFromString(fakeSelector)); 26 | method_exchangeImplementations(originalMethod, fakeMethod); 27 | }]; 28 | } 29 | 30 | - (NSURLSessionDataTask *)fake_dataTaskWithRequest:(NSURLRequest *)request { 31 | NSURLSessionDataTask *task = [self fake_dataTaskWithRequest:request]; 32 | NSLog(@"[URLSession] %@ %@ -> %p", NSStringFromSelector(_cmd), request.URL, task); 33 | [self recordTask:task]; 34 | return task; 35 | } 36 | 37 | /* Creates a data task to retrieve the contents of the given URL. */ 38 | - (NSURLSessionDataTask *)fake_dataTaskWithURL:(NSURL *)url { 39 | NSURLSessionDataTask *task = [self fake_dataTaskWithURL:url]; 40 | NSLog(@"[URLSession] %@ %@ -> %p", NSStringFromSelector(_cmd), url, task); 41 | 42 | [self recordTask:task]; 43 | return task; 44 | } 45 | 46 | /* Creates an upload task with the given request. The body of the request will be created from the file referenced by fileURL */ 47 | - (NSURLSessionUploadTask *)fake_uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL { 48 | NSURLSessionUploadTask *task = [self fake_uploadTaskWithRequest:request fromFile:fileURL]; 49 | NSLog(@"[URLSession] %@ %@ -> %p", NSStringFromSelector(_cmd), request.URL, task); 50 | 51 | [self recordTask:task]; 52 | return task; 53 | } 54 | 55 | /* Creates an upload task with the given request. The body of the request is provided from the bodyData. */ 56 | - (NSURLSessionUploadTask *)fake_uploadTaskWithRequest:(NSURLRequest *)request fromData:(NSData *)bodyData { 57 | NSURLSessionUploadTask * task = [self fake_uploadTaskWithRequest:request fromData:bodyData]; 58 | NSLog(@"[URLSession] %@ %@ -> %p", NSStringFromSelector(_cmd), request.URL, task); 59 | 60 | [self recordTask:task]; 61 | return task; 62 | } 63 | 64 | /* Creates an upload task with the given request. The previously set body stream of the request (if any) is ignored and the URLSession:task:needNewBodyStream: delegate will be called when the body payload is required. */ 65 | - (NSURLSessionUploadTask *)fake_uploadTaskWithStreamedRequest:(NSURLRequest *)request { 66 | NSURLSessionUploadTask *task = [self fake_uploadTaskWithStreamedRequest:request]; 67 | NSLog(@"[URLSession] %@ %@ -> %p", NSStringFromSelector(_cmd), request.URL, task); 68 | 69 | [self recordTask:task]; 70 | return task; 71 | } 72 | 73 | /* Creates a download task with the given request. */ 74 | - (NSURLSessionDownloadTask *)fake_downloadTaskWithRequest:(NSURLRequest *)request { 75 | NSURLSessionDownloadTask *task = [self fake_downloadTaskWithRequest:request]; 76 | NSLog(@"[URLSession] %@ %@ -> %p", NSStringFromSelector(_cmd), request.URL, task); 77 | 78 | [self recordTask:task]; 79 | return task; 80 | } 81 | 82 | /* Creates a download task to download the contents of the given URL. */ 83 | - (NSURLSessionDownloadTask *)fake_downloadTaskWithURL:(NSURL *)url { 84 | NSURLSessionDownloadTask *task = [self fake_downloadTaskWithURL:url]; 85 | NSLog(@"[URLSession] %@ %@ -> %p", NSStringFromSelector(_cmd), url, task); 86 | 87 | [self recordTask:task]; 88 | return task; 89 | } 90 | 91 | /* Creates a download task with the resume data. If the download cannot be successfully resumed, URLSession:task:didCompleteWithError: will be called. */ 92 | - (NSURLSessionDownloadTask *)fake_downloadTaskWithResumeData:(NSData *)resumeData { 93 | NSURLSessionDownloadTask *task = [self fake_downloadTaskWithResumeData:resumeData]; 94 | NSLog(@"[URLSession] %@ -> %p", NSStringFromSelector(_cmd), task); 95 | 96 | [self recordTask:task]; 97 | return task; 98 | } 99 | 100 | - (void)recordTask:(NSURLSessionTask *)task 101 | { 102 | [HBURLSessionMap recordSessionTask:task ofSession:self]; 103 | } 104 | 105 | @end 106 | 107 | 108 | -------------------------------------------------------------------------------- /KIDDNS/Classes/URLProtocol/RxHTTPDNSURLProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // RxHTTPDNSURLProtocol.h 3 | // RxData 4 | // 5 | // Created by yiyang on 2018/8/29. 6 | // 7 | 8 | #import "HBHTTPURLProtocol.h" 9 | 10 | @interface RxHTTPDNSURLProtocol : HBHTTPURLProtocol 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /KIDDNS/Classes/URLProtocol/RxHTTPDNSURLProtocol.m: -------------------------------------------------------------------------------- 1 | // 2 | // RxHTTPDNSURLProtocol.m 3 | // RxData 4 | // 5 | // Created by yiyang on 2018/8/29. 6 | // 7 | 8 | #import "RxHTTPDNSURLProtocol.h" 9 | #import "DNSCenter.h" 10 | #import "KIDAuthChallenger.h" 11 | #import "DNSCenter_internal.h" 12 | 13 | @interface RxHTTPDNSURLProtocol() 14 | 15 | @property (nonatomic, strong) NSURLSessionTask *taskWithIP; 16 | @property (nonatomic, strong) NSURLSessionTask *taskWithDomain; 17 | 18 | @end 19 | 20 | @implementation RxHTTPDNSURLProtocol 21 | 22 | + (BOOL)shouldInterceptRequest:(NSURLRequest *)request 23 | { 24 | if ([DNSCenter defaultCenter].automaticDNS == NO) { 25 | return NO; 26 | } 27 | NSString *ua = request.allHTTPHeaderFields[@"User-Agent"]; 28 | if ([ua containsString:@"Mozilla"]) { 29 | return NO; 30 | } 31 | 32 | NSString *host = request.URL.host; 33 | if ([host containsString:@"taobao"] || [host containsString:@"aliyun"]) { 34 | return NO; 35 | } 36 | NSString *dnsResultURL = [[DNSCenter defaultCenter] dnsResultForURL:request.URL.absoluteString]; 37 | if (dnsResultURL.length > 0) { 38 | return YES; 39 | } 40 | return NO; 41 | } 42 | 43 | - (void)startLoading 44 | { 45 | self.taskWithDomain = nil; 46 | self.taskWithIP = nil; 47 | NSMutableURLRequest *newQuest = nil; 48 | if ([self.request isKindOfClass:[NSMutableURLRequest class]]) { 49 | newQuest = (NSMutableURLRequest *)self.request; 50 | } else { 51 | newQuest = [self.request mutableCopy]; 52 | } 53 | 54 | [[self class] markRequestAsIgnored:newQuest]; 55 | 56 | self.taskWithDomain = [self taskWithRequest:newQuest]; 57 | 58 | NSString *dnsResultURL = [[DNSCenter defaultCenter] dnsResultForURL:newQuest.URL.absoluteString]; 59 | if (dnsResultURL.length > 0) { 60 | 61 | NSMutableDictionary *headers = [newQuest.allHTTPHeaderFields mutableCopy]; 62 | if (headers == nil) { 63 | headers = [NSMutableDictionary new]; 64 | } 65 | headers[@"Host"] = newQuest.URL.host; 66 | 67 | //cookies 68 | NSMutableArray *cookieArray = [NSMutableArray array]; 69 | NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; 70 | for (NSHTTPCookie *cookie in [cookieStorage cookies]) { 71 | if ([_taskWithDomain.currentRequest.URL.host containsString:cookie.domain]){ 72 | [cookieArray addObject:cookie]; 73 | } 74 | } 75 | if (cookieArray != nil && cookieArray.count > 0) { 76 | NSDictionary *cookieDic = [NSHTTPCookie requestHeaderFieldsWithCookies:cookieArray]; 77 | if ([cookieDic objectForKey:@"Cookie"]) { 78 | [headers setValue:cookieDic[@"Cookie"] forKey:@"Cookie"]; 79 | } 80 | } 81 | 82 | newQuest.allHTTPHeaderFields = [headers copy]; 83 | newQuest.URL = [NSURL URLWithString:dnsResultURL]; 84 | self.taskWithIP = [self taskWithRequest:newQuest]; 85 | } 86 | [self startTask]; 87 | } 88 | 89 | - (void)startTask 90 | { 91 | if (self.taskWithIP) { 92 | self.currentTask = self.taskWithIP; 93 | self.dnsResult.useHTTPDNS = YES; 94 | } else if (self.taskWithDomain) { 95 | self.currentTask = self.taskWithDomain; 96 | self.dnsResult.useLocalDNS = YES; 97 | } 98 | if (self.currentTask) { 99 | [self.currentTask resume]; 100 | } 101 | } 102 | 103 | #pragma mark - NSURLSessionDataDelegate 104 | 105 | - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler 106 | { 107 | 108 | [KIDAuthChallenger didReceiveChallengeOnHost:self.taskWithDomain.currentRequest.URL.host session:session challenge:challenge completion:completionHandler]; 109 | } 110 | 111 | - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error 112 | { 113 | //cookies 114 | NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response; 115 | NSArray *cookieArray = [NSHTTPCookie cookiesWithResponseHeaderFields:response.allHeaderFields forURL:self.request.URL]; 116 | if (cookieArray != nil) { 117 | NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; 118 | for (NSHTTPCookie *cookie in cookieArray) { 119 | if ([self.request.URL.host containsString:cookie.domain]){ 120 | [cookieStorage setCookie:cookie]; 121 | } 122 | } 123 | } 124 | 125 | if (error == nil) { 126 | [self.client URLProtocolDidFinishLoading:self]; 127 | if (task == self.taskWithIP) { 128 | self.dnsResult.successByHTTPDNS = YES; 129 | } 130 | self.dnsResult.successAfterAll = YES; 131 | [self logDNSResult]; 132 | } else { 133 | if (task == self.taskWithIP) { 134 | NSLog(@"[DNSProtocol] ip task failed, retry with domain task"); 135 | self.taskWithIP = nil; 136 | self.dnsResult.successByHTTPDNS = NO; 137 | [self startTask]; 138 | return; 139 | } 140 | if ([[error domain] isEqual:NSURLErrorDomain] && error.code == NSURLErrorCancelled) { 141 | // Do nothing. This happens in two cases: 142 | // 143 | // o during a redirect, in which case the redirect code has already told the client about 144 | // the failure 145 | // 146 | // o if the request is cancelled by a call to -stopLoading, in which case the client doesn't 147 | // want to know about the failure 148 | } else { 149 | 150 | [self.client URLProtocol:self didFailWithError:error]; 151 | } 152 | [self logDNSResult]; 153 | } 154 | } 155 | 156 | @end 157 | -------------------------------------------------------------------------------- /KIDDNS/Classes/URLProtocol/RxSNIURLProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // RxSNIURLProtocol.h 3 | // RxData 4 | // 5 | // Created by yiyang on 2018/8/31. 6 | // 7 | 8 | #import "HBHTTPURLProtocol.h" 9 | 10 | @interface RxSNIURLProtocol : HBHTTPURLProtocol 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /KIDDNS/Classes/URLProtocol/RxSNIURLProtocol.m: -------------------------------------------------------------------------------- 1 | // 2 | // RxSNIURLProtocol.m 3 | // RxData 4 | // 5 | // Created by yiyang on 2018/8/31. 6 | // 7 | 8 | #import "RxSNIURLProtocol.h" 9 | #import "DNSCenter.h" 10 | #import 11 | 12 | 13 | #define kAnchorAlreadyAdded @"AnchorAlreadyAdded" 14 | 15 | @interface RxSNIURLProtocol() { 16 | NSInputStream *_inputStream; 17 | NSRunLoop *_currentRunloop; 18 | NSRunLoopMode _runloopMode; 19 | NSMutableURLRequest *_currentRequest; 20 | NSMutableURLRequest *_requestWithIP; 21 | NSMutableURLRequest *_requestWithDomain; 22 | } 23 | @end 24 | 25 | @implementation RxSNIURLProtocol 26 | 27 | + (BOOL)shouldInterceptRequest:(NSURLRequest *)request 28 | { 29 | if ([DNSCenter defaultCenter].automaticDNS == NO) { 30 | return NO; 31 | } 32 | NSString *ua = request.allHTTPHeaderFields[@"User-Agent"]; 33 | if ([ua containsString:@"Mozilla"]) { 34 | return NO; 35 | } 36 | 37 | NSString *host = request.URL.host; 38 | if ([host containsString:@"taobao"] || [host containsString:@"aliyun"]) { 39 | return NO; 40 | } 41 | 42 | if ([request.URL.scheme isEqualToString:@"https"]) { 43 | NSString *dnsResultURL = [[DNSCenter defaultCenter] dnsResultForURL:request.URL.absoluteString]; 44 | if (dnsResultURL.length > 0) { 45 | return YES; 46 | } 47 | } 48 | return NO; 49 | } 50 | 51 | - (void)startLoading 52 | { 53 | NSMutableURLRequest *newQuest = nil; 54 | if ([self.request isKindOfClass:[NSMutableURLRequest class]]) { 55 | newQuest = (NSMutableURLRequest *)self.request; 56 | } else { 57 | newQuest = [self.request mutableCopy]; 58 | } 59 | 60 | [[self class] markRequestAsIgnored:newQuest]; 61 | 62 | _requestWithDomain = [newQuest mutableCopy]; 63 | 64 | NSString *dnsResultURL = [[DNSCenter defaultCenter] dnsResultForURL:newQuest.URL.absoluteString]; 65 | // NSString *ip = nil; 66 | if (dnsResultURL.length > 0) { 67 | 68 | NSMutableDictionary *headers = [newQuest.allHTTPHeaderFields mutableCopy]; 69 | if (headers == nil) { 70 | headers = [NSMutableDictionary new]; 71 | } 72 | headers[@"Host"] = newQuest.URL.host; 73 | newQuest.allHTTPHeaderFields = [headers copy]; 74 | 75 | newQuest.URL = [NSURL URLWithString:dnsResultURL]; 76 | _requestWithIP = newQuest; 77 | } 78 | 79 | 80 | [self startRequest]; 81 | } 82 | 83 | - (void)startRequest 84 | { 85 | if (_requestWithIP != nil) { 86 | self.dnsResult.useHTTPDNS = YES; 87 | [self startRequest:_requestWithIP]; 88 | } else { 89 | self.dnsResult.useLocalDNS = YES; 90 | [self startRequest:_requestWithDomain]; 91 | } 92 | } 93 | 94 | - (void)startRequest:(NSURLRequest *)request 95 | { 96 | if ([request isKindOfClass:[NSMutableURLRequest class]]) { 97 | _currentRequest = (NSMutableURLRequest *)request; 98 | } else { 99 | _currentRequest = [request mutableCopy]; 100 | } 101 | 102 | NSDictionary *headers = request.allHTTPHeaderFields; 103 | CFStringRef requestBody = CFSTR(""); 104 | CFDataRef bodyData = CFStringCreateExternalRepresentation(kCFAllocatorDefault, requestBody, kCFStringEncodingUTF8, 0); 105 | if (request.HTTPBody) { 106 | bodyData = (__bridge_retained CFDataRef)request.HTTPBody; 107 | } else if (request.HTTPBodyStream) { 108 | NSInputStream *bodyStream = request.HTTPBodyStream; 109 | NSInteger maxlength = 1024; 110 | uint8_t d[maxlength]; 111 | BOOL eof = NO; 112 | NSMutableData *streamData = [NSMutableData new]; 113 | [bodyStream open]; 114 | while (!eof) { 115 | NSInteger bytesRead = [bodyStream read:d maxLength:maxlength]; 116 | if (bytesRead == 0) { 117 | eof = YES; 118 | } else if (bytesRead == -1) { 119 | eof = YES; 120 | } else if (bodyStream.streamError == nil) { 121 | [streamData appendBytes:d length:bytesRead]; 122 | } 123 | } 124 | bodyData = (__bridge_retained CFDataRef)[streamData copy]; 125 | [bodyStream close]; 126 | } 127 | 128 | CFStringRef url = (__bridge CFStringRef)[request.URL absoluteString]; 129 | CFURLRef requestURL = CFURLCreateWithString(kCFAllocatorDefault, url, NULL); 130 | 131 | CFStringRef httpMethod = (__bridge_retained CFStringRef)request.HTTPMethod; 132 | 133 | CFHTTPMessageRef cfRequst = CFHTTPMessageCreateRequest(kCFAllocatorDefault, httpMethod, requestURL, kCFHTTPVersion1_1); 134 | // body 135 | CFHTTPMessageSetBody(cfRequst, bodyData); 136 | 137 | // header 138 | [headers enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { 139 | CFStringRef cfKey = (__bridge CFStringRef)key; 140 | CFStringRef cfValue = (__bridge CFStringRef)obj; 141 | CFHTTPMessageSetHeaderFieldValue(cfRequst, cfKey, cfValue); 142 | }]; 143 | 144 | //cookies 145 | NSMutableArray *cookieArray = [NSMutableArray array]; 146 | NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; 147 | for (NSHTTPCookie *cookie in [cookieStorage cookies]) { 148 | if ([_requestWithDomain.URL.host containsString:cookie.domain]){ 149 | [cookieArray addObject:cookie]; 150 | } 151 | } 152 | 153 | if (cookieArray != nil && cookieArray.count > 0) { 154 | NSDictionary *cookieDic = [NSHTTPCookie requestHeaderFieldsWithCookies:cookieArray]; 155 | if ([cookieDic objectForKey:@"Cookie"]) { 156 | NSString *cookieString = [cookieDic valueForKey:@"Cookie"]; 157 | CFStringRef cfKey = (__bridge_retained CFStringRef)@"Cookie"; 158 | CFStringRef cfValue = (__bridge_retained CFStringRef)cookieString; 159 | CFHTTPMessageSetHeaderFieldValue(cfRequst, cfKey, cfValue); 160 | 161 | CFRelease(cfKey); 162 | CFRelease(cfValue); 163 | } 164 | } 165 | 166 | CFReadStreamRef readStream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, cfRequst); 167 | _inputStream = (__bridge_transfer NSInputStream *)readStream; 168 | 169 | NSString *host = headers[@"host"]; 170 | if (!host) { 171 | host = request.URL.host; 172 | } 173 | NSDictionary *sslProperties = [[NSDictionary alloc] initWithObjectsAndKeys:host, (__bridge id)kCFStreamSSLPeerName, nil]; 174 | [_inputStream setProperty:sslProperties forKey:(__bridge_transfer NSString *)kCFStreamPropertySSLSettings]; 175 | _inputStream.delegate = self; 176 | 177 | 178 | if (!_currentRunloop) { 179 | _currentRunloop = [NSRunLoop currentRunLoop]; 180 | } 181 | if (!_runloopMode) { 182 | _runloopMode = NSRunLoopCommonModes; 183 | NSRunLoopMode mode = [NSRunLoop currentRunLoop].currentMode; 184 | if (mode != nil && ![mode isEqualToString:_runloopMode]) { 185 | _runloopMode = mode; 186 | } 187 | } 188 | 189 | [_inputStream scheduleInRunLoop:_currentRunloop forMode:_runloopMode]; 190 | [_inputStream open]; 191 | 192 | CFRelease(cfRequst); 193 | CFRelease(requestURL); 194 | cfRequst = NULL; 195 | CFRelease(bodyData); 196 | CFRelease(httpMethod); 197 | } 198 | 199 | - (void)stopLoading 200 | { 201 | if (_inputStream.streamStatus == NSStreamStatusOpen) { 202 | [_inputStream removeFromRunLoop:_currentRunloop forMode:NSRunLoopCommonModes]; 203 | [_inputStream setDelegate:nil]; 204 | [_inputStream close]; 205 | } 206 | [self.client URLProtocol:self didFailWithError:[[NSError alloc] initWithDomain:@"stop loading" code:-1 userInfo:nil]]; 207 | } 208 | 209 | #pragma mark - NSStreamDelegate 210 | 211 | - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode 212 | { 213 | if (eventCode == NSStreamEventHasBytesAvailable) { 214 | CFReadStreamRef readStream = (__bridge CFReadStreamRef) aStream; 215 | CFHTTPMessageRef message = (CFHTTPMessageRef) CFReadStreamCopyProperty(readStream, kCFStreamPropertyHTTPResponseHeader); 216 | if (CFHTTPMessageIsHeaderComplete(message)) { 217 | // 以防response的header信息不完整 218 | UInt8 buffer[16 * 1024]; 219 | UInt8 *buf = NULL; 220 | unsigned long length = 0; 221 | NSInputStream *inputstream = (NSInputStream *) aStream; 222 | BOOL alreadyAdded = [objc_getAssociatedObject(aStream, kAnchorAlreadyAdded) boolValue]; 223 | if (!alreadyAdded) { 224 | objc_setAssociatedObject(aStream, kAnchorAlreadyAdded, @YES, OBJC_ASSOCIATION_RETAIN); 225 | // 通知client已收到response,只通知一次 226 | NSDictionary *headDict = (__bridge_transfer NSDictionary *) (CFHTTPMessageCopyAllHeaderFields(message)); 227 | CFStringRef httpVersion = CFHTTPMessageCopyVersion(message); 228 | // 获取响应头部的状态码 229 | CFIndex myErrCode = CFHTTPMessageGetResponseStatusCode(message); 230 | NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:_currentRequest.URL statusCode:myErrCode HTTPVersion:(__bridge NSString *) httpVersion headerFields:headDict]; 231 | 232 | [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; 233 | 234 | CFRelease(httpVersion); 235 | } 236 | if (![inputstream getBuffer:&buf length:&length]) { 237 | NSInteger amount = [inputstream read:buffer maxLength:sizeof(buffer)]; 238 | buf = buffer; 239 | length = amount; 240 | } 241 | NSData *data = [[NSData alloc] initWithBytes:buf length:length]; 242 | 243 | [self.client URLProtocol:self didLoadData:data]; 244 | } 245 | CFRelease(message); 246 | } else if (eventCode == NSStreamEventErrorOccurred) { 247 | [aStream removeFromRunLoop:_currentRunloop forMode:_runloopMode]; 248 | [aStream setDelegate:nil]; 249 | [aStream close]; 250 | // 通知client发生错误了 251 | if (_currentRequest == _requestWithIP) { 252 | _requestWithIP = nil; 253 | [self startRequest]; 254 | } else { 255 | [self logDNSResult]; 256 | [self.client URLProtocol:self didFailWithError:[aStream streamError]]; 257 | } 258 | } else if (eventCode == NSStreamEventEndEncountered) { 259 | if (_currentRequest == _requestWithIP) { 260 | self.dnsResult.successByHTTPDNS = YES; 261 | } 262 | self.dnsResult.successAfterAll = YES; 263 | [self logDNSResult]; 264 | [self handleResponse]; 265 | } 266 | } 267 | 268 | #pragma mark - Response 269 | 270 | /** 271 | * 根据服务器返回的响应内容进行不同的处理 272 | */ 273 | - (void)handleResponse { 274 | // 获取响应头部信息 275 | CFReadStreamRef readStream = (__bridge CFReadStreamRef) _inputStream; 276 | CFHTTPMessageRef message = (CFHTTPMessageRef) CFReadStreamCopyProperty(readStream, kCFStreamPropertyHTTPResponseHeader); 277 | if (CFHTTPMessageIsHeaderComplete(message)) { 278 | // 确保response头部信息完整 279 | NSDictionary *headDict = (__bridge_transfer NSDictionary *) (CFHTTPMessageCopyAllHeaderFields(message)); 280 | 281 | //cookies 282 | NSArray *cookieArray = [NSHTTPCookie cookiesWithResponseHeaderFields:headDict forURL:self.request.URL]; 283 | if (cookieArray != nil) { 284 | NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; 285 | for (NSHTTPCookie *cookie in cookieArray) { 286 | if ([self.request.URL.host containsString:cookie.domain]){ 287 | [cookieStorage setCookie:cookie]; 288 | } 289 | } 290 | } 291 | 292 | // 获取响应头部的状态码 293 | CFIndex myErrCode = CFHTTPMessageGetResponseStatusCode(message); 294 | 295 | // 把当前请求关闭 296 | [_inputStream removeFromRunLoop:_currentRunloop forMode:_runloopMode]; 297 | [_inputStream setDelegate:nil]; 298 | [_inputStream close]; 299 | 300 | if (myErrCode >= 200 && myErrCode < 300) { 301 | 302 | // 返回码为2xx,直接通知client 303 | [self.client URLProtocolDidFinishLoading:self]; 304 | 305 | } else if (myErrCode >= 300 && myErrCode < 400) { 306 | // 返回码为3xx,需要重定向请求,继续访问重定向页面 307 | NSString *location = headDict[@"Location"]; 308 | if (!location) 309 | location = headDict[@"location"]; 310 | NSURL *url = [[NSURL alloc] initWithString:location]; 311 | _currentRequest.URL = url; 312 | if ([[_currentRequest.HTTPMethod lowercaseString] isEqualToString:@"post"]) { 313 | // 根据RFC文档,当重定向请求为POST请求时,要将其转换为GET请求 314 | _currentRequest.HTTPMethod = @"GET"; 315 | _currentRequest.HTTPBody = nil; 316 | } 317 | 318 | /***********重定向通知client处理或内部处理*************/ 319 | // client处理 320 | // NSURLResponse* response = [[NSURLResponse alloc] initWithURL:curRequest.URL MIMEType:headDict[@"Content-Type"] expectedContentLength:[headDict[@"Content-Length"] integerValue] textEncodingName:@"UTF8"]; 321 | // [self.client URLProtocol:self wasRedirectedToRequest:curRequest redirectResponse:response]; 322 | 323 | // 内部处理,将url中的host通过HTTPDNS转换为IP,不能在startLoading线程中进行同步网络请求,会被阻塞 324 | _requestWithDomain = [_currentRequest mutableCopy]; 325 | NSString *dnsResultURL = [[DNSCenter defaultCenter] dnsResultForURL:_currentRequest.URL.absoluteString]; 326 | if (dnsResultURL.length > 0) { 327 | NSLog(@"Get IP from HTTPDNS Successfully!"); 328 | _currentRequest.URL = [NSURL URLWithString:dnsResultURL]; 329 | 330 | [_currentRequest setValue:url.host forHTTPHeaderField:@"host"]; 331 | _requestWithIP = _currentRequest; 332 | } 333 | [self startRequest]; 334 | } else { 335 | // 其他情况,直接返回响应信息给client 336 | [self.client URLProtocolDidFinishLoading:self]; 337 | } 338 | } else { 339 | // 头部信息不完整,关闭inputstream,通知client 340 | [_inputStream removeFromRunLoop:_currentRunloop forMode:_runloopMode]; 341 | [_inputStream setDelegate:nil]; 342 | [_inputStream close]; 343 | [self.client URLProtocolDidFinishLoading:self]; 344 | } 345 | 346 | CFRelease(message); 347 | } 348 | 349 | @end 350 | -------------------------------------------------------------------------------- /KIDDNS/Classes/URLProtocol/URLProtocolLog.h: -------------------------------------------------------------------------------- 1 | // 2 | // URLProtocolLog.h 3 | // RxData 4 | // 5 | // Created by yiyang on 2018/8/29. 6 | // 7 | 8 | #import 9 | 10 | @interface URLProtocolLog : NSObject 11 | 12 | + (void)logWithPrefix:(NSString *)prefix format:(NSString *)format, ...; 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /KIDDNS/Classes/URLProtocol/URLProtocolLog.m: -------------------------------------------------------------------------------- 1 | // 2 | // URLProtocolLog.m 3 | // RxData 4 | // 5 | // Created by yiyang on 2018/8/29. 6 | // 7 | 8 | #import "URLProtocolLog.h" 9 | 10 | @interface URLProtocolLog() 11 | { 12 | dispatch_queue_t queue; 13 | } 14 | @end 15 | 16 | @implementation URLProtocolLog 17 | 18 | + (instancetype)sharedInstance { 19 | static URLProtocolLog *instance = nil; 20 | static dispatch_once_t onceToken; 21 | dispatch_once(&onceToken, ^{ 22 | instance = [URLProtocolLog new]; 23 | }); 24 | return instance; 25 | } 26 | 27 | - (instancetype)init 28 | { 29 | if (self = [super init]) { 30 | queue = dispatch_queue_create("rxdata.urlprotocol.log", DISPATCH_QUEUE_SERIAL); 31 | } 32 | return self; 33 | } 34 | 35 | + (void)logWithPrefix:(NSString *)prefix format:(NSString *)format, ... 36 | { 37 | 38 | va_list arguments; 39 | va_start(arguments, format); 40 | URLProtocolLog *log = [self sharedInstance]; 41 | [log logWithPrefix:prefix format:format arguments:arguments]; 42 | va_end(arguments); 43 | } 44 | 45 | - (void)logWithPrefix:(NSString *)prefix format:(NSString *)format arguments:(va_list)arguments 46 | { 47 | assert(prefix != nil); 48 | assert(format != nil); 49 | 50 | NSString * now; 51 | NSString * str; 52 | 53 | now = [NSDate date].description; 54 | 55 | NSThread *thread = [NSThread currentThread]; 56 | 57 | str = [[NSString alloc] initWithFormat:format arguments:arguments]; 58 | assert(str != nil); 59 | 60 | fprintf(stderr, "[%s][%p][%s][%s] %s\n",now.UTF8String, thread, thread.name.UTF8String, [prefix UTF8String], [str UTF8String]); 61 | } 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 VIPKID 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KIDDNS 2 | 3 | [![CI Status](https://img.shields.io/travis/yiyangest/KIDDNS.svg?style=flat)](https://travis-ci.org/yiyangest/KIDDNS) 4 | [![Version](https://img.shields.io/cocoapods/v/KIDDNS.svg?style=flat)](https://cocoapods.org/pods/KIDDNS) 5 | [![License](https://img.shields.io/cocoapods/l/KIDDNS.svg?style=flat)](https://cocoapods.org/pods/KIDDNS) 6 | [![Platform](https://img.shields.io/cocoapods/p/KIDDNS.svg?style=flat)](https://cocoapods.org/pods/KIDDNS) 7 | 8 | KIDDNS是一个基于`NSURLProtocol`的HTTPDNS库,底层依托于阿里云的HTTPDNS服务,能够有效的避免DNS污染等问题,加速app中的网络请求。能够覆盖常见的HTTP及HTTPS场景,并且也兼容了HTTPS中的SNI场景。由于采用了`NSURLProtocol`,能够做到对业务方近乎无感的接入,只需简单配置,即可开始享受HTTPDNS的加速。 9 | 10 | 有关底层阿里云的HTTPDNS服务的使用及原理请参考[阿里云官方文档](https://help.aliyun.com/document_detail/30141.html?spm=a2c4g.11186623.6.580.25367797Vze70z)。 11 | 12 | ## 安装 13 | 14 | KIDDNS is available through [CocoaPods](https://cocoapods.org). To install 15 | it, simply add the following line to your Podfile: 16 | 17 | ```ruby 18 | pod 'KIDDNS' 19 | ``` 20 | 21 | Podfile中需要添加VIPKID的Spec源,以及阿里云的Spec源 22 | 23 | ```ruby 24 | source "https://github.com/VIPKID-OpenSource/Specs" 25 | source "https://github.com/aliyun/aliyun-specs" 26 | ``` 27 | 28 | ## 前提条件 29 | 30 | 需要拥有阿里云账号,并创建了开通HTTPDNS服务的应用。且阿里云控制台中配置好了app里需要开通HTTPDNS服务的域名。 31 | 32 | ## 使用方法 33 | 34 | 通过`CocoaPods`将`KIDDNS`库集成到工程后,只需简单配置,即可开始使用. 35 | 36 | 使用配置项初始化`HTTPDNS`服务,配置项包含阿里云应用的appId和appKey, 以及app需要预解析的域名列表。 37 | 38 | ```objc 39 | NSArray *presolvedHostlist = @[@"api.abc.com", @"gateway.abc.com"]; 40 | KIDDNSConfig *config = [KIDDNSConfig new]; 41 | config.accountId = 100000; 42 | config.key = @"your app key"; 43 | config.presolvedHosts = presolvedHostlist; 44 | [[DNSCenter defaultCenter] initializeDNSServiceWithConfig:config]; 45 | ``` 46 | 47 | *建议将初始化服务放在`didFinishLauch`中,尽可能放在网络请求之前。* 48 | 49 | 在app内部,你也可以针对某域名设置黑名单或者白名单,其规则是: 50 | * 如果域名在黑名单内,则该域名相关的请求不会走`HTTPDNS`服务; 51 | * 如果域名不在黑名单内,而在白名单内,则该域名相关的请求会尝试走`HTTPDNS`服务; 52 | * 如果域名既不在黑名单内,且不在白名单内,并且白名单不为空,则该域名相关请求不走`HTTPDNS`服务; 53 | * 如果域名既不在黑名单内,且不在白名单内,且白名单为空,则该域名相关请求会尝试走`HTTPDNS`服务; 54 | 55 | 黑白名单均是域名列表,支持正则匹配。 56 | 57 | ```objc 58 | [DNSCenter defaultCenter].whiteList = @[@".vipkid.com.cn",@"api.abc.com"]; 59 | ``` 60 | 61 | 当app不需要`KIDDNS`服务运行时,也可以通过`automaticDNS`属性关闭KIDDNS服务。关闭之后KIDDNS中的`NSURLProtocol`就不再起作用。 62 | 63 | ```objc 64 | [DNSCenter defaultCenter].automaticDNS = NO; 65 | ``` 66 | 67 | 初始化`KIDDNS`服务之后,`automaticDNS`默认开启。 68 | 69 | 在`KIDDNS`服务开启的情况下,每个经过KIDDNS服务的请求都会被记录其是否走了`HTTPDNS`服务,以及是否最终成功。接入方如果关心这类记录,可以实现一个遵循`KIDDNSLogger`协议的日志类,并将该类添加到`KIDDNS`的日志服务中。*KIDDNS本身并不保存这些记录,请求结束后,记录即被销毁。* 70 | 71 | ```objc 72 | @interface KIDLogger : NSObject 73 | 74 | @end 75 | 76 | @implementation KIDLogger 77 | 78 | - (void)logLevel:(KIDDNSLogLevel)level result:(KIDDNSResult *)result 79 | { 80 | NSLog(@"[DNSLog][%@] result: %@", @(level), result); 81 | } 82 | 83 | @end 84 | 85 | ... 86 | 87 | 88 | [[DNSCenter defaultCenter] addLogger:[KIDLogger new]]; 89 | 90 | ``` 91 | 92 | ## 容错重试 93 | 94 | KIDDNS在底层拿到了请求域名的IP后,才会对请求进行拦截并进行重写。如果使用IP的方式进行网络请求而导致请求失败,KIDDNS会尝试使用原域名的方式再发起一次网络请求,如果仍然失败,则请求最终以失败结束,如果请求成功,则对请求发起方来说,请求依然成功,对中间的失败过程无感。 95 | 96 | ## 注意事项 97 | 98 | *注意: 由于WKWebView页面及Cookie在HTTPDNS场景中很难处理,因此对于WebView请求KIDDNS并没有进行处理,仍然将使用系统默认的DNS服务* 99 | 100 | ## Author 101 | 102 | yiyangest, y31210@gmail.com 103 | 104 | ## License 105 | 106 | KIDDNS is available under the MIT license. See the LICENSE file for more info. 107 | -------------------------------------------------------------------------------- /_Pods.xcodeproj: -------------------------------------------------------------------------------- 1 | Example/Pods/Pods.xcodeproj --------------------------------------------------------------------------------