DNS 报文详解

本文主要讲解 DNS 的报文结构

DNS(域名系统)负责将我们熟知的域名翻译成 IP 地址,其相关定义由 RFC 1034 和 RFC 1035 给出。

为了实现更好的扩展性,DNS 采用分层次的服务器组织结构,主要分为三层:

  1. 根 DNS 服务器
  2. 顶级域名(TLD)DNS 服务器
  3. 权威 DNS 服务器

这些服务器的层次结构如下图所示:

DNS 的层次结构

关于 DNS 的工作过程及相关信息,请参见深入浅出 DNS 解析,本文主要介绍 DNS 报文结构。

DNS 的报文结构

DNS 报文包含以下几个部分:

  1. 首部(前 12 个字节):

    • 标识符(2 字节):用于标识请求及其响应报文
    • 标志(2 字节):
      • QR(1 bit):查询/响应标志,0 表示查询,1 表示响应
      • opcode(4 bit):操作码
        • 0:标准查询
        • 1:反向查询
        • 2:服务器状态请求
        • [3,15]:保留值
      • AA(1 bit):授权回答标志,表示响应服务器是否为域名的权威服务器
      • TC(1 bit):截断标志,表示报文是否因超过长度而被截断
      • RD(1 bit):期望递归标志,由请求方设置,响应方返回相同值
      • RA(1 bit):支持递归标志,由响应方设置,表示是否支持递归查询
      • rcode(4 bit):返回码
        • 0:无错误
        • 1:格式错误
        • 2:服务器失败
        • 3:域名不存在
        • 4:查询类型不支持
        • 5:查询被拒绝
        • [6,15]:保留值
  2. 数量字段(8 字节):

    • Questions:查询问题数量
    • Answers:回答数量
    • Authoritative nameservers:授权域名服务器数量
    • Additional records:附加信息数量
  • 问题(Questions)部分包括:

    • 查询的域名 8bit 为单位,长度不受限
    • 查询的协议类型 16bit
    • 查询的类 16bit
  • 回答(Answers)/权威(Authoritys)/附加(Additionals)部分格式相同:

    • NAME 资源记录包含的域名.
    • TYPE 表示 DNS 协议的类型.
    • CLASS 表示 RDATA 的类.
    • TTL 4 字节无符号整数表示资源记录可以缓存的时间。0 代表只能被传输,但是不能被缓存。
    • RDLENGTH 2 个字节无符号整数表示 RDATA 的长度
    • RDATA 不定长字符串来表示记录,格式根 TYPE 和 CLASS 有关。比如,TYPE 是 A,CLASS 是 IN,那么 RDATA 就是一个 4 个字节的 ARPA 网络地址。

每个 DNS 响应报文包含一条或者多条资源记录(resource records ,RRs),资源记录包含下列字段的 4 元组:

text

(Name,Value,Type,TTL)

其中 TTL 表示生存时间,决定了资源记录应该从缓存中删除的时间。

  • 如果是 Type=A,Name 为主机名,Value 是对应的 IP 地址(如 bar.foo.com,xxx.xxx.xxx.xxx,A)
  • 如果是 Type=NS,Name 为一个域(如 foo.com),Value 是知道如何获取该域中的主机 IP 地址的权威 DNS 服务器的主机名(如 foo.com,dns.foo.com,NS)
  • 如果是 Type=CNAME,Value 是别名为 Name 的主机对应的规范主机名。(如 foo.com,relay1.bar.foo.com,CNAME)
  • 如果是 Type=MX,Value 是别名为 Name 的邮件服务器对应的规范主机名。(如 foo.com,mail.bar.foo.com,MX)

这里我们使用 WireShark 抓包实际看看,启动 WireShark 时可以设置捕获过滤器为:

text

udp port 53

这样我们只抓取通过 UDP 53 端口的 DNS 请求,此外如果需要仅仅显示特定的 DNS 查询,还可以进一步应用显示过滤器,例如这里我们仅查看www.techkoala.top的查询记录,则显示过滤器设置为:

text

dns.qry.name==www.techkoala.top
16 进制表示
WireShark 中 DNS 请求报文及其结构

可以看出

  • 标识为:0x0000cd13
  • 这是一个请求报文,仅在 Questions 部分有值
标志部分各个字段的值:
查询部分各个字段的值

同时 WireShark 还贴心的告诉我们,响应报文的在总抓取包的编号为 10,方便我们快速找到请求报文对应的响应报文。

16 进制表示
WireShark 中 DNS 响应报文及其结构
标志部分各个字段的值
查询部分各个字段的值