NSEC协议中的Bitmap实现

在DNSSEC协议中,NSEC类型用于对不存在的域名或者其查询类型不存在进行的权威应答。其中域名不存在比较好理解,就是该域名不再权威数据库中。但是对于查询类型不存在的NSEC协议中通过一个新的数据结构Bitmap格式来对于该域名的所有类型进行标记并返回给查询者,这种数据结构可以保存该域名所有DNS类型是否存在的信息,并且占用资源较少。

NSEC实例

查询域名不存在的时候,比如我们查询not-exist.isc.org这个域名实际是不存在的,权威服务器响应数据包含如下的数据内容, 对于不存在的域名,可按照其字母顺序,查找最接近的两个域名(小于该值和大于该值)

  • 搜索范围中小于该值的最大域名
  • 否定缓存时间TTL
  • Class类型 IN
  • 类型NSEC
  • 搜索范围中大于该值的最小域名
  • nook2.isc.org.域名包含的存在的类型集合
nook2.isc.org.		3599	IN	NSEC	nrt1.isc.org. A RRSIG NSEC

下面是一个简单的类型不存在的NSEC记录,我们查询的为isc.org的CNAME记录,它本身并不存在该记录,服务器自动返回的NSEC记录如下面所示:

  • 查询域名
  • 否定缓存时间TTL
  • Class类型 IN
  • 类型NSEC
  • 下一个域名
  • isc.org.域名包含的存在的类型集合
isc.org.		3599	IN	NSEC	_adsp._domainkey.isc.org. A NS SOA MX TXT AAAA NAPTR DS RRSIG NSEC DNSKEY SPF CAA

数据结构

对于NSEC响应的数据信息,主要包含的两部分内容:

  • 下一个域名名称,这里不包含glue记录,在下一个域名串联起来的链表中最后一个域名为该权威记录SOA记录所有者域名。
  • 域名包含的已存在类型集合,这里使用Bit map结构保存类型信息
                         1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    /                      Next Domain Name                         /
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    /                   List of Type Bit Map(s)                     /
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Bitmap类型信息存储方式实例如下:

下面的NSEC记录保存了域名 alfa.example.com. 以及下一个域名为host.example.com. 其中alfa.example.com. 包含类型A MX RRSIG NSEC TYPE1234, 其中前面部分用来保存域名数据,0x00代表结束符号,

  alfa.example.com. 86400 IN NSEC host.example.com. A MX RRSIG NSEC
   TYPE1234
   
  0x04 'h'  'o'  's'  't'
  0x07 'e'  'x'  'a'  'm'  'p'  'l'  'e'
  0x03 'c'  'o'  'm'  0x00
  0x00 0x06 0x40 0x01 0x00 0x00 0x00 0x03
  0x04 0x1b 0x00 0x00 0x00 0x00 0x00 0x00
  0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
  0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
  0x00 0x00 0x00 0x00 0x20 

关于Bitmap的存储规则如下:

  • IANA规定的类型范围为0-65535
  • 将其分为256块,每一块保存其中的256个bit位
  • 对于所处的bit块如果全为0则自动跳过忽略
  • 对于其中包含有对应类型的块,第一个为块的index号码 第二个值为长度
  • 对于其类型的计算通过: indexOfBlock * 256 + offsetOfBit 即可

解析代码实现如下:

func unpackDataNsec(msg []byte, off int) ([]uint16, int, error) {
	var nsec []uint16
	length, window, lastwindow := 0, 0, -1
	for off < len(msg) {
		if off+2 > len(msg) {
			return nsec, len(msg), &Error{err: "overflow unpacking nsecx"}
		}
		window = int(msg[off])
		length = int(msg[off+1])
		off += 2
		if window <= lastwindow {
			// RFC 4034: Blocks are present in the NSEC RR RDATA in
			// increasing numerical order.
			return nsec, len(msg), &Error{err: "out of order NSEC block"}
		}
		if length == 0 {
			// RFC 4034: Blocks with no types present MUST NOT be included.
			return nsec, len(msg), &Error{err: "empty NSEC block"}
		}
		if length > 32 {
			return nsec, len(msg), &Error{err: "NSEC block too long"}
		}
		if off+length > len(msg) {
			return nsec, len(msg), &Error{err: "overflowing NSEC block"}
		}

		// Walk the bytes in the window and extract the type bits
		for j, b := range msg[off : off+length] {
			// Check the bits one by one, and set the type
			if b&0x80 == 0x80 {
				nsec = append(nsec, uint16(window*256+j*8+0))
			}
			if b&0x40 == 0x40 {
				nsec = append(nsec, uint16(window*256+j*8+1))
			}
			if b&0x20 == 0x20 {
				nsec = append(nsec, uint16(window*256+j*8+2))
			}
			if b&0x10 == 0x10 {
				nsec = append(nsec, uint16(window*256+j*8+3))
			}
			if b&0x8 == 0x8 {
				nsec = append(nsec, uint16(window*256+j*8+4))
			}
			if b&0x4 == 0x4 {
				nsec = append(nsec, uint16(window*256+j*8+5))
			}
			if b&0x2 == 0x2 {
				nsec = append(nsec, uint16(window*256+j*8+6))
			}
			if b&0x1 == 0x1 {
				nsec = append(nsec, uint16(window*256+j*8+7))
			}
		}
		off += length
		lastwindow = window
	}
	return nsec, off, nil
}

测试数据:

data := []byte{0x00,0x06,0x40,0x01,0x00,0x00,0x00,0x03,
		0x04,0x1b,0x00,0x00,0x00,0x00,0x00,0x00,
		0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
		0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
		0x00,0x00,0x00,0x00,0x20 }
	result, _, err := unpackDataNsec(data, 0)
	if err != nil{
		panic(err)
	}
	fmt.Printf("result = %v", result)

发表评论

电子邮件地址不会被公开。 必填项已用*标注