libcurl 使用 https 时切换 ip 遇到的问题

2019-01-24  本文已影响15人  守拙圆

1 问题由来

开发的产品使用到的某服务器后台接口,因为业务的特殊性,在常规的host访问失败时,需要前端再使用指定的ip访问,而且是https。

开始
使用ip访问https的首要问题就是证书验证流程。在提出此需求时,后台同学给了一份阿里云的参考:HTTPS(含SNI)业务场景“IP直连”方案说明 。此中提到的例子是Andriod和ios上的应用,方案就是在使用的对应库中修改https的证书验证过程,使其最终合法。

而开发的桌面App的网络请求使用的是libcurl实现,于是开始查找文档。半天时间里浏览器了一遍libcurl的文档,也看到了有相关ssl相关的参数控制,但一个是看起来很复杂,一个是也没有找到有效解决问题的方法。同时也在网络搜索,意外找到了github上这篇 DNS污染方案调研/iOS防DNS污染方案调研---SNI业务场景 ,里面有清晰的描述,使用 CURLOPT_RESOLVE。

一测试,果然OK。于是欣喜若狂——绕过验证方法的方式太笨,而且还得改请求的地方,将url中的host改为ip。也感叹,浏览了半天我怎么就是没有看到CURLOPT_RESOLVE这个参数呢。

意外
以上发生在评估阶段。等开始进入实质开发阶段,心想,这次可便宜我了。这个周期的任务——秒提。嘿嘿,就好像少了一个需求。不曾想在严格按照需求开发时发生了意外……

首先实际需求:对servicehost 的每次访问需要显式控制是否要使用指定ip(还是偏向常规访问,用ip嘛,只是备案了)。

一个当前情况:App中使用的请求接口是multi handle(设计之初就是为了同时跑多个请求),恰恰是这点——multi handle ——共用 dns cache 。

评估时只是为了验证,简单的在每一个easy handle初始化时写死了如下调用:

host_list_ = curl_slist_append(NULL, "servicehost:443:8.8.8.8");
curl_easy_setopt(curl_handle, CURLOPT_RESOLVE, host_list_);
也即对 servicehost 的每一次(当然包括第一次了)访问都使用 8.8.8.8 这个ip进行访问——8.8.8.8 被 cache 了。后续就算不想使用这个ip也“不行”。

反过来,如果第一次使用常规访问,dns获取到的是9.9.9.9。从测试结果看,即便后续请求设定了servicehost:443:8.8.8.8,实际请求仍然使用的第一次访问servicehost解析到的9.9.9.9——此次调用CURLOPT_RESOLVE不起作用。看起来好像不管是CURLOPT_RESOLVE调用,还是常规请求,dns都被cache了。

收场
再看文档:Remove names from the DNS cache again, to stop providing these fake resolves, by including a string in the linked list that uses the format "-HOST:PORT".

好了,因为场景中对于servicehost的第一次访问是常规host方式,假设ip是9.9.9.9。后续某次想使用8.8.8.8访问,于是需要移除对应的dns cache。

......
host_list = curl_slist_append(host_list, "-servicehost:443");
host_list = curl_slist_append(host_list, "servicehost:443:8.8.8.8");
curl_easy_setopt(curl, CURLOPT_RESOLVE, host);
......
如果简单粗暴处理,不开启ip直连时,解析url将对应host的dns cache清除。但实际情况是,只有少量的host才有此需求。一刀切,难免影响了绝大多数常规host访问的效率。

我的做法是:记录那些曾经调用了CURLOPT_RESOLVE的hosts。在每一次请求时判断:当前host没有开启ip访问,但在上述hosts中才清理其dns cache,同时从hosts移除记录。

上一篇下一篇

猜你喜欢

热点阅读