sing-box
sing-box copied to clipboard
由于UDP DNS响应长度超过512B进行截取导致某些域名无法获取IP信息
Operating system
Linux
System version
openwrt 23.05
Installation type
Original sing-box Command Line
If you are using a graphical client, please provide the version of the client.
No response
Version
sing-box version 1.8.5
Environment: go1.21.6 linux/arm64
Tags: with_gvisor,with_quic,with_dhcp,with_wireguard,with_ech,with_utls,with_reality_server,with_acme,with_clash_api
Revision: b27bc45cf2550e00823e799a313f8226443aa58e
CGO: disabled
Description
在 17aebc5 对 cn-beijing-data.aliyundrive.net 这类响应超出 512B 的域名进行修复后,又出现 api.aliyundrive.com 连续AAAA记录,最后一个A类记录IP信息,由于sing-box DNS响应没有进行压缩,导致这类地址被截断后拿不到ip信息。我猜测压缩会影响处理性能,但又觉得能正确处理更重要些,是否可以在超出限制的情况下尝试压缩再去截取那。
$ nslookup api.aliyundrive.com
Server: 127.0.0.1
Address: 127.0.0.1:53
Non-authoritative answer:
Non-authoritative answer:
api.aliyundrive.com canonical name = beijing.tfe.alibaba-clould.alibabacorp.com
beijing.tfe.alibaba-clould.alibabacorp.com canonical name = beijing.tfe.alibaba-clould.alibabacorp.com.gds.alibabadns.com
beijing.tfe.alibaba-clould.alibabacorp.com.gds.alibabadns.com canonical name = bj-static.tfe.alibaba-clould.alibabacorp.com
bj-static.tfe.alibaba-clould.alibabacorp.com canonical name = bj-static.tfe.alibaba-clould.alibabacorp.com.gds.alibabadns.com
$ curl api.aliyundrive.com
curl: (6) Could not resolve host: api.aliyundrive.com
diff --git a/outbound/dns.go b/outbound/dns.go
index 0f003377..529c796c 100644
--- a/outbound/dns.go
+++ b/outbound/dns.go
@@ -241,7 +241,7 @@ func (d *DNS) newPacketConnection(ctx context.Context, conn N.PacketConn, readWa
return err
}
timeout.Update()
- response = truncateDNSMessage(response, 512) // TODO: add an option to custom UDP buffer size
+ response = compressAndTruncateDNSMessage(response, 512) // TODO: add an option to custom UDP buffer size
responseBuffer := buf.NewSize(dns.FixedPacketSize)
responseBuffer.Resize(1024, 0)
n, err := response.PackBuffer(responseBuffer.FreeBytes())
@@ -265,12 +265,17 @@ func (d *DNS) newPacketConnection(ctx context.Context, conn N.PacketConn, readWa
return group.Run(fastClose)
}
-func truncateDNSMessage(response *mDNS.Msg, maxLen int) *mDNS.Msg {
+func compressAndTruncateDNSMessage(response *mDNS.Msg, maxLen int) *mDNS.Msg {
responseLen := response.Len()
if responseLen <= maxLen {
return response
}
response = response.Copy()
+ response.Compress = true
+ responseLen = response.Len()
+ if responseLen <= maxLen {
+ return response
+ }
for len(response.Answer) > 0 && responseLen > maxLen {
response.Answer = response.Answer[:len(response.Answer)-1]
response.Truncated = true
Reproduction
如上
Logs
No response
Integrity requirements
- [x] I confirm that I have read the documentation, understand the meaning of all the configuration items I wrote, and did not pile up seemingly useful options or default values.
- [X] I confirm that I have provided the server and client configuration files and process that can be reproduced locally, instead of a complicated client configuration file that has been stripped of sensitive data.
- [X] I confirm that I have provided the simplest configuration that can be used to reproduce the error I reported, instead of depending on remote servers, TUN, graphical interface clients, or other closed-source software.
- [X] I confirm that I have provided the complete configuration files and logs, rather than just providing parts I think are useful out of confidence in my own intelligence.
最近遇到了一点问题 https://github.com/xiaorouji/openwrt-passwall/issues/2960 我比较好奇的是,为什么 Sing-Box DNS 的响应会比 dns2tcp/Xray DNS 大很多?
Sing-Box DNS
root@OpenWrt:~# dig www.youtube.com -p 15353
;; Truncated, retrying in TCP mode.
; <<>> DiG 9.18.24 <<>> www.youtube.com -p 15353
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48655
;; flags: qr rd ra; QUERY: 1, ANSWER: 17, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; MBZ: 0x0072, udp: 1232
;; QUESTION SECTION:
;www.youtube.com. IN A
;; ANSWER SECTION:
www.youtube.com. 114 IN CNAME youtube-ui.l.google.com.
youtube-ui.l.google.com. 114 IN A 172.217.25.14
youtube-ui.l.google.com. 114 IN A 142.250.66.46
youtube-ui.l.google.com. 114 IN A 142.250.66.110
youtube-ui.l.google.com. 114 IN A 142.250.204.110
youtube-ui.l.google.com. 114 IN A 142.250.66.78
youtube-ui.l.google.com. 114 IN A 142.250.207.78
youtube-ui.l.google.com. 114 IN A 172.217.24.238
youtube-ui.l.google.com. 114 IN A 142.250.204.46
youtube-ui.l.google.com. 114 IN A 172.217.27.46
youtube-ui.l.google.com. 114 IN A 172.217.24.110
youtube-ui.l.google.com. 114 IN A 142.250.204.78
youtube-ui.l.google.com. 114 IN A 142.250.199.78
youtube-ui.l.google.com. 114 IN A 142.251.222.206
youtube-ui.l.google.com. 114 IN A 142.250.66.142
youtube-ui.l.google.com. 114 IN A 172.217.31.14
youtube-ui.l.google.com. 114 IN A 172.217.27.14
;; Query time: 29 msec
;; SERVER: 127.0.0.1#15353(127.0.0.1) (TCP)
;; WHEN: Sat Feb 24 02:05:13 CST 2024
;; MSG SIZE rcvd: 720
dns2tcp
root@OpenWrt:~# dig www.youtube.com -p 15353
; <<>> DiG 9.18.24 <<>> www.youtube.com -p 15353
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 7423
;; flags: qr rd ra; QUERY: 1, ANSWER: 17, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;www.youtube.com. IN A
;; ANSWER SECTION:
www.youtube.com. 86 IN CNAME youtube-ui.l.google.com.
youtube-ui.l.google.com. 86 IN A 142.250.204.142
youtube-ui.l.google.com. 86 IN A 142.250.204.46
youtube-ui.l.google.com. 86 IN A 172.217.24.78
youtube-ui.l.google.com. 86 IN A 142.250.204.78
youtube-ui.l.google.com. 86 IN A 216.58.200.238
youtube-ui.l.google.com. 86 IN A 172.217.27.46
youtube-ui.l.google.com. 86 IN A 142.250.204.110
youtube-ui.l.google.com. 86 IN A 142.251.220.46
youtube-ui.l.google.com. 86 IN A 172.217.31.14
youtube-ui.l.google.com. 86 IN A 172.217.25.14
youtube-ui.l.google.com. 86 IN A 172.217.24.238
youtube-ui.l.google.com. 86 IN A 172.217.27.14
youtube-ui.l.google.com. 86 IN A 172.217.24.110
youtube-ui.l.google.com. 86 IN A 216.58.203.78
youtube-ui.l.google.com. 86 IN A 142.251.220.14
youtube-ui.l.google.com. 86 IN A 142.250.199.78
;; Query time: 59 msec
;; SERVER: 127.0.0.1#15353(127.0.0.1) (UDP)
;; WHEN: Sat Feb 24 02:17:57 CST 2024
;; MSG SIZE rcvd: 334
Xray DNS
root@OpenWrt:~# dig www.youtube.com -p 15353
; <<>> DiG 9.18.24 <<>> www.youtube.com -p 15353
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 13693
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 16, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;www.youtube.com. IN A
;; ANSWER SECTION:
www.youtube.com. 600 IN A 142.250.199.78
www.youtube.com. 600 IN A 172.217.24.238
www.youtube.com. 600 IN A 172.217.24.78
www.youtube.com. 600 IN A 142.250.204.110
www.youtube.com. 600 IN A 142.250.204.46
www.youtube.com. 600 IN A 142.250.207.78
www.youtube.com. 600 IN A 142.250.204.78
www.youtube.com. 600 IN A 172.217.27.46
www.youtube.com. 600 IN A 172.217.31.14
www.youtube.com. 600 IN A 142.250.204.142
www.youtube.com. 600 IN A 172.217.24.110
www.youtube.com. 600 IN A 216.58.200.238
www.youtube.com. 600 IN A 172.217.25.14
www.youtube.com. 600 IN A 216.58.203.78
www.youtube.com. 600 IN A 172.217.27.14
www.youtube.com. 600 IN A 142.250.66.142
;; Query time: 0 msec
;; SERVER: 127.0.0.1#15353(127.0.0.1) (UDP)
;; WHEN: Sat Feb 24 14:46:15 CST 2024
;; MSG SIZE rcvd: 289
@mm11253 sing-box没有对dns响应体进行压缩,可能出于性能考虑,目前还没选项进行设置,还在等开发者回复 https://github.com/SagerNet/sing-box/pull/1442
@mm11253 sing-box没有对dns响应体进行压缩,可能出于性能考虑,目前还没选项进行设置,还在等开发者回复 #1442
引用隔壁 ChinaDNS-NG 作者的回复 https://github.com/zfl9/chinadns-ng/issues/144#issuecomment-1962291623
拒绝连接是因为 chinadns-ng 目前还没实施 tcp 监听。用 zig 重写的 1.0/2.0 版本已经加入 tcp 支持了。
结合你引用的几个 issue 推测,有这几方面的原因:
- 上游未遵循 EDNS 的 udp bufsz 扩展信息,你给出的示例显示 dig 的 bufsz 是 1232 字节,即使超过 512 字节的响应,也不应该设置 TC 标志,因为实际缓冲区大小是 1232 字节,并不会“截断”
- 上游未实施 DNS 域名压缩导致响应消息比较大,容易超出传统的 512 字节限制,而又由于第 1 条原因,导致很多“不必要的截断,然后通过 TCP 重试”的行为
- chinadns-ng 目前尚未支持 TCP 监听/上游,导致 dig(查询客户端)在收到带 TC 标志的udp响应后,通过 tcp 重试查询时,未能连接上 chinadns-ng,于是出现连接被拒,查询超时。
所以压缩还是有必要的吧
Still in Todo.
https://github.com/SagerNet/sing-box/blob/ab272cd9533ecb699743502ebb1a169fc6d1655d/outbound/dns.go#L244
https://www.rfc-editor.org/rfc/rfc2671 https://www.rfc-editor.org/rfc/rfc1035
I gotcha, but I reckon compressing the response takes precedence over customizing the UDP buffer size.
应已在最新版本修复。
Most DNS related issues had been fixed. but DNS response still not compressed by default? Even when the request buffer size larger than 512 bytes it should try to get the response under 512 bytes. The 512 bytes guarantees the DNS packets can be reassembled if fragmented in the transit.
The first one from dnsmasq which is also hijacked by singbox.
root@sam46:~# kdig www.youtube.com
;; ->>HEADER<<- opcode: QUERY; status: NOERROR; id: 43430
;; Flags: qr rd ra; QUERY: 1; ANSWER: 17; AUTHORITY: 0; ADDITIONAL: 0
;; QUESTION SECTION:
;; www.youtube.com. IN A
;; ANSWER SECTION:
www.youtube.com. 1794 IN CNAME youtube-ui.l.google.com.
youtube-ui.l.google.com. 1794 IN A 64.233.170.93
youtube-ui.l.google.com. 1794 IN A 64.233.170.190
youtube-ui.l.google.com. 1794 IN A 142.251.175.190
youtube-ui.l.google.com. 1794 IN A 142.251.175.91
youtube-ui.l.google.com. 1794 IN A 142.251.175.136
youtube-ui.l.google.com. 1794 IN A 142.251.175.93
youtube-ui.l.google.com. 1794 IN A 74.125.24.190
youtube-ui.l.google.com. 1794 IN A 74.125.24.93
youtube-ui.l.google.com. 1794 IN A 74.125.24.136
youtube-ui.l.google.com. 1794 IN A 74.125.130.136
youtube-ui.l.google.com. 1794 IN A 74.125.68.136
youtube-ui.l.google.com. 1794 IN A 74.125.68.190
youtube-ui.l.google.com. 1794 IN A 74.125.68.93
youtube-ui.l.google.com. 1794 IN A 74.125.68.91
youtube-ui.l.google.com. 1794 IN A 64.233.170.91
youtube-ui.l.google.com. 1794 IN A 64.233.170.136
;; Received 326 B
;; Time 2024-02-29 17:41:50 WITA
;; From 127.0.0.1@53(UDP) in 0.6 ms
root@sam46:~# kdig www.youtube.com @1.1.1.1
;; ->>HEADER<<- opcode: QUERY; status: NOERROR; id: 23934
;; Flags: qr rd ra; QUERY: 1; ANSWER: 17; AUTHORITY: 0; ADDITIONAL: 0
;; QUESTION SECTION:
;; www.youtube.com. IN A
;; ANSWER SECTION:
www.youtube.com. 300 IN CNAME youtube-ui.l.google.com.
youtube-ui.l.google.com. 300 IN A 64.233.170.91
youtube-ui.l.google.com. 300 IN A 64.233.170.136
youtube-ui.l.google.com. 300 IN A 64.233.170.93
youtube-ui.l.google.com. 300 IN A 64.233.170.190
youtube-ui.l.google.com. 300 IN A 142.251.175.190
youtube-ui.l.google.com. 300 IN A 142.251.175.91
youtube-ui.l.google.com. 300 IN A 142.251.175.136
youtube-ui.l.google.com. 300 IN A 142.251.175.93
youtube-ui.l.google.com. 300 IN A 74.125.24.190
youtube-ui.l.google.com. 300 IN A 74.125.24.93
youtube-ui.l.google.com. 300 IN A 74.125.24.136
youtube-ui.l.google.com. 300 IN A 74.125.130.136
youtube-ui.l.google.com. 300 IN A 74.125.68.136
youtube-ui.l.google.com. 300 IN A 74.125.68.190
youtube-ui.l.google.com. 300 IN A 74.125.68.93
youtube-ui.l.google.com. 300 IN A 74.125.68.91
;; Received 709 B
;; Time 2024-02-29 17:41:57 WITA
;; From 1.1.1.1@53(UDP) in 150.9 ms
https://github.com/miekg/dns/blob/2230854ba97edcf29ac55a1f274e49cec11bf9bb/msg_truncate.go#L46
Looking at popular DNS solution like blocky and adguardhome it force enable compression. Maybe we should follow that?
diff --git a/client_truncate.go b/client_truncate.go
index a0b4afd..90cf3dd 100644
--- a/client_truncate.go
+++ b/client_truncate.go
@@ -14,6 +14,7 @@ func TruncateDNSMessage(request *dns.Msg, response *dns.Msg, frontHeadroom int)
}
}
response.Truncate(maxLen)
+ response.Compress = true
buffer := buf.NewSize(frontHeadroom + 1 + maxLen)
buffer.Resize(frontHeadroom, 0)
rawMessage, err := response.PackBuffer(buffer.FreeBytes())
https://github.com/0xERR0R/blocky/blob/efc14d25ca57dbc0652d4e9784ba2e61646caeaa/server/server.go#L652
https://github.com/AdguardTeam/dnsproxy/blob/68d417bfdc10e87e5d268aca3bd055e9fd88d206/proxy/dnscontext.go#L128
https://github.com/XTLS/Xray-core/blob/8fe8aa5432d6f14f62aaecd57d7f35fda04d5c0e/proxy/dns/dns.go#L276
https://github.com/MetaCubeX/mihomo/blob/7eb16a098a92e43c4a8871c0103d670462252bbc/dns/server.go#L35
这种情况不是应该用TCP重传吗?为啥要改UDP响应?