DoH(DNS on HTTPS)和DoT(DNS on TLS)协议详解

目录

简介

DNS over HTTPS利用HTTP协议的GET命令发出经由JSON等编码的DNS解析请求。较于传统的DNS协议,此处的HTTP协议通信处于具有加密作用的SSL/TLS协议(两者统称作HTTPS)的保护之下。但是,由于HTTPS本身需要经由多次数据来回传递才能完成协议初始化,其域名解析耗时较原DNS协议会显著增加。^[1]^DNS over TLS(缩写:DoT)是通过传输层安全协议(TLS)来加密并打包域名系统(DNS)的安全协议。^[2]^本文基于RFC 8484 - DNS Queries over HTTPS (DoH)介绍DoH协议的详情。

此外,本文还基于Boost的Asio、Beast实现了DoH和DoT的解析器ink19/Boost.dns

详情

DoH和DoT的请求包编码方式就是基于普通的DNS编码,具体可以参考这一篇博文^[3]^。为了简便,本文使用了c-ares^[4]^,对DNS的请求和回包进行解析。

请求

DoH

DoH的请求基于HTTPS,其将DNS的请求包使用base64编码,放在了dns参数上,如:

http 复制代码
GET /dns-query?dns=BAABAAABAAAAAAABBGhvbWUFaW5rMTkCY24AAAEAAQAAKQIAAAAAAAAA HTTP/1.1
Host: doh.pub
User-Agent: Boost.Beast/347
Accept: application/dns-message

其中需要注意的是Acceptapplication/dns-message

DoT

DoT是直接数据流传输,因此不需要使用base64编码,但是为了方便读写,需要在请求包前增加两个字节的请求长度。如:

c++ 复制代码
std::vector<boost::asio::const_buffer> req_buff;
uint16_t send_len = req.size();
send_len = htons(send_len);
req_buff.push_back(asio::buffer(&send_len, sizeof(send_len)));
req_buff.push_back(asio::buffer(req.data(), req.size()));

返回

DoH

DoH的回包放在body中,为二进制。直接读取解析即可。

DoT

DoT的回包和请求包类似,使用二进制,并有两个字节为前导长度。

c-ares的使用

打包

打包使用函数ares_create_query,签名为

c++ 复制代码
#include <ares.h>
 
int ares_create_query(const char *name,
                      int dnsclass,
                      int type,
                      unsigned short id,
                      int rd,
                      unsigned char **buf,
                      int *buflen,
                      int max_udp_size);

获取的buf,使用结束后需要使用ares_free_string释放。

dnsclasstype<arpa/nameser.h>中定义;id为16位,用于标记请求id;rd用于标识是否需要递归解析。

解析

解析使用ares_parse_xxxx_reply函数,比如A类型解析的签名为

c++ 复制代码
#include <ares.h>
 
int ares_parse_a_reply(const unsigned char *abuf, int alen,
                       struct hostent **host,
                       struct ares_addrttl *addrttls, int *naddrttls);

其中host需要使用ares_free_hostent进行释放。

使用方法为(注:该函数的使用方法在官方文档中描述的比较模糊,本文是在github中搜索ares_parse_a_reply后,找到了node库使用该函数的方法^[5]^)

c++ 复制代码
struct hostent *hosts;
int ret = ares_parse_a_reply((const unsigned char *)rsp.data(), rsp.size(),
                              &hosts, NULL, NULL);

for (int i = 0; hosts->h_addr_list[i] != NULL; ++i) {
  uint32_t ip = ntohl(*(uint32_t *)hosts->h_addr_list[i]))
}
ares_free_hostent(hosts);

  1. DNS over HTTPS - 维基百科,自由的百科全书 ↩︎

  2. DNS over TLS - 维基百科,自由的百科全书 ↩︎

  3. 自己动手实现DNS协议 - 苍青浪 - 博客园 ↩︎

  4. c-ares documentation | c-ares: a modern asynchronous DNS resolver ↩︎

  5. node/src/node_cares.cc at 496be457b6a2bc5b01ec13644b9c9783976159b2 · kuno/node ↩︎

相关推荐
火山引擎边缘云14 天前
揭秘字节跳动内部流量调度与容灾实践【上】
后端·云计算·dns
thinking-fish18 天前
DNS介绍(5):DNS 劫持及解决方案
网络·网络安全·dns·dns劫持
hong_zc19 天前
网络原理(四)—— 网络层、数据链路层 与 DNS
网络·dns·数据链路层·网络层
Algorithm-00721 天前
软件测试预备知识⑤—搭建 DNS 服务器
运维·服务器·dns
放飞自我的Coder23 天前
【DNS 阿里云,域名解析,解析到IP的指定端口】
网络·阿里云·dns
探索云原生25 天前
基于 Admission Webhook 实现 Pod DNSConfig 自动注入
云原生·kubernetes·go·dns
探索云原生1 个月前
使用 NodeLocalDNS 提升集群 DNS 性能和可靠性
linux·docker·云原生·kubernetes·go·dns
冷心笑看丽美人1 个月前
探秘 DNS 服务器:揭开域名解析的神秘面纱
linux·运维·服务器·dns
ZachOn1y1 个月前
计算机网络:应用层 —— 应用层概述
计算机网络·http·https·应用层·dns
Chicheng_MA2 个月前
Linux DNS 协议概述
linux·dns