前言
DNS协议作为着互联网客户端-服务器通信模式得第一关,在当下每天都有成千上亿上网记录产生得当今社会,其重要性自然不可言喻。在国内比较有名得DNS服务器有电信得114.114.114.114、阿里云得223.5.5.5,DNSPod得119.29.29.29,配置一个好的DNS服务器可以缩短请求响应时间、降低DNS劫持概率,提升上网体验。
上面这些都是互联网公用DNS服务器,本文博主教大家使用 Java Netty
自建DNS代理服务器,目前网上对于使用Netty自建DNS服务器得教程良莠不齐,大多没有代理步骤,达不到博主想要得代理效果,因而创建此文。觉得本文有帮助得可以关注博主
一、自建DNS代理服务器有哪些优势
- 域名控制:对于特定域名可以自由控制访问权限(屏蔽对特定网站访问)
- 域名记录:记录局域网内各个主机得域名访问(记录员工上网记录)
- 配置内网域名:通过自建DNS服务器可以配置内网域名,节约成本
- DNS负载均衡:通过自建DNS服务器可以轻松实现对于访问域名得负载均衡配置
- CDN网络:返回距离客户端最近的可用IP
- ...
二、自建DNS代理服务器代码
1. 添加域名黑名单文件,Maven项目在 resources
文件夹下添加 black_list.txt
文件,注意域名最后的 .
,在 Netty
自带的DNS查询Handler中,报文返回的域名最后都是带 .
的。实际上每个域名后都有一个 .
,它表示根域名,通常它都是被隐藏的。
// 添加域名黑名单
google.com.
facebook.com.
复制代码
接着解析 black_list.txt
文件,初始化内容放入 BLACK_LIST_DOMAIN
private static final List<String> BLACK_LIST_DOMAIN = new ArrayList<>();
static {
String s;
try (InputStream is = DnsServer.class.getClassLoader().getResourceAsStream("black_list.txt");
BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
while (StrUtil.isNotBlank(s = br.readLine())) {
BLACK_LIST_DOMAIN.add(s);
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
复制代码
2. 使用UDP协议绑定本机53端口(DNS协议默认使用UDP 53端口),并初始化 ProxyUdp
DNS请求代理对象
@Slf4j
public final class DnsServer {
private static final List<String> BLACK_LIST_DOMAIN = new ArrayList<>();
static {
...
}
public static void main(String[] args) throws Exception {
ProxyUdp proxyUdp = new ProxyUdp();
proxyUdp.init();
final int[] num = {0};
final NioEventLoopGroup group = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group).channel(NioDatagramChannel.class)
.handler(new ChannelInitializer<NioDatagramChannel>() {
@Override
protected void initChannel(NioDatagramChannel nioDatagramChannel) {
nioDatagramChannel.pipeline().addLast(...);
}
}).option(ChannelOption.SO_BROADCAST, true);
int port = 53;
ChannelFuture future = bootstrap.bind(port).addListener(future1 -> {
log.info("server listening port:{}", port);
});
future.channel().closeFuture().addListener(future1 -> {
if (future.isSuccess()) {
log.info(future.channel().toString());
}
});
}
}
复制代码
3. 给 nioDatagramChannel.pipeline()
添加多个 ChannelHandler
对象,接受DNS查询报文以及发送DNS响应报文。划重点来了 在 new SimpleChannelInboundHandler<DatagramDnsQuery>()
中解析客户端发送DNS查询报文, 获取访问域名信息,如果访问域名在黑名单中,则通过 getDatagramDnsResponse()
方法直接返回 192.168.1.1
的DNS响应报文,反之则通过 proxyUdp
对象转发DNS查询。
nioDatagramChannel.pipeline().addLast(new DatagramDnsQueryDecoder());