EDNS

这个DNS系列现在有以下几篇文章
DNS EDNS DNSSEC DNS over HTTPS 完整代码请看DNS

EDNS

上一篇我们写了DNS,EDNS就是DNS的功能扩充.
上一篇的DNS里头部有ARCOUNT的计数,EDNS就包含在那个里面,EDNS发送的请求在请求头之后的Additional中,应答在Answer和Authoritative之后,我们可看以下图,跟清晰了吧

1
2
3
4
5
6
7
8
9
10
11
+---------------------+
| Header |
+---------------------+
| Question | the question for the name server
+---------------------+
| Answer | RRs answering the question
+---------------------+
| Authority | RRs pointing toward an authority
+---------------------+
| Additional | RRs holding additional information
+---------------------+

协议格式

具体协议内容请看rfc2671,rfc7871

Additinoal包含以下部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
     Field Name   Field Type     Description
------------------------------------------------------
NAME domain name empty (root domain)
TYPE u_int16_t OPT
CLASS u_int16_t sender's UDP payload size
TTL u_int32_t extended RCODE and flags
RDLEN u_int16_t describes RDATA
RDATA octet stream {attribute,value} pairs


TTL中的东西并不是我们之前说到的TTL,这里的TTL只是一个名称,没有具体的含义,TTL中包含以下内容

+0 (MSB) +1 (LSB)
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
0: | EXTENDED-RCODE | VERSION |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
2: | Z |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+


EXTENDED-RCODE Forms upper 8 bits of extended 12-bit RCODE. Note
that EXTENDED-RCODE value "0" indicates that an
unextended RCODE is in use (values "0" through "15").
VERSION EDNS VERSION
Z Set to zero by senders and ignored by receivers,
unless modified in a subsequent specification.

这里Z我们之后会用到(不是这一篇文章)


RDATA中就是对各种扩充功能的请求字段,而每个不同的功能又有着不同的请求协议
RDATA的格式如下

+0 (MSB) +1 (LSB)
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
0: | OPTION-CODE |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
2: | OPTION-LENGTH |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
4: | |
/ OPTION-DATA /
/ /
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

OPTION-CODE (Assigned by IANA.)
OPTION-LENGTH Size (in octets) of OPTION-DATA.
OPTION-DATA Varies per OPTION-CODE.

如SubNet中的请求字段就包含在其中RDATA中,所以我们要先完成SubNet的字节数组
才能生成一个Additional,因为请求中需要数据的长度RDLEN

因为EDNS中扩展的协议非常多,这里我们就以常用的SubNet为例吧,更多其他类型请看dns-parameters
因为我们以SubNet为例,所以先要了解下SubNet的具体协议格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
             +0 (MSB)                            +1 (LSB)
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
0: | OPTION-CODE |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
2: | OPTION-LENGTH |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
4: | FAMILY |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
6: | SOURCE PREFIX-LENGTH | SCOPE PREFIX-LENGTH |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
8: | ADDRESS... /
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

o SOURCE PREFIX-LENGTH, an unsigned octet representing the leftmost
number of significant bits of ADDRESS to be used for the lookup.
In responses, it mirrors the same value as in the queries.

o SCOPE PREFIX-LENGTH, an unsigned octet representing the leftmost
number of significant bits of ADDRESS that the response covers.
In queries, it MUST be set to 0.

o ADDRESS, variable number of octets, contains either an IPv4 or
IPv6 address, depending on FAMILY, which MUST be truncated to the
number of bits indicated by the SOURCE PREFIX-LENGTH field,
padding with 0 bits to pad to the end of the last octet needed.

这里前面三个字段就是我们之前RDATA中规定的协议格式,后面就是SubNet的格式
SubNet的OPTION-CODE为8,
OPTION-LENGTH就是完成之后的长度
FAMILY表示设置的IP的类型 IPv4为1,IPv6为2 更多类型请看address-family-numbers
SOURCE PREFIX-LENGTH为查询中位置偏移(就是CIDR中的mask),具体请了解CIDR,是计算机网络很基础的知识
SCOPE PREFIX-LENGTH是响应返回的位置偏移,在查询中应为0
ADDRESS就是设置的SubNet的IP地址,依据上面的FAMILY

请求

看过具体协议格式规定后,我们就可以来完成代码了

先定义以下各种请求的OPTION-CODE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var (
trueReserved = EDNSOPT{0b00000000, 0b00000000} //0
trueLLQ = EDNSOPT{0b00000000, 0b00000001} //1 Optional
trueUL = EDNSOPT{0b00000000, 0b00000010} //2 On-hold
trueNSID = EDNSOPT{0b00000000, 0b00000011} //3 Standard
trueReserved2 = EDNSOPT{0b00000000, 0b00000100} //4
trueDAU = EDNSOPT{0b00000000, 0b00000101} //5 Standard
trueDHU = EDNSOPT{0b00000000, 0b00000110} //6 Standard
trueN3U = EDNSOPT{0b00000000, 0b00000111} //7 Standard
trueEdnsClientSubnet = EDNSOPT{0b00000000, 0b00001000} //8 Optional
trueEDNSEXPIRE = EDNSOPT{0b00000000, 0b00001001} //9 Optional
trueCOOKIE = EDNSOPT{0b00000000, 0b00001010} //10 Standard
trueTcpKeepalive = EDNSOPT{0b00000000, 0b00001011} //11 Standard
truePadding = EDNSOPT{0b00000000, 0b00001100} //12 Standard
trueCHAIN = EDNSOPT{0b00000000, 0b00001101} //13 Standard
trueKEYTAG = EDNSOPT{0b00000000, 0b00001110} //14 Optional
trueExtendedDNSError = EDNSOPT{0b00000000, 0b00001111} //15 Standard
trueEDNSClientTag = EDNSOPT{0b00000000, 0b00010000} //16 Optional
trueEDNSServerTag = EDNSOPT{0b00000000, 0b00010001} //17 Optional
)

然后完成SubNet的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func createEdnsClientSubnet(ip net.IP) []byte {
trueoptionCode := []byte{EdnsClientSubnet[0], EdnsClientSubnet[1]}

truefamily := []byte{0b00000000, 0b00000001} // 1:Ipv4 2:IPv6 https://www.iana.org/assignments/address-family-numbers/address-family-numbers.xhtml
truesourceNetmask := []byte{0b00100000} // 32
truescopeNetmask := []byte{0b00000000} //0 In queries, it MUST be set to 0.
truesubnet := ip.To4() //depending family
trueif subnet == nil {
truetruesubnet = ip.To16()
truetruefamily = []byte{0b00000000, 0b00000010}
true}
trueoptionData := bytes.Join([][]byte{family, sourceNetmask, scopeNetmask, subnet}, []byte{})

trueoptionLength := getLength(len(optionData))

truereturn bytes.Join([][]byte{optionCode, optionLength[:], optionData}, []byte{})
}

再完成Additional,加上完整的请求头,注意这里我们要修改一下请求头中ArCount的大小

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func createEDNSReq(domain string, reqType2 reqType, eDNS []byte) []byte {
truenormalReq := creatRequest(domain, reqType2)
truenormalReq[10] = 0b00000000
truenormalReq[11] = 0b00000001
truename := []byte{0b00000000}
truetypeR := []byte{0b00000000, 0b00101001} //41
truepayloadSize := []byte{0b00010000, 0b00000000} //4096
trueextendRcode := []byte{0b00000000}
trueeDNSVersion := []byte{0b00000000}
truez := []byte{0b00000000, 0b00000000}
truevar dataLength [2]byte
trueif eDNS != nil {
truetruedataLength = getLength(len(eDNS))
true}
truereturn bytes.Join([][]byte{normalReq, name, typeR, payloadSize, extendRcode, eDNSVersion, z, dataLength[:], eDNS}, []byte{})
}

应答

我们先分析一下应答的解析顺序,就跟最开始的一样

  • Header
  • Answer
  • Authority
  • Additional

Header和Answer的解析我们上一篇已经完成了,这里需要完成Authority和Additional

因为我们不需要Authority内的具体内容,这里我们就直接获取到Authority之后的数据就行了

1
2
3
4
5
6
7
8
9
10
11
12
13
func resolveAuthoritative(c []byte, nsCount int, b []byte) (left []byte) {
truefor nsCount != 0 {
truetruensCount--
truetrue_, _, c = getName(c, b)
truetruec = c[2:] // type
truetruec = c[2:] // class
truetruec = c[4:] // ttl
truetruedataLength := int(c[0])<<8 + int(c[1])
truetruec = c[2:] // data length
truetruec = c[dataLength:]
true}
truereturn c
}

Additional, SubNet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
func resolveAdditional(b []byte, arCount int) {
truefor arCount != 0 {
truetruearCount--
truetrue//name := b[:1]
truetrueb = b[1:]
truetruetypeE := b[:2]
truetrueb = b[2:]
truetrue//payLoadSize := b[:2]
truetrueb = b[2:]
truetrue//rCode := b[:1]
truetrueb = b[1:]
truetrue//version := b[:1]
truetrueb = b[1:]
truetrue//z := b[:2]
truetrueb = b[2:]
truetruedataLength := int(b[0])<<8 + int(b[1])
truetrueb = b[2:]
truetrue//log.Println(name, typeE, payLoadSize, rCode, version, z, dataLength)
truetrueif typeE[0] != 0 || typeE[1] != 41 {
truetruetrue//optData := b[:dataLength]
truetruetrueb = b[dataLength:]
truetruetruecontinue
truetrue}

truetrueif dataLength == 0 {
truetruetruecontinue
truetrue}
truetrueoptCode := EDNSOPT{b[0], b[1]}
truetrueb = b[2:]
truetrueoptionLength := int(b[0])<<8 + int(b[1])
truetrueb = b[2:]
truetrueswitch optCode {
truetruecase EdnsClientSubnet:
truetruetrue//family := b[:2]
truetruetrueb = b[2:]
truetruetrue//sourceNetmask := b[:1]
truetruetrue//log.Println("sourceNetmask", sourceNetmask)
truetruetrueb = b[1:]
truetruetrue//scopeNetmask := b[:1]
truetruetrue//log.Println("scopeNetmask", scopeNetmask)
truetruetrueb = b[1:]
truetruetrue// Subnet IP
truetruetrue//if family[0] == 0 && family[1] == 1 {
truetruetrue// log.Println(b[:4])
truetruetrue//}
truetruetrue//if family[0] == 0 && family[1] == 2 {
truetruetrue// log.Println(b[:16])
truetruetrue//}

truetruetrueb = b[optionLength-4:]
truetruedefault:
truetruetrue//log.Println("opt data:", b[:optionLength])
truetruetrueb = b[optionLength:]
truetrue}
true}
}

最后需要在之前的DNS分析加上这两个函数,完整代码请看开头


rfc2671
rfc7871
dns-parameters
address-family-numbers