I have a requirement that pre-populate the DNS cache. I find libcurl has an API to specify domain-port-IP pair to DNS resolve for a request. See http://curl.haxx.se/libcurl/c/CURLOPT_RESOLVE.html .
I'm using AFNetworking now. However, I have a strong requirement on this libcurl API. Can AFNetworking have any way to do this?
AFNetworking uses the standard NSURL Loading system, and NSURLSession. If there is a way to support it there, you should be able to, regardless of if you are using AFNetworking. I am personally not familiar with that feature.
If you find anything out, I would love to hear about it!
I do It,and it works well in AFN1.3.x.Good Luck.The Comment write in Chinese.Somebody see this ,just Read the code In attach file!
通过IP直接访问网站,可以解决DNS劫持问题,如果是HTTP请求,使用ip地址直接访问接口,配合header中Host字段带上原来的域名信息即可;如果是 https请求,会很麻烦,需要 Overriding TLS Chain Validation Correctly;curl 中有一个 -resolve 方法可以实现使用指定ip访问https网站,iOS中集成curl库应该也可以,不过改动太大,未验证;对于服务器IP经常变的情况,可能需要使用httpDNS服务,参见:https://www.dnspod.cn/httpdns.
/** 可信任的域名,用于支持通过ip访问此域名下的https链接.
Trusted domain, this domain for support via IP access HTTPS links.
*/
@property(nonatomic, strong) NSMutableArray * trustHostnames;
- (void)connection:(NSURLConnection *)connection
willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
if (self.authenticationChallenge) {
self.authenticationChallenge(connection, challenge);
return;
}
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
/* 添加可信任的域名,以支持:直接使用ip访问特定https服务器.
Add trusted domain name to support: direct use of IP access specific HTTPS server.*/
for (NSString * trustHostname in [self trustHostnames]) {
serverTrust = AFChangeHostForTrust(serverTrust, trustHostname);
}
....
static inline SecTrustRef AFChangeHostForTrust(SecTrustRef trust, NSString * trustHostname)
{
if ( ! trustHostname || [trustHostname isEqualToString:@""]) {
return trust;
}
CFMutableArrayRef newTrustPolicies = CFArrayCreateMutable(
kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
SecPolicyRef sslPolicy = SecPolicyCreateSSL(true, (CFStringRef)trustHostname);
CFArrayAppendValue(newTrustPolicies, sslPolicy);
#ifdef MAC_BACKWARDS_COMPATIBILITY
/* This technique works in OS X (v10.5 and later) */
SecTrustSetPolicies(trust, newTrustPolicies);
CFRelease(oldTrustPolicies);
return trust;
#else
/* This technique works in iOS 2 and later, or
OS X v10.7 and later */
CFMutableArrayRef certificates = CFArrayCreateMutable(
kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
/* Copy the certificates from the original trust object */
CFIndex count = SecTrustGetCertificateCount(trust);
CFIndex i=0;
for (i = 0; i < count; i++) {
SecCertificateRef item = SecTrustGetCertificateAtIndex(trust, i);
CFArrayAppendValue(certificates, item);
}
/* Create a new trust object */
SecTrustRef newtrust = NULL;
if (SecTrustCreateWithCertificates(certificates, newTrustPolicies, &newtrust) != errSecSuccess) {
/* Probably a good spot to log something. */
return NULL;
}
return newtrust;
#endif
}
/* 使用AOP方式,指定可信任的域名, 以支持:直接使用ip访问特定https服务器.*/
[AFURLConnectionOperation aspect_hookSelector:@selector(trustHostnames) withOptions:AspectPositionInstead usingBlock: ^(id<AspectInfo> info){
__autoreleasing NSArray * trustHostnames = @[@"www.example.com"];
NSInvocation *invocation = info.originalInvocation;
[invocation setReturnValue:&trustHostnames];
}error:NULL];
此处用到的是一个 iOS AOP库,不熟悉的点这里: http://www.ios122.com/2015/08/aspects/.
Hey guys, Thank you very much.. I will try it in my project….Thanks.!
But , I have a another question , Does it work in AFNetwork 3.x version and Https Request?
Thanks !
Thank you
@HarrySu @AmdyTeng https://github.com/alibaba/alicloud-ios-demo?spm=5176.7711373890.0.0.hvS1Iw This Will help More.
Many Thanks iOS122… I will try …
On Jan 31, 2016, at 13:25, ios122 [email protected] wrote:
@HarrySu https://github.com/HarrySu @AmdyTeng https://github.com/AmdyTeng https://github.com/alibaba/alicloud-ios-demo?spm=5176.7711373890.0.0.hvS1Iw https://github.com/alibaba/alicloud-ios-demo?spm=5176.7711373890.0.0.hvS1Iw This Will help More.
—
Reply to this email directly or view it on GitHub https://github.com/AFNetworking/AFNetworking/issues/2954#issuecomment-177398370.
IT seems AFNetworking3.x does not sopport connect HTTPS by IP address。
I found it!!!
最近我们公司也要用httpDns把原始host换成IP发请求, 然后遇到了ssl校验不通过的问题,
第三方HTTPDns的文档或demo上都说在sessiontask 代理方法里去改host, 但是afn 根本就不会走task的代理方法, 它走的是session的代理方法;两个方法我都分别列在下面:
afn不走的task的代理方法:
-(void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler;
afn走的session的代理方法:
-(void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler;
这两个方法唯一的区别就是一个带task一个不带task, 但是致命问题就产生了, 不带task的session的代理方法无法拿到原始的没有换成IP的host, 也就无法再进行domain校验时再偷梁换柱将IP换成原始host地址去校验!
那么, 怎么解决这个问题, 让我们能使用afn用IP地址去请求呢?
首先明确解决思路:
在进行ssl校验时, 把ip换回原始host, 用原始host与后台服务器进行校验.
具体实现:
我们先去看看afn走的session的代理方法里做了什么:
-(void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__block NSURLCredential *credential = nil;
if (self.sessionDidReceiveAuthenticationChallenge) {//注意看这里
disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
} else {
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
if (credential) {
disposition = NSURLSessionAuthChallengeUseCredential;
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
} else {
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
}
if (completionHandler) {
completionHandler(disposition, credential);
}
}
注意看我标注"注意看这里"的那一行, 只要self.sessionDidReceiveAuthenticationChallenge有值他就不会走底下的else方法(也就是系统默认的校验方法), 就会走我们定义的校验方法;
那么好了我们只需要给它sessionDidReceiveAuthenticationChallenge即可:
[mgr setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession * _Nonnull session, NSURLAuthenticationChallenge * _Nonnull challenge, NSURLCredential *__autoreleasing _Nullable * _Nullable credential) {
//证书校验替换IP
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
NSString *originHost = weakMgr.requestSerializer.HTTPRequestHeaders[@"host"];
if ([weakMgr.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:originHost]) {//这个方法是校验ssl的关键校验时需要把IP替换成原始host
*credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
if (credential) {
disposition = NSURLSessionAuthChallengeUseCredential;
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
} else {
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
return disposition;
}];
这里有个关键的地方就是, evaluateServerTrust:(SecTrustRef)serverTrust
forDomain:(NSString *)domain 这个方法会进行domain校验, 也就是ssl校验, 它需要传校验的host 和 服务器的serverTrust, 两者匹配通过则ssl校验通过, 我们就是要在这里将host换成原始host, 之后就顺利通过啦;
我将原始host存在请求头里, 这样就能通过请求头取到,上面有写到:
NSString *originHost = weakMgr.requestSerializer.HTTPRequestHeaders[@"host"];
Cheers~
备注: mgr是业务代码 它是指AFHTTPSessionManager的实例;
@lafayea 这个非常有用
Most helpful comment
I found it!!!
最近我们公司也要用httpDns把原始host换成IP发请求, 然后遇到了ssl校验不通过的问题,
第三方HTTPDns的文档或demo上都说在sessiontask 代理方法里去改host, 但是afn 根本就不会走task的代理方法, 它走的是session的代理方法;两个方法我都分别列在下面:
afn不走的task的代理方法:
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler;afn走的session的代理方法:
-(void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler;这两个方法唯一的区别就是一个带task一个不带task, 但是致命问题就产生了, 不带task的session的代理方法无法拿到原始的没有换成IP的host, 也就无法再进行domain校验时再偷梁换柱将IP换成原始host地址去校验!
那么, 怎么解决这个问题, 让我们能使用afn用IP地址去请求呢?
首先明确解决思路:
在进行ssl校验时, 把ip换回原始host, 用原始host与后台服务器进行校验.
具体实现:
我们先去看看afn走的session的代理方法里做了什么:
注意看我标注"注意看这里"的那一行, 只要self.sessionDidReceiveAuthenticationChallenge有值他就不会走底下的else方法(也就是系统默认的校验方法), 就会走我们定义的校验方法;
那么好了我们只需要给它sessionDidReceiveAuthenticationChallenge即可:
[mgr setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession * _Nonnull session, NSURLAuthenticationChallenge * _Nonnull challenge, NSURLCredential *__autoreleasing _Nullable * _Nullable credential) {//证书校验替换IPNSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {NSString *originHost = weakMgr.requestSerializer.HTTPRequestHeaders[@"host"];if ([weakMgr.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:originHost]) {//这个方法是校验ssl的关键校验时需要把IP替换成原始host*credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];if (credential) {disposition = NSURLSessionAuthChallengeUseCredential;} else {disposition = NSURLSessionAuthChallengePerformDefaultHandling;}} else {disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;}} else {disposition = NSURLSessionAuthChallengePerformDefaultHandling;}return disposition;}];这里有个关键的地方就是, evaluateServerTrust:(SecTrustRef)serverTrust
forDomain:(NSString *)domain 这个方法会进行domain校验, 也就是ssl校验, 它需要传校验的host 和 服务器的serverTrust, 两者匹配通过则ssl校验通过, 我们就是要在这里将host换成原始host, 之后就顺利通过啦;
我将原始host存在请求头里, 这样就能通过请求头取到,上面有写到:
NSString *originHost = weakMgr.requestSerializer.HTTPRequestHeaders[@"host"];Cheers~
备注: mgr是业务代码 它是指AFHTTPSessionManager的实例;