背景

我们公司的产品使用 libcurl 作为基础网络库,线上环境中经常会有域名解析失败导致的问题。libcurl 的域名解析默认情况下是调用系统 API 完成的,并且用户的网络环境可能比较复杂,比如:是否连接了代理服务器,是否开启防火墙,域名解析过程是不是被运营商劫持等等。所以对于此类问题,通常是只能在特定的机器和网络环境下复现,非常难确定具体原因。

排查这类问题中我们也逐步有了一些想法:

  1. 网络诊断工具
  2. 域名解析备份机制
  3. 域名解析 PK 机制

这篇文章主要记录一下我是如何实现 libcurl 域名解析 PK 机制的。

首先需要弄清下面两个问题:

  1. libcurl 的域名解析流程
  2. 域名解析 PK 流程

libcurl 的域名解析流程

域名解析是网络连接的第一步,libcurl 使用了一个状态机管理网络连接的每个状态,代码在 multi.c 这个文件中:

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
27
28
29
30
31
32
33
34
static CURLMcode multi_runsingle(struct Curl_multi *multi,struct curltime now,struct Curl_easy *data)
{...switch(data->mstate) {case CURLM_STATE_INIT:/* init this transfer. */result=Curl_pretransfer(data);if(!result) {/* after init, go CONNECT */multistate(data, CURLM_STATE_CONNECT);Curl_pgrsTime(data, TIMER_STARTOP);rc = CURLM_CALL_MULTI_PERFORM;}break;case CURLM_STATE_CONNECT_PEND:/* We will stay here until there is a connection available. Thenwe try again in the CURLM_STATE_CONNECT state. */break;case CURLM_STATE_CONNECT:/* Connect. We want to get a connection identifier filled in. */Curl_pgrsTime(data, TIMER_STARTSINGLE);result = Curl_connect(data, &data->easy_conn,&async, &protocol_connect);...case CURLM_STATE_WAITRESOLVE:...result = Curl_resolver_is_resolved(data->easy_conn, &dns);...
}

CURLM_STATE_CONNECT 这个状态时会发起连接请求 Curl_connect, 解析域名的调用逻辑就封装在这个方法里面。libcurl 的域名解析有同步和异步两种方式,默认是异步的方式。异步域名解析的接口定义在 asyn.h 这个头文件中。

主要接口如下:

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
27
28
29
30
.../** Curl_resolver_is_resolved()** Called repeatedly to check if a previous name resolve request has* completed. It should also make sure to time-out if the operation seems to* take too long.** Returns normal CURLcode errors.*/
CURLcode Curl_resolver_is_resolved(struct connectdata *conn,struct Curl_dns_entry **dns);/** Curl_resolver_getaddrinfo() - when using this resolver** Returns name information about the given hostname and port number. If* successful, the 'hostent' is returned and the forth argument will point to* memory we need to free after use. That memory *MUST* be freed with* Curl_freeaddrinfo(), nothing else.** Each resolver backend must of course make sure to return data in the* correct format to comply with this.*/
Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,const char *hostname,int port,int *waitp);
...

Curl_resolver_getaddrinfos 是域名解析的接口,具体实现有两种方式:asyn-thread 和 asyn-ares; 前者是在开启了一个线程然后调用系统的域名解析API,后者是使用 c-ares 这个库实现异步域名解析。默认情况下,libcurl 使用的是 asyn-thread, 如果你想使用 asyn-ares, 需要打开 USE_ARES 这个编译选项。

Curl_resolver_is_resolved 返回域名解析是否完成,libcurl multi 中有个 hearbeat 就是通过调用这个方法轮询域名解析是否完成。

整体流程图如下:

域名解析 PK 流程

c-ares 是一个跨平台异步域名解析库,完整地实现了 DNS 协议标准,没有使用平台相关个API;因此,当我们遇到系统域名解析问题时,很自然地想到了是否可以使用 asyn-ares 做 backup 或者同时发起 asyn-thread 和 asyn-ares 两种解析方式。
但是 libcurl 中默认只能使用一种域名解析方式,也就时说如果打开了 USE_ARES 编译选项,就无法使用 aysn-thread 这种方式做域名解析了。所以这里需要重新定义一个编译选项并实现 asyn 接口。

如何在 libcurl 中实现域名解析 PK 呢?总结一下有以下需要解决的问题:

  1. 并行地发起 asyn-thread 和 asyn-ares 两种域名解析请求
  2. 如果其中一个解析成功
    • 返回解析结果,状态机更新,继续处理下一个状态
    • 取消另一个正在处理的域名解析请求
  3. 解析失败
    • 如果其中一个请求失败了,忽略失败处理逻辑,继续等待另一个域名解析请求返回
    • 如果两个请求都失败了,进行失败处理
  4. 取消逻辑
  5. 解析状态检测
  6. 数据结构更新

参考资料

  1. 从Chrome源码看DNS解析过程
  2. https://medium.com/tenable-techblog/remotely-exploiting-zoom-meetings-5a811342ba1d

libcurl 域名解析分析相关推荐

  1. Github域名解析连接慢问题

    Github域名解析连接慢问题 1. Github访问慢问题 2. Github连接解决方案 2.1 使用 Gitee 的镜像仓库 2.2 配置本地的 hosts 文件 3. DNS域名解析分析 3. ...

  2. python网络编程/套接字和DNS(验证加进一步应用)

    1.验证课本代码清单 4-1 调用getaddrinfo进行http ping(检查某一个主机是否开启HTTP服务,确定web服务器的状态) 运行结果: 参数: (1)getaddrinfo(host ...

  3. nginx关于域名解析的源码分析

    在nginx中,nginx需要频繁进行域名解析的过程做了自己的优化,使用了自己的一套域名解析过程,并做了缓存处理.我们可以设置DNS解析服务器的地址,即通过resolver指令来设置DNS服务器的地址 ...

  4. 通过Wireshark抓包分析谈谈DNS域名解析的那些事儿

    本文主要想通过动手实际分析一下是如何通过DNS服务器来解析域名获取对应IP地址的,毕竟,纸上得来终觉浅,绝知此事要躬行. 域名与IP地址 当在浏览器上敲下"www.baidu.com&quo ...

  5. 百度登陆协议分析!!!用libcurl来模拟百度登陆

    有空就分析了下百度的登陆协议. 大家看代码: size_t CURLWriteDataCallbak(char *data, size_t size, size_t nmemb, string *wr ...

  6. nginx域名解析部分完整源码分析

    nginx域名解析流程 域名查询的函数接口介绍 在使用同步IO的情况下,调用gethostbyname()或者gethostbyname_r()就可以根据域名查询到对应的IP地址, 但因为可能会通过网 ...

  7. 【工具篇】07. DNS 域名解析 ❀ 数据包分析工具 Wireshark

    [简介]DNS(Domain Name System,域名系统),因特网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而不用去记住能够被机器直接读取的IP数串.通过主机 ...

  8. 6个免费DNS解析服务商评测分析 适用于网站域名解析应用

    这几天我们很多网友应该知道CloudXNS DNS解析服务商预计7月15日会宣布停止提供免费解析服务而主营商业服务.虽然网络上提供免费DNS解析服务商很多,但是毕竟这么多年CloudXNS域名解析稳定 ...

  9. DNS协议分析(域名解析)

    一. DNS协议理论知识 1.1. 域名结构 域名系统并不像电话号码通讯录那么简单,通讯录主要是单个个体在使用,同一个名字出现在不同个体的通讯录里并不会出现问题,但域名是群体中所有人都在用的,必须要保 ...

最新文章

  1. Android中对Log日志文件的分析[转]
  2. CVPR 2021 | 国防科大:基于几何稳定性分析的物体位姿估计方法
  3. 不断前行的SD-WAN如何使现代企业受益—Vecloud微云
  4. 计算机视觉方面2016年重要会议deadline
  5. luogu4267 TamingtheHerd (dp)
  6. python计算累计收益率的函数,在Python中计算累积密度函数的导数
  7. 总裁徐雷“接管”京东 但拍板的仍是刘强东
  8. matlab晶闸管没有触发就导通,单相全控桥式晶闸管整流电路(纯电阻负载)
  9. arcgis python 教程-终于晓得arcgis-python入门教程
  10. 微信语音技术原理_微信语音多群直播原理-一起学堂
  11. VsCode中文乱码问题
  12. Smobiler控件的使用:ListView的数据绑定及实现多选
  13. java面试170道精讲:留着慢慢看
  14. .net6智能提示设置为中文
  15. Linux正确删除软链接
  16. 解决模拟器方向键无法使用问题
  17. java的字典序排序_java字典序排序
  18. 技术人员通往财务自由之路
  19. 230109-MacOS解决brew安装慢的问题
  20. 单例模式的两种实现方式:恶汉式和懒汉式

热门文章

  1. git教程——简单总结
  2. 实现instanceof关键字
  3. Redis事件管理(三)
  4. 在windows安装配置Git开发环境
  5. linux下的网络管理命令,常用linux网络管理命令(下)
  6. 2*2矩阵训练集比例对BP神经网络分类性能影响
  7. 十六进制编码_十六进制色值的那些秘密
  8. 【控制】《多智能体机器人系统信息融合与协调》范波老师-第3章-多 Agent 协调的学习与对策
  9. 【PC工具】更新chrome谷歌浏览器最新离线安装版各种版本,最好用的浏览器没有之一...
  10. BRCM5.02编译九:cannot find -lncurses