DNS域名解析流程和java的简单模拟

DNS解析流程

当我们访问http://www.itkaoti.top的时候, 浏览器是如何通过www.itkaoti.top 转为ip 来找到我们的服务器资源的?

将域名 转为 ip 就是DNS域名解析。其大概流程如下:

  1. 先去找hosts文件, 如果存在就直接返回。

我们通过修改hosts 使得我们访问 baidu.itkaoti.top 去访问baidu,

在hosts文件中添加 180.101.49.11 baidu.itkaoti.top, 浏览器访问,百度限制了, 但是我们可以看到域名被重定向了

image-20210929120303065image-20210929133558992

  1. 如果hosts中没有设置,就向本地DNS服务器(local DNS server),如果存在就直接返回了

上面 nslookup www.baidu.com, 我上面的119.29.29.29 就是本地dns服务器, 我本地使用的是 DNSPod Public DNS 提供的公共DNS域名解析。

image-20210929134508029
  1. 如果本地域名也没有就会请求根服务器(13台) , 然后通过轮询或者迭代查询, 返回对应的ip

DNS服务器一般分三种,根DNS服务器,顶级DNS服务器,权威DNS服务器。

总结来说就是3点:

  1. hosts文件
  2. 本地DNS服务器
  3. 域名服务器

DNS查询的方式

上面的第3步, 我们写的比较简单, 会分为2种查询方式

如果有缓存,直接返回不讨论, 因为上面的几个步骤都会有缓存

递归查询

image-20210929144558335

迭代查询

image-20210929143057884

java代码模拟实现

这边不进行特别深入的讲解, 尤其是dns请求响应协议, 我们只用java简单的发送一个udp请求给域名服务器

dnsjava简化我们udp发送字节内容的构建

<dependency>
    <groupId>dnsjava</groupId>
    <artifactId>dnsjava</artifactId>
    <version>2.1.8</version>
</dependency>
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.7.11</version>
</dependency>

代码

import cn.hutool.core.io.FileUtil;
import org.xbill.DNS.*;

import java.io.File;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;

public class DNSClient {

    public static void main(String[] args) throws Exception {
        DatagramSocket datagramSocket = new DatagramSocket(65532);
        send(datagramSocket, "www.itkaoti.top");
        receive(datagramSocket);
   }

    public static void send(DatagramSocket datagramSocket, String domain) throws IOException {
        Record queryRecord = Record.newRecord(Name.fromString(domain + "."), Type.A, DClass.IN);
        Message queryMessage = Message.newQuery(queryRecord);
        // 将二进制写入到文本中
        FileUtil.writeBytes(queryMessage.toWire(), new File("C:\\Users\\xiaodoubi\\Desktop\\html\\1.hex"));

        byte[] buffer = queryMessage.toWire();
        DatagramPacket datagramPacket = new DatagramPacket(buffer, 0, buffer.length, new InetSocketAddress("119.29.29.29", 53));
        // 发送
        datagramSocket.send(datagramPacket);
    }

    public static void receive(DatagramSocket datagramSocket) throws Exception {
        byte[] bytes = new byte[1024];
        DatagramPacket datagramPacket1 = new DatagramPacket(bytes, bytes.length);
        datagramSocket.receive(datagramPacket1);
        int length = datagramPacket1.getLength();
//        System.out.println(Arrays.toString(bytes));
        FileUtil.writeBytes(bytes, new File("C:\\Users\\xiaodoubi\\Desktop\\html\\1_resp.hex"), 0, length, false);

    }
}

运行代码的时候, 我们要打开wireshark软件, 用来查看 我们向域名服务器发送的报文和接受到的报文。

  • 发送的报文(文本, 使用hex打开)
image-20210929181609980
  • wireshark抓到的发送的报文
image-20210929181635816
  • 接受到报文(文本, 使用hex打开)
image-20210929181849916
  • wireshark接收到的报文
image-20210929181823617

将协议点开查看具体的内容

image-20210929182103426

当然我们可以向根域名解析服务器请求(然后一级一级的请求下去), 大家可以试试

参考

1. 如何设置本地dns域名解析

2. DNS解析的过程是什么,求详细的?--知乎

3. 本地dns服务器到底是什么?有没有精确的概念?--知乎

4. 理解 DNS 的三种查询方式 --掘金

5. 根域名服务器