实现DNS协议java版

结构

   +---------------------+
    |        Header       | 报文头
    +---------------------+
    |       Question      | 要查询的问题
    +---------------------+
    |        Answer       | 服务器的应答
    +---------------------+
    |      Authority      | 权威的应答
    +---------------------+
    |      Additional     | 附加信息
    +---------------------+

请求对应图中如下:

在这里插入图片描述

服务器应答如下:

在这里插入图片描述

1.1 Header的格式

/**
 * 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位标示,服务器给出应答的时候会带相同的标示字段回来,这样请求客户端就可以区分不同的请求应答了。
  • QR 1个比特位用来区分是请求(0)还是应答(1)。
  • OPCODE 4个比特位用来设置查询的种类,应答的时候会带相同值,可用的值如下:
    • 0 标准查询 (QUERY)
    • 1 反向查询 (IQUERY)
    • 2 服务器状态查询 (STATUS)
    • 3-15 保留值,暂时未使用
  • AA 授权应答(Authoritative Answer) - 这个比特位在应答的时候才有意义,指出给出应答的服务器是查询域名的授权解析服务器。注意因为别名的存在,应答可能存在多个主域名,这个AA位对应请求名,或者应答中的第一个主域名。
  • TC 截断(TrunCation) - 表示是否被截断。值为 1 时,表示响应已超过 512 字节并已被截断,只返回前 512 个字节。
  • RD 期望递归(Recursion Desired) - 这个比特位被请求设置,应答的时候使用的相同的值返回。如果设置了RD,就建议域名服务器进行递归解析,递归查询的支持是可选的。如果该位为 0,且被请求的名称服务器没有一个授权回答,它将返回一个能解答该查询的其他名称服务器列表。这种方式被称为迭代查询。
  • RA 支持递归(Recursion Available) - 这个比特位在应答中设置或取消,用来代表服务器是否支持递归查询。
  • Z 保留值,暂时未使用。在所有的请求和应答报文中必须置为0。
  • answer authenticated 回答认证,服务端设置。
  • AD 表示认证数据。
  • RCODE 应答码(Response code) - 这4个比特位在应答报文中设置,代表的含义如下:
    • 0 没有错误。
    • 1 报文格式错误(Format error) - 服务器不能理解请求的报文。
    • 2 服务器失败(Server failure) - 因为服务器的原因导致没办法处理这个请求。
    • 3 名字错误(Name Error) - 只有对授权域名解析服务器有意义,指出解析的域名不存在。
    • 4 没有实现(Not Implemented) - 域名服务器不支持查询类型。
    • 5 拒绝(Refused) - 服务器由于设置的策略拒绝给出应答。比如,服务器不希望对某些请求者给出应答,或者服务器不希望进行某些操作(比如区域传送zone transfer)。
    • 6-15 保留值,暂时未使用。
  • QDCOUNT 无符号16位整数表示报文请求段中的问题记录数。
  • ANCOUNT 无符号16位整数表示报文回答段中的回答记录数。
  • NSCOUNT 无符号16位整数表示报文授权段中的授权记录数。
  • ARCOUNT 无符号16位整数表示报文附加段中的附加记录数。

请求的格式

在这里插入图片描述

  • 事务id(Transaction ID):0x003a
  • Flags:DNS 报文中的标志字段。
    • QR:0。表示是一个请求
    • OPCODE:0000。表示是一个标准查询。
    • AA:0。应答的时候才会设置。
    • TC:0。表示没有被截断。
    • RD:1。表示期望递归解析。
    • RA:0。应答的事后设置。表示是否支持递归。
    • Z:0。保留,均设置0。
    • answer authenticated:0。回答认证,服务端设置。
    • AD:0。表示没有认证数据。
    • RCODE:0000。应答报文中设置。
  • Questions:1。表示请求的question有一个
  • Answers RRs:DNS 响应的数目,服务端设置。

应答的格式

在这里插入图片描述

  • 事务id:0x003a。同上一样,说明是同一个事务响应请求。

  • Flags:DNS 报文中的标志字段。

    • QR:1。表示是一个应答。

    • OPCODE:0000。表示是一个标准查询。

    • AA:0。应答的服务器不是查询域名的授权解析服务器。

    • TC:0。表示没有被截断。

    • RD:1。表示期望递归解析。

    • RA:1。表示服务器支持递归解析。

    • Z:0。保留,均设置0。

    • answer authenticated:0。回答认证,服务端设置。

    • AD:0。表示没有认证数据。

    • RCODE:0000。表示没有错误。

  • Questions:1。表示请求的question有一个

  • Answers RRs:4。表明服务端响应了4个回答。

1.2 Question的格式

在大多数查询中,Question段包含着问题(question),比如,指定问什么。这个段包含QDCOUNT(usually 1)个问题,每个问题为下面的格式:

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

字段含义如下:

  • QNAME:要查询的域名,有时也会是 IP 地址,用于反向查询。域名被编码为一些labels序列,每个labels包含一个字节表示后续字符串长度,以及这个字符串,以0长度和空字符串来表示域名结束。注意这个字段可能为奇数字节,不需要进行边界填充对齐。
  • QTYPE:DNS 查询请求的资源类型。通常查询类型为 A 类型,表示由域名获取对应的 IP4 地址。2个字节表示查询类型,取值可以为任何可用的类型值,以及通配码来表示所有的资源记录。
  • QCLASS:地址类型,通常为互联网地址,值为 1。2个字节表示查询的协议类,比如,IN代表Internet。

常见QTYPE

代码号码定义的 RFC描述功能
A1RFC 1035IP 地址记录传回一个 32 比特的 IPv4 地址,最常用于映射主机名称IP地址,但也用于DNSBLRFC 1101)等。
AAAA28RFC 3596IPv6 IP 地址记录传回一个 128 比特的 IPv6 地址,最常用于映射主机名称到 IP 地址。
AFSDB18RFC 1183AFS文件系统(Andrew File System)数据库核心的位置,于域名以外的 AFS 客户端常用来联系 AFS 核心。这个记录的子类型是被过时的的 DCE/DFS(DCE Distributed File System)所使用。
APL42RFC 3123地址前缀列表指定地址列表的范围,例如:CIDR 格式为各个类型的地址(试验性)。
CAA257RFC 6844权威认证授权DNS认证机构授权,限制主机/域的可接受的CA
CDNSKEY60RFC 7344子关键记录关键记录记录的子版本,用于转移到父级
CDS59RFC 7344子委托签发者委托签发者记录的子版本,用于转移到父级
CERT37RFC 4398证书记录存储 PKIXSPKIPGP等。
CNAME5RFC 1035规范名称记录一个主机名字的别名:域名系统将会继续尝试查找新的名字。
DHCID49RFC 4701DHCP(动态主机设置协议)识别码用于将 FQDN 选项结合至 DHCP
DLV32769RFC 4431DNSSEC(域名系统安全扩展)来源验证记录为不在DNS委托者内发布DNSSEC的信任锚点,与 DS 记录使用相同的格式,RFC 5074 介绍了如何使用这些记录。
DNAME39RFC 2672代表名称DNAME 会为名称和其子名称产生别名,与 CNAME 不同,在其标签别名不会重复。但与 CNAME 记录相同的是,DNS将会继续尝试查找新的名字。
DNSKEY48RFC 4034DNS 关键记录于DNSSEC内使用的关键记录,与 KEY 使用相同格式。
DS43RFC 4034委托签发者此记录用于鉴定DNSSEC已授权区域的签名密钥。
HIP55RFC 5205主机鉴定协议将端点标识符及IP 地址定位的分开的方法。
IPSECKEY45RFC 4025IPSEC 密钥IPSEC 同时使用的密钥记录。
KEY25RFC 2535[1]RFC 2930[2]关键记录只用于 SIG(0)(RFC 2931)及 TKEY(RFC 2930)。[3]RFC 3455 否定其作为应用程序键及限制DNSSEC的使用。[4]RFC 3755 指定了 DNSKEY 作为DNSSEC的代替。[5]
LOC记录(LOC record)29RFC 1876位置记录将一个域名指定地理位置。
MX记录(MX record)15RFC 1035电邮交互记录引导域名到该域名的邮件传输代理(MTA, Message Transfer Agents)列表。
NAPTR记录(NAPTR record)35RFC 3403命名管理指针允许基于正则表达式的域名重写使其能够作为 URI、进一步域名查找等。
NS2RFC 1035名称服务器记录委托DNS区域(DNS zone)使用已提供的权威域名服务器。
NSEC47RFC 4034下一代安全记录DNSSEC 的一部分 — 用来验证一个未存在的服务器,使用与 NXT(已过时)记录的格式。
NSEC350RFC 5155NSEC 记录第三版用作允许未经允许的区域行走以证明名称不存在性的 DNSSEC 扩展。
NSEC3PARAM51RFC 5155NSEC3 参数与 NSEC3 同时使用的参数记录。
OPENPGPKEY61RFC 7929OpenPGP公钥记录基于DNS的域名实体认证方法,用于使用OPENPGPKEY DNS资源记录在特定电子邮件地址的DNS中发布和定位OpenPGP公钥。
PTR12RFC 1035指针记录引导至一个规范名称(Canonical Name)。与 CNAME 记录不同,DNS“不会”进行进程,只会传回名称。最常用来运行反向 DNS 查找,其他用途包括引作 DNS-SD
RRSIG46RFC 4034DNSSEC 证书DNSSEC 安全记录集证书,与 SIG 记录使用相同的格式。
RP17RFC 1183负责人有关域名负责人的信息,电邮地址的 @ 通常写为 a
SIG24RFC 2535证书SIG(0)(RFC 2931)及 TKEY(RFC 2930)使用的证书。[5]RFC 3755 designated RRSIG as the replacement for SIG for use within DNSSEC.[5]
SOA6RFC 1035权威记录的起始指定有关DNS区域的权威性信息,包含主要名称服务器、域名管理员的电邮地址、域名的流水式编号、和几个有关刷新区域的定时器。
SPF99RFC 4408SPF 记录作为 SPF 协议的一部分,优先作为先前在 TXT 存储 SPF 数据的临时做法,使用与先前在 TXT 存储的格式。
SRV记录(SRV record)33RFC 2782服务定位器广义为服务定位记录,被新式协议使用而避免产生特定协议的记录,例如:MX 记录。
SSHFP44RFC 4255SSH 公共密钥指纹DNS 系统用来发布 SSH 公共密钥指纹的资源记录,以用作辅助验证服务器的真实性。
TA32768DNSSEC 信任当局DNSSEC 一部分无签订 DNS 根目录的部署提案,,使用与 DS 记录相同的格式[6][7]
TKEY记录(TKEY record)249RFC 2930秘密密钥记录TSIG提供密钥材料的其中一类方法,that is 在公共密钥下加密的 accompanying KEY RR。[8]
TSIG250RFC 2845交易证书用以认证动态更新(Dynamic DNS)是来自合法的客户端,或与 DNSSEC 一样是验证回应是否来自合法的递归名称服务器。[9]
TXT16RFC 1035文本记录最初是为任意可读的文本 DNS 记录。自1990年起,些记录更经常地带有机读数据,以 RFC 1464 指定:机会性加密(opportunistic encryption)、Sender Policy Framework(虽然这个临时使用的 TXT 记录在 SPF 记录推出后不被推荐)、DomainKeys、DNS-SD等。
URI256RFC 7553统一资源标识符可用于发布从主机名到URI的映射。

请求格式

在这里插入图片描述

应为上述协议头的questions个数为1,说明由一个question。

  • name:www.biying.com。查询的域名为这个。

    • 注意看这里LabelCount=3,表示分了三段。这里每段我通过反编译发现都是通过.进行分割。拆分三段分别为

    • www,biying,com。

    • 在这里插入图片描述

    • 每一个label由长度和数据组成。这里看第一段第一个字节为3,表示有3长度字节。反编译后为www。

    • 在这里插入图片描述

    • 同理一直到第三段,都是这样。然后三段过后紧随其后的是8bit的0,表示读取结束。

    • 代码如下:

      • //域名被编码为一些labels序列,每个labels包含一个字节表示后续字符串长度,以及这个字符串,以0长度和空字符串来表示域名结束。
        String[] split = host.split("\\.");
        int length = 0;
        for (int i = 0; i < split.length; i++) {
            //存储字节
            length++;
            length += split[i].length();
        }
        byte[] name = new byte[length + 1];
        int off = 0;
        for (int i = 0; i < split.length; i++) {
            byte[] bytes = split[i].getBytes();
            name[off++] = (byte) split[i].length();
            off = copy(off, name, bytes);
        }
        //最后一位需要置0
        name[name.length - 1] = 0x00;
        
  • type:A。说明由域名获取ip地址。

  • class:默认1。表示为互联网地址。

1.3 Answer

应答,授权,附加段都共用相同的格式:多个资源记录,资源记录的个数由报文头段中对应的几个数值确定,每个资源记录格式如下:

                                    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 2个字节表示资源记录的类型,指出RDATA数据的含义

  • CLASS 2个字节表示RDATA的类

  • TTL 4字节无符号整数表示资源记录可以缓存的时间。0代表只能被传输,但是不能被缓存。

  • RDLENGTH 2个字节无符号整数表示RDATA的长度

  • RDATA 不定长字符串来表示记录,格式跟TYPE和CLASS有关。比如,TYPE是A,CLASS 是 IN,那么RDATA就是一个4个字节的ARPA网络地址。这里也会采用压缩。

    • 资源数据格式种类包含如下:

    • 数字:八位位组表示数,例如,IPv4地址是4个八位组整数,而IPv6地址是一个16个八位组整数。

    • 域名:可用标签序列来表示。每一个标签前面有1个字节长度字段,它定义标签中的字段数。长度字段的两个高位永远是0,标

      签的长度不能超过63字节。

    • 偏移指针:域名可以用偏移指针来替换。偏移指针是2字节字段,它的两个高位置为1

    • 字符串:用1字节的长度字段后面跟着长度字段数。长度字段并不像域名长度字段那样受限。字符串可以多达256个字符。

1.3.1 报文压缩

为了减小报文,域名系统使用一种压缩方法来消除报文中域名的重复。使用这种方法,后面重复出现的域名或者labels被替换为指向之前出现位置的指针。指针占用2个字节,格式如下:

     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    | 1 1|                OFFSET                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

前两个比特位都为1。因为lablels限制为不多于63个字节,所以label的前两位一定为0,这样就可以让指针与label进行区分。(10 和 01 组合保留,以便日后使用) 。偏移值(OFFSET)表示从报文开始的字节指针。偏移量为0表示ID字段的第一个字节。
压缩方法让报文中的域名成为:
- 以0结尾的labels序列
- 一个指针
- 指针结尾的labels序列
指针只能在域名不是特殊格式的时候使用,否则域名服务器或解析器需要知道资源记录的格式。目前还没有这种情况,但是以后可能会出现。
如果报文中的域名需要计算长度,并且使用了压缩算法,那么应该使用压缩后的长度,而不是压缩前的长度。
程序可以自由选择是否使用指针,虽然这回降低报文的容量,而且很容易产生截断。不过所有的程序都应该能够理解收到的报文中包含的指针。

1.3.2 应答的格式

在这里插入图片描述

  • name:www.biying.com。查询的域名为这个。

  • type:5。表示CNAME,一个主机名字的别名。

  • class:默认1。表示为互联网地址。

  • ttl:3590。表示缓存59分钟,50秒。

  • RDLENGTH:39。表示data占39字节。

  • RDATA:主机别名。

可以看到name对应的是2字节。那为什么能够显示域名了?

答案:这是根据该2字节算出一个数字。该数字表示对于整个报文体的偏移量。

高端第1~2字节为1,因为lablels限制为不多于63个字节,所以label的前两位一定为0,这样就可以让指针与label进行区分。

在这里插入图片描述

后面的14位对应12,表示报文体第12位开始。

在这里插入图片描述

第12位是03,按照label的规则逐个解析就行了。解析到最后一行一定空行或者8字节0。

在这里插入图片描述

解析为同上一样,说明解析成功。

在这里插入图片描述

cname解析也同上,这里框1表示读取后续5位。框二同理。框三有点不同,但是可以看到第7~8位为1。说明与后续1字节组成偏移量。这里偏移量对应的是com。当读取偏移量后,也是根据读取字节为0结尾。

在这里插入图片描述

解析结果如下:

在这里插入图片描述

可参考如下代码解析。
在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值