手把手教你在netty中使用TCP协议请求DNS服务器

简介DNS的全称domain name system , 既然是一个系统就有客户端和服务器之分 。一般情况来说我们并不需要感知这个DNS客户端的存在 , 因为我们在浏览器访问某个域名的时候 , 浏览器作为客户端已经实现了这个工作 。
但是有时候我们没有使用浏览器 , 比如?.NETty环境中 , 如何构建一个DNS请求呢?
DNS传输协议简介在RFC的规范中 , DNS传输协议有很多种,如下所示:

  • DNS-over-UDP/53简称”Do53″,是使用UDP进行DNS查询传输的协议 。
  • DNS-over-TCP/53简称”Do53/TCP”,是使用TCP进行DNS查询传输的协议 。
  • DNSCrypt,对DNS传输协议进行加密的方法 。
  • DNS-over-TLS简称”DoT”,使用TLS进行DNS协议传输 。
  • DNS-over-HTTPS简称”DoH”,使用HTTPS进行DNS协议传输 。
  • DNS-over-TOR,使用VPN或者tunnels连接DNS 。
这些协议都有对应的实现方式 , 我们先来看下Do53/TCP , 也就是使用TCP进行DNS协议传输 。
DNS的IP地址先来考虑一下如何在netty中使用Do53/TCP协议 , 进行DNS查询 。
因为DNS是客户端和服务器的模式 , 我们需要做的是构建一个DNS客户端 , 向已知的DNS服务器端进行查询 。
已知的DNS服务器地址有哪些呢?
除了13个root DNS IP地址以外 , 还出现了很多免费的公共DNS服务器地址,比如我们常用的阿里DNS,同时提供了IPv4/IPv6 DNS和DoT/DoH服务 。
IPv4: 223.5.5.5223.6.6.6IPv6: 2400:3200::12400:3200:baba::1DoH 地址: https://dns.alidns.com/dns-queryDoT 地址: dns.alidns.com再比如百度DNS , 提供了一组IPv4和IPv6的地址:
IPv4: 180.76.76.76IPv6: 2400:da00::6666还有114DNS:
114.114.114.114114.114.115.115当然还有很多其他的公共免费DNS , 这里我选择使用阿里的IPv4:223.5.5.5为例 。
有了IP地址 , 我们还需要指定netty的连接端口号 , 这里默认的是53 。
然后就是我们要查询的域名了 , 这里以www.flydean.com为例 。
你也可以使用你系统中配置的DNS解析地址 , 以mac为例 , 可以通过nslookup进行查看本地的DNS地址:
nslookupwww.flydean.comServer:8.8.8.8Address:8.8.8.8#53Non-authoritative answer:www.flydean.com canonical name = flydean.com.Name:flydean.comAddress: 47.107.98.187Do53/TCP在netty中的使用有了DNS Server的IP地址 , 接下来我们需要做的就是搭建netty client , 然后向DNS server端发送DNS查询消息 。
搭建DNS netty client因为我们进行的是TCP连接 , 所以可以借助于netty中的NIO操作来实现 , 也就是说我们需要使用NioEventLoopGroup和NIOSocketChannel来搭建netty客户端:
【手把手教你在netty中使用TCP协议请求DNS服务器】 final String dnsServer = "223.5.5.5";final int dnsPort = 53;EventLoopGroup group = new NioEventLoopGroup();Bootstrap b = new Bootstrap();b.group(group).channel(NioSocketChannel.class).handler(new Do53ChannelInitializer());final Channel ch = b.connect(dnsServer, dnsPort).sync().channel();netty中的NIO Socket底层使用的就是TCP协议 , 所以我们只需要像常用的netty客户端服务一样构建客户端即可 。
然后调用Bootstrap的connect方法连接到DNS服务器 , 就建立好了channel连接 。
这里我们在handler中传入了自定义的Do53ChannelInitializer , 我们知道handler的作用是对消息进行编码、解码和对消息进行读取 。因为目前我们并不知道客户端查询的消息格式 , 所以Do53ChannelInitializer的实现我们在后面再进行详细讲解 。
发送DNS查询消息netty提供了DNS消息的封装 , 所有的DNS消息 , 包括查询和响应都是DnsMessage的子类 。
每个DnsMessage都有一个唯一标记的ID , 还有代表这个message类型的DnsOpCode 。
对于DNS来说 , opCode有下面这几种:
public static final DnsOpCode QUERY = new DnsOpCode(0, "QUERY");public static final DnsOpCode IQUERY = new DnsOpCode(1, "IQUERY");public static final DnsOpCode STATUS = new DnsOpCode(2, "STATUS");public static final DnsOpCode NOTIFY = new DnsOpCode(4, "NOTIFY");public static final DnsOpCode UPDATE = new DnsOpCode(5, "UPDATE");因为每个DnsMessage都可能包含4个sections,每个section都以DnsSection来表示 。因为有4个section , 所以在DnsSection定义了4个section类型:


推荐阅读