when RULE_INIT {
set ::header_without_id [binary format S5 {0x8180 0x0001 0x0001 0x0000 0x0000}]
#不带DNS id号,直接构造ID号后面的5个值
#predefined fixed 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 |
# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
#
#opcode = QUERY, rcode = NOERROR
#header flags: response, auth. answer, want recursion, recursion avail.
#questions = 1, answers = 1, authority records = 0, additional = 0
set ::answerpart [binary format S6c4 {0xC00C 0x0001 0x0001 0x0000 0x0D1B 0x0004} {200 100 4 10}]
#0xC00C 表示等于查询中的,抓包发现这里固定是0xc00c
#predefined Fixed Answer section
#Name: same as qestion
#Type: Host address
#Class: INET
#Time to live: 55 minutes, 55seconds
#Data length: 4
#Addr: 200.100.4.10, you can modify your own IP address here
#Data Structure
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
# | Name |
# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
# | Answer Type |
# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
# | Class |
# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
# | Time to live part 1 |
# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
# | Time to live part 2 |
# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
# | IP Address part 1 |
# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
# | IP Address part 2 |
# +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
}
when SERVER_DATA {
#check rcode
binary scan [ string range [UDP::payload] 2 3 ] S sflags
#string range 取第三字节到第4字节结束(从0开始计,id占去2个字节,2-3正好是flags占用的2个字节)
#binary scan 负责解码这2个字节,并解释成2进制,参数S (S后没具体数字值)表示截取16个bit位并以big-endian方式排序,解码出的二进制放入sflags变量中
#big-endian 方式是一种网络协议在内存中排序方式,高位字节(即2进制中前面的位)在内存中的低位,因此与正常思维一致,顺序在内存中排列
#如果我们将0x1234abcd写入到以0x0000开始的内存中,则结果为
# big-endian little-endian
#0x0000 0x12 0xcd
#0x0001 0x34 0xab
#0x0002 0xab 0x34
#0x0003 0xcd 0x12
set rcode [expr $sflags & 0x000f]
#将DNS头中的flags位和0x000f进行与运算,因为flags中的后4位是rcode位,DNS应答包是否是一个错误包,由这4位指示,f等于1111,相当于只在乎后4位,其他不关心(类似掩码)
if {$rcode == 3 }{
#如果rcode=3
# skip the DNS header, jump to the Q
NAME part of first QUESTIO
N
# byte contains the first part length
#直接跳到dns应答包中的queries部分扫描(即上图结构中的name部分,该部分占2个字节)
binary scan [string range [UDP::payload] 12 13 ] c foo
#截取域名中的主机字节数,例如ww22w.mycisco.cn 中的ww22w 是5个字节
set byte [expr $foo & 0xff]
# make the byte an unsigned integer,返回域名主机头的位数,ww22w是5
#从queries部分截取的1字节进行所有位都关心的与运算
set offset 12
# initialize our posisition in the QNAME parsing and the text QNAME,将游标放在queries部分的开头
set i 0
# $i is a sanity check so this logic won't spin on invalid QNAMEs
############# /extract QNAME from QUESTION header下面部分主要为了获取dns应答中queries中包含的那个域名和type值#############
while {byte>0 &&i < 10} {
#i值控制域名中一共几个点分,一般不会有超过10个点分开的域名解析吧。。
# grab a part and put it in our text QNAME section
set offset [expr offset +byte + 1]
#将游标放到主机头后例如ww22w.mycisco.cn,放到mycisco.cn的开头,上面+1表示偏移那个点。
#在queries中的name部分,域名中1个字母占1个字节,在每个点分段落的还多出一个字节表示了该段落占几个字节,例如ww22w占5个字节,所以
#ww22w,实际表示为05 77 77 32 32 77,05就表示主机头占了几个字节,同理,后面的段落也是第一个字节表示该段落字节总数。域名最后还会多占一个空字节。
set byte [string range [UDP::payload] offset [exproffset + 1]]
#抓取下一个域名段落的长度,例如ww22w.mybind.com中的mybind长度
# grab the length of the next part, and make it an unsigned integer
binary scan $byte c foo
#转化为长度
set byte [expr $foo & 0xff]
#全关心位的于运算,如果不等于0说明该位置有字母存在,如果运算后变为0,说明已经没有抓取到字母了
incr i
}
incr offset
# increment offset past the final part so it points at the QTYPE field
#确保指针游到queries中name部分的最后,这样就等于到了type部分的开始
############# extract QTYPE from QUESTION header #############
# grab the next 2 bytes that represent the QTYPE
binary scan [string range [UDP::payload] offset [exproffset + 2]] S qtype
#上面抓取type中的2个字节(16位),输出给qtype
# see if the QTYPE is 0x0001 (TYPE_AAAA), if it's a A query, then replace the content
if {$qtype == 0x0001} {
#Pack the respond packet
#first a2 is id
#second a* is predefined header without id
#third a* is question part extract from return packet
#fourth a* is predefined answer section
UDP::payload replace 0 0 [binary format a2a*a*a* [string range [UDP::payload] 0 1] ::header_without_id [string range [UDP::payload] 12 [exproffset+3]] $::answerpart]
#将udp有效负载从0开始的全部替换成后面构造的二进制。binary format中的a表示用二进制填充a后指定的位数,如果后面的变量里的值不够指定位数就用0补充。 *表示变量有多少就填多少。
#[string range [UDP::payload] 12 [expr $offset+3]] 此时offset游标落在queries中的TYPE开头,此后4字节是type和class值,因此+3正好是4字节
}
}
}
文章评论
那请问如何用linux的dns服务器实现这个功能呢?也需要改数据包么?
@S_niper 应该有第三方软件可以实现吧我想。原理应都类似。新年快乐。