当前位置: 首页 > 后端技术 > Java

Netty系列:在netty中使用tls协议请求DNS服务器

时间:2023-04-01 14:17:18 Java

介绍上一篇我们讲了如何在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服务器:223.5.5.5之前,使用TCP和UDP协议时,查询的DNS端口是53,如果改成DoT,那么端口需要改成853。搭建一个支持DoT的netty客户端DoT底层还是TCP协议,也就是TLSoverTCP,所以我们需要使用NioEventLoopGroup和NioSocketChannel搭建一个netty客户端,如下图:EventLoopGroupgroup=newNioEventLoopGroup();Bootstrapb=newBootstrap();b.group(group).channel(NioSocketChannel.class).handler(newDotChannelInitializer(sslContext,dnsServer,dnsPort));最终通道ch=b.connect(dnsServer,dnsPort).sync().channel();这里我们选择NioEventLoopGroup和NioSocketChannel。然后将自定义的DotChannelInitializer传递给Bootstrap。DotChannelInitializer包含自定义处理程序和netty自己的处理程序。我们看一下DotChannelInitializer的定义和它的构造函数:this.dnsServer=dnsServer;this.dnsServer=dnsServer;DNS端口;}DotChannelInitializer需要三个参数,分别是sslContext、dnsServer和dnsPort。这三个参数在sslContext中使用:protectedvoidinitChannel(SocketChannelch){ChannelPipelinep=ch.pipeline();p.addLast(sslContext.newHandler(ch.alloc(),dnsServer,dnsPort)).addLast(newTcpDnsQueryEncoder()).addLast(newTcpDnsResponseDecoder()).addLast(newDotChannelInboundHandler());}SslContext主要用于TLS配置,下面是SslContext的定义:SslProviderprovider=SslProvider.isAlpnSupported(SslProvider.OPENSSL)?SslProvider.OPENSSL:SslProvider.JDK;最终SslContextsslContext=SslContextBuilder.forClient().sslProvider(provider).protocols("TLSv1.3","TLSv1.2").build();因为SslProvider有很多种,可以选择openssl,也可以选择JDK自带的。这里我们使用openssl,为了提供openssl的支持,我们还需要提供openssl的依赖包如下:io.nettynetty-tcnative2.0.51.Finalio.nettynetty-tcnative-boringssl-static2.0.51.Final拥有提供程序后,您可以调用SslContextBuilder.forClient方法来创建SslContext。这里我们指定SSL协议为“TLSv1.3”和“TLSv1.2”。然后调用sslContext的newHandler方法创建一个支持ssl的handler:sslContext.newHandler(ch.alloc(),dnsServer,dnsPort)newHandler还需要指定dnsServer和dnsPort信息。处理完ssl,接下来就是dns查询和响应的编解码。这里我们使用TcpDnsQueryEncoder和TcpDnsResponseDecoder。TcpDnsQueryEncoder和TcpDnsResponseDecoder在介绍使用netty搭建tcp客户端的时候已经有详细的讲解,这里不再赘述。编解码后就是自定义的消息处理器DotChannelInboundHandler:classDotChannelInboundHandlerextendsSimpleChannelInboundHandlerDotChannelInboundHandler中定义了消息的具体处理方法:privatestaticvoidreadMsg(DefaultDnsResponsemsg){if(msg.count(QUDnsSection)0){DnsQuestion问题=msg.recordAt(DnsSection.QUESTION,0);log.info("问题是:{}",问题);}inti=0,count=msg.count(DnsSection.ANSWER);while(i