前言
DNS协议作为着互联网客户端-服务器通信模式得第一关,在当下每天都有成千上亿上网记录产生得当今社会,其重要性自然不可言喻。在国内比较有名得DNS服务器有电信得114.114.114.114、阿里云得223.5.5.5,DNSPod得119.29.29.29,配置一个好的DNS服务器可以缩短请求响应时间、降低DNS劫持概率,提升上网体验。
上面这些都是互联网公用DNS服务器,本文博主教大家使用 Java Netty
自建DNS代理服务器,目前网上对于使用Netty自建DNS服务器得教程良莠不齐,大多没有代理步骤,达不到博主想要得代理效果,因而创建此文。觉得本文有帮助得可以关注博主github
一、自建DNS代理服务器有哪些优势
- 域名控制:对于特定域名可以自由控制访问权限(屏蔽对特定网站访问)
- 域名记录:记录局域网内各个主机得域名访问(记录员工上网记录)
- 配置内网域名:通过自建DNS服务器可以配置内网域名,节约成本
- DNS负载均衡:通过自建DNS服务器可以轻松实现对于访问域名得负载均衡配置
- ...
二、自建DNS代理服务器代码
- 添加域名黑名单文件,
resources
文件夹下添加black_list.txt
文件
google.com.
facebook.com.
初始化 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);
}
}
- 使用
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());
}
});
}
}
- 给
nioDatagramChannel.pipeline()
添加ChannelHandler
nioDatagramChannel.pipeline().addLast(new DatagramDnsQueryDecoder());
nioDatagramChannel.pipeline().addLast(new SimpleChannelInboundHandler<DatagramDnsQuery>() {
@Override
protected void channelRead0(ChannelHandlerContext ctx, DatagramDnsQuery msg) {
try {
DefaultDnsQuestion dnsQuestion = msg.recordAt(DnsSection.QUESTION);
String name = dnsQuestion.name();
log.info(name + ++num[0]);
Channel channel = ctx.channel();
int id = msg.id();
channel.attr(AttributeKey.<DatagramDnsQuery>valueOf(String.valueOf(id))).set(msg);
if (BLACK_LIST_DOMAIN.contains(name)) {
DnsQuestion question = msg.recordAt(DnsSection.QUESTION);
DatagramDnsResponse dnsResponse = getDatagramDnsResponse(msg, id, question);
channel.writeAndFlush(dnsResponse);
return;
}
proxyUdp.send(name, msg.id(), channel);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
private DatagramDnsResponse getDatagramDnsResponse(DatagramDnsQuery msg, int id, DnsQuestion question) {
DatagramDnsResponse dnsResponse = new DatagramDnsResponse(msg.recipient(), msg.sender(), id);
dnsResponse.addRecord(DnsSection.QUESTION, question);
D