netty 系列之: 在 netty 中使用 tls 协议请求 DNS 服务器
简介
在前面的文章中我们讲过了如何在 netty 中构造客户端分别使用 tcp 和 udp 协议向 DNS 服务器请求消息。在请求的过程中并没有进行消息的加密,所以这种请求是不安全的。
那么有同学会问了,就是请求解析一个域名的 IP 地址而已,还需要安全通讯吗?
事实上,不加密的 DNS 查询消息是很危险的,如果你在访问一个重要的网站时候,DNS 查询消息被监听或者篡改,有可能你收到的查询返回 IP 地址并不是真实的地址,而是被篡改之后的地址,从而打开了钓鱼网站或者其他恶意的网站,从而造成了不必要的损失。
所以 DNS 查询也是需要保证安全的。
幸运的是在 DNS 的传输协议中特意指定了一种加密的传输协议叫做 DNS-over-TLS,简称("DoT")。
那么在 netty 中可以使用 DoT 来进行 DNS 服务查询吗?一起来看看吧。
支持 DoT 的 DNS 服务器
因为 DNS 中有很多传输协议规范,但并不是每个 DNS 服务器都支持所有的规范,所以我们在使用 DoT 之前需要找到一个能够支持 DoT 协议的 DNS 服务器。
这里我还是选择使用阿里 DNS 服务器:
之前使用 TCP 和 UDP 协议的时候查询的 DNS 端口是 53,如果换成了 DoT,那么端口就需要变成 853。
搭建支持 DoT 的 netty 客户端
DoT 的底层还是 TCP 协议,也就是说 TLS over TCP,所以我们需要使用 NioEventLoopGroup 和 NioSocketChannel 来搭建 netty 客户端,如下所示:
这里选择的是 NioEventLoopGroup 和 NioSocketChannel。然后向 Bootstrap 中传入自定义的 DotChannelInitializer 即可。
DotChannelInitializer 中包含了自定义的 handler 和 netty 自带的 handler。
我们来看下 DotChannelInitializer 的定义和他的构造函数:
DotChannelInitializer 需要三个参数分别是 sslContext,dnsServer 和 dnsPort。
这三个参数都是在 sslContext 中使用的:
SslContext 主要用来进行 TLS 配置,下面是 SslContext 的定义:
因为 SslProvider 有很多种,可以选择 openssl,也可以选择 JDK 自带的。
这里我们使用的 openssl,要想提供 openssl 的支持,我们还需要提供 openssl 的依赖包如下:
有了 provider 之后,就可以调用 SslContextBuilder.forClient 方法来创建 SslContext。
这里我们指定 SSL 的 protocol 是"TLSv1.3"和"TLSv1.2"。
然后再调用 sslContext 的 newHandler 方法就创建好了支持 ssl 的 handler:
newHandler 还需要指定 dnsServer 和 dnsPort 信息。
处理完 ssl,接下来就是对 dns 查询和响应的编码解码器,这里使用的是 TcpDnsQueryEncoder 和 TcpDnsResponseDecoder。
TcpDnsQueryEncoder 和 TcpDnsResponseDecoder 在之前介绍使用 netty 搭建 tcp 客户端的时候就已经详细解说过了,这里就不再进行讲解了。
编码解码之后,就是自定义的消息处理器 DotChannelInboundHandler:
DotChannelInboundHandler 中定义了消息的具体处理方法:
读取的逻辑很简单,先从 DefaultDnsResponse 中读取 QUESTION,打印出来,然后再读取它的 ANSWER,因为这里是 A address,所以调用 NetUtil.bytesToIpAddress 方法将 ANSWER 转换为 ip 地址打印出来。
最后我们可能得到这样的输出:
TLS 的客户端请求
我们创建好 channel 之后,就需要向 DNS server 端发送查询请求了。因为是 DoT,那么和普通的 TCP 查询有什么区别呢?
答案是并没有什么区别,因为 TLS 的操作 SslHandler 我们已经在 handler 中添加了。所以这里的查询和普通查询没什么区别。
同样我们需要构建一个 DnsQuery,这里使用的是 DefaultDnsQuery,通过传入一个 randomID 和 opcode 即可。
因为是查询,所以这里的 opcode 是 DnsOpCode.QUERY。
然后需要向 QUESTION section 中添加一个 DefaultDnsQuestion,用来查询具体的域名和类型。
这里的 queryDomain 是 www.flydean.com,查询类型是 A,表示的是对域名进行 IP 解析。
最后将得到的 query,写入到 channel 中即可。
总结
这里我们使用 netty 构建了一个基于 TLS 的 DNS 查询客户端,除了添加 TLS handler 之外,其他操作和普通的 TCP 操作类似。但是要注意的是,要想客户端可以正常工作,我们需要请求支持 DoT 协议的 DNS 服务器才可以。
本文的代码,大家可以参考:
版权声明: 本文为 InfoQ 作者【程序那些事】的原创文章。
原文链接:【http://xie.infoq.cn/article/b8ba22e10be1f3934ec1bf803】。文章转载请联系作者。
评论