标签归档:DNS

DNS:域名系统(二)

文章《DNS:域名系统(一)》从 DNS 系统的结构和工作原理的角度描绘了 DNS 系统的基本概貌,本文将继续深入探讨 DNS 的话题,以 DNS 客户端的视角窥探 DNS 协议的细节。

在 Internet 中,DNS 客户端与服务器之间使用 TCP 或 UDP 协议传送 DNS 消息,服务器端开放的端口号均为 53。在大多数情况下,客户端通过 UDP 协议向服务端发送 DNS 查询请求,服务端也通过 UDP 协议向客户端发送响应。在客户端或服务端认为一次查询或响应需要运载的数据量超过 512 字节时,需要改用 TCP 协议来传送请求或响应,后文将对这种情形有所解释。

DNS 请求与响应的消息均由消息头部与消息实体两部分组成,请求与响应的消息头部的格式是一致的,下面是 DNS 消息头部结构的图示[1]

                                    1  1  1  1  1  1
      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                      ID                       |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    QDCOUNT                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    ANCOUNT                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    NSCOUNT                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    ARCOUNT                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
  • ID 是一个 16 位的标识符,由客户端根据具体情况设置,当服务端回应这个请求时,会将同样的 ID 值填写在响应消息头部的 ID 字段中。
  • QR 是一个占 1 位的字段用于标记这个 DNS 消息是请求(Query)还是响应 (Response)。当 DNS 消息是一个请求消息时,这个字段被设置为 0,反之当 DNS 消息是一个响应消息时,这个字段被设置为 1。
  • Opcode 是一个 4 位的操作代码,它由客户端设置,服务器在响应中会填入与对应请求相同的 Opcode。目前,Opcode 有三种有效的取值,它们的值与含义如下:
    • 0 – 标准请求。
    • 1 – 反向解析请求,用于查询 PTR 记录。
    • 2 – 服务器状态查询。
  • 接下来是一个包含 4 位的 flag,从低到高依次是 AA、TC、RD 和 RA,每个 flag 的含义分别如下:
    • AA(Authoritative Answer)标记只在响应消息中有效,置位表示该响应来自请求域名对应的域中的授权服务器。
    • TC(Truncation)标记表示该消息因传输长度的限制而被截断。通常发生在客户端以 UDP 协议发起解析请求,而服务端的全部响应超过 512 字节时,服务端将响应在 512 字节处截断,并置位 TC,提示客户端收到的响应可能是不完整的。
    • RD(Recursion Desired)递归解析请求,要求 DNS 服务端以递归解析的方式处理请求。由客户端在请求中设置,服务端在响应中置位与对应的请求一致。值得注意的是,DNS 协议不强制要求所有 DNS 服务器提供递归解析的功能,因此即使客户端在请求中置位 RD,也有可能不会得到期望的响应,可以用下面提到的 RA 标记判断服务器是否支持递归解析。
    • RA(Recursion Available)递归可用。这个标记由服务端设置,在响应中有效。当 RA 置位时,表示该服务器支持递归解析,向它发起的递归解析请求都能得到期望的结果。
  • Z 所在的位置是一段 4 位的保留区域,必须填为 0。
  • RCODE(Response code)是由服务端在响应中设置的 4 位响应代码,不同的值含义分别如下:
    • 0 – 没有错误。
    • 1 – 格式错误。服务器无法解释请求。
    • 2 – 服务器失败。因为服务器的某些故障而不能完成解析请求。
    • 3 – 名字错误。这个错误代码只会出现在授权的域名服务器的响应中,含义为请求查询的域名不存在。
    • 4 – 未实现。当前服务器不支持这种查询。
    • 5 – 拒绝。服务器主动拒绝当前的查询请求。
    • 6 ~ 15 – 为其他失败保留的代码。
  • QDCOUNT 是一个 16 位的整数,表示消息实体中问题的数量。
  • ANCOUNT 是一个 16 位的整数,表示消息实体中答案的数量。
  • NSCOUNT 是一个 16 位的整数,表示消息实体中 NameServer 记录的数量。
  • ARCOUNT 是一个 16 位的整数,表示消息实体中授权记录的数量。

值得注意的一点是,当客户端程序通过 UDP 协议发起一次 DNS 解析请求,后又收到一个 TC 置位的服务器响应时,客户端程序一般要用 TCP 协议与服务器建立连接,重新发起一次解析请求以获得完整的解析结果。

消息实体紧随消息头部,消息实体由问题部分和资源记录部分组成,DNS 请求的消息实体只包含问题部分,DNS 响应的消息实体既有问题部分又有资源记录部分。其中资源记录又分三大块:DNS 回答资源记录、授权服务器资源记录和附加的资源记录。一个包含消息头部和完整消息实体的 DNS 消息如下所示。

    +---------------------+
    |        Header       |
    +---------------------+
    |       Question      | the question for the name server
    +---------------------+
    |        Answer       | RRs answering the question
    +---------------------+
    |      Authority      | RRs pointing toward an authority
    +---------------------+
    |      Additional     | RRs holding additional information
    +---------------------+

Question 是 DNS 解析请求的问题部分,在 DNS 响应消息中也包含这个部分,内容与对应的请求消息中的 Question 一致。Question 中有请求解析的域名、请求的资源类型和网络类型。Question 的结构如下所示。

                                    1  1  1  1  1  1
      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                                               |
    /                     QNAME                     /
    /                                               /
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                     QTYPE                     |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                     QCLASS                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

QNAME 是被查询的域的域名,它的长度是不定长的。在《DNS:域名系统(一)》中提到,域名系统树中任意一个结点域名是由该结点到根结点的路径上经过的所有结点的标签一起组成的,书写上用圆点分隔这些标签(label),如 work.luodichen.com 等。域名在 QNAME 中的格式与书写格式稍有不同,同样是从枝叶到树根的标签依次排列,但每个标签之前还有一个字节表示该标签的长度。注意,树根的 NULL 结点也要包含在其中,树根总是以一个值为 0 的字节表示,同时也是 QNAME 字段结束的标识。例如域名 work.luodichen.com 在 QNAME 中存储形式的 C 语言表示如下:

特别地,当解析的目标域名是根域名时,QNAME 的格式同样遵循上面所说的规则,向 QNAME 中填入根域名的长度 0,其后不再有任何字符。

QTYPE 是一个 2 字节的查询类型字段,表示希望解析域名的资源记录的类型,DNS 协议规定的资源记录类型较多,有些是已经废弃不用的,有些是为了适应互联网的发展新加的。下面的代码列出了几种常见的资源记录类型的缩写、数值和说明。

QCLASS 字段表示网络类型,一般设置为 1(Internet)。

DNS 响应消息中包含 Answer、Authority 和 Additional 等三组资源记录,每一组资源记录都有 0 个或多个资源记录,上文已经提到,每组资源记录的数目在协议头部对应的字段中。每个资源记录的格式都是一致的,它们的结构如下所示:

                                    1  1  1  1  1  1
      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                                               |
    /                                               /
    /                      NAME                     /
    |                                               |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                      TYPE                     |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                     CLASS                     |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                      TTL                      |
    |                                               |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                   RDLENGTH                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
    /                     RDATA                     /
    /                                               /
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

NAME 是变长字段,表示该资源记录对应的域名,它的格式可能是压缩形式的,后文中将讨论 DNS 消息中压缩名字的格式。

TYPE 与 CLASS 与上文所说的 QTYPE 和 QCLASS 类似,表示这个资源记录的类型与网络类型。

TTL 是一个 32 位的整数,表示这个资源记录的生存时间(秒),TTL 是一个资源记录能在 DNS 缓存中生存的最长时间。

RDLENGTH 是一个 16 位的整数,它表示 RDATA 字段的长度(字节)。

RDATA 是一个变长的字段,根据资源记录类型(TYPE)的不同而有不同的格式,例如在 A(IP 地址)记录中,RDATA 是一个 32 位的 IP 地址。更多资源记录类型对应的 RDATA 格式请参考文本参考文献中提到的 RFC 1035 文档和其他相关的 RFC 文档。

为了节省 DNS 查询和响应的网络流量,DNS 协议使用一种压缩的策略来消除 DNS 消息中域名相关字段可能产生的重复内容,从而减小 DNS 消息的长度,这对于一种主要使用 UDP 来传送消息的协议来说是很重要的。前文中提到,一个域名可以表示为多个标签连续排列,并在每个标签前用一个字节表示标签长度的方式来表示,为了下文描述方便,将这个字节称为该标签的“标签头”。此外,DNS 协议还允许使用一种指针的方式引用消息实体中当前标签所在的位置之前任意位置出现过的域名或域名的一部分。DNS 规定,当一个“标签头”的高 2 位都为 1 时,这个标签头余下的 6 位以及标签头后一个字节的 8 位共 14 位构成一个指针。指针的整数数值是一个偏移量,偏移的起始位置是消息实体的第一个字节,指针指向的是另一个标签头,因为指针的目标可能是一个普通的标签头也可能是另一个带指针的标签头,因此需要递归地处理新的标签头,直到遇到根结点标签。下面是一个在 Answer 资源记录中 NAME 字段以指针表示的实例,可以看到 Answer 资源记录的 Name 字段是 0xc00c,指针偏移值是 12,指向 Queries 中 Name 字段的一个域名 luodichen.com。

dns-compress图 1 解析域名 luodichen.com 的响应消息

在编写文本的同时,还用 C++ 开发了一个同时支持 Windows 和 Linux 平台的 DNS 客户端库,源码托管在 GitHub,查看

(允许转载本文,转载请注明出处 http://luodichen.com/blog/?p=367 )

参考文献
[1] P. Mockapetris, DOMAIN NAMES – IMPLEMENTATION AND SPECIFICATION, RFC 1035, November 1987; www.ietf.org/rfc/rfc1035.txt

DNS:域名系统(一)

两年前写了《DNS协议及DNS客户端的实现》,最近一段时间计划重新写一个 DNS 客户端库,翻看当初写的文章,觉得太浅、太粗,甚至有错漏。于是决定在写 DNS 库的同时,写一篇博客,算是对以前文章的补充和修正。

将域名系统理解成一个“将 IP 地址和域名相互映射的分布式数据库”是片面的,域名系统面向的是域名和与域名相关联的资源,包括但不仅限于 IP 地址。域名系统的主要设计目标是(在网络中)实现一个统一的名字空间(name space)用于关联名字与其对应的资源(resources)。为了避免尤其是编码方式导致的一些问题,在域名中不应该包含网络 id、地址、路由或类似的其他信息[1]

DNS 是一个分布式的系统,由 Internet 上遍布世界各地的域名服务器组成;分布式域名系统的另一个含义是,在整个系统中,每一个节点的服务器都只存有部分域名的记录。当想要解析一个域名时,可能需要直接或间接地访问多个域名服务器才能得到结果。

DNS 的名字空间是一个树状结构,树中的每一个结点都拥有一个标签(label),规定标签的长度在 0-63 字符之间,其中 0 字符长的标签(NULL)由根结点保留。另外,兄弟结点之间不能有相同的标签,但非兄弟结点(即使是父子结点)之间可以拥有相同的标签。标签由英文字母(a-z, A-Z)、数字(0-9)和连字符(-)组成,不区分大小写,连字符不能连续出现,也不能出现在标签的第一个字符处。例如,label0、label-0 是合法的,而 -label、label–0 是非法的结点标签;label0 与 LABEL0 是等效的标签。每一个结点都对应一个域名,结点的域名由结点与根之间的路径上所有的结点(包括结点和根本身)的标签组成,这些标签从左到右依次排列,用点号(.)连接。下图展示了互联网中真实 DNS 名字空间的一部分,大部分结点还有其他子结点没有画出。

dns图1 互联网中 DNS 域名系统的一部分

根结点的直接子结点所在的域称为顶级域,对应的域名称为顶级域名,顶级域名目前由 ICANN(互联网名称与数字地址分配机构)负责分配和管理,关于域的分配和授权将在下文中讨论。从名称来看,顶级域可以分为以下几种:

  • 普通组织域名,通常为三个字符,是最常见的域名,如 com(商业机构)、net(网络服务)、org(非盈利性组织)等;
  • 国家域名,通常为两个字符,分配给世界各国使用,如 cn(中国)、tw(台湾)、us(美国)、jp(日本)等;
  • 用于地址到名字转换的特殊域 arpa。

最近 ICANN 还分配了一些由汉字组成的顶级域名,如“中国”等。

域名系统的正常运作离不开域名服务器(Server)和域名解析器(Resolver)。域名服务器中存储有它所管辖的域的域名和资源记录数据,并运行着一个域名服务程序,接受网络上的域名查询请求,并查询相应的记录,向查询者返回结果。域名解析器是一个客户端程序,它由应用程序调用,并通过网络接口访问一台或多台域名服务器,最终返回给调用者域名解析结果。

域名系统的中心是根结点,管理根结点所在域的服务器是根域名服务器,是最根本、最重要的域名服务器。全球共有 13 台根域名服务器,它们都有自己的域名,分别是 a.root-servers.net、b.root-servers.net,…,一直到 m.root-servers.net。这里的“一台根域名服务器”指的是逻辑上的数量,并不是指服务器的主机数量,事实上“一台”根域名服务器都由多台主机组成,它们共有同一个域名和 IP 地址。所有的根域名服务器都只存有全部顶级域名的记录,并只能提供对顶级域名的解析服务。

前面讲道,想要解析一个域名可能需要向多个域名服务器发起解析请求,这是由域名系统的层次结构以及授权机制决定的。ICANN 负责管理和分配全部顶级域名,并将顶级域名分别授权给所有这些域名的机构。例如,顶级域名 cn 由 ICANN 授权给 CNNIC(中国互联网络信息中心)管理。这些拥有顶级域名的二级域名机构可以布署域名服务器,并再授权给下一级的其他机构来管理他们的域名或子域名。这样,一个由根结点出发,逐级授权的域名系统就能建立起来了。

所有根域名服务器都是权威的(authoritative)域名服务器,权威域名服务器意味着它的提供的域名记录是“授权的”。同样,由 ICANN 授权的顶级域名管理机构以及它们授权的机构所属的服务器也是权威的域名服务器。权威域名服务器上的授权记录是由管理员通过配置文件设置的,另外,授权域名服务器之间还会定期通过“区域传递”来交换这些记录以保持持续的更新。

一次典型的域名解析是解析器首先向根域名服务器发起请求,请求查询被解析域名所在的顶级域名的信息。根域名服务器通常会返回所有该顶级域名的域名服务器的名称,解析器再向顶级域名所有者服务器发起解析,此时可能收到解析的结果,也可能收到下一个域名服务器的名称。如果是后者,则解析器再次向下一级域名服务器发起请求,直到得到它想要的结果。下面是用 dig 命令跟踪解析域名 work.luodichen.com 的 IP 地址的结果。

MacBook-Air-luodichen:~ luodichen$ dig +trace work.luodichen.com

; <<>> DiG 9.8.3-P1 <<>> +trace work.luodichen.com
;; global options: +cmd
.                       408915  IN      NS      k.root-servers.net.
.                       408915  IN      NS      c.root-servers.net.
.                       408915  IN      NS      l.root-servers.net.
.                       408915  IN      NS      g.root-servers.net.
.                       408915  IN      NS      d.root-servers.net.
.                       408915  IN      NS      f.root-servers.net.
.                       408915  IN      NS      b.root-servers.net.
.                       408915  IN      NS      h.root-servers.net.
.                       408915  IN      NS      i.root-servers.net.
.                       408915  IN      NS      a.root-servers.net.
.                       408915  IN      NS      m.root-servers.net.
.                       408915  IN      NS      e.root-servers.net.
.                       408915  IN      NS      j.root-servers.net.
;; Received 228 bytes from 202.96.128.86#53(202.96.128.86) in 392 ms

com.                    172800  IN      NS      l.gtld-servers.net.
com.                    172800  IN      NS      c.gtld-servers.net.
com.                    172800  IN      NS      e.gtld-servers.net.
com.                    172800  IN      NS      b.gtld-servers.net.
com.                    172800  IN      NS      i.gtld-servers.net.
com.                    172800  IN      NS      j.gtld-servers.net.
com.                    172800  IN      NS      g.gtld-servers.net.
com.                    172800  IN      NS      h.gtld-servers.net.
com.                    172800  IN      NS      k.gtld-servers.net.
com.                    172800  IN      NS      a.gtld-servers.net.
com.                    172800  IN      NS      d.gtld-servers.net.
com.                    172800  IN      NS      m.gtld-servers.net.
com.                    172800  IN      NS      f.gtld-servers.net.
;; Received 508 bytes from 192.36.148.17#53(192.36.148.17) in 413 ms

luodichen.com.          172800  IN      NS      f1g1ns1.dnspod.net.
luodichen.com.          172800  IN      NS      f1g1ns2.dnspod.net.
;; Received 250 bytes from 192.41.162.30#53(192.41.162.30) in 390 ms

work.luodichen.com.     600     IN      NS      ns1.oray.net.
work.luodichen.com.     600     IN      NS      ns2.oray.net.
;; Received 88 bytes from 112.90.82.194#53(112.90.82.194) in 87 ms

work.luodichen.com.     60      IN      A       113.76.146.88
;; Received 52 bytes from 61.174.40.200#53(61.174.40.200) in 81 ms

可以看到在解析 work.luodichen.com 的过程中,经历了如下几个阶段

  1. 向本地配置的 DNS 服务器 202.96.128.86 查询根域名 . 的域名服务器,得到 13 个根域名服务器的域名;
  2. 向其中某个根域名服务器 192.36.148.17 查询顶级域名 com 的域名服务器,得到一些服务器列表;
  3. 向其中某个 com 域名服务器查询 luodichen.com 的域名服务器,得到 f1g1ns1.dnspod.net 和 f1g1ns2.dnspod.net 两个域名服务器的域名;
  4. 向其中某个 luodichen.com 的域名服务器查询 work.luodichen.com 的域名服务器,得到 ns1.oray.net 和 ns2.oray.net 两个域名服务器的域名;
  5. 向其中某个 work.luodichen.com 的域名服务器查询 work.luodichen.com 的 IP 地址,得到最终结果是 113.76.146.88。

在实际的终端应用程序中,可能并不需要像以上的实验那样从根服务器开始一级一级地发起查询请求。如果所有的终端应用程序都这样做,一方面根域名服务器将承担巨大的网络压力;另一方面一次域名解析可能耗费非常长的时间,影响效率。解决这个问题的是缓存(Cache)技术。

除根域名之外的一些域名服务器,它们除了运行着域名服务程序之外,还运行着域名解析器。当这些服务器收到一个不属于它管辖的区域的域名解析请求时,它会尝试以客户端的身份向别的域名服务器发起解析请求,并将最终的解析结果返回给它的客户端,这种服务器称作递归域名服务器,它的客户端发起的解析请求称为递归解析请求。这种服务器同时还可能存在着一个高速缓存(Cache),用来缓存上面提到的这些域名解析的结果,当下次又有相同的域名的请求时,服务器会直接将缓存中的记录返回给客户端,并标记这个记录是“非权威的(non-authoritative)”查询结果。为了确保缓存中的信息能及时更新,每条 DNS 记录都有对应的存活时间(TTL, time to live),当缓存中的信息存在的时间超过它的 TTL 时,这条缓存记录就会过期失效,下次请求这条记录时,服务器又会以递归查询的方式解析这个域名,并再次存入缓存中。

为了解决 DNS 查询效率的问题,通常我们在终端系统中配置的是从 ISP 处获取的 DNS 服务器地址或者一些商业 DNS 服务器地址,如 114.114.114.114、8.8.8.8 等。这些服务器都属于带有缓存的递归服务器,终端解析器只要向这些服务器发送一次递归查询请求就能很快地得到查询结果。

解析器通过 DNS 协议向域名服务器发起查询请求,关于 DNS 协议的细节,以及域名系统多个资源记录作用与格式将在下一篇博文中讨论。

(允许转载本文,转载请注明出处:http://luodichen.com/blog/?p=318 )

参考文献
[1] P. Mockapetris, DOMAIN NAMES – CONCEPTS AND FACILITIES, RFC 1034, November 1987; www.ietf.org/rfc/rfc1034.txt
[2] P. Mockapetris, DOMAIN NAMES – IMPLEMENTATION AND SPECIFICATION, RFC 1035, November 1987; www.ietf.org/rfc/rfc1035.txt
[3] W. Richard Stevens, TCP/IP 详解 卷1:协议[M]. 机械工业出版社, 2000.

DNS协议及DNS客户端的实现

DNS(Domain Name System)是一个提供域名解析服务的分布式系统,主要功能是完成域名与其对应的IP地址之间的相互转换。网络中提供DNS服务的主机叫做DNS服务器,而向DNS服务器发起查询请求的主机叫做DNS客户端,客户端与服务器之间通过DNS协议这一种应用层协议来相互通信并交换数据。

DNS协议建立在UDP或TCP协议之上,DNS服务器开放UDP:53端口和TCP:53端口监听客户端发来的请求。一般情况下客户端通过UDP协议封装请求报文,服务器也用UDP协议封装回应报文;由于广域网中不适合传输过大的UDP数据包,因此规定当封装了DNS回应的UDP数据包长度可能超过512字节时,客户端应该使用TCP协议连接DNS服务器并传输请求和回应,具体包括以下两种情况:(1)客户端认为UDP回应包长度可能超过512字节,主动使用TCP协议;(2)客户端第一次使用UDP协议发送DNS请求,服务端发现UDP回应包会超过512字节,截断UDP包中的回应报文,并在回应报文中为TC字段置1以通知客户端该报文已经被截断,客户端收到之后再发起一次TCP请求。

DNS数据报由头部和记录部分组成,其中请求报文只有问题部分,而回应报文可以有问题部分、回答部分、授权部分和附加部分。
net

DNS头部包含了标识、标志以及各个记录部分的记录个数。net_dns_header

其中标识字段用于确定请求会话,DNS服务保证请求和其对应的回应报文中标识字段的值是相等的。标志字段被分成若干子字段,用于标识数据包的各个属性。DNS头部的C语言结构可以表示如下:

问题部分由问题记录组成,资源部分、授权部分和附加部分由资源记录组成,问题记录和资源记录的结构是不一样的。
问题记录的格式如下图所示
net_a_record
查询名字是将要查询的域名,域名是一个由圆点分为若干节的字符串,这里的名字也由若干节组成,每一节第一个字节是这一个节中包含字符的长度。如下图
net_query_name

查询类型一般是A(地址)记录,表示把域名转换为32位的IPv4地址。全部的查询类型如下所示

查询类别一般都为1(Internet)。

资源记录的格式如下图所示
net_res_record
其中前三个字段与问题记录一致。生存时间表示该DNS回应记录可以在主机缓存中生存的时间,以秒为单位。资源数据长度是紧随其后的资源数据的长度,资源数据的具体内容与域类型字段有关,在只考虑A类型的时候这里的记录是一个32位的IP地址。

为了使DNS报文尽可能短,DNS协议对于可能重复出现的域名节采取压缩策略,因此通常情况下在一个报文中不会存有完全相同的域名节。当出现重复的域名节时,域名节的第一个字节将不是这个节的字符长度,而是以11开头的一个指针,指向该节在报文中第一次出现的位置。
net_pointer

下面是一个用UDP协议实现的DNS客户端类(没有实现TCP方式),测试Demo运行结果如图所示

dns_resolver


最新的相关文章

DNS:域名系统(一)
DNS:域名系统(二)