├── 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 |
--------------------------------------------------------------------------------