iOS HTTPDNS集成,结合AFNetwork进行“ip直连”

        本期是通过NSURLProtocol拦截的方式替换ip,包括SNI的处理。后期有时间会有一章通过hook网络框架AFN的方式,这种方式也无需改变AFN源码,包括在HTTPS证书校验的过程。而本文主要针对HTTPS协议进行说明,HTTP协议没有SSL/TLS的证书验证的过程,处理起来比较简单,也无需用到CFNetwork,所以暂时不做讲解。还有一篇结合SDWebImage对HTTP协议进行"ip直连"的文章。无论是HTTP还是HTTPS都可结合AFNetwork和SDWebImage进行"ip直连",其中原理都是相通的。若有不足或误区,盼请指正。

        大概说一下本章内容:本文对HTTPDNS做一个较为详细的讲解,在看本文之前,您需要对系统偏底层的CFNetwork网络框架有一个比较基础的认知。整体总结为以下几点。

一、什么是HTTPDNS?

        首先,DNS解析的目的就是通过某个域名:host(www.xxx.com)找到ip地址(111.111.1.1)。对于HTTPDNS的含义,用通俗简单一点语言解释。就是跳过运营商的DNS解析,通过ip地址直接精准对目标服务器进行连接。可避免被劫持,并大大缩短DNS解析时间,这也是对网络请求的一个重要优化环节(DNS优化)。

        我们都知道一般的request发出去之后的第一步就是对请求域名进行解析,而一般情况下我们的解析都是走运营商的LocalDNS,首先LocalDNS会对请求过的数据做缓存,因为缓存的时效性,我们想拿到最新最准确的数据时就会受限。其次,在这个DNS解析的过程中容易被外界劫持,注入一些未知的内容(像广告一类)后返回,甚至返回一个全新的ip给用户,可能危及到用户的个人隐私。而HTTPDNS是让用户绕过LocalDNS,在发送请求之前在本地就把host换成ip,直接对准目标服务器,这样就无需去问运营商服务器索要ip了,本来耗时不短的DNS的解析过程耗已在本地客户端完成了,所以DNS解析的时间就大大缩短了。   

        至于怎么拿到host对应的ip呢,因为客户端没法拿到当前host对应的最优ip(比如根据发送请求的地域不同等原因,对应的最优ip也可能不同),所以最好不要写死在客户端,而是通过外界拿到。两种方案:1、服务器直接下发,此种情况也是比较复杂,因为涉及的很多容灾的处理,比如在ip链接失败的时候,需要用LocalDNS做兜底,并向服务器上传错误日志,询问是否需要更新本地ip集合等等一系列复杂的操作。2、通过三方拿到,比如DNSPOD,阿里等。个人建议选择支持预处理的三方,在启动app的时候,预先拿到需要的ip集合,比如阿里。


二、结合AFNetwork的普通网络请,通过NSURLProtocol对网络进行拦截,然后进行"ip直连"。

        此文主要通过NSURLProtocol拦截请求的方式进行“ip直连”,以及post请求时,body“丢失”问题处理,SNI场景处理,利用CFNetwork对SNI的处理。先简单介绍一下NSURLProtocol,NSURLProtocol是苹果提供给开发者们专门用来劫持网络请求的一个抽象类,所以一般用到的时候选择用继承NSURLProtocol的方式,本文中的WYCustomURLProtocol就只这样一个子类。先了解几个重要的api:       

1、+ (BOOL)canInitWithRequest:(NSURLRequest *)request;// 通过协议或是域名判断哪些域名的请求需要拦截的就返YES,反之返NO。   

2、+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request;  // 上面方法的返YES时,会走到这个方法,你可以直接返回request,也可以重定向一个request返回。

3、- (void)startLoading;// 重定向的request就是从此发出去的

4、- (void)stopLoading; //  当cancel请求的时候,会调用此方法。

接下来就开始附上code了,要用UrlProtocol之前需要对之进行注册。

第1步、在初始化AFHTTPSessionManager的时候,对其进行注册:如图 1

图 1

第2步、若你使用到了YTK框架,那么可以这样初始化,此处为了便于理解所以引用了YTK,实际操作的时候,建议对外提供一个注册接口,不要直接用此种直接依赖TYK的方式,以免污染HTTPDNS部分。 图 2

图 2

第3步、对于要过滤拦截的域名需在此方法判断 图 3

图 3

第4步、请求ip替换host的过程,并给header添加host, 如图 4。此处只要能正确替换,大家可以各显神通。

图 4

其中的wy_bodyForPost目的是为了取回body,方法体如图 5 

图 5

        此处需要说明一下,通过NSURLProtocol拦截的post请求的body并非真的丢失了,只是body数据在URL loading system中到达这里之前就已被转成stream了。可以在request.HTTPBodyStream中解析它。

第5步、利用CFNetwork重新构建C语言的request,并发送。如图 6和 图7

图 6
图 7

三、SNI场景。

        什么是SNI场景呢,SNI全称 Server Name Indication,是TLS的扩展。我们知道一个服务器可能对应了多个域名的情况,客户端与服务端在建立HTTPS连接的过程中要进行SSL/TLS握手。整个握手过程步骤分5步:

1、客户端发起握手请求,携带随机数、支持算法列表等参数。

2、服务端收到请求,选择合适的算法,下发公钥证书和随机数。

3、客户端对服务端证书进行校验,并发送随机数信息,该信息使用公钥加密。

4、服务端通过私钥获取随机数信息。

5、双方根据以上交互的信息生成session ticket,用作该连接后续数据传输的加密密钥。

而握手失败就发生在第3步,客户端验证后端给的证书过程如下:

1、客户端用本地的根证书解开证书链,确认服务端下发的证书是由可信任机构颁发。

2、客户端检查证书的domain域和扩展域,验证是否包含本次请求的host。

若是在握手过程中客户端要是没有指定需要访问的目标域名,这就会导致一个问题:如果一台服务机对应了多个域名,每个域名对应的证书也不一样,那服务端也不知道该返回给客户端哪一套证书进行校验,导致第3步的校验失败,以至握手失败。但若是我们指定该SSL/TLS的目标域名,那服务端就知道该返回那一套证书了,即可通过证书验证,握手成功。

那么如何指定目标域名呢?我们可以为SNI扩展字段添加一个host。而iOS的上层网络库NSURLConnection/NSURLSession没有提供对SNI字段进行配置的接口,所以要用到Socket层级的底层网络库CFNetwork,对SNI字段进行配置。如图 8

图 8

补充:1、为什么要在SNI的扩展里面添加传host,我不是在header传了host了吗?此处做一个简要说明:由于HTTP的host字段在header中。而SSL/TLS的握手以及证书验证都是在HTTP开始之前,所以header里面的host对于如何判断选取证书是没有关系的。这个时候,SSL/TLS协议的HELLO中增加了一个server name字段(SNI扩展字段),让HTTP的客户端可以提前设置host,从而使服务端在SSL/TLS的握手阶段可以选择正确的证书。在SSL/TLS握手过程中,若不是SNI的情况,意味着服务器只对应了一个域名,一套证书,所以可以直接返回即为正确的证书,这也是为什么不是SNI的情况下无需配置SNI的原因。前面我们说了SNI呢是一对多的情况,在拿不到host的时候不知道返回哪一套证书,就会返回默认的一套或不返回,这个时候我们就需要在配置SNI了,添加server name字段,对应的value即为host值。(比如:若是你需要通过HTTPS访问CDN节点资源,而CDN的节点往往服务了多个域名,所以需要通过SNI指定具体的域名证书进行通信。)

四、证书校验

        在证书校验的过程中解决了SNI的问题之后呢,服务端会返回给客户端一个与请求域名相匹配的证书,在客户端验证的时候若是默认取的请求地址中的host,而此时默认host早被我们换成了ip地址了,与证书是不匹配的,所以我们需要将header中的host取出进行证书验证。数据处理过程如图9 ;证书验证过程如图 10,一般SecTrustResultType的值为kSecTrustResultProceed或kSecTrustResultUnspecified表明验证通过。

图 9
图 10

下面主要是一些读取数据过程中对数据的处理,细节就不多说了,基本都是更CFNetwork相关部分。 如图 11

图 11
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,302评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,563评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,433评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,628评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,467评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,354评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,777评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,419评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,725评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,768评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,543评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,387评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,794评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,032评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,305评论 1 252
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,741评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,946评论 2 336

推荐阅读更多精彩内容