一般来说,自己编写DNS是没有必要的,目前开源的dns服务软件很多,功能也很强大。但是,有时候又是很有必要的,有着诸多好处。比如说,用于企业内网,简化DNS配置,可以根据企业需求添加新的功能,非常灵活。本文试着用java实现一个最简单的DNS服务。

DNS是基于udp协议的,默认端口为53。

在自己电脑上实现dns服务(作为dns服务器),首先需要程序监听udp 53端口。在java中,和udp相关的类为DatagramSocket以及DatagramPacket。具体信息可以查看API,或者参考http://www.cnblogs.com/hq-antoine/archive/2012/02/11/2346908.html。

之后,需要另一台电脑作为客户端,设置dns地址为服务器的ip地址。

public class UDPServer {private static DatagramSocket socket;
    public UDPServer() {//设置socket,监听端口53try {this.socket = new DatagramSocket(53);} catch (SocketException e) {e.printStackTrace();}}public void start() {System.out.println("Starting。。。。。。\n");while (true) {try {byte[] buffer = new byte[1024];DatagramPacket request = new DatagramPacket(buffer, buffer.length);socket.receive(request);//输出客户端的dns请求数据InetAddress sourceIpAddr = request.getAddress();int sourcePort = request.getPort();System.out.println("\nsourceIpAddr = " + sourceIpAddr.toString() + "\nsourcePort = " + sourcePort);System.out.println("data = " + new String(request.getData(), 0 , request.getLength()));} catch (SocketException e) {System.out.println("SocketException:");e.printStackTrace();} catch (IOException e) {System.out.println("IOException:");e.printStackTrace();}}}
}

运行程序报错,错误提示如下:

根据异常提示,打开53端口异常,需要确认操作权限。1024以下端口默认系统保留,只有root用户才能使用。使用root账号运行程序,之后在客户端上使用nslookup www.baidu.com命令测试,在服务器上输出信息如下:

得到客户端的ip地址为10.211.55.253,端口为43065,但是下面这个data是什么玩意?

分析:出现乱码,原因在于request.getData()获取到的数据并非为String类型,因此不能简单粗暴的通过new String(request.getData(), 0, request.getLength())强制为String类型输出。猜测应该是符合dns数据格式的字节流,下面通过抓包软件wireshark进行分析。

发现dns数据包中,包括ID, Flags, Questions, Answer RRs, Authority RRs, Additional RRs以及Queries等字段。如果自己编写程序,通过分析字节流来提取出所需要的信息,是一个比较麻烦的事情。幸好有dnsjava这个开源项目,里面的Message类已经帮我们把这些事情都处理好了。

更改代码如下,

 1 package com.everSeeker;
 2
 3 import org.xbill.DNS.Message;
 4
 5 import java.io.IOException;
 6 import java.net.DatagramPacket;
 7 import java.net.DatagramSocket;
 8 import java.net.InetAddress;
 9 import java.net.SocketException;
10
11 public class UDPServer {
12     private static DatagramSocket socket;
13
14     public UDPServer() {
15         //设置socket,监听端口53
16         try {
17             this.socket = new DatagramSocket(53);
18         } catch (SocketException e) {
19             e.printStackTrace();
20         }
21     }
22
23     public void start() {
24         System.out.println("Starting。。。。。。\n");
25         while (true) {
26             try {
27                 byte[] buffer = new byte[1024];
28                 DatagramPacket request = new DatagramPacket(buffer, buffer.length);
29                 socket.receive(request);
30                 //输出客户端的dns请求数据
31                 InetAddress sourceIpAddr = request.getAddress();
32                 int sourcePort = request.getPort();
33                 System.out.println("\nsourceIpAddr = " + sourceIpAddr.toString() + "\nsourcePort = " + sourcePort);
34 //                System.out.println("data = " + new String(request.getData(), 0 , request.getLength()));
35
36                 Message indata = new Message(request.getData());
37                 System.out.println("indata = " + indata.toString());
38             } catch (SocketException e) {
39                 System.out.println("SocketException:");
40                 e.printStackTrace();
41             } catch (IOException e) {
42                 System.out.println("IOException:");
43                 e.printStackTrace();
44             }
45         }
46     }
47 }

重新测试,输出信息为:

发现,输出信息与我们通过抓包得到的信息一致。其中,Questions记录了需要解析的域名www.baidu.com,type为A。而Answers为空,是因为这是一个域名解析请求信息,下面我们只需要把解析的结果放入Answers这个字段,并返回给客户端,即完成了最简单的dns功能。

分析dnsjava的源码,发现Message类中有一个变量private List [] sections, 长度为4,记录了Questions, Answers, Authority RRs, Additional RRs这4个字段。更改代码如下,

 1 package com.everSeeker;
 2
 3 import org.xbill.DNS.*;
 4
 5 import java.io.IOException;
 6 import java.net.DatagramPacket;
 7 import java.net.DatagramSocket;
 8 import java.net.InetAddress;
 9 import java.net.SocketException;
10
11 public class UDPServer {
12     private static DatagramSocket socket;
13
14     public UDPServer() {
15         //设置socket,监听端口53
16         try {
17             this.socket = new DatagramSocket(53);
18         } catch (SocketException e) {
19             e.printStackTrace();
20         }
21     }
22
23     public void start() {
24         System.out.println("Starting。。。。。。\n");
25         while (true) {
26             try {
27                 byte[] buffer = new byte[1024];
28                 DatagramPacket request = new DatagramPacket(buffer, buffer.length);
29                 socket.receive(request);
30                 //输出客户端的dns请求数据
31                 InetAddress sourceIpAddr = request.getAddress();
32                 int sourcePort = request.getPort();
33                 System.out.println("\nsourceIpAddr = " + sourceIpAddr.toString() + "\nsourcePort = " + sourcePort);
34                 //分析dns数据包格式
35                 Message indata = new Message(request.getData());
36                 System.out.println("\nindata = " + indata.toString());
37                 Record question = indata.getQuestion();
38                 System.out.println("question = " + question);
39                 String domain = indata.getQuestion().getName().toString();
40                 System.out.println("domain = " + domain);
41                 //解析域名
42                 InetAddress answerIpAddr = Address.getByName(domain);
43                 Message outdata = (Message)indata.clone();
44                 //由于接收到的请求为A类型,因此应答也为ARecord。查看Record类的继承,发现还有AAAARecord(ipv6),CNAMERecord等
45                 Record answer = new ARecord(question.getName(), question.getDClass(), 64, answerIpAddr);
46                 outdata.addRecord(answer, Section.ANSWER);
47                 //发送消息给客户端
48                 byte[] buf = outdata.toWire();
49                 DatagramPacket response = new DatagramPacket(buf, buf.length, sourceIpAddr, sourcePort);
50                 socket.send(response);
51             } catch (SocketException e) {
52                 System.out.println("SocketException:");
53                 e.printStackTrace();
54             } catch (IOException e) {
55                 System.out.println("IOException:");
56                 e.printStackTrace();
57             }
58         }
59     }
60 }

继续测试,客户端nslookup www.baidu.com,输出结果为:

测试成功,这样一个最简单的dns就完成了。

转载于:https://www.cnblogs.com/everSeeker/p/5297871.html

试着用java实现DNS(一)——DatagramSocket, DatagramPacket, Message相关推荐

  1. 【LeetCode-面试算法经典-Java实现】【015-3 Sum(三个数的和)】

    [015-3 Sum(三个数的和)] [LeetCode-面试算法经典-Java实现][全部题目文件夹索引] 原题 Given an array S of n integers, are there ...

  2. java socket datagramsocket_用DatagramSocket写的个渣渣聊天后台,求拍砖

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 UDPTool的实现类UDPToolImpl: package service; import java.net.DatagramPacket; impo ...

  3. 【LeetCode-面试算法经典-Java实现】【109-Convert Sorted List to Binary Search Tree(排序链表转换成二叉排序树)】...

    [109-Convert Sorted List to Binary Search Tree(排序链表转换成二叉排序树)] [LeetCode-面试算法经典-Java实现][全部题目文件夹索引] 原题 ...

  4. 【LeetCode-面试算法经典-Java实现】【054-Spiral Matrix(螺旋矩阵)】

    [054-Spiral Matrix(螺旋矩阵)] [LeetCode-面试算法经典-Java实现][全部题目文件夹索引] 原题 Given a matrix of m x n elements (m ...

  5. 【LeetCode-面试算法经典-Java实现】【129-Sum Root to Leaf Numbers(全部根到叶子结点组组成的数字相加)】...

    [129-Sum Root to Leaf Numbers(全部根到叶子结点组组成的数字相加)] [LeetCode-面试算法经典-Java实现][全部题目文件夹索引] 原题 Given a bina ...

  6. 【LeetCode-面试算法经典-Java实现】【226-Invert Binary Tree(反转二叉树)】

    [226-Invert Binary Tree(反转二叉树)] [LeetCode-面试算法经典-Java实现][所有题目目录索引] 代码下载[https://github.com/Wang-Jun- ...

  7. java 设置dns_通过代码直接设置Java的DNS - Java Dns Cache Manipulator

    通过代码直接设置Java的DNS - Java Dns Cache Manipulator 通过代码直接设置Java的DNS(实际上设置的是DNS Cache),支持JDK 6+. 通过代码直接设置J ...

  8. 【LeetCode-面试算法经典-Java实现】【198-House Robber(抢劫犯)】

    [198-House Robber(抢劫犯)] [LeetCode-面试算法经典-Java实现][所有题目目录索引] 代码下载[https://github.com/Wang-Jun-Chao] 原题 ...

  9. java 实现dns劫持_JavaScript 防 http 劫持与 XSS

    原标题:JavaScript 防 http 劫持与 XSS 原文作者: 作为前端,一直以来都知道HTTP劫持与XSS跨站脚本(Cross-site ing).CSRF跨站请求伪造(Cross-site ...

  10. 升职加薪必看!如何试出一个Java开发者真正的水平

    01 Mysql 1. 数据库三范式及判断.E-R图 2. innodb和myisam存储引擎的区别 3. 索引分类(主键.唯一索引.全文索引.覆盖索引等等),最左前缀原则,哪些条件无法使用索引 4. ...

最新文章

  1. 学习笔记:文本过滤_____unix 下的通配符
  2. Flutter 2 源码阅读
  3. Python3 pickle模块的使用详解
  4. 理解流量监管和整形的关键算法—令牌桶
  5. 城市大轰炸++(洛谷P1847题题解,Java语言描述)
  6. ajax跨域获取数据后处理,简单实现ajax获取跨域数据
  7. 11.05T3 map
  8. SSM使用拦截器功能
  9. pta c语言期末上机考试题库,PTA 程序设计 判断题-期末复习
  10. 【论文阅读笔记】Securing software by enforcing data-flow integrity
  11. CSS设置div上下居中
  12. 奥克兰大学计算机专业好找工作吗,留学选择奥克兰大学的计算机专业肯定不会后悔...
  13. arcgis js for JavaScript 4.X 移动轨迹动画
  14. mini QQ(项目一)
  15. python之路--生成器
  16. 若依RuoYi-Vue前后端项目启动流程
  17. Word 排版:插入题注和引用题注
  18. 智能家居云服务器设计规格
  19. EasyExcel使用教程
  20. linux解压多个part rar,【linux】安装rar,并解压被压缩成多个rar的文件

热门文章

  1. 关于Microsoft Office 2007 Beta 简体中文版的一些消息
  2. mysql 创建用户并赋予用户权限
  3. Javascript如何深拷贝对象
  4. 用计算机怎么录音,如何用电脑进行录音
  5. php怎么处理一对多,php - 如何处理内部连接以及与OOP的一对多关系? - SO中文参考 - www.soinside.com...
  6. 在VMware安装Ubuntu 16.04
  7. feign调用接口返回html,Spring Cloud Feign接口返回流
  8. d3.js中点可以用图片吗_拿什么拯救你,长英文命名——用中文(也许标点也可以)试试...
  9. 计算机应用的核心能力,应用能力为核心的高职计算机应用分析
  10. java预科_java复习预科知识-Markdown学习