dns ptr反查和报文解析实践
目录
前文尝试单次查询对比数据小结
实例分析Obsoleting IQUERYPTR反查报文请求报文标志
返回报文标志答案nameResource Data
收获参考原文地址
前文
之前在php扫描同路由下的设备中用到php 内置的gethostbyaddr去反查地址名称,但速度太慢了。 如下数据,扫描加反查要22秒。
gethostbyaddr
ip=192.168.2.1 name=RT-AC3100-C45B.lan send=1 recive=1 loss=0.00% min=2.53ms max=2.53ms avg=2.53ms
ip=192.168.2.13 name=BL-c0-74-68.lan send=1 recive=1 loss=0.00% min=49.27ms max=49.27ms avg=49.27ms
ip=192.168.2.50 name=192.168.2.50 send=1 recive=1 loss=0.00% min=2.40ms max=2.40ms avg=2.40ms
ip=192.168.2.78 name=TL-IPC42A-4.lan send=1 recive=1 loss=0.00% min=7.54ms max=7.54ms avg=7.54ms
ip=192.168.2.205 name=MI5-xiaomishouji.lan send=1 recive=1 loss=0.00% min=158.06ms max=158.06ms avg=158.06ms
ip=192.168.2.215 name=lwb-R5zen.lan send=1 recive=1 loss=0.00% min=0.23ms max=0.23ms avg=0.23ms
ip=192.168.2.240 name=192.168.2.240 send=1 recive=1 loss=0.00% min=3.25ms max=3.25ms avg=3.25ms
total time_ms 22011.496067047
total cnt 6
尝试
在我思考如何能加速度的过程中,我逛到了php 的官方文档。其中有个评论是自己构建了一个可以设定超时,向指定服务器查询的gethostbyaddr_timeout。一顿操作,试了下。效果还可以,同样的代码,扫描加反查只要3.8秒。
gethostbyaddr_timeout
total time_ms 3872.8039264679
total cnt 6
单次查询对比
数据
没有host返回的
目标 1.0.0.2结果
次序方法返回花费时间(s)1gethostbyaddr1.0.0.29.21347713470462gethostbyaddr_timeout1.0.0.20.0140390396118163gethostbyaddr1.0.0.29.02304315567024gethostbyaddr_timeout1.0.0.20.013981819152832 有host返回的
目标 8.8.8.8清除本地dns缓存
次序方法返回花费时间(s)1gethostbyaddrdns.google0.00718188285827642gethostbyaddr_timeoutdns.google0.00413489341735843gethostbyaddrdns.google0.000324964523315434gethostbyaddr_timeoutdns.google0.0037519931793213
清除本地dns缓存
次序方法返回花费时间(s)1gethostbyaddrdns.google0.00397014617919922gethostbyaddr_timeoutdns.google0.00485181808471683gethostbyaddrdns.google0.000657081604003914gethostbyaddr_timeoutdns.google0.0048589706420898
小结
无host返回,gethostbyaddr_timeout 优势很明显。有host返回时
无本地dns缓存时
gethostbyaddr_timeout 首次稍微快一倍 有本地dns缓存时,gethostbyaddr 和 gethostbyaddr 差距很小 这应该是本地查和到指定dns服务器查的差距gethostbyaddr 首次调用后之后的速度就很快了,似乎有进程内缓存gethostbyaddr_timeout 每次调用时间差不多
实例分析
首先我的脚本是扫描局域网内的设备,局域网内的路由器本身就是一个dns服务器。对于该场景下,明显通过gethostbyaddr_timeout向路由查询会就可以了。gethostbyaddr 会进行迭代查询,一层又一层,如果没有host返回,9s的阻塞不能接受的。并且,每个ip是运行没有host的,这并不是一个强制性的规范,如果直接使用,妥妥的九九八十一难了。正如开头,刚好2个设备就是没有host的,其22秒的组成也就是 9 + 9 + 4。
Obsoleting IQUERY
IQUERY 已过时, 且已被弃用。主要原因是:
概念不明确,逆向查询处理会给服务器带来相当沉重的负担并且需要维护一个键值数据库对于大型权威服务器会返回包过大容易被发起拒绝服务攻击服务商通常会禁用这个功能无法告知去那里获取全部记录没有成型应用有更好替代方案:指针记录(PTR)的反向解析
PTR反查报文
目标 192.168.2.215,PTR 发送一个查询报文,但查询内容为是 192.168.2.15 书写顺序相反的 215.2.168.192 作为前缀的 in-addr.arpa 子域名,即
215.2.168.192.in-addr.arpa
implode('.',array_reverse(explode('.','192.168.2.215')))
// 215.2.168.192
请求报文
## 头部
53 49 1 0 # 53 49 标识 1 RD=1 ,表示客户端希望服务器可以执行递归查询
0 1 0 0 # 问题记录 1个
0 0 0 0
## 问题
3 # 长度 3
50 49 53 # 215
1 # 长度 1
50 # 2
3 # 长度 3
49 54 56 # 168
3 # 长度 3
49 57 50 # 192
7 # 长度 7
105 110 45 97 100 100 114 # in-addr
4 # 长度 4
97 114 112 97 # arpa
0 # 表示 根域
0 12 0 1 # type 12 PTR反查, class 1 ipv4
标志
其中 53 49 1 0 的标志“1 0”,转为二进制为
0 0000 0 0 1 0 000 0000
qurey codeopcode x4AATCRDRA保留位 x 3response code x 400001000查询请求标准查询-非截短递归查询-0没有差错
返回报文
// 头部
55 53 133 128
0 1 0 1 ## 01 问题1 ,01 答案 1
0 0 0 0
// 问题
3 50 49 53
1 50
3 49 54 56
3 49 57 50
7 105 110 45 97 100 100 114
4 97 114 112 97
0
0 12 0 1
// 答案
192 12 ## 压缩域名
0 12 0 1 ## type ,class
0 0 0 0 ## TTL
0 15 ## 资源长度
9 ## 长度
108 119 98 45 82 53 122 101 110 ## 见 Resource Data
3 ## 长度
108 97 110 ## 见 Resource Data
0 ## 根域
标志
“55 53 133 128”中的标志“133 128”,转为二进制为
1 0000 1 0 1 1 000 0000
qurey codeopcode x4AATCRDRA保留位 x 3response code x 410101100查询应答标准查询权威回答非截短递归查询服务器支持递归查询0没有差错
答案
name
192 12 转为 二进制,这里并没有直接保存name,而是域名压缩,因为在问题里面已经包含了域名信息,所以这里采用字节偏移来表示。
11000000 1100
域名在报文中第二次出现时,只用两个字节来保存。第一个字节最高两位都是 1 ,余下部分和第二个字节组合在一起,表示域名第一次出现时在报文中的偏移量。通过这个偏移量,就可以找到对应的域名。
这里的12刚好是头部的长度,如果只有一个问题,这应该是个固定值,如果还有一个问题,那么第二个问题这个偏移就会再加上第一个问题的长度。
Resource Data
108 119 98 45 82 53 122 101 110
(('108 119 98 45 82 53 122 101 110'.split(' ')).map(function(item){return String.fromCharCode(item)})).join('')
// 'lwb-R5zen'
108 97 110
(('108 97 110'.split(' ')).map(function(item){return String.fromCharCode(item)})).join('')
// 'lan'
点拼接也就是
ip=192.168.2.215 name=lwb-R5zen.lan
收获
原本以为反查用的是dns中的IQUERY,甚至在看到IQUERY启用前,我还以为PTR只是反查报文的一种格式,其实二者完全不一样。dns的报文头是最容易理解的,固定长度,一一对应即可。而到了问题和答案,除了特定的结构外,还需要通过计算长度去获取后续信息,即便在PNG格式中已经接触过一次,还是在阅读上花费了不少时间。应答的name压缩还是让我有点惊讶,最开始还理所应当的以为是问题一样,看半天怎么都对不上。直到发现是name压缩,不得不说,确实巧妙。之前已经看过并摘录了DNS报文格式,但实际上,读抄远远不如上手操作一番,唯一的好处就是查的时候方便许多。
参考
gethostbyaddr_timeout 反向DNS 小菜学网络之DNS报文格式 [摘]DNS报文格式解析
原文地址
dns ptr反查和报文解析实践 https://bjun.tech/blog/xphp/131