可能是最全的iOS端HttpDns集成方案

******科普片**

1、DNS劫持的危害

不知道大家有沒有發(fā)現(xiàn)這樣一個現(xiàn)象,在打開一些網(wǎng)頁的時候會彈出一些與所瀏覽網(wǎng)頁不相關(guān)的內(nèi)容比如這樣奇(se)怪(qing)的東西


圖一

或者這樣


圖二
,其實(shí)造成這樣的原因就是DNS劫持,在我們正常瀏覽的網(wǎng)頁鏈接里面被惡意插入一些奇怪的東西。不止是這些,DNS劫持還會對我們的個人信息安全造成很大的傷害,釣魚網(wǎng)站之類的,也許我們所訪問的網(wǎng)站根本不是我們需要的網(wǎng)站,或者根本打不開網(wǎng)頁,有時還會消耗我們過多的流量。
2、什么是DNS解析

現(xiàn)在假如我們訪問一個網(wǎng)站www.baidu.com從按下回車到百度頁面顯示到我們的電腦上會經(jīng)歷如下幾個步驟

  • 1:計(jì)算機(jī)會向我們的運(yùn)營商(移動、電信、聯(lián)通等)發(fā)出打開www.baidu.com的請求。
  • 2:運(yùn)營商收到請求后會到自己的DNS服務(wù)器中找www.baidu.com這個域名所對應(yīng)的服務(wù)器的IP地址(也就是百度的服務(wù)器的IP地址),這里比如是180.149.132.47。
  • 3:運(yùn)營商用第二步得到的IP地址去找到百度的服務(wù)器請求得到數(shù)據(jù)后返回給我們。

其中第二步就是我們所說的DNS解析過程,域名和IP地址的關(guān)系其實(shí)就是我們的身份證號和姓名的關(guān)系,都是來標(biāo)記一個人或者是一個網(wǎng)站的,只是IP地址\身份證號只是一串沒有意義的數(shù)字,辨識度低,又不好記,所以就會在IP上加上一個域名以便區(qū)分,或是做的更加個性化,但是如果真的要來準(zhǔn)確的區(qū)分還是要靠身份證號碼或者是IP的,所以DNS解析就應(yīng)運(yùn)而生了。

3:什么是DNS劫持

DNS劫持,是指在DNS解析過程中攔截域名解析的請求,然后做一些自己的處理,比如返回假的IP地址或者什么都不做使請求失去響應(yīng),其效果就是對特定的網(wǎng)絡(luò)不能反應(yīng)或訪問的是假網(wǎng)址。根本原因就是以下兩點(diǎn):

  • 1:惡意攻擊,攔截運(yùn)營商的解析過程,把自己的非法東西嵌入其中。
  • 2:運(yùn)營商為了利益或者一些其他的因素,允許一些第三方在自己的鏈接里打打廣告之類的。
4:防止DNS劫持

了解了DNS劫持的相關(guān)資料后我們就知道了,防止NDS劫持就要從第二步入手,因?yàn)镈NS解析過程是運(yùn)營商來操作的,我們不能去干涉他們,不然我們也就成了劫持者了,所以我們要做的就是在我們請求之前對我們的請求鏈接做一些修改,將我們原本的請求鏈接www.baidu.com 修改為180.149.132.47,然后請求出去,這樣的話就運(yùn)營商在拿到我們的請求后發(fā)現(xiàn)我們直接用的就是IP地址就會直接給我們放行,而不會去走他自己DNS解析了,也就是說我們把運(yùn)營商要做的事情自己先做好了。不走他的DNS解析也就不會存在DNS被劫持的問題,從根本是解決了。

******技術(shù)篇**

5:項(xiàng)目中的實(shí)際操作
5.1:DNSPOD相關(guān)

我們知道要要把項(xiàng)目中請求的接口替換成成IP其實(shí)很簡單,URL是字符串,域名替換IP,無非就是一個字符串替換而已,的確這塊其實(shí)沒有什么技術(shù)含量,而且現(xiàn)在像阿里云(沒開源),七牛云(開源),等一些比較大的平臺在這方面也都有了比較成熟的解決方案,一個SDK,傳個普通的URL進(jìn)去就會返回一個域名被替換成IP的URL出來,也比較好用,這里要說一下IP地址的來源,如何拿到一個域名所對應(yīng)的IP呢?這里就是需要用到另一個服務(wù)——HTTPDNS,國內(nèi)比較有名的就是DNSPOD,包括阿里,七牛等也是使用他們的DNS服務(wù)來解析,就是這個


DNSPOD logo

簡介

他會給我們提供一個接口,我們使用HTTP請求的方式去請求這個接口,參數(shù)帶上我們的域名,他們就會把域名對應(yīng)的IP列表返回回來。類似這樣:

///這個請求URL的結(jié)構(gòu)是固定的119.29.29.29是DNSPOD固定的服務(wù)器地址,ttl參數(shù)的意思是返回結(jié)果是否帶ttl是個BOOL,dn就是我們需要解析的域名,id就是我們在dnspod上注冊時候他給我們的一個KEY
NSString *url = [NSString stringWithFormat:@"http://119.29.29.29/d?ttl=1&dn=www.baidu.com&id=KEY"];
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];
NSData * data = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&networkError];

這里使用同步還是異步都是可以的,具體根據(jù)你們業(yè)務(wù)需求。

5.2:項(xiàng)目中的使用

其實(shí)dnspod最難的部分是接入的部分,因?yàn)椴煌腁PP不同的網(wǎng)絡(luò)環(huán)境會導(dǎo)致各種各樣的問題,如果你是一個新的項(xiàng)目那么接入難度會大大降低,因?yàn)槟阃耆梢宰约悍庋b一套網(wǎng)絡(luò)請求,把DNS解析相關(guān)的邏輯都封裝到自己的網(wǎng)絡(luò)請求中,這樣你就可以得到APP所有的網(wǎng)絡(luò)層的控制權(quán),想干什么就干什么,但是如果是在一個已經(jīng)比較完善的APP中加入DNS防劫持的話那就是比較困難,因?yàn)槟悴荒苣玫剿芯W(wǎng)絡(luò)請求的控制權(quán)這篇文章中我主要使用是NSURLProtocol + Runtime hook方式來處理這些東西的,NSURLProtocol屬于iOS黑魔法的一種可以攔截任何從APP的 URL Loading System系統(tǒng)中發(fā)出的請求,其中包括如下

  • File Transfer Protocol (ftp://)
  • Hypertext Transfer Protocol (http://)
  • Hypertext Transfer Protocol with encryption (https://)
  • Local file URLs (file:///)
  • Data URLs (data://)

如果你的請求不在以上列表中就不能進(jìn)行攔截了,比如WKWebview,AVPlayer(比較特殊,雖然請求也是http/https但是就是不走這套系統(tǒng),蘋果爸爸就是這樣~)等,其實(shí)對于正常來說光用已經(jīng)NSURLProtocol足夠了。
??NSURLProtocol這個類我們不能直接使用,我們需要自己創(chuàng)建一個他的子類然后在我們的子類中操作他們像這樣

// 注冊自定義protocol
[NSURLProtocol registerClass:[CustomURLProtocol class]];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
configuration.protocolClasses = @[[CustomURLProtocol class]];

在這個類中我們可以攔截到請求,然后進(jìn)行處理。這個類中有四個非常重要的方法

+ (BOOL)canInitWithRequest:(NSURLRequest *)request;
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request;
- (void)startLoading;
//對于攔截的請求,NSURLProtocol對象在停止加載時調(diào)用該方法
- (void)stopLoading;
+ (BOOL)canInitWithRequest:(NSURLRequest *)request;

通過返回值來告訴NSUrlProtocol對進(jìn)來的請求是否攔截,比如我只攔截HTTP的,或者是某個域名的請求之類

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request;

如果上面的方法返回YES那么request會傳到這里,這個地方通常不做處理 直接返回request

- (void)startLoading;

這個地方就是對我們攔截的請求做一些處理,我們文中所做的IP對域名的替換就在這里進(jìn)行,處理完之后將請求轉(zhuǎn)發(fā)出去,比如這樣

- (void)startLoading {
///其中customRequest是處理過的請求(域名替換后的)
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[[NSURLSessionConfiguration alloc] init] delegate:self delegateQueue:nil];
    NSURLSessionDataTask *task = [session dataTaskWithRequest:customRequest];
    [task resume];
}

你可以在 - startLoading 中使用任何方法來對協(xié)議對象持有的 request 進(jìn)行轉(zhuǎn)發(fā),包括 NSURLSession、 NSURLConnection 甚至使用 AFNetworking 等網(wǎng)絡(luò)庫,只要你能在回調(diào)方法中把數(shù)據(jù)傳回 client,幫助其正確渲染就可以,比如這樣:

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {
    [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageAllowed];
    
    completionHandler(NSURLSessionResponseAllow);
}

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
    [[self client] URLProtocol:self didLoadData:data];
}

client在后面會有講解。

- (void)stopLoading;

請求完畢后調(diào)用
大概的執(zhí)行流程是這樣


流程

在NSURLProtocol中有一個貫穿始終的變量

/*! 
    @method client
    @abstract Returns the NSURLProtocolClient of the receiver. 
    @result The NSURLProtocolClient of the receiver.  
*/
@property (nullable, readonly, retain) id <NSURLProtocolClient> client;

你可以認(rèn)為是這個是請求的發(fā)送者,打個比方,A想給B發(fā)送一個消息,由于距離遙遠(yuǎn)于是A去了郵局,A把消息內(nèi)容告訴了郵局,并且A在郵局登記了自己名字方便B有反饋的時候郵局來通知A查收。這個例子中郵局就是NSURLProtocol,A在郵局登記的名字就是client。所有的 client 都實(shí)現(xiàn)了 NSURLProtocolClient 協(xié)議,協(xié)議的作用就是在 HTTP 請求發(fā)出以及接受響應(yīng)時向其它對象傳輸數(shù)據(jù):

@protocol NSURLProtocolClient <NSObject>
...
- (void)URLProtocol:(NSURLProtocol *)protocol didReceiveResponse:(NSURLResponse *)response cacheStoragePolicy:(NSURLCacheStoragePolicy)policy;

- (void)URLProtocol:(NSURLProtocol *)protocol didLoadData:(NSData *)data;

- (void)URLProtocolDidFinishLoading:(NSURLProtocol *)protocol;
...
@end

當(dāng)然這個協(xié)議中還有很多其他的方法,比如 HTTPS 驗(yàn)證、重定向以及響應(yīng)緩存相關(guān)的方法,你需要在合適的時候調(diào)用這些代理方法,對信息進(jìn)行傳遞。
到此正常情況下的DNS的解析過程已經(jīng)結(jié)束,如果你發(fā)現(xiàn)按照如上操作之后并沒有達(dá)到預(yù)期效果那么請往下看,(通常情況下完成以上操作 原有的URL的就會變成http://123.456.789.123/XXX/XXX/XXX的格式。如果發(fā)現(xiàn)請求不成功就往下看吧)

6:遇到的坑點(diǎn)
6.1:我們知道運(yùn)營商本來是根據(jù)域名來確定一個URL的,我們將域名改為IP之后雖然不用運(yùn)營商幫我們解析了,但是運(yùn)營商在收到一串?dāng)?shù)字的時候也是懵逼狀態(tài),我們還是需要將域名傳給他們,但是不能用正常的方式傳,我們需要把原來的域名加到http請求的Header中的host字段下,根據(jù)Http協(xié)議的規(guī)定,如果在URL中無法找到域名的話就會去Header中找,這樣一來我們既把域名告訴了運(yùn)營商同時也直接制定了IP地址,這個是必須配置的,不然的話是請求不成功的。
[mutableRequest setValue:self.request.URL.host forHTTPHeaderField:@"HOST"];
加上Header再去請求就沒問題了,不過有些特殊的情況下會需要帶上cookie,同樣也是加到Header中
[mutableRequest setValue:YOUR Cookie forHTTPHeaderField:@"Cookie"];
6.2:關(guān)于AfNetworking的問題,現(xiàn)在大部分網(wǎng)絡(luò)請求是基于Afnetworking的,這里有一個坑,我們知道我們注冊CustomProtocol的時候是這樣
 // 注冊自定義protocol
[NSURLProtocol registerClass:[CustomURLProtocol class]];
 NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
configuration.protocolClasses = @[[CustomURLProtocol class]];
在系統(tǒng)的configuration加入我們的CustomProtocol,protocolClasses是一個數(shù)組里面可以放很多各種不同的CustomProtocol,我們看一下afnetworking的初始化方法。
AFHTTPSessionManager * sessionManager = [AFHTTPSessionManager manager];
我相信大家通常都會這么來創(chuàng)建,但是這里我要說下manager并不是一個單利,最后都會調(diào)到一個方法
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
    self = [super init];
    if (!self) {
        return nil;
    }

    if (!configuration) {
        configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    }

    self.sessionConfiguration = configuration;
    self.operationQueue = [[NSOperationQueue alloc] init];
    self.operationQueue.maxConcurrentOperationCount = 1;

    self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
    .
    .
    .
}

大家注意第二個判斷,如果沒有傳入configuration的話他會創(chuàng)建一個默認(rèn)的,這樣以至于我們之前在configuration的protocolClasses中注冊類全部被這個新的configuration替換掉了,所以無法解析。這里我采取的辦法就是runtime hook,因?yàn)閔ook第三方的代碼并不是一個很好的辦法,所以我直接hook NSURLSession的sessionWithConfiguration方法,因?yàn)橥ㄟ^觀察Afnetworking的源碼最終都是走到這里的。Hook之后把自己的configuration換進(jìn)去,像這樣
+ (NSURLSession *)swizzle_sessionWithConfiguration:(NSURLSessionConfiguration *)configuration {
    
    NSURLSessionConfiguration *newConfiguration = configuration;
    // 在現(xiàn)有的Configuration中插入我們自定義的protocol
    if (configuration) {
        NSMutableArray *protocolArray = [NSMutableArray arrayWithArray:configuration.protocolClasses];
        [protocolArray insertObject:[CustomProtocol class] atIndex:0];
        newConfiguration.protocolClasses = protocolArray;
    }
    else {
        newConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
        NSMutableArray *protocolArray = [NSMutableArray arrayWithArray:configuration.protocolClasses];
        [protocolArray insertObject:[CustomProtocol class] atIndex:0];
        newConfiguration.protocolClasses = protocolArray;
    }
    
    return [self swizzle_sessionWithConfiguration:newConfiguration];
}
然后就完美解決了。不過要注意下系統(tǒng)的是有兩個方法的
/*
 * Customization of NSURLSession occurs during creation of a new session.
 * If you only need to use the convenience routines with custom
 * configuration options it is not necessary to specify a delegate.
 * If you do specify a delegate, the delegate will be retained until after
 * the delegate has been sent the URLSession:didBecomeInvalidWithError: message.
 */
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration;
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(nullable id <NSURLSessionDelegate>)delegate delegateQueue:(nullable NSOperationQueue *)queue;
這兩個方法不能確定最終會走那個,所以為了保險起見都hook下,hook的方式是一樣的
6.3:AVPlayer請求,AVPlayer是我們iOS系統(tǒng)中系統(tǒng)自帶的播放視頻的框架,用到地方也很多,但是這個是比較坑的,因?yàn)锳VPlayer雖然也有http/https/file……請求這個概念,但是AVPlayer所有的請求都不會走URL Loading System,也就是說所有由AVPlayer發(fā)出的請求都不能被我們的CustomProtocol攔截,這時候大家也許會問,不對呀,我們正常調(diào)試的時候可以被攔截到的啊。其實(shí)蘋果官方上是說AVPlayer在真機(jī)調(diào)試和模擬器調(diào)試時候走的完全不是一套策略,也就是說在模擬器運(yùn)行時候是完全正常的,可以被攔截到也可以被解析,但是在真機(jī)上面就恰恰相反了,因?yàn)槲覀冏詈筮€是以真機(jī)為準(zhǔn),所以我們采取的辦法還是hook,因?yàn)槲覀冃枰诿襟wURL傳給AVPlayer前就要將相關(guān)東西配置好,域名替換啊,加host啊之類的,所以我們要找AVPlayer的入口,先看初始化方法,我發(fā)現(xiàn)項(xiàng)目中使用一個AVURLAsset來初始化AVPlayer,那么AVURLAsset又是什么呢?繼續(xù)查到AVURLAsset的初始化方法,可以發(fā)現(xiàn)這個方法:
/*!
  @method       initWithURL:options:
  @abstract     Initializes an instance of AVURLAsset for inspection of a media resource.
  @param        URL
                An instance of NSURL that references a media resource.
  @param        options
                An instance of NSDictionary that contains keys for specifying options for the initialization of the AVURLAsset. See AVURLAssetPreferPreciseDurationAndTimingKey and AVURLAssetReferenceRestrictionsKey above.
  @result       An instance of AVURLAsset.
*/
- (instancetype)initWithURL:(NSURL *)URL options:(nullable NSDictionary<NSString *, id> *)options NS_DESIGNATED_INITIALIZER;
其中URL就是我們傳給AVPlayer播放的URL,找到目標(biāo)就Hook下就可以了,具體過程就不多說了還是字符串替換,但是有一點(diǎn)需要注意的是,我之前上文說過做完IP對域名的替換之后還需要設(shè)置下request的Host,但是這個地方只有一個URL并沒有Request該如何處理呢?其實(shí)這個方法里面的opinion參數(shù)就是處理這個的,可以添加cookie之類的類似與httpheader的東西,可以添加這幾個Key
AVF_EXPORT NSString *const AVURLAssetPreferPreciseDurationAndTimingKey NS_AVAILABLE(10_7, 4_0);
AVF_EXPORT NSString *const AVURLAssetReferenceRestrictionsKey NS_AVAILABLE(10_7, 5_0);
AVF_EXPORT NSString *const AVURLAssetHTTPCookiesKey NS_AVAILABLE_IOS(8_0);
AVF_EXPORT NSString *const AVURLAssetAllowsCellularAccessKey NS_AVAILABLE_IOS(10_0);
但是并沒有發(fā)現(xiàn)和Host相關(guān)的Key,其實(shí)這個key是有的就是AVURLAssetHTTPHeaderFieldsKey只是因?yàn)檫@個Key沒暴露出來。這個地方不太確定是不是蘋果的私有API,網(wǎng)上查了大量的資料也沒有個說法,甚至我親自去蘋果開發(fā)者去問,蘋果也沒有給任何答復(fù),各種說法都有,具體使用的話就是
[self swizzle_initWithURL:videoURL options:@{AVURLAssetHTTPHeaderFieldsKey : @{@"Host":host}}]
這樣使用是沒有任何問題的,但是畢竟是沒有暴露出來的方法,我們不能這樣明目張膽的使用,其實(shí)對于字符串來說還是比較好規(guī)避的,只要不要明文出現(xiàn)這個KEY就可以,我在這里使用了一個加密,吧key變成密文然后這個地方通過解密獲取,就像這樣:
//加密后的KEY
const NSString * headerKey = @"35905FF45AFA4C579B7DE2403C7CA0CCB59AA83D660E60C9D444AFE13323618F";
.
.
.
//getRequestHeaderKey方法為解密方法
return [self swizzle_initWithURL:videoURL options:@{[self getRequestHeaderKey] : @{@"Host":host}}];
這樣之后就大功告成了,AVPlayer可以在DNS被劫持的情況下播放了,
6.4:POST請求這塊也算是一個大坑,我們知道http的post請求會包含一個body體,里面包含我們需要上傳的參數(shù)等一些資料,對于POST請求我們的NSURLProtocol是可以正常攔截的,但是我們攔截之后發(fā)現(xiàn)無論怎么樣我們獲得的body體都為nil!后來查了一些資料發(fā)下又是蘋果爸爸在做手腳。NSURLProtocol在攔截NSURLSession的POST請求時不能獲取到Request中的HTTPBody,這個貌似早就國外的論壇上傳開了,但國內(nèi)好像還鮮有人知,據(jù)蘋果官方的解釋是Body是NSData類型,即可能為二進(jìn)制內(nèi)容,而且還沒有大小限制,所以可能會很大,為了性能考慮,索性就攔截時就不拷貝了(內(nèi)流滿面臉)。為了解決這個問題,我們可以通過把Body數(shù)據(jù)放到Header中,不過Header的大小好像是有限制的,我試過2M是沒有問題,不過超過10M就直接Request timeout了。。。而且當(dāng)Body數(shù)據(jù)為二進(jìn)制數(shù)據(jù)時這招也沒轍了,因?yàn)镠eader里都是文本數(shù)據(jù),另一種方案就是用一個NSDictionary或NSCache保存沒有請求的Body數(shù)據(jù),用URL為key,最后方法就是別用NSURLSession,老老實(shí)實(shí)用古老的NSURLConnection算了。。。你以為這么就結(jié)束了嗎?并沒有,后來查了大量的資料發(fā)現(xiàn),既然post請求的httpbody沒有蘋果復(fù)制下來,那我們就不用httpbody,我們再往底層去看就會發(fā)現(xiàn)HTTPBodyStream這個東西我們可以通過他來獲取請求的body體具體代嗎如下
#pragma mark -
#pragma mark 處理POST請求相關(guān)POST  用HTTPBodyStream來處理BODY體
- (NSMutableURLRequest *)handlePostRequestBodyWithRequest:(NSMutableURLRequest *)request {
    NSMutableURLRequest * req = [request mutableCopy];
    if ([request.HTTPMethod isEqualToString:@"POST"]) {
        if (!request.HTTPBody) {
            uint8_t d[1024] = {0};
            NSInputStream *stream = request.HTTPBodyStream;
            NSMutableData *data = [[NSMutableData alloc] init];
            [stream open];
            while ([stream hasBytesAvailable]) {
                NSInteger len = [stream read:d maxLength:1024];
                if (len > 0 && stream.streamError == nil) {
                    [data appendBytes:(void *)d length:len];
                }
            }
            req.HTTPBody = [data copy];
            [stream close];
        }
    }
    return req;
}

這樣之后的req就是攜帶了body體的request啦,可以愉快地做post請求啦。
6.5:WKWebview是新出的瀏覽器控件,這里就不多說了,WKWebview不走URL Loading System,所以也不會被攔截,不過也是有辦法的,但是因?yàn)檫@次項(xiàng)目中沒有用到,所以沒有過多的去研究,后續(xù)我會寫一篇關(guān)于這個博客,不是很難,依舊是runtime大法。
6.6:SNI環(huán)境,這個可是坑了我好久好久的東西,所以我會放在最后去說,SNI環(huán)境因?yàn)樯婕暗阶C書驗(yàn)證所以是在https的基礎(chǔ)上來說的,SNI(Server Name Indication)是為了解決一個服務(wù)器使用多個域名和證書的擴(kuò)展。一句話簡述它的工作原理就是,在連接到服務(wù)器建立SSL鏈接之前先發(fā)送要訪問站點(diǎn)的域名(Hostname),這樣服務(wù)器根據(jù)這個域名返回一個合適的證書。其實(shí)關(guān)于SNI環(huán)境在這里就不過多解釋,阿里云文檔有很明白的解釋,同時他也有安卓和iOS在SNI環(huán)境下的處理文檔,我們發(fā)現(xiàn)安卓部分寫的很詳細(xì),可是已到了iOS這邊就這樣了:
阿里云文檔截圖
三行文字加三個鏈接就完事了。其實(shí)在遇到這個坑的時候我也查過很多相關(guān)資料,無非就是這三行話加這三個鏈接復(fù)制來復(fù)制去,沒有實(shí)質(zhì)性的進(jìn)展,大部分公司或者是項(xiàng)目沒有這么重的Httpdns需求,所以也就不會有這個環(huán)境,即使遇到了也就直接關(guān)閉httpdns了,后來只能自己去用CFNetwork一點(diǎn)點(diǎn)實(shí)現(xiàn)。具體代碼就不跟大家粘貼了因?yàn)樯婕暗揭恍┕緝?nèi)部的代碼,不過我會把我主要的參考資料發(fā)給大家。這里有個小技巧,因?yàn)槎荚谡fCFNetwork是比較底層的網(wǎng)絡(luò)實(shí)現(xiàn),好多東西需要開發(fā)者自行處理比如一些變量的釋放之類的,所以我們能少用盡量少用,因?yàn)镃fnetwork是為SNI(https)環(huán)境服務(wù),所以我們在攔截判斷的時候可以區(qū)分是用上層的網(wǎng)絡(luò)請求轉(zhuǎn)發(fā)還是用底層的cfnetwork來轉(zhuǎn)發(fā),
 if ([self.request.URL.scheme isEqualToString:@"https"] ) {
//使用CFnetwork
        curRequest = req;
        self.task = [[CustomCFNetworkRequestTask alloc] initWithURLRequest:originalRequest swizzleRequest:curRequest delegate:self];
        if (self.task) {
            [self.task startLoading];
        }
    } else {
//使用普通網(wǎng)絡(luò)請求
        NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
        self.session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:[NSOperationQueue mainQueue]];
        NSURLSessionTask *task = [self.session dataTaskWithRequest:req];
        [task resume];
    }
我是這么做的。
6.7:在NSURLProtocol中的那幾個類方法中是可以發(fā)送同步請求的,但是在實(shí)例方法發(fā)送同步請求就會卡死,所以實(shí)例方法中不能有任何的阻塞,進(jìn)行同步操作。不然就卡死。
7:總結(jié)

完成了以上的步驟之后你回發(fā)現(xiàn)在DNS壞掉的情況下手機(jī)里面除了微信QQ(他們也做了DNS解析)之外其他應(yīng)用都不能上網(wǎng)了但是你的App依然可以正常瀏覽網(wǎng)絡(luò)數(shù)據(jù)。這就是我最近在做的時候遇到的一些問題,有什么問題及時與我交流吧

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,428評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,024評論 3 413
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,285評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,548評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,328評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,878評論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,971評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,098評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,616評論 1 331
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,554評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,725評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,243評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,971評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,361評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,613評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,339評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,695評論 2 370

推薦閱讀更多精彩內(nèi)容

  • 概覽 緩存組件應(yīng)該說是每個客戶端程序必備的核心組件,試想對于每個界面的訪問都必須重新請求勢必降低用戶體驗(yàn)。但是如何...
    默默_David閱讀 1,951評論 1 9
  • 前言 DNS劫持指在劫持的網(wǎng)絡(luò)范圍內(nèi)攔截域名解析的請求,分析請求的域名,把審查范圍以外的請求放行,否則返回假的IP...
    sindri的小巢閱讀 9,457評論 16 87
  • DNS(Domain Name System,域名系統(tǒng)),因特網(wǎng)上作為域名和IP地址相互映射的一個分布式數(shù)據(jù)庫,能...
    一直在努力hard閱讀 4,660評論 3 19
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,775評論 18 139
  • 所謂習(xí)慣,不過是一些無聊的堅(jiān)持,未曾放棄過而已! 可是,很少有人做到。
    地平線上的背影閱讀 154評論 0 0