跳转至

DNS Transport Protocol

约 1775 个字 预计阅读时间 6 分钟

报文格式

DNS 报文分为请求应答两种,这两种报文结构类似,大致有五部分:

  • 报文头/Header:长 12 字节,描述报文类型,以及下面四个小节的情况;
  • 问题节/Question:保存查询信息,长度不定;
  • 答案节/Answer:保存查询结果,长度不定;
  • 授权信息节/Authority:保存授权信息,长度不定;
  • 附加信息节/Additional:保存附加信息,长度不定。

报文头的长度是固定的,一共是 12 个字节,包含了报文的基本信息:

  • 标识/Identifier:一个 16 位 2 字节的 ID,在应答之中原样返回,以此匹配请求与应答;
  • 标志/Flags:一些标志位,一共 16 位 2 字节,从低到高依次为:
    • QR:占 1 位,标识报文类型,0 为请求,1 为应答,DNS 回复时将其置为 1;
    • Opcode:占 4 位,定义查询和应答的类型。0 表示标准查询,1 表示反向查询(由 IP 地址获得主机域名),2 表示请求服务器状态;
    • AA/授权应答/Authoritative Answer:占 1 位,这个比特位在应答的时候才有意义,指出给出应答的服务器是否为查询域名的授权解析服务器,0 表示结果不是由域名的权威服务器返回的,因为查询对象是本地的 DNS 缓存服务器,1 表示结果是由域名的权威服务器返回的;
    • TC/截断/TrunCation:占 1 位,用来指出是否报文比允许的长度还要长,进而导致被截断;
    • RD/期望递归/Recursion Desired:占 1 位,在请求的时候设置,并且要在应答时返回,如果该位为 1,服务器就必须处理这个请求,如果服务器没有授权回答,它必须替客户端请求其他 DNS 服务器,这也是所谓的 递归查询;该位为 0 时,如果服务器没有授权回答,它就返回一个能够处理该查询的服务器列表给客户端,由客户端自己进行 迭代查询
    • RA/支持递归/Recursion Available:占 1 位,如果服务器支持递归查询,就会在应答中设置该位,以告知客户端;
    • Z/ZERO:占 3 位,未用保留值,值都为 0;
    • RCODE:占 4 位,返回码,表示请求结果,通常为 0/没有差错 和 3/名字差错,当出现差错的时候,这个差错由权威服务器返回,表示待查询的域名不存在;
    • QDCOUNT:占 16 位 2 字节,表示一个 16 位无符号整数,指明报文请求段中的问题记录数,就是要查询的域名,一般为 1;
    • ANCOUNT:占 16 位 2 字节,表示一个 16 位无符号整数,指明报文回答段中的回答记录数,查询报文中该字段为 0;
    • NSCOUNT:占 16 位 2 字节,表示一个 16 位无符号整数,指明报文授权段中的授权记录数,查询报文中该字段为 0;
    • ARCOUNT:占 16 位 2 字节,表示一个 16 位无符号整数,指明报文附加段中的附加记录数,查询报文中该字段为 0。

问题节/Question

查询包只有报文头和问题节两部分,报文头的 QDCOUNT 指明报文含有多少个查询结构,每个查询结构都由下面的字段组成:

  • QNAME:查询的域名,长度不固定,ascii 编码,域名内以 . 切分,具体分隔方法(DNS 标准名称表示法)如下:
    • 比如查询 example.com.,由于长度不固定,加之以 . 进行了切分,切分下来的部分都叫做 label,所以我们在每个 label 前面都加上一个前导字节,表示该 label 的字节长度,所以例子的域名就变成 \x07example\x03com\x00,这里最后的空字符串代表根域。
    • 但是域名表示还有相应的域名压缩方法,对于这种不需要域名压缩的一般的域名,前导字节的高两位必须需要为 0,后边表示该 label 的长度,由此可见,每一级域名的长度理论上最多可以达到 63/0x3f 个字符。
  • QTYPE:查询的类型,长 16 位 2 字节,常见的有:
    • A 记录类型,值为 1,查询主机 IP 地址;
    • NS 记录类型,值为 2,查询权威名称服务器;
    • CNAME 记录类型,值为 5,查询 CNAME 别名;
    • MX 记录类型,值为 15/0x0f,邮件交换;
    • TXT 记录类型,值为 16/0x10,文本记录;
  • QCLASS:查询的类,长 16 位 2 字节,常见的有:
    • IN 类,值为 1,Internet 类;
    • CH 类,值为 3,Chaos 类(这是啥);
    • HS 类,值为 4,Hesiod 类(这是啥);
    • * 类,值为 255/0xff,所有类。

资源记录格式/Resource Record/RR

答案节、授权信息节与附加信息节这些字段都应该使用资源记录/RR 格式,分为 6 个部分:

  • NAME:回复查询的域名,长度不固定,跟查询域名对应,这里为了减少包长度,一般进行域名压缩,下面会讲;
  • TYPE:占 16 位 2 字节,跟上面的 QTYPE 意思基本一样。
  • CLASS:占 16 位 2 字节,跟上面的 QCLASS 基本一样。
  • TTL:占 32 位,表示一个 32 位无符号整数,标识资源记录可以缓存的时间,单位是秒。0 代表只能被传输,但是不能被缓存。
  • RDLENGTH:占 16 位 2 字节,表示一个 16 位无符号整数,标识 RDATA 的长度。
  • RDATA:不定长字符串来表示记录,格式根 TYPE 和 CLASS 有关。比如:TYPE 是 A,CLASS 是 IN,那么 RDATA 就是一个 4 个字节的 ARPA 网络地址。

但是问题节与答案节等的 NAME 部分会有很大的重叠,所以我们会进行域名压缩,这样可以减少包的长度,节约报文空间:当域名在报文之中第二次出现的时候,只用两个字节来保存。第一个字节的最高两位都是 1,余下部分和第二个字节组合在一起,表示域名第一次出现时在报文内的偏移量/Offset,可以叫它压缩指针,通过这个压缩指针就可以找到对应的域名,真实的域名在整个数据包的 Offset 处。由于一般的报文头的域名位置还是很靠前的,所以我们一般会见到 0x0Cxx 这样的压缩指针。

一个域名 NAME 里边最多只能有一个压缩指针,换句话说,一个域名只有可能有下面三种形式:

  • 一堆 label 后边跟着一个 0x00 表示的根域(也表示了结束);
  • 一个压缩指针;
  • 一堆 label 序列,后边跟一个压缩指针。

有时候我们甚至还要在一个字段里边表示一个电子邮件,位了方便复用,我们将电子邮件里边的 @ 替换成一个 .,这样编码就都一样了。

dnscat Protocol