├── httpdns-sni ├── httpdns-sni │ ├── BDHttpDns.framework │ │ ├── _CodeSignature │ │ │ ├── CodeSignature │ │ │ ├── CodeDirectory │ │ │ ├── CodeRequirements │ │ │ ├── CodeRequirements-1 │ │ │ └── CodeResources │ │ ├── BDHttpDns │ │ ├── Info.plist │ │ ├── Modules │ │ │ └── module.modulemap │ │ └── Headers │ │ │ ├── BDHttpDns.h │ │ │ ├── BDHttpDnsResult.h │ │ │ └── BDHttpDnsSDK.h │ ├── Assets.xcassets │ │ ├── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── ViewController.h │ ├── CFHTTPSURLProtocol.h │ ├── CFHttpMessageURLProtocol.h │ ├── AppDelegate.h │ ├── main.m │ ├── Info.plist │ ├── Base.lproj │ │ ├── Main.storyboard │ │ └── LaunchScreen.storyboard │ ├── AppDelegate.m │ ├── ViewController.m │ ├── CFHttpMessageURLProtocol.m │ └── CFHTTPSURLProtocol.m └── httpdns-sni.xcodeproj │ ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── project.pbxproj ├── README.md ├── LICENSE └── .gitignore /httpdns-sni/httpdns-sni/BDHttpDns.framework/_CodeSignature/CodeSignature: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /httpdns-sni/httpdns-sni/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /httpdns-sni/httpdns-sni/BDHttpDns.framework/BDHttpDns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoLong1010/HTTPDNS-SNI/HEAD/httpdns-sni/httpdns-sni/BDHttpDns.framework/BDHttpDns -------------------------------------------------------------------------------- /httpdns-sni/httpdns-sni/BDHttpDns.framework/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoLong1010/HTTPDNS-SNI/HEAD/httpdns-sni/httpdns-sni/BDHttpDns.framework/Info.plist -------------------------------------------------------------------------------- /httpdns-sni/httpdns-sni/BDHttpDns.framework/Modules/module.modulemap: -------------------------------------------------------------------------------- 1 | framework module BDHttpDns { 2 | umbrella header "BDHttpDns.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /httpdns-sni/httpdns-sni/BDHttpDns.framework/_CodeSignature/CodeDirectory: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoLong1010/HTTPDNS-SNI/HEAD/httpdns-sni/httpdns-sni/BDHttpDns.framework/_CodeSignature/CodeDirectory -------------------------------------------------------------------------------- /httpdns-sni/httpdns-sni/BDHttpDns.framework/_CodeSignature/CodeRequirements: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoLong1010/HTTPDNS-SNI/HEAD/httpdns-sni/httpdns-sni/BDHttpDns.framework/_CodeSignature/CodeRequirements -------------------------------------------------------------------------------- /httpdns-sni/httpdns-sni/BDHttpDns.framework/_CodeSignature/CodeRequirements-1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoLong1010/HTTPDNS-SNI/HEAD/httpdns-sni/httpdns-sni/BDHttpDns.framework/_CodeSignature/CodeRequirements-1 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 优化HTTPDNS-SNI问题的解决方案 2 | HTTP DNS可以解决传统DNS的存在的问题 3 | - 域名劫持 4 | - 调度不精准 5 | - 解析生效滞后 6 | - 延迟大 7 | 8 | 同时也会带来一些问题,其中一个就是SNI 证书校验问题。在原有解决方案的基础上,本人做了一些优化。具体可以查看[文章](https://www.jianshu.com/p/66ffa9b69c17) 9 | -------------------------------------------------------------------------------- /httpdns-sni/httpdns-sni.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /httpdns-sni/httpdns-sni/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // httpdns-sni 4 | // 5 | // Created by Csy on 2018/10/23. 6 | // Copyright © 2018 Csy. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /httpdns-sni/httpdns-sni/CFHTTPSURLProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // CFHTTPSURLProtocol.h 3 | // BDHttpDnsSDKDemo 4 | // 5 | // Created by Csy on 2018/10/18. 6 | // Copyright © 2018 baidu. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CFHTTPSURLProtocol : NSURLProtocol 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /httpdns-sni/httpdns-sni.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /httpdns-sni/httpdns-sni/CFHttpMessageURLProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // MyCFHttpMessageURLProtocol.h 3 | // NSURLProtocolDemo 4 | // 5 | // Created by fuyuan.lfy on 16/6/14. 6 | // Copyright © 2016年 Jaylon. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CFHttpMessageURLProtocol : NSURLProtocol 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /httpdns-sni/httpdns-sni/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // httpdns-sni 4 | // 5 | // Created by Csy on 2018/10/23. 6 | // Copyright © 2018 Csy. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /httpdns-sni/httpdns-sni/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // httpdns-sni 4 | // 5 | // Created by Csy on 2018/10/23. 6 | // Copyright © 2018 Csy. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /httpdns-sni/httpdns-sni/BDHttpDns.framework/Headers/BDHttpDns.h: -------------------------------------------------------------------------------- 1 | // 2 | // BDHttpDns.h 3 | // BDHttpDns 4 | // 5 | // Created by Liu,Xue(OP) on 2017/9/21. 6 | // Copyright © 2017年 baidu. All rights reserved. 7 | // 8 | 9 | #ifndef BDHttpDns_h 10 | #define BDHttpDns_h 11 | 12 | #import 13 | 14 | #import 15 | #import 16 | 17 | //! Project version number for BDHttpDns. 18 | FOUNDATION_EXPORT double BDHttpDnsVersionNumber; 19 | 20 | //! Project version string for BDHttpDns. 21 | FOUNDATION_EXPORT const unsigned char BDHttpDnsVersionString[]; 22 | 23 | #endif /* BDHttpDns_h */ 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 xiaoLong1010 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /httpdns-sni/httpdns-sni/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | # CocoaPods 32 | # 33 | # We recommend against adding the Pods directory to your .gitignore. However 34 | # you should judge for yourself, the pros and cons are mentioned at: 35 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 36 | # 37 | # Pods/ 38 | 39 | # Carthage 40 | # 41 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 42 | # Carthage/Checkouts 43 | 44 | Carthage/Build 45 | 46 | # fastlane 47 | # 48 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 49 | # screenshots whenever they are needed. 50 | # For more information about the recommended setup visit: 51 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 52 | 53 | fastlane/report.xml 54 | fastlane/Preview.html 55 | fastlane/screenshots/**/*.png 56 | fastlane/test_output 57 | 58 | # Code Injection 59 | # 60 | # After new code Injection tools there's a generated folder /iOSInjectionProject 61 | # https://github.com/johnno1962/injectionforxcode 62 | 63 | iOSInjectionProject/ 64 | -------------------------------------------------------------------------------- /httpdns-sni/httpdns-sni/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 | -------------------------------------------------------------------------------- /httpdns-sni/httpdns-sni/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 | -------------------------------------------------------------------------------- /httpdns-sni/httpdns-sni/Assets.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 | } -------------------------------------------------------------------------------- /httpdns-sni/httpdns-sni/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // httpdns-sni 4 | // 5 | // Created by Csy on 2018/10/23. 6 | // Copyright © 2018 Csy. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // 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. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // 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. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // 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. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /httpdns-sni/httpdns-sni/BDHttpDns.framework/Headers/BDHttpDnsResult.h: -------------------------------------------------------------------------------- 1 | // 2 | // BDHttpDnsResult.h 3 | // BDHttpDns 4 | // 5 | // Created by Liu,Xue(OP) on 2017/12/18. 6 | // Copyright © 2017年 baidu. All rights reserved. 7 | // 8 | 9 | #ifndef BDHttpDnsResult_h 10 | #define BDHttpDnsResult_h 11 | 12 | #import 13 | 14 | /** 15 | * Httpdns解析状态码 16 | */ 17 | typedef NS_ENUM(NSInteger, BDHttpDnsResolveStatus) { 18 | BDHttpDnsResolveOK = 0, // 解析成功 19 | BDHttpDnsInputError, // 输入参数错误 20 | BDHttpDnsResolveErrCacheMiss, // 由于cache未命中导致的解析失败,仅在解析时指定cache only标志时有效 21 | BDHttpDnsResolveErrDnsResolve, // dns解析失败 22 | }; 23 | 24 | /** 25 | * Httpdns解析结果来源 26 | */ 27 | typedef NS_ENUM(NSInteger, BDHttpDnsResolveType) { 28 | BDHttpDnsResolveNone = 0, // 没有有效的解析结果 29 | BDHttpDnsResolveFromHttpDnsCache, // 解析结果来自httpdns cache 30 | BDHttpDnsResolveFromHttpDnsExpiredCache, // 解析结果来自过期的httpdns cache 31 | BDHttpDnsResolveFromDnsCache, // 解析结果来自dns cache 32 | BDHttpDnsResolveFromDns, // 解析结果来自dns解析 33 | }; 34 | 35 | /** 36 | * Httpdns解析结果类 37 | */ 38 | @interface BDHttpDnsResult : NSObject 39 | 40 | /** 41 | * 初始化BDHttpDnsResult实例 42 | * 43 | * @param status // 解析状态码,取值为BDHttpDnsResolveStatus枚举 44 | * @param type // 解析结果来源类型,取值为枚举BDHttpDnsResolveType 45 | * @param ipv4List // ipv4解析结果数组,数组元素为IPv4地址格式的字符串,如“192.168.1.2” 46 | * @param ipv6List // ipv6解析结果,数组元素为IPv6地址格式的字符串,如“2000:0:0:0:0:0:0:1” 47 | */ 48 | - (nullable instancetype)initWithStatus:(NSInteger)status 49 | type:(NSInteger)type 50 | ipv4List:(nullable NSArray *)ipv4List 51 | ipv6List:(nullable NSArray *)ipv6List; 52 | 53 | /** 54 | * 解析状态码,取值为枚举BDHttpDnsResolveStatus 55 | */ 56 | @property (readonly) NSInteger status; 57 | 58 | /** 59 | * 解析结果来源类型,取值为枚举BDHttpDnsResolveType 60 | */ 61 | @property (readonly) NSInteger type; 62 | 63 | /** 64 | * ipv4解析结果列表 65 | */ 66 | @property (nullable, readonly, copy) NSArray *ipv4List; 67 | 68 | /** 69 | * ipv6解析结果列表 70 | */ 71 | @property (nullable, readonly, copy) NSArray *ipv6List; 72 | 73 | @end 74 | 75 | #endif /* BDHttpDnsResult_h */ 76 | -------------------------------------------------------------------------------- /httpdns-sni/httpdns-sni/BDHttpDns.framework/Headers/BDHttpDnsSDK.h: -------------------------------------------------------------------------------- 1 | // 2 | // BDHttpDnsSDK.h 3 | // BDHttpdns 4 | // 5 | // Created by Liu,Xue(OP) on 2017/9/11. 6 | // Copyright © 2017年 baidu. All rights reserved. 7 | // 8 | 9 | #ifndef BDHttpDnsSDK_h 10 | #define BDHttpDnsSDK_h 11 | 12 | #import 13 | 14 | #import "BDHttpDnsResult.h" 15 | 16 | /** 17 | * Httpdns缓存的处理策略 18 | */ 19 | typedef NS_ENUM(NSInteger, BDHttpDnsCachePolicy) { 20 | BDHttpDnsCachePolicyAggressive = 1, // 激进的使用策略,即使缓存过期也无条件使用 21 | BDHttpDnsCachePolicyTolerant, // 一定限度容忍过期缓存,容忍至下次请求httpdns服务端得到结果,默认策略 22 | BDHttpDnsCachePolicyStrict, // 严格的使用策略,不使用过期缓存 23 | }; 24 | 25 | /** 26 | * Httpdns服务接口类 27 | */ 28 | @interface BDHttpDns : NSObject 29 | 30 | /** 31 | * 获取Httpdns服务实例 32 | */ 33 | + (instancetype _Nonnull)sharedInstance; 34 | 35 | /** 36 | * 设置Httpdns服务的account id 37 | * 38 | * @param accountID 用户的account id,需要预先申请 39 | */ 40 | - (void)setAccountID:(NSString *_Nonnull)accountID; 41 | 42 | /** 43 | * 设置Httpdns服务的secret 44 | * 45 | * @param secret 用户的secret,需要预先申请 46 | */ 47 | - (void)setSecret:(NSString *_Nonnull)secret; 48 | 49 | /** 50 | * 设置Httpdns缓存处理策略 51 | * 52 | * @param policy 过期缓存处理策略,取值范围见枚举BDHttpdnsCachePolicy 53 | * 默认为BDHttpdnsCachePolicyTolerate 54 | */ 55 | - (void)setCachePolicy:(BDHttpDnsCachePolicy)policy; 56 | 57 | /** 58 | * 设置预加载域名,调用此接口后会立刻发起异步解析 59 | * 域名数量上限为8个 60 | * 61 | * @param hosts 预加载域名列表 62 | */ 63 | - (void)setPreResolveHosts:(NSArray *_Nonnull)hosts; 64 | 65 | /** 66 | * 设置网络切换处理策略,默认为清理缓存并立刻发送批量域名预解析 67 | * 68 | * @param clearCache 网络切换时,是否清除Httpdns缓存,避免在缓存中获取跨网解析结果 69 | * @param isPrefetch 网络切换时,是否立刻发送批量域名预解析,及时更新缓存 70 | */ 71 | - (void)setNetworkSwitchPolicyClearCache:(BOOL)clearCache httpDnsPrefetch:(BOOL)isPrefetch; 72 | 73 | /** 74 | * 设置HttpDns解析所使用的请求类型,默认为https 75 | * 76 | * @param enable YES: https请求;NO: http请求 77 | */ 78 | - (void)setHttpsRequestEnable:(BOOL)enable; 79 | 80 | /** 81 | * 设置Log开关,默认关闭 82 | * 83 | * @param enable YES: 打开Log;NO: 关闭Log 84 | */ 85 | - (void)setLogEnable:(BOOL)enable; 86 | 87 | /** 88 | * 同步解析接口 89 | * 90 | * @param host 待解析域名 91 | * @param cacheOnly 是否使用HttpDnsCache 92 | * cacheOnly为YES: 仅使用httpDnsCahe,此时接口行为表现为同步非阻塞接口 93 | * cacheOnly为NO: 若cache不命中,则SDK会继续进行DNS解析,此时接口行为表现为同步阻塞接口 94 | * 95 | * @return BDHttpDnsResult* 使用BDHttpDnsResult结构封装的域名解析结果 96 | */ 97 | - (BDHttpDnsResult *_Nonnull)syncResolve:(NSString *_Nonnull)host cacheOnly:(BOOL)cacheOnly; 98 | 99 | /** 100 | * 异步解析接口 101 | * 102 | * @param host 待解析域名 103 | * @param completionHandler 异步解析回调函数,参数为使用BDHttpDnsResult结构封装的域名解析结果 104 | */ 105 | - (void)asyncResolve:(NSString *_Nonnull)host completionHandler:(void (^_Nonnull)(BDHttpDnsResult * _Nonnull result))completionHandler; 106 | 107 | @end 108 | 109 | #endif /* BDHttpDnsSDK_h */ 110 | -------------------------------------------------------------------------------- /httpdns-sni/httpdns-sni/BDHttpDns.framework/_CodeSignature/CodeResources: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | files 6 | 7 | Headers/BDHttpDns.h 8 | 9 | Du5PMbTAdRZQbvH7B43Yk8PnyBM= 10 | 11 | Headers/BDHttpDnsResult.h 12 | 13 | YGdOZeTYVHibGjTsJbdgfew59yI= 14 | 15 | Headers/BDHttpDnsSDK.h 16 | 17 | F1hqSCLKGZyKdOELQuGPKdZYXOo= 18 | 19 | Info.plist 20 | 21 | qwxKSNZbLExVp1AMcBXMSKU5xRI= 22 | 23 | Modules/module.modulemap 24 | 25 | C08XdGYGnT/dU9gAyhaOAB4ovBw= 26 | 27 | 28 | files2 29 | 30 | Headers/BDHttpDns.h 31 | 32 | hash 33 | 34 | Du5PMbTAdRZQbvH7B43Yk8PnyBM= 35 | 36 | hash2 37 | 38 | IniwLB05PCOddMlccDcmXsiHavrLIXp1OuXzgnft+CY= 39 | 40 | 41 | Headers/BDHttpDnsResult.h 42 | 43 | hash 44 | 45 | YGdOZeTYVHibGjTsJbdgfew59yI= 46 | 47 | hash2 48 | 49 | LBP2YIx5sLv+5hEl0UcJrt4Y/KzrCDGlWPer9O8IChk= 50 | 51 | 52 | Headers/BDHttpDnsSDK.h 53 | 54 | hash 55 | 56 | F1hqSCLKGZyKdOELQuGPKdZYXOo= 57 | 58 | hash2 59 | 60 | jJ3iCr2DIjx4WkLjjLZq1GIijxebPOqwmNctEjQXhU0= 61 | 62 | 63 | Modules/module.modulemap 64 | 65 | hash 66 | 67 | C08XdGYGnT/dU9gAyhaOAB4ovBw= 68 | 69 | hash2 70 | 71 | SHQIznStbUW8KIrl19qboZYLkZFi21f9qyg9l+fHWSw= 72 | 73 | 74 | 75 | rules 76 | 77 | ^ 78 | 79 | ^.*\.lproj/ 80 | 81 | optional 82 | 83 | weight 84 | 1000 85 | 86 | ^.*\.lproj/locversion.plist$ 87 | 88 | omit 89 | 90 | weight 91 | 1100 92 | 93 | ^Base\.lproj/ 94 | 95 | weight 96 | 1010 97 | 98 | ^version.plist$ 99 | 100 | 101 | rules2 102 | 103 | .*\.dSYM($|/) 104 | 105 | weight 106 | 11 107 | 108 | ^ 109 | 110 | weight 111 | 20 112 | 113 | ^(.*/)?\.DS_Store$ 114 | 115 | omit 116 | 117 | weight 118 | 2000 119 | 120 | ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/ 121 | 122 | nested 123 | 124 | weight 125 | 10 126 | 127 | ^.* 128 | 129 | ^.*\.lproj/ 130 | 131 | optional 132 | 133 | weight 134 | 1000 135 | 136 | ^.*\.lproj/locversion.plist$ 137 | 138 | omit 139 | 140 | weight 141 | 1100 142 | 143 | ^Base\.lproj/ 144 | 145 | weight 146 | 1010 147 | 148 | ^Info\.plist$ 149 | 150 | omit 151 | 152 | weight 153 | 20 154 | 155 | ^PkgInfo$ 156 | 157 | omit 158 | 159 | weight 160 | 20 161 | 162 | ^[^/]+$ 163 | 164 | nested 165 | 166 | weight 167 | 10 168 | 169 | ^embedded\.provisionprofile$ 170 | 171 | weight 172 | 20 173 | 174 | ^version\.plist$ 175 | 176 | weight 177 | 20 178 | 179 | 180 | 181 | 182 | -------------------------------------------------------------------------------- /httpdns-sni/httpdns-sni/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // httpdns-sni 4 | // 5 | // Created by Csy on 2018/10/23. 6 | // Copyright © 2018 Csy. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import 11 | #import "CFHTTPSURLProtocol.h" 12 | #import "CFHttpMessageURLProtocol.h" 13 | 14 | @interface ViewController () 15 | @property (nonatomic, weak) BDHttpDns *httpdns; 16 | 17 | @end 18 | 19 | @implementation ViewController 20 | 21 | - (void)viewDidLoad { 22 | [super viewDidLoad]; 23 | 24 | [self setupHTTPDNS]; 25 | 26 | [self testSNI]; 27 | } 28 | 29 | - (void)setupHTTPDNS { 30 | self.httpdns = [BDHttpDns sharedInstance]; 31 | [self.httpdns setAccountID:@"your account"]; 32 | [self.httpdns setSecret:@"your secret"]; 33 | [self.httpdns setLogEnable:YES]; 34 | [self.httpdns setHttpsRequestEnable:YES]; 35 | 36 | NSArray * hosts = [[NSArray alloc] initWithObjects:@"www.baidu.com", @"cloud.baidu.com", nil]; 37 | [self.httpdns setPreResolveHosts:hosts]; 38 | } 39 | 40 | - (void)testNSURLSession { 41 | NSString *urlString = @"https://www.baidu.com"; 42 | [self doRequestWithURLString:urlString]; 43 | } 44 | 45 | - (void)testNSURLSessionWithRedirect { 46 | NSString *urlStr = @"https://dou.bz/23o8PS"; 47 | [self doRequestWithURLString:urlStr]; 48 | } 49 | 50 | - (void)testSNI { 51 | // 设置原始url,获取hosSt 52 | NSString *urlStr = @"https://dou.bz/23o8PS"; 53 | NSURL *url = [NSURL URLWithString:urlStr]; 54 | NSString *host = url.host; 55 | if (!host) { 56 | NSLog(@"Err: get nil host from originUrlStr(%@)", urlStr); 57 | return; 58 | } 59 | 60 | // 通过httpdns解析,使用ip地址的URL 61 | NSURL *ipURL = [self ipURLByHttpDnsResolve:url]; 62 | 63 | NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; 64 | // 使用自定义Protocol拦截该请求,Protocol内部使用CFNetwork发送发送请求 65 | config.protocolClasses = @[[CFHttpMessageURLProtocol class]]; 66 | NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]]; 67 | 68 | // 将host保存到请求头,Protocol中使用该host进行https的校验 69 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:ipURL]; 70 | [request setValue:host forHTTPHeaderField:@"host"]; 71 | 72 | // 发请求 73 | NSURLSessionDataTask *task = [session dataTaskWithRequest:request]; 74 | [task resume]; 75 | } 76 | 77 | - (void)doRequestWithURLString:(NSString *)urlString { 78 | NSURL *url = [NSURL URLWithString:urlString]; 79 | 80 | NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; 81 | NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]]; 82 | 83 | // 将host保存到请求头,Protocol中使用该host进行https的校验 84 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; 85 | // [request setValue:host forHTTPHeaderField:@"host"]; 86 | 87 | // 发请求 88 | NSURLSessionDataTask *task = [session dataTaskWithRequest:request]; 89 | [task resume]; 90 | } 91 | 92 | - (NSURL *)ipURLByHttpDnsResolve:(NSURL *)originalURL { 93 | NSString *urlStr = originalURL.absoluteString; 94 | NSString *host = originalURL.host; 95 | 96 | NSURL *url = originalURL; 97 | // 读取httpdns解析结果 98 | BDHttpDnsResult *result = [self.httpdns syncResolve:host cacheOnly:NO]; 99 | 100 | // 获取失败,则使用原url 101 | if (![result ipv4List] && ![result ipv6List]) { 102 | NSLog(@"Get empty iplist from httpdns, use origin url"); 103 | } else { 104 | // httpdns获取解析结果成功,使用ip替换url中host 105 | // 优先使用ipv6结果,对于ipv6结果,需要在ip前后增加[]字符 106 | // 若不存在ipv6结果,则使用ipv4结果 107 | NSRange hostRange = [urlStr rangeOfString:host]; 108 | if (NSNotFound != hostRange.location) { 109 | NSString *ip = nil; 110 | if ([result ipv6List]) { 111 | NSLog(@"Use ipv6List(%@)", [result ipv6List]); 112 | ip = [[NSString alloc] initWithFormat:@"[%@]", [result ipv6List][0]]; 113 | } else { 114 | NSLog(@"Use ipv4List(%@)", [result ipv4List]); 115 | ip = [result ipv4List][0]; 116 | } 117 | 118 | // 使用ip替换url 119 | urlStr = [urlStr stringByReplacingCharactersInRange:hostRange withString:ip]; 120 | url = [NSURL URLWithString:urlStr]; 121 | NSLog(@"Use httpdns ip(%@) for host(%@), url(%@)", ip, host, urlStr); 122 | } 123 | } 124 | return url; 125 | } 126 | 127 | 128 | #pragma mark -- NSURLSessionDelegate 129 | - (void)URLSession:(NSURLSession *)session 130 | task:(NSURLSessionTask *)task 131 | willPerformHTTPRedirection:(NSHTTPURLResponse *)response 132 | newRequest:(NSURLRequest *)newRequest 133 | completionHandler:(void (^)(NSURLRequest *))completionHandler { 134 | NSLog(@"-----%@",NSStringFromSelector(_cmd)); 135 | 136 | // 将newRequest进行域名解析,生成新的request 137 | NSString *host = newRequest.URL.host; 138 | NSURL *ipURL = [self ipURLByHttpDnsResolve:newRequest.URL]; 139 | NSMutableURLRequest *ipRequest = [NSMutableURLRequest requestWithURL:ipURL]; 140 | [ipRequest setValue:host forHTTPHeaderField:@"host"]; 141 | 142 | completionHandler(ipRequest); 143 | } 144 | 145 | // 处理证书异常,默许IP直连方式 146 | - (void) URLSession:(NSURLSession *)session 147 | task:(NSURLSessionTask *)task 148 | didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge 149 | completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler { 150 | NSLog(@"-----%@",NSStringFromSelector(_cmd)); 151 | if (!challenge) { 152 | return; 153 | } 154 | 155 | NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling; 156 | NSURLCredential *credential = nil; 157 | 158 | // 判断服务器返回的证书是否是服务器信任的 159 | if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { 160 | // 创建证书校验策略 161 | NSMutableArray *policies = [NSMutableArray array]; 162 | 163 | // 使用域名代替IP进行校验 164 | NSString* host = [[task originalRequest] valueForHTTPHeaderField:@"host"]; 165 | // if (host == nil) { 166 | // host = task.currentRequest.URL.host; 167 | // } 168 | [policies addObject:(__bridge_transfer id) SecPolicyCreateSSL(true, (__bridge CFStringRef) host)]; 169 | 170 | // 绑定校验策略到服务端的证书上 171 | SecTrustSetPolicies(challenge.protectionSpace.serverTrust, (__bridge CFArrayRef) policies); 172 | 173 | // 评估当前serverTrust是否可信任, 174 | // 官方建议在result = kSecTrustResultUnspecified 或 kSecTrustResultProceed 175 | // 的情况下serverTrust可以被验证通过,https://developer.apple.com/library/ios/technotes/tn2232/_index.html 176 | // 关于SecTrustResultType的详细信息请参考SecTrust.h 177 | SecTrustResultType result; 178 | SecTrustEvaluate(challenge.protectionSpace.serverTrust, &result); 179 | BOOL isTrusted = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed); 180 | 181 | // 新的校验策略添加成功 182 | if (isTrusted) { 183 | // disposition:如何处理证书 184 | // NSURLSessionAuthChallengePerformDefaultHandling:默认方式处理 185 | // NSURLSessionAuthChallengeUseCredential:使用指定的证书 186 | // NSURLSessionAuthChallengeCancelAuthenticationChallenge:取消请求 187 | disposition = NSURLSessionAuthChallengeUseCredential; 188 | credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; 189 | } else { 190 | disposition = NSURLSessionAuthChallengePerformDefaultHandling; 191 | } 192 | } else { 193 | disposition = NSURLSessionAuthChallengePerformDefaultHandling; 194 | } 195 | 196 | // 应用证书策略 197 | if (completionHandler) { 198 | completionHandler(disposition, credential); 199 | } 200 | } 201 | 202 | - (void)URLSession:(NSURLSession *)session 203 | dataTask:(NSURLSessionDataTask *)dataTask 204 | didReceiveResponse:(NSURLResponse *)response 205 | completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler { 206 | NSLog(@"-----%@",NSStringFromSelector(_cmd)); 207 | // NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response; 208 | // NSLog(@"-----Response for url(%@), httpcode(%ld)",httpResponse.URL, (long)httpResponse.statusCode); 209 | completionHandler(NSURLSessionResponseAllow); 210 | } 211 | 212 | - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { 213 | NSLog(@"-----%@",NSStringFromSelector(_cmd)); 214 | } 215 | 216 | - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { 217 | NSLog(@"-----%@",NSStringFromSelector(_cmd)); 218 | } 219 | 220 | @end 221 | -------------------------------------------------------------------------------- /httpdns-sni/httpdns-sni/CFHttpMessageURLProtocol.m: -------------------------------------------------------------------------------- 1 | 2 | // MyCFHttpMessageURLProtocol.m 3 | // NSURLProtocolDemo 4 | // 5 | // Created by fuyuan.lfy on 16/6/14. 6 | // Copyright © 2016年 Jaylon. All rights reserved. 7 | // 8 | 9 | #import "CFHttpMessageURLProtocol.h" 10 | #import 11 | #import 12 | 13 | #define protocolKey @"CFHttpMessagePropertyKey" 14 | #define kAnchorAlreadyAdded @"AnchorAlreadyAdded" 15 | 16 | @interface CFHttpMessageURLProtocol () { 17 | NSMutableURLRequest *curRequest; 18 | NSRunLoop *curRunLoop; 19 | NSInputStream *inputStream; 20 | } 21 | 22 | @end 23 | 24 | @implementation CFHttpMessageURLProtocol 25 | 26 | /** 27 | * 是否拦截处理指定的请求 28 | * 29 | * @param request 指定的请求 30 | * 31 | * @return 返回YES表示要拦截处理,返回NO表示不拦截处理 32 | */ 33 | + (BOOL)canInitWithRequest:(NSURLRequest *)request { 34 | 35 | /* 防止无限循环,因为一个请求在被拦截处理过程中,也会发起一个请求,这样又会走到这里,如果不进行处理,就会造成无限循环 */ 36 | if ([NSURLProtocol propertyForKey:protocolKey inRequest:request]) { 37 | return NO; 38 | } 39 | 40 | NSString *url = request.URL.absoluteString; 41 | 42 | // 如果url以https开头,则进行拦截处理,否则不处理 43 | if ([url hasPrefix:@"https"]) { 44 | return YES; 45 | } 46 | return NO; 47 | } 48 | 49 | /** 50 | * 如果需要对请求进行重定向,添加指定头部等操作,可以在该方法中进行 51 | */ 52 | + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request { 53 | return request; 54 | } 55 | 56 | /** 57 | * 开始加载,在该方法中,加载一个请求 58 | */ 59 | - (void)startLoading { 60 | NSMutableURLRequest *request = [self.request mutableCopy]; 61 | // 表示该请求已经被处理,防止无限循环 62 | [NSURLProtocol setProperty:@(YES) forKey:protocolKey inRequest:request]; 63 | curRequest = request; 64 | [self startRequest]; 65 | } 66 | 67 | /** 68 | * 取消请求 69 | */ 70 | - (void)stopLoading { 71 | if (inputStream.streamStatus == NSStreamStatusOpen) { 72 | [inputStream removeFromRunLoop:curRunLoop forMode:NSRunLoopCommonModes]; 73 | [inputStream setDelegate:nil]; 74 | [inputStream close]; 75 | } 76 | [self.client URLProtocol:self didFailWithError:[[NSError alloc] initWithDomain:@"stop loading" code:-1 userInfo:nil]]; 77 | } 78 | 79 | /** 80 | * 使用CFHTTPMessage转发请求 81 | */ 82 | - (void)startRequest { 83 | // 原请求的header信息 84 | NSDictionary *headFields = curRequest.allHTTPHeaderFields; 85 | // 添加http post请求所附带的数据 86 | CFStringRef requestBody = CFSTR(""); 87 | CFDataRef bodyData = CFStringCreateExternalRepresentation(kCFAllocatorDefault, requestBody, kCFStringEncodingUTF8, 0); 88 | if (curRequest.HTTPBody) { 89 | bodyData = (__bridge_retained CFDataRef) curRequest.HTTPBody; 90 | } else if (headFields[@"originalBody"]) { 91 | // 使用NSURLSession发POST请求时,将原始HTTPBody从header中取出 92 | bodyData = (__bridge_retained CFDataRef) [headFields[@"originalBody"] dataUsingEncoding:NSUTF8StringEncoding]; 93 | } 94 | 95 | CFStringRef url = (__bridge CFStringRef) [curRequest.URL absoluteString]; 96 | CFURLRef requestURL = CFURLCreateWithString(kCFAllocatorDefault, url, NULL); 97 | 98 | // 原请求所使用的方法,GET或POST 99 | CFStringRef requestMethod = (__bridge_retained CFStringRef) curRequest.HTTPMethod; 100 | 101 | // 根据请求的url、方法、版本创建CFHTTPMessageRef对象 102 | CFHTTPMessageRef cfrequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, requestMethod, requestURL, kCFHTTPVersion1_1); 103 | CFHTTPMessageSetBody(cfrequest, bodyData); 104 | 105 | // copy原请求的header信息 106 | for (NSString *header in headFields) { 107 | if (![header isEqualToString:@"originalBody"]) { 108 | // 不包含POST请求时存放在header的body信息 109 | CFStringRef requestHeader = (__bridge CFStringRef) header; 110 | CFStringRef requestHeaderValue = (__bridge CFStringRef) [headFields valueForKey:header]; 111 | CFHTTPMessageSetHeaderFieldValue(cfrequest, requestHeader, requestHeaderValue); 112 | } 113 | } 114 | 115 | // 创建CFHTTPMessage对象的输入流 116 | CFReadStreamRef readStream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, cfrequest); 117 | inputStream = (__bridge_transfer NSInputStream *) readStream; 118 | 119 | // 设置SNI host信息,关键步骤 120 | NSString *host = [curRequest.allHTTPHeaderFields objectForKey:@"host"]; 121 | if (!host) { 122 | host = curRequest.URL.host; 123 | } 124 | [inputStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL forKey:NSStreamSocketSecurityLevelKey]; 125 | NSDictionary *sslProperties = [[NSDictionary alloc] initWithObjectsAndKeys: 126 | host, (__bridge id) kCFStreamSSLPeerName, 127 | nil]; 128 | [inputStream setProperty:sslProperties forKey:(__bridge_transfer NSString *) kCFStreamPropertySSLSettings]; 129 | [inputStream setDelegate:self]; 130 | 131 | if (!curRunLoop) 132 | // 保存当前线程的runloop,这对于重定向的请求很关键 133 | curRunLoop = [NSRunLoop currentRunLoop]; 134 | // 将请求放入当前runloop的事件队列 135 | [inputStream scheduleInRunLoop:curRunLoop forMode:NSRunLoopCommonModes]; 136 | [inputStream open]; 137 | 138 | CFRelease(cfrequest); 139 | CFRelease(requestURL); 140 | CFRelease(url); 141 | cfrequest = NULL; 142 | CFRelease(bodyData); 143 | CFRelease(requestBody); 144 | CFRelease(requestMethod); 145 | } 146 | 147 | /** 148 | * 根据服务器返回的响应内容进行不同的处理 149 | */ 150 | - (void)handleResponse { 151 | // 获取响应头部信息 152 | CFReadStreamRef readStream = (__bridge_retained CFReadStreamRef) inputStream; 153 | CFHTTPMessageRef message = (CFHTTPMessageRef) CFReadStreamCopyProperty(readStream, kCFStreamPropertyHTTPResponseHeader); 154 | if (CFHTTPMessageIsHeaderComplete(message)) { 155 | // 确保response头部信息完整 156 | NSDictionary *headDict = (__bridge_transfer <#Objective-C type#>)<#expression#> <#CF type#>)<#expression#> NSDictionary *) (CFHTTPMessageCopyAllHeaderFields(message)); 157 | 158 | // 获取响应头部的状态码 159 | CFIndex myErrCode = CFHTTPMessageGetResponseStatusCode(message); 160 | 161 | // 把当前请求关闭 162 | [inputStream removeFromRunLoop:curRunLoop forMode:NSRunLoopCommonModes]; 163 | [inputStream setDelegate:nil]; 164 | [inputStream close]; 165 | 166 | if (myErrCode >= 200 && myErrCode < 300) { 167 | 168 | // 返回码为2xx,直接通知client 169 | [self.client URLProtocolDidFinishLoading:self]; 170 | 171 | } else if (myErrCode >= 300 && myErrCode < 400) { 172 | // 返回码为3xx,需要重定向请求,继续访问重定向页面 173 | NSString *location = headDict[@"Location"]; 174 | if (!location) 175 | location = headDict[@"location"]; 176 | NSURL *url = [[NSURL alloc] initWithString:location]; 177 | curRequest.URL = url; 178 | if ([[curRequest.HTTPMethod lowercaseString] isEqualToString:@"post"]) { 179 | // 根据RFC文档,当重定向请求为POST请求时,要将其转换为GET请求 180 | curRequest.HTTPMethod = @"GET"; 181 | curRequest.HTTPBody = nil; 182 | } 183 | 184 | /***********重定向通知client处理或内部处理*************/ 185 | // client处理 186 | // NSURLResponse* response = [[NSURLResponse alloc] initWithURL:curRequest.URL MIMEType:headDict[@"Content-Type"] expectedContentLength:[headDict[@"Content-Length"] integerValue] textEncodingName:@"UTF8"]; 187 | // [self.client URLProtocol:self wasRedirectedToRequest:curRequest redirectResponse:response]; 188 | // return; 189 | 190 | // 内部处理,将url中的host通过HTTPDNS转换为IP,不能在startLoading线程中进行同步网络请求,会被阻塞 191 | // NSString *ip = [[HttpDnsService sharedInstance] getIpByHostAsync:url.host]; 192 | // if (ip) { 193 | // NSLog(@"Get IP from HTTPDNS Successfully!"); 194 | // NSRange hostFirstRange = [location rangeOfString:url.host]; 195 | // if (NSNotFound != hostFirstRange.location) { 196 | // NSString *newUrl = [location stringByReplacingCharactersInRange:hostFirstRange withString:ip]; 197 | // curRequest.URL = [NSURL URLWithString:newUrl]; 198 | // [curRequest setValue:url.host forHTTPHeaderField:@"host"]; 199 | // } 200 | // } 201 | // 内部处理,将url中的host通过HTTPDNS转换为IP,不能在startLoading线程中进行同步网络请求,会被阻塞 202 | BDHttpDnsResult *result = [[BDHttpDns sharedInstance] syncResolve:url.host cacheOnly:NO]; 203 | NSString *ip = nil; 204 | if (![result ipv4List] && ![result ipv6List]) { 205 | NSLog(@"Get empty iplist from httpdns, use origin url"); 206 | } else { 207 | // 解析出IP 208 | // 优先使用ipv6结果,对于ipv6结果,需要在ip前后增加[]字符 209 | // 若不存在ipv6结果,则使用ipv4结果 210 | if ([result ipv6List]) { 211 | NSLog(@"Use ipv6List(%@)", [result ipv6List]); 212 | ip = [[NSString alloc] initWithFormat:@"[%@]", [result ipv6List][0]]; 213 | } else { 214 | NSLog(@"Use ipv4List(%@)", [result ipv4List]); 215 | ip = [result ipv4List][0]; 216 | } 217 | } 218 | 219 | if (ip) { 220 | NSLog(@"Get IP from HTTPDNS Successfully!"); 221 | NSRange hostFirstRange = [location rangeOfString:url.host]; 222 | if (NSNotFound != hostFirstRange.location) { 223 | NSString *newUrl = [location stringByReplacingCharactersInRange:hostFirstRange withString:ip]; 224 | curRequest.URL = [NSURL URLWithString:newUrl]; 225 | [curRequest setValue:url.host forHTTPHeaderField:@"host"]; 226 | } 227 | } 228 | [self startRequest]; 229 | } else { 230 | // 其他情况,直接返回响应信息给client 231 | [self.client URLProtocolDidFinishLoading:self]; 232 | } 233 | } else { 234 | // 头部信息不完整,关闭inputstream,通知client 235 | [inputStream removeFromRunLoop:curRunLoop forMode:NSRunLoopCommonModes]; 236 | [inputStream setDelegate:nil]; 237 | [inputStream close]; 238 | [self.client URLProtocolDidFinishLoading:self]; 239 | } 240 | } 241 | 242 | #pragma mark - NSStreamDelegate 243 | /** 244 | * input stream 收到header complete后的回调函数 245 | */ 246 | - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode { 247 | if (eventCode == NSStreamEventHasBytesAvailable) { 248 | CFReadStreamRef readStream = (__bridge_retained CFReadStreamRef) aStream; 249 | CFHTTPMessageRef message = (CFHTTPMessageRef) CFReadStreamCopyProperty(readStream, kCFStreamPropertyHTTPResponseHeader); 250 | if (CFHTTPMessageIsHeaderComplete(message)) { 251 | // 以防response的header信息不完整 252 | UInt8 buffer[16 * 1024]; 253 | UInt8 *buf = NULL; 254 | unsigned long length = 0; 255 | NSInputStream *inputstream = (NSInputStream *) aStream; 256 | NSNumber *alreadyAdded = objc_getAssociatedObject(aStream, kAnchorAlreadyAdded); 257 | if (!alreadyAdded || ![alreadyAdded boolValue]) { 258 | objc_setAssociatedObject(aStream, kAnchorAlreadyAdded, [NSNumber numberWithBool:YES], OBJC_ASSOCIATION_COPY); 259 | // 通知client已收到response,只通知一次 260 | NSDictionary *headDict = (__bridge NSDictionary *) (CFHTTPMessageCopyAllHeaderFields(message)); 261 | CFStringRef httpVersion = CFHTTPMessageCopyVersion(message); 262 | // 获取响应头部的状态码 263 | CFIndex myErrCode = CFHTTPMessageGetResponseStatusCode(message); 264 | NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:curRequest.URL statusCode:myErrCode HTTPVersion:(__bridge NSString *) httpVersion headerFields:headDict]; 265 | 266 | [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; 267 | 268 | // 验证证书 269 | SecTrustRef trust = (__bridge SecTrustRef) [aStream propertyForKey:(__bridge NSString *) kCFStreamPropertySSLPeerTrust]; 270 | SecTrustResultType res = kSecTrustResultInvalid; 271 | NSMutableArray *policies = [NSMutableArray array]; 272 | NSString *domain = [[curRequest allHTTPHeaderFields] valueForKey:@"host"]; 273 | if (domain) { 274 | [policies addObject:(__bridge_transfer id) SecPolicyCreateSSL(true, (__bridge CFStringRef) domain)]; 275 | } else { 276 | [policies addObject:(__bridge_transfer id) SecPolicyCreateBasicX509()]; 277 | } 278 | /* 279 | * 绑定校验策略到服务端的证书上 280 | */ 281 | SecTrustSetPolicies(trust, (__bridge CFArrayRef) policies); 282 | if (SecTrustEvaluate(trust, &res) != errSecSuccess) { 283 | [aStream removeFromRunLoop:curRunLoop forMode:NSRunLoopCommonModes]; 284 | [aStream setDelegate:nil]; 285 | [aStream close]; 286 | [self.client URLProtocol:self didFailWithError:[[NSError alloc] initWithDomain:@"can not evaluate the server trust" code:-1 userInfo:nil]]; 287 | } 288 | if (res != kSecTrustResultProceed && res != kSecTrustResultUnspecified) { 289 | /* 证书验证不通过,关闭input stream */ 290 | [aStream removeFromRunLoop:curRunLoop forMode:NSRunLoopCommonModes]; 291 | [aStream setDelegate:nil]; 292 | [aStream close]; 293 | [self.client URLProtocol:self didFailWithError:[[NSError alloc] initWithDomain:@"fail to evaluate the server trust" code:-1 userInfo:nil]]; 294 | 295 | } else { 296 | // 证书通过,返回数据 297 | if (![inputstream getBuffer:&buf length:&length]) { 298 | NSInteger amount = [inputstream read:buffer maxLength:sizeof(buffer)]; 299 | buf = buffer; 300 | length = amount; 301 | } 302 | NSData *data = [[NSData alloc] initWithBytes:buf length:length]; 303 | 304 | [self.client URLProtocol:self didLoadData:data]; 305 | } 306 | } else { 307 | // 证书已验证过,返回数据 308 | if (![inputstream getBuffer:&buf length:&length]) { 309 | NSInteger amount = [inputstream read:buffer maxLength:sizeof(buffer)]; 310 | buf = buffer; 311 | length = amount; 312 | } 313 | NSData *data = [[NSData alloc] initWithBytes:buf length:length]; 314 | 315 | [self.client URLProtocol:self didLoadData:data]; 316 | } 317 | } 318 | } else if (eventCode == NSStreamEventErrorOccurred) { 319 | [aStream removeFromRunLoop:curRunLoop forMode:NSRunLoopCommonModes]; 320 | [aStream setDelegate:nil]; 321 | [aStream close]; 322 | // 通知client发生错误了 323 | [self.client URLProtocol:self didFailWithError:[aStream streamError]]; 324 | } else if (eventCode == NSStreamEventEndEncountered) { 325 | [self handleResponse]; 326 | } 327 | } 328 | 329 | @end 330 | -------------------------------------------------------------------------------- /httpdns-sni/httpdns-sni/CFHTTPSURLProtocol.m: -------------------------------------------------------------------------------- 1 | // 2 | // CFHTTPSURLProtocol.m 3 | // BDHttpDnsSDKDemo 4 | // 5 | // Created by Csy on 2018/10/18. 6 | // Copyright © 2018 baidu. All rights reserved. 7 | // 8 | 9 | #import "CFHTTPSURLProtocol.h" 10 | #import 11 | #import 12 | 13 | // 用于标记是否为同一个请求,防止无限循环请求 14 | static NSString * const kReuqestIdentifiers = @"com.baidu.httpdns.request"; 15 | 16 | // 标记是否同一个数据流 17 | static char * const kHasEvaluatedStream = "com.baidu.httpdns.stream"; 18 | 19 | @interface CFHTTPSURLProtocol () 20 | 21 | @property(nonatomic, strong) NSMutableURLRequest *mutableRequest; 22 | @property(nonatomic, strong) NSInputStream *inputStream; 23 | @property(nonatomic, strong) NSRunLoop *runloop; 24 | 25 | @end 26 | 27 | @implementation CFHTTPSURLProtocol 28 | 29 | #pragma mark - Override 30 | 31 | /** 32 | * 是否拦截处理指定的请求 33 | * 34 | * @param request 指定的请求 35 | * 36 | * @return YES:拦截处理; NO:不拦截处理 37 | */ 38 | + (BOOL)canInitWithRequest:(NSURLRequest *)request { 39 | // 防止无限循环 40 | if ([NSURLProtocol propertyForKey:kReuqestIdentifiers inRequest:request]) { 41 | return NO; 42 | } 43 | 44 | // 只处理https 45 | NSString *urlString = request.URL.absoluteString; 46 | if ([urlString hasPrefix:@"https"]) { 47 | return YES; 48 | } 49 | 50 | return NO; 51 | } 52 | 53 | /** 54 | * 可以直接返回request; 也可以在这里修改request,比如添加header,修改host等 55 | * 56 | * @param request 原始请求 57 | * 58 | * @return 原始请求或者新的请求 59 | */ 60 | + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request { 61 | return request; 62 | } 63 | 64 | /** 65 | * 开始加载 66 | */ 67 | - (void)startLoading { 68 | NSMutableURLRequest *mutableRequest = [self.request mutableCopy]; 69 | self.mutableRequest = mutableRequest; 70 | 71 | // 防止无限循环,表示该请求已经被处理 72 | [NSURLProtocol setProperty:@(YES) forKey:kReuqestIdentifiers inRequest:mutableRequest]; 73 | 74 | // 发送请求 75 | [self startRequest]; 76 | } 77 | 78 | /** 79 | * 取消加载 80 | */ 81 | - (void)stopLoading { 82 | // 关闭inputStream 83 | if (self.inputStream.streamStatus == NSStreamStatusOpen) { 84 | [self closeInputStream]; 85 | } 86 | } 87 | 88 | #pragma mark - Request 89 | - (void)startRequest { 90 | // 创建请求 91 | CFHTTPMessageRef requestRef = [self createCFRequest]; 92 | CFAutorelease(requestRef); 93 | 94 | // 添加请求头 95 | [self addHeadersToRequestRef:requestRef]; 96 | 97 | // 添加请求体 98 | [self addBodyToRequestRef:requestRef]; 99 | 100 | // 创建CFHTTPMessage对象的输入流 101 | CFReadStreamRef readStream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, requestRef); 102 | self.inputStream = (__bridge_transfer NSInputStream *) readStream; 103 | 104 | // 设置SNI 105 | [self setupSNI]; 106 | 107 | // 设置Runloop 108 | [self setupRunloop]; 109 | 110 | // 打开输入流 111 | [self.inputStream open]; 112 | } 113 | 114 | - (CFHTTPMessageRef)createCFRequest { 115 | // 创建url 116 | CFStringRef urlStringRef = (__bridge CFStringRef) [self.mutableRequest.URL absoluteString]; 117 | CFURLRef urlRef = CFURLCreateWithString(kCFAllocatorDefault, urlStringRef, NULL); 118 | CFAutorelease(urlRef); 119 | 120 | // 读取HTTP method 121 | CFStringRef methodRef = (__bridge CFStringRef) self.mutableRequest.HTTPMethod; 122 | 123 | // 创建request 124 | CFHTTPMessageRef requestRef = CFHTTPMessageCreateRequest(kCFAllocatorDefault, methodRef, urlRef, kCFHTTPVersion1_1); 125 | 126 | return requestRef; 127 | } 128 | 129 | - (void)addHeadersToRequestRef:(CFHTTPMessageRef)requestRef { 130 | // 遍历请求头,将数据塞到requestRef 131 | // 不包含POST请求时存放在header的body信息 132 | NSDictionary *headFields = self.mutableRequest.allHTTPHeaderFields; 133 | for (NSString *header in headFields) { 134 | if (![header isEqualToString:@"originalBody"]) { 135 | CFStringRef requestHeader = (__bridge CFStringRef) header; 136 | CFStringRef requestHeaderValue = (__bridge CFStringRef) [headFields valueForKey:header]; 137 | CFHTTPMessageSetHeaderFieldValue(requestRef, requestHeader, requestHeaderValue); 138 | } 139 | } 140 | } 141 | 142 | - (void)addBodyToRequestRef:(CFHTTPMessageRef)requestRef { 143 | NSDictionary *headFields = self.mutableRequest.allHTTPHeaderFields; 144 | 145 | // POST请求时,将原始HTTPBody从header中取出 146 | CFStringRef requestBody = CFSTR(""); 147 | CFDataRef bodyDataRef = CFStringCreateExternalRepresentation(kCFAllocatorDefault, requestBody, kCFStringEncodingUTF8, 0); 148 | if (self.mutableRequest.HTTPBody) { 149 | bodyDataRef = (__bridge_retained CFDataRef) self.mutableRequest.HTTPBody; 150 | } else if (headFields[@"originalBody"]) { 151 | bodyDataRef = (__bridge_retained CFDataRef) [headFields[@"originalBody"] dataUsingEncoding:NSUTF8StringEncoding]; 152 | } 153 | 154 | // 将body数据塞到requestRef 155 | CFHTTPMessageSetBody(requestRef, bodyDataRef); 156 | 157 | CFRelease(bodyDataRef); 158 | } 159 | 160 | - (void)setupSNI { 161 | // 读取请求头中的host 162 | NSString *host = [self.mutableRequest.allHTTPHeaderFields objectForKey:@"host"]; 163 | if (!host) { 164 | host = self.mutableRequest.URL.host; 165 | } 166 | 167 | // 设置HTTPS的校验策略 168 | [self.inputStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL forKey:NSStreamSocketSecurityLevelKey]; 169 | NSDictionary *sslProperties = [[NSDictionary alloc] initWithObjectsAndKeys: 170 | host, (__bridge id) kCFStreamSSLPeerName, 171 | nil]; 172 | [self.inputStream setProperty:sslProperties forKey:(__bridge NSString *) kCFStreamPropertySSLSettings]; 173 | [self.inputStream setDelegate:self]; 174 | } 175 | 176 | - (void)setupRunloop { 177 | // 保存当前线程的runloop,这对于重定向的请求很关键 178 | if (!self.runloop) { 179 | self.runloop = [NSRunLoop currentRunLoop]; 180 | } 181 | 182 | // 将请求放入当前runloop的事件队列 183 | [self.inputStream scheduleInRunLoop:self.runloop forMode:NSRunLoopCommonModes]; 184 | } 185 | 186 | #pragma mark - Response 187 | 188 | /** 189 | * 响应结束 190 | */ 191 | - (void)endResponse { 192 | // 读取响应头部信息 193 | CFReadStreamRef readStream = (__bridge CFReadStreamRef) self.inputStream; 194 | CFHTTPMessageRef messageRef = (CFHTTPMessageRef) CFReadStreamCopyProperty(readStream, kCFStreamPropertyHTTPResponseHeader); 195 | CFAutorelease(messageRef); 196 | 197 | // 头部信息不完整,关闭inputstream,通知client 198 | if (!CFHTTPMessageIsHeaderComplete(messageRef)) { 199 | [self closeInputStream]; 200 | [self.client URLProtocolDidFinishLoading:self]; 201 | return; 202 | } 203 | 204 | // 把当前请求关闭 205 | [self closeInputStream]; 206 | 207 | // 通知上层响应结束 208 | [self.client URLProtocolDidFinishLoading:self]; 209 | } 210 | 211 | - (void)doRedirect:(NSDictionary *)headDict { 212 | // 读取重定向的location,设置成新的url 213 | NSString *location = headDict[@"Location"]; 214 | if (!location) 215 | location = headDict[@"location"]; 216 | NSURL *url = [[NSURL alloc] initWithString:location]; 217 | self.mutableRequest.URL = url; 218 | 219 | // 根据RFC文档,当重定向请求为POST请求时,要将其转换为GET请求 220 | if ([[self.mutableRequest.HTTPMethod lowercaseString] isEqualToString:@"post"]) { 221 | self.mutableRequest.HTTPMethod = @"GET"; 222 | self.mutableRequest.HTTPBody = nil; 223 | } 224 | 225 | // 内部处理,将url中的host通过HTTPDNS转换为IP 226 | // 解析出IP 227 | // 优先使用ipv6结果,对于ipv6结果,需要在ip前后增加[]字符 228 | // 若不存在ipv6结果,则使用ipv4结果 229 | BDHttpDnsResult *result = [[BDHttpDns sharedInstance] syncResolve:url.host cacheOnly:NO]; 230 | NSString *ip = nil; 231 | if (![result ipv4List] && ![result ipv6List]) { 232 | NSLog(@"Get empty iplist from httpdns, use origin url"); 233 | } else { 234 | if ([result ipv6List]) { 235 | NSLog(@"Use ipv6List(%@)", [result ipv6List]); 236 | ip = [[NSString alloc] initWithFormat:@"[%@]", [result ipv6List][0]]; 237 | } else { 238 | NSLog(@"Use ipv4List(%@)", [result ipv4List]); 239 | ip = [result ipv4List][0]; 240 | } 241 | } 242 | 243 | // 使用ip替换host 244 | if (ip) { 245 | NSLog(@"Get IP from HTTPDNS Successfully!"); 246 | NSRange hostFirstRange = [location rangeOfString:url.host]; 247 | if (NSNotFound != hostFirstRange.location) { 248 | NSString *newUrl = [location stringByReplacingCharactersInRange:hostFirstRange withString:ip]; 249 | self.mutableRequest.URL = [NSURL URLWithString:newUrl]; 250 | [self.mutableRequest setValue:url.host forHTTPHeaderField:@"host"]; 251 | } 252 | } 253 | 254 | [self startRequest]; 255 | } 256 | 257 | - (void)closeInputStream { 258 | [self closeStream:self.inputStream]; 259 | } 260 | 261 | - (void)closeStream:(NSStream *)aStream { 262 | [aStream removeFromRunLoop:self.runloop forMode:NSRunLoopCommonModes]; 263 | [aStream setDelegate:nil]; 264 | [aStream close]; 265 | } 266 | 267 | #pragma mark - NSStreamDelegate 268 | - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode { 269 | switch (eventCode) { 270 | case NSStreamEventHasBytesAvailable: { 271 | // stream类型校验 272 | if (![aStream isKindOfClass:[NSInputStream class]]) { 273 | break; 274 | } 275 | NSInputStream *inputStream = (NSInputStream *) aStream; 276 | CFReadStreamRef readStream = (__bridge CFReadStreamRef) inputStream; 277 | 278 | // 响应头完整性校验 279 | CFHTTPMessageRef messageRef = (CFHTTPMessageRef) CFReadStreamCopyProperty(readStream, kCFStreamPropertyHTTPResponseHeader); 280 | CFAutorelease(messageRef); 281 | if (!CFHTTPMessageIsHeaderComplete(messageRef)) { 282 | return; 283 | } 284 | CFIndex statusCode = CFHTTPMessageGetResponseStatusCode(messageRef); 285 | 286 | // https校验过了,直接读取数据 287 | if ([self hasEvaluatedStreamSuccess:aStream]) { 288 | [self readStreamData:inputStream]; 289 | } else { 290 | // 添加校验标记 291 | objc_setAssociatedObject(aStream, 292 | kHasEvaluatedStream, 293 | @(YES), 294 | OBJC_ASSOCIATION_RETAIN); 295 | 296 | if ([self evaluateStreamSuccess:aStream]) { // 校验成功,则读取数据 297 | // 非重定向 298 | if (![self isRedirectCode:statusCode]) { 299 | // 读取响应头 300 | [self readStreamHeader:messageRef]; 301 | 302 | // 读取响应数据 303 | [self readStreamData:inputStream]; 304 | } else { // 重定向 305 | // 关闭流 306 | [self closeStream:aStream]; 307 | 308 | // 处理重定向 309 | [self handleRedirect:messageRef]; 310 | } 311 | } else { 312 | // 校验失败,关闭stream 313 | [self closeStream:aStream]; 314 | [self.client URLProtocol:self didFailWithError:[[NSError alloc] initWithDomain:@"fail to evaluate the server trust" code:-1 userInfo:nil]]; 315 | } 316 | } 317 | } 318 | break; 319 | 320 | case NSStreamEventErrorOccurred: { 321 | [self closeStream:aStream]; 322 | 323 | // 通知client发生错误了 324 | [self.client URLProtocol:self didFailWithError:[aStream streamError]]; 325 | } 326 | break; 327 | 328 | case NSStreamEventEndEncountered: { 329 | [self endResponse]; 330 | } 331 | break; 332 | 333 | default: 334 | break; 335 | } 336 | } 337 | 338 | - (BOOL)hasEvaluatedStreamSuccess:(NSStream *)aStream { 339 | NSNumber *hasEvaluated = objc_getAssociatedObject(aStream, kHasEvaluatedStream); 340 | if (hasEvaluated && hasEvaluated.boolValue) { 341 | return YES; 342 | } 343 | return NO; 344 | } 345 | 346 | - (void)readStreamHeader:(CFHTTPMessageRef )message { 347 | // 读取响应头 348 | CFDictionaryRef headerFieldsRef = CFHTTPMessageCopyAllHeaderFields(message); 349 | NSDictionary *headDict = (__bridge_transfer NSDictionary *)headerFieldsRef; 350 | 351 | // 读取http version 352 | CFStringRef httpVersionRef = CFHTTPMessageCopyVersion(message); 353 | NSString *httpVersion = (__bridge_transfer NSString *)httpVersionRef; 354 | 355 | // 读取状态码 356 | CFIndex statusCode = CFHTTPMessageGetResponseStatusCode(message); 357 | 358 | // 非重定向的数据,才上报 359 | if (![self isRedirectCode:statusCode]) { 360 | NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:self.mutableRequest.URL statusCode:statusCode HTTPVersion: httpVersion headerFields:headDict]; 361 | [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; 362 | } 363 | } 364 | 365 | - (BOOL)evaluateStreamSuccess:(NSStream *)aStream { 366 | // 证书相关数据 367 | SecTrustRef trust = (__bridge SecTrustRef) [aStream propertyForKey:(__bridge NSString *) kCFStreamPropertySSLPeerTrust]; 368 | SecTrustResultType res = kSecTrustResultInvalid; 369 | NSMutableArray *policies = [NSMutableArray array]; 370 | NSString *domain = [[self.mutableRequest allHTTPHeaderFields] valueForKey:@"host"]; 371 | if (domain) { 372 | [policies addObject:(__bridge_transfer id) SecPolicyCreateSSL(true, (__bridge CFStringRef) domain)]; 373 | } else { 374 | [policies addObject:(__bridge_transfer id) SecPolicyCreateBasicX509()]; 375 | } 376 | 377 | // 证书校验 378 | SecTrustSetPolicies(trust, (__bridge CFArrayRef) policies); 379 | if (SecTrustEvaluate(trust, &res) != errSecSuccess) { 380 | return NO; 381 | } 382 | if (res != kSecTrustResultProceed && res != kSecTrustResultUnspecified) { 383 | return NO; 384 | } 385 | return YES; 386 | } 387 | 388 | - (void)readStreamData:(NSInputStream *)aInputStream { 389 | UInt8 buffer[16 * 1024]; 390 | UInt8 *buf = NULL; 391 | NSUInteger length = 0; 392 | 393 | // 从stream读数据 394 | if (![aInputStream getBuffer:&buf length:&length]) { 395 | NSInteger amount = [self.inputStream read:buffer maxLength:sizeof(buffer)]; 396 | buf = buffer; 397 | length = amount; 398 | } 399 | NSData *data = [[NSData alloc] initWithBytes:buf length:length]; 400 | 401 | // 数据上报 402 | [self.client URLProtocol:self didLoadData:data]; 403 | } 404 | 405 | - (BOOL)isRedirectCode:(NSInteger)statusCode { 406 | if (statusCode >= 300 && statusCode < 400) { 407 | return YES; 408 | } 409 | return NO; 410 | } 411 | 412 | - (void)handleRedirect:(CFHTTPMessageRef )messageRef { 413 | // 响应头 414 | CFDictionaryRef headerFieldsRef = CFHTTPMessageCopyAllHeaderFields(messageRef); 415 | NSDictionary *headDict = (__bridge_transfer NSDictionary *)headerFieldsRef; 416 | 417 | // 响应头的loction 418 | NSString *location = headDict[@"Location"]; 419 | if (!location) 420 | location = headDict[@"location"]; 421 | NSURL *redirectUrl = [[NSURL alloc] initWithString:location]; 422 | 423 | // 读取http version 424 | CFStringRef httpVersionRef = CFHTTPMessageCopyVersion(messageRef); 425 | NSString *httpVersion = (__bridge_transfer NSString *)httpVersionRef; 426 | 427 | // 读取状态码 428 | CFIndex statusCode = CFHTTPMessageGetResponseStatusCode(messageRef); 429 | 430 | // 生成response 431 | NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:self.mutableRequest.URL statusCode:statusCode HTTPVersion: httpVersion headerFields:headDict]; 432 | 433 | // 上层实现了redirect协议,则回调到上层 434 | // 否则,内部进行redirect 435 | if ([self.client respondsToSelector:@selector(URLProtocol:wasRedirectedToRequest:redirectResponse:)]) { 436 | [self.client URLProtocol:self 437 | wasRedirectedToRequest:[NSURLRequest requestWithURL:redirectUrl] 438 | redirectResponse:response]; 439 | } else { 440 | [self doRedirect:headDict]; 441 | } 442 | } 443 | 444 | @end 445 | -------------------------------------------------------------------------------- /httpdns-sni/httpdns-sni.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2028DB3621848635006D4CBE /* BDHttpDns.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2028DB3521848635006D4CBE /* BDHttpDns.framework */; }; 11 | 2028DB3B2184865B006D4CBE /* CFHTTPSURLProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = 2028DB382184865B006D4CBE /* CFHTTPSURLProtocol.m */; }; 12 | 2028DB3C2184865B006D4CBE /* CFHttpMessageURLProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = 2028DB392184865B006D4CBE /* CFHttpMessageURLProtocol.m */; }; 13 | 20698BA2217F4A94001E28DC /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 20698BA1217F4A94001E28DC /* AppDelegate.m */; }; 14 | 20698BA5217F4A94001E28DC /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 20698BA4217F4A94001E28DC /* ViewController.m */; }; 15 | 20698BA8217F4A94001E28DC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 20698BA6217F4A94001E28DC /* Main.storyboard */; }; 16 | 20698BAA217F4A96001E28DC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 20698BA9217F4A96001E28DC /* Assets.xcassets */; }; 17 | 20698BAD217F4A96001E28DC /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 20698BAB217F4A96001E28DC /* LaunchScreen.storyboard */; }; 18 | 20698BB0217F4A96001E28DC /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 20698BAF217F4A96001E28DC /* main.m */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXFileReference section */ 22 | 2028DB3521848635006D4CBE /* BDHttpDns.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = BDHttpDns.framework; path = "httpdns-sni/BDHttpDns.framework"; sourceTree = ""; }; 23 | 2028DB372184865B006D4CBE /* CFHTTPSURLProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CFHTTPSURLProtocol.h; sourceTree = ""; }; 24 | 2028DB382184865B006D4CBE /* CFHTTPSURLProtocol.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CFHTTPSURLProtocol.m; sourceTree = ""; }; 25 | 2028DB392184865B006D4CBE /* CFHttpMessageURLProtocol.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CFHttpMessageURLProtocol.m; sourceTree = ""; }; 26 | 2028DB3A2184865B006D4CBE /* CFHttpMessageURLProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CFHttpMessageURLProtocol.h; sourceTree = ""; }; 27 | 20698B9D217F4A94001E28DC /* httpdns-sni.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "httpdns-sni.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 28 | 20698BA0217F4A94001E28DC /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 29 | 20698BA1217F4A94001E28DC /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 30 | 20698BA3217F4A94001E28DC /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 31 | 20698BA4217F4A94001E28DC /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 32 | 20698BA7217F4A94001E28DC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 33 | 20698BA9217F4A96001E28DC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 34 | 20698BAC217F4A96001E28DC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 35 | 20698BAE217F4A96001E28DC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 36 | 20698BAF217F4A96001E28DC /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 37 | /* End PBXFileReference section */ 38 | 39 | /* Begin PBXFrameworksBuildPhase section */ 40 | 20698B9A217F4A94001E28DC /* Frameworks */ = { 41 | isa = PBXFrameworksBuildPhase; 42 | buildActionMask = 2147483647; 43 | files = ( 44 | 2028DB3621848635006D4CBE /* BDHttpDns.framework in Frameworks */, 45 | ); 46 | runOnlyForDeploymentPostprocessing = 0; 47 | }; 48 | /* End PBXFrameworksBuildPhase section */ 49 | 50 | /* Begin PBXGroup section */ 51 | 2028DB3421848635006D4CBE /* Frameworks */ = { 52 | isa = PBXGroup; 53 | children = ( 54 | 2028DB3521848635006D4CBE /* BDHttpDns.framework */, 55 | ); 56 | name = Frameworks; 57 | sourceTree = ""; 58 | }; 59 | 20698B94217F4A94001E28DC = { 60 | isa = PBXGroup; 61 | children = ( 62 | 20698B9F217F4A94001E28DC /* httpdns-sni */, 63 | 20698B9E217F4A94001E28DC /* Products */, 64 | 2028DB3421848635006D4CBE /* Frameworks */, 65 | ); 66 | sourceTree = ""; 67 | }; 68 | 20698B9E217F4A94001E28DC /* Products */ = { 69 | isa = PBXGroup; 70 | children = ( 71 | 20698B9D217F4A94001E28DC /* httpdns-sni.app */, 72 | ); 73 | name = Products; 74 | sourceTree = ""; 75 | }; 76 | 20698B9F217F4A94001E28DC /* httpdns-sni */ = { 77 | isa = PBXGroup; 78 | children = ( 79 | 2028DB3A2184865B006D4CBE /* CFHttpMessageURLProtocol.h */, 80 | 2028DB392184865B006D4CBE /* CFHttpMessageURLProtocol.m */, 81 | 2028DB372184865B006D4CBE /* CFHTTPSURLProtocol.h */, 82 | 2028DB382184865B006D4CBE /* CFHTTPSURLProtocol.m */, 83 | 20698BA0217F4A94001E28DC /* AppDelegate.h */, 84 | 20698BA1217F4A94001E28DC /* AppDelegate.m */, 85 | 20698BA3217F4A94001E28DC /* ViewController.h */, 86 | 20698BA4217F4A94001E28DC /* ViewController.m */, 87 | 20698BA6217F4A94001E28DC /* Main.storyboard */, 88 | 20698BA9217F4A96001E28DC /* Assets.xcassets */, 89 | 20698BAB217F4A96001E28DC /* LaunchScreen.storyboard */, 90 | 20698BAE217F4A96001E28DC /* Info.plist */, 91 | 20698BAF217F4A96001E28DC /* main.m */, 92 | ); 93 | path = "httpdns-sni"; 94 | sourceTree = ""; 95 | }; 96 | /* End PBXGroup section */ 97 | 98 | /* Begin PBXNativeTarget section */ 99 | 20698B9C217F4A94001E28DC /* httpdns-sni */ = { 100 | isa = PBXNativeTarget; 101 | buildConfigurationList = 20698BB3217F4A96001E28DC /* Build configuration list for PBXNativeTarget "httpdns-sni" */; 102 | buildPhases = ( 103 | 20698B99217F4A94001E28DC /* Sources */, 104 | 20698B9A217F4A94001E28DC /* Frameworks */, 105 | 20698B9B217F4A94001E28DC /* Resources */, 106 | ); 107 | buildRules = ( 108 | ); 109 | dependencies = ( 110 | ); 111 | name = "httpdns-sni"; 112 | productName = "httpdns-sni"; 113 | productReference = 20698B9D217F4A94001E28DC /* httpdns-sni.app */; 114 | productType = "com.apple.product-type.application"; 115 | }; 116 | /* End PBXNativeTarget section */ 117 | 118 | /* Begin PBXProject section */ 119 | 20698B95217F4A94001E28DC /* Project object */ = { 120 | isa = PBXProject; 121 | attributes = { 122 | LastUpgradeCheck = 1000; 123 | ORGANIZATIONNAME = Csy; 124 | TargetAttributes = { 125 | 20698B9C217F4A94001E28DC = { 126 | CreatedOnToolsVersion = 10.0; 127 | }; 128 | }; 129 | }; 130 | buildConfigurationList = 20698B98217F4A94001E28DC /* Build configuration list for PBXProject "httpdns-sni" */; 131 | compatibilityVersion = "Xcode 9.3"; 132 | developmentRegion = en; 133 | hasScannedForEncodings = 0; 134 | knownRegions = ( 135 | en, 136 | Base, 137 | ); 138 | mainGroup = 20698B94217F4A94001E28DC; 139 | productRefGroup = 20698B9E217F4A94001E28DC /* Products */; 140 | projectDirPath = ""; 141 | projectRoot = ""; 142 | targets = ( 143 | 20698B9C217F4A94001E28DC /* httpdns-sni */, 144 | ); 145 | }; 146 | /* End PBXProject section */ 147 | 148 | /* Begin PBXResourcesBuildPhase section */ 149 | 20698B9B217F4A94001E28DC /* Resources */ = { 150 | isa = PBXResourcesBuildPhase; 151 | buildActionMask = 2147483647; 152 | files = ( 153 | 20698BAD217F4A96001E28DC /* LaunchScreen.storyboard in Resources */, 154 | 20698BAA217F4A96001E28DC /* Assets.xcassets in Resources */, 155 | 20698BA8217F4A94001E28DC /* Main.storyboard in Resources */, 156 | ); 157 | runOnlyForDeploymentPostprocessing = 0; 158 | }; 159 | /* End PBXResourcesBuildPhase section */ 160 | 161 | /* Begin PBXSourcesBuildPhase section */ 162 | 20698B99217F4A94001E28DC /* Sources */ = { 163 | isa = PBXSourcesBuildPhase; 164 | buildActionMask = 2147483647; 165 | files = ( 166 | 2028DB3C2184865B006D4CBE /* CFHttpMessageURLProtocol.m in Sources */, 167 | 2028DB3B2184865B006D4CBE /* CFHTTPSURLProtocol.m in Sources */, 168 | 20698BA5217F4A94001E28DC /* ViewController.m in Sources */, 169 | 20698BB0217F4A96001E28DC /* main.m in Sources */, 170 | 20698BA2217F4A94001E28DC /* AppDelegate.m in Sources */, 171 | ); 172 | runOnlyForDeploymentPostprocessing = 0; 173 | }; 174 | /* End PBXSourcesBuildPhase section */ 175 | 176 | /* Begin PBXVariantGroup section */ 177 | 20698BA6217F4A94001E28DC /* Main.storyboard */ = { 178 | isa = PBXVariantGroup; 179 | children = ( 180 | 20698BA7217F4A94001E28DC /* Base */, 181 | ); 182 | name = Main.storyboard; 183 | sourceTree = ""; 184 | }; 185 | 20698BAB217F4A96001E28DC /* LaunchScreen.storyboard */ = { 186 | isa = PBXVariantGroup; 187 | children = ( 188 | 20698BAC217F4A96001E28DC /* Base */, 189 | ); 190 | name = LaunchScreen.storyboard; 191 | sourceTree = ""; 192 | }; 193 | /* End PBXVariantGroup section */ 194 | 195 | /* Begin XCBuildConfiguration section */ 196 | 20698BB1217F4A96001E28DC /* Debug */ = { 197 | isa = XCBuildConfiguration; 198 | buildSettings = { 199 | ALWAYS_SEARCH_USER_PATHS = NO; 200 | CLANG_ANALYZER_NONNULL = YES; 201 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 202 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 203 | CLANG_CXX_LIBRARY = "libc++"; 204 | CLANG_ENABLE_MODULES = YES; 205 | CLANG_ENABLE_OBJC_ARC = YES; 206 | CLANG_ENABLE_OBJC_WEAK = YES; 207 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 208 | CLANG_WARN_BOOL_CONVERSION = YES; 209 | CLANG_WARN_COMMA = YES; 210 | CLANG_WARN_CONSTANT_CONVERSION = YES; 211 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 212 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 213 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 214 | CLANG_WARN_EMPTY_BODY = YES; 215 | CLANG_WARN_ENUM_CONVERSION = YES; 216 | CLANG_WARN_INFINITE_RECURSION = YES; 217 | CLANG_WARN_INT_CONVERSION = YES; 218 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 219 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 220 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 221 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 222 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 223 | CLANG_WARN_STRICT_PROTOTYPES = YES; 224 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 225 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 226 | CLANG_WARN_UNREACHABLE_CODE = YES; 227 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 228 | CODE_SIGN_IDENTITY = "iPhone Developer"; 229 | COPY_PHASE_STRIP = NO; 230 | DEBUG_INFORMATION_FORMAT = dwarf; 231 | ENABLE_STRICT_OBJC_MSGSEND = YES; 232 | ENABLE_TESTABILITY = YES; 233 | GCC_C_LANGUAGE_STANDARD = gnu11; 234 | GCC_DYNAMIC_NO_PIC = NO; 235 | GCC_NO_COMMON_BLOCKS = YES; 236 | GCC_OPTIMIZATION_LEVEL = 0; 237 | GCC_PREPROCESSOR_DEFINITIONS = ( 238 | "DEBUG=1", 239 | "$(inherited)", 240 | ); 241 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 242 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 243 | GCC_WARN_UNDECLARED_SELECTOR = YES; 244 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 245 | GCC_WARN_UNUSED_FUNCTION = YES; 246 | GCC_WARN_UNUSED_VARIABLE = YES; 247 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 248 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 249 | MTL_FAST_MATH = YES; 250 | ONLY_ACTIVE_ARCH = YES; 251 | SDKROOT = iphoneos; 252 | }; 253 | name = Debug; 254 | }; 255 | 20698BB2217F4A96001E28DC /* Release */ = { 256 | isa = XCBuildConfiguration; 257 | buildSettings = { 258 | ALWAYS_SEARCH_USER_PATHS = NO; 259 | CLANG_ANALYZER_NONNULL = YES; 260 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 261 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 262 | CLANG_CXX_LIBRARY = "libc++"; 263 | CLANG_ENABLE_MODULES = YES; 264 | CLANG_ENABLE_OBJC_ARC = YES; 265 | CLANG_ENABLE_OBJC_WEAK = YES; 266 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 267 | CLANG_WARN_BOOL_CONVERSION = YES; 268 | CLANG_WARN_COMMA = YES; 269 | CLANG_WARN_CONSTANT_CONVERSION = YES; 270 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 271 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 272 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 273 | CLANG_WARN_EMPTY_BODY = YES; 274 | CLANG_WARN_ENUM_CONVERSION = YES; 275 | CLANG_WARN_INFINITE_RECURSION = YES; 276 | CLANG_WARN_INT_CONVERSION = YES; 277 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 278 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 279 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 280 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 281 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 282 | CLANG_WARN_STRICT_PROTOTYPES = YES; 283 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 284 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 285 | CLANG_WARN_UNREACHABLE_CODE = YES; 286 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 287 | CODE_SIGN_IDENTITY = "iPhone Developer"; 288 | COPY_PHASE_STRIP = NO; 289 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 290 | ENABLE_NS_ASSERTIONS = NO; 291 | ENABLE_STRICT_OBJC_MSGSEND = YES; 292 | GCC_C_LANGUAGE_STANDARD = gnu11; 293 | GCC_NO_COMMON_BLOCKS = YES; 294 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 295 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 296 | GCC_WARN_UNDECLARED_SELECTOR = YES; 297 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 298 | GCC_WARN_UNUSED_FUNCTION = YES; 299 | GCC_WARN_UNUSED_VARIABLE = YES; 300 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 301 | MTL_ENABLE_DEBUG_INFO = NO; 302 | MTL_FAST_MATH = YES; 303 | SDKROOT = iphoneos; 304 | VALIDATE_PRODUCT = YES; 305 | }; 306 | name = Release; 307 | }; 308 | 20698BB4217F4A96001E28DC /* Debug */ = { 309 | isa = XCBuildConfiguration; 310 | buildSettings = { 311 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 312 | CODE_SIGN_STYLE = Automatic; 313 | FRAMEWORK_SEARCH_PATHS = ( 314 | "$(inherited)", 315 | "$(PROJECT_DIR)/httpdns-sni", 316 | ); 317 | INFOPLIST_FILE = "httpdns-sni/Info.plist"; 318 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 319 | LD_RUNPATH_SEARCH_PATHS = ( 320 | "$(inherited)", 321 | "@executable_path/Frameworks", 322 | ); 323 | PRODUCT_BUNDLE_IDENTIFIER = "com.xiaolong.httpdns-sni"; 324 | PRODUCT_NAME = "$(TARGET_NAME)"; 325 | TARGETED_DEVICE_FAMILY = "1,2"; 326 | }; 327 | name = Debug; 328 | }; 329 | 20698BB5217F4A96001E28DC /* Release */ = { 330 | isa = XCBuildConfiguration; 331 | buildSettings = { 332 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 333 | CODE_SIGN_STYLE = Automatic; 334 | FRAMEWORK_SEARCH_PATHS = ( 335 | "$(inherited)", 336 | "$(PROJECT_DIR)/httpdns-sni", 337 | ); 338 | INFOPLIST_FILE = "httpdns-sni/Info.plist"; 339 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 340 | LD_RUNPATH_SEARCH_PATHS = ( 341 | "$(inherited)", 342 | "@executable_path/Frameworks", 343 | ); 344 | PRODUCT_BUNDLE_IDENTIFIER = "com.xiaolong.httpdns-sni"; 345 | PRODUCT_NAME = "$(TARGET_NAME)"; 346 | TARGETED_DEVICE_FAMILY = "1,2"; 347 | }; 348 | name = Release; 349 | }; 350 | /* End XCBuildConfiguration section */ 351 | 352 | /* Begin XCConfigurationList section */ 353 | 20698B98217F4A94001E28DC /* Build configuration list for PBXProject "httpdns-sni" */ = { 354 | isa = XCConfigurationList; 355 | buildConfigurations = ( 356 | 20698BB1217F4A96001E28DC /* Debug */, 357 | 20698BB2217F4A96001E28DC /* Release */, 358 | ); 359 | defaultConfigurationIsVisible = 0; 360 | defaultConfigurationName = Release; 361 | }; 362 | 20698BB3217F4A96001E28DC /* Build configuration list for PBXNativeTarget "httpdns-sni" */ = { 363 | isa = XCConfigurationList; 364 | buildConfigurations = ( 365 | 20698BB4217F4A96001E28DC /* Debug */, 366 | 20698BB5217F4A96001E28DC /* Release */, 367 | ); 368 | defaultConfigurationIsVisible = 0; 369 | defaultConfigurationName = Release; 370 | }; 371 | /* End XCConfigurationList section */ 372 | }; 373 | rootObject = 20698B95217F4A94001E28DC /* Project object */; 374 | } 375 | --------------------------------------------------------------------------------