feat(xdns): multi-resolver fan-out + fix(kcp): reduce aggressive retransmissions#5872
feat(xdns): multi-resolver fan-out + fix(kcp): reduce aggressive retransmissions#5872nnemirovsky wants to merge 25 commits intoXTLS:mainfrom
Conversation
Add optional `resolvers` config field to XDNS finalmask. When set, the client sends DNS queries through public DNS resolvers instead of connecting directly to the server on port 53. - One UDP socket per resolver with independent receive goroutines - Round-robin query distribution across resolvers - Backward compatible: omitting resolvers preserves direct mode - Fix server sendLoop starvation under mKCP retransmission flood - Drain excess query records to skip stale queries - Reduce server response delay from 1s to 50ms - Increase server write queue from 512 to 4096
…s enabled The cwnd *= 20 multiplier allowed 20x more packets in flight than the congestion window, defeating the purpose of congestion control. When congestion is enabled, respect the actual cwnd without the multiplier. This prevents mKCP from flooding low-bandwidth transports like XDNS. Also increase connection timeout from 30s to 120s to accommodate high-latency transports like DNS tunneling.
|
粗略看了下,我设计 xdns 里的 closed 不需要 sync,不存在竞态,xdns 的服务端天然支持从不同 dns 收发数据,理论无需改动,多 conn 收发也只需改动客户端 怎么说呢,我能理解你的想法,但放在 mask 里并不合适,mask 不应该进行额外的 dial,目前是允许 xdns 在任意层的, 如果要接受这个 pr,要么把 xdns 剥离到独立传输层,要么加上限制 xdns 和 xicmp 一样只能处于最外层,且不能搭配 udphop 与 dialerproxy |
|
或许可以利用 writeto 的 addr 特性来实现 multi dns,如果能不新建 conn,那么可以留在 mask |
Address LjhAUMEM review: masks should not create new connections. Use WriteTo addr on the existing PacketConn to send to different resolvers instead of creating separate sockets. Revert server changes (server already supports data from different DNS sources). Remove sockopt files, sync changes, and layer validation.
XDNS 可以限制为必须在最外层,也就 noise 有点用,udphop 对它来说没啥用,
没看代码,这个特性就简单些用数组而不是 map 吧,随机选择,重复次数多的 ip 更容易被选中 |
|
@LjhAUMEM 或许改一下 noise 让它兼容一下 XDNS 在倒数第二层的情况 |
|
但公共 UDP 明文 DNS 基本上都是 53 端口而且这时候 DNS 又不需要握手,可能审查者就直接按 DNS 按单个包分析流量了
|
可以搞成忽略前面所有的 noise
|
|
不过这个最好搭配个 mux,kcp 客户端没有 mux 单条连接其实不会持续很久,客户端日志的大量 xdns closed 能证明这一点,再加上 multi dns, |
XDRIVE 强制 mux 所以 mux 的增强和分享已经提上日程了 |
|
If some of the DNS resolvers in the list become unavailable will it quickly switch to using the others? |
|
@nnemirovsky You can keep the original multi-connection method, but it should be possible to modify only the client side without changing the server side. |
Per review: separate UDP sockets per resolver avoids ISP source-port rate limiting. Each resolver has its own recvLoop goroutine. Client-only changes, server unchanged.
|
@hippo2025 Currently it's round-robin without failover. If a resolver stops responding, queries sent to it are lost and KCP handles retransmission on the next round. With 3 resolvers and 1 dead, throughput drops ~33% but the tunnel stays up. @LjhAUMEM Updated to use separate sockets per resolver (no server changes). Reverted all sync/server modifications from earlier. |
|
@nnemirovsky Do you mind if I make changes directly on your branch? @RPRX |
|
单个文件的修改可以点铅笔 要大改我一般都是关了在本地重新弄一个 写co author就是了 |
|
@LjhAUMEM go ahead, feel free to push directly to the branch. |
|
|
@LjhAUMEM 给你 Maintain 权限了 |
|
@nnemirovsky I think we can start testing {
"log": { "loglevel": "debug" },
"inbounds": [
{
"listen": "127.0.0.1",
"port": 1080,
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": true
}
}
],
"outbounds": [
{
"protocol": "vless",
"settings": {
"address": "127.0.0.1",
"port": 53,
"id": "5783a3e7-e373-51cd-8642-c83782b807c5",
"encryption": "none"
},
"streamSettings": {
"network": "kcp",
"kcpSettings": {
"mtu": 130
},
"finalmask": {
"udp": [
{
"type": "xdns",
"settings": {
"domain": "", // 1
"resolvers": [
"8.8.8.8:53",
"1.1.1.1:53",
"[2001:4860:4860::8888]:53",
"[2606:4700:4700::1111]:53"
]
}
}
]
}
}
}
]
}{
"log": { "loglevel": "debug" },
"inbounds": [
{
// "listen": "127.0.0.1",
"port": 53,
"protocol": "vless",
"settings": {
"clients": [
{
"id": "5783a3e7-e373-51cd-8642-c83782b807c5"
}
],
"decryption": "none"
},
"streamSettings": {
"network": "kcp",
"kcpSettings": {
"mtu": 900
},
"finalmask": {
"udp": [
{
"type": "xdns",
"settings": {
"domain": "" // 1
}
}
]
}
}
}
],
"outbounds": [
{
"protocol": "freedom"
}
]
} |
|
不是,我说这个域名的意思是,服务端也支持多个域名,然后客户端可以任意选用,不是用域名解析出 IP 地址 就和 ECH 那个拿别的域名查 config 是一个道理 |
|
done,不过可能得说明下 domains 仅服务端,代码里没对客户端做检查 |
|
The client must send queries to the public DNS resolvers with the correct domain. If there are multiple domains, then the client must send different queries. |
自己开多 outbound + balancers,还没迎来重构呢还一个劲的堆功能 |
|
Well, sorry, maybe it's OK for now. If the server supports multiple domains, then the cliend could have different outbounds for different domains, there is no real need to use several domains simultaneously. |
|
直接 break 掉现有配置吧,只支持 domains 和 resolvers,后者要写 example.com+udp://1.1.1.1:53 这样指定域名和 UDP
|
|
|
|
domains 是服务端的,resolvers 是客户端的 example.com+udp://1.1.1.1:53 这样的格式,先只支持 UDP |
|
|
|
|
48eda89 to
bb3304e
Compare
|
应该可以了 {
"log": { "loglevel": "debug" },
"inbounds": [
{
"listen": "127.0.0.1",
"port": 1080,
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": true
}
}
],
"outbounds": [
{
"protocol": "vless",
"settings": {
"address": "127.0.0.1",
"port": 53,
"id": "5783a3e7-e373-51cd-8642-c83782b807c5",
"encryption": "none"
},
"streamSettings": {
"network": "kcp",
"kcpSettings": {
"mtu": 130
},
"finalmask": {
"udp": [
{
"type": "xdns",
"settings": {
"resolvers": [
"xxx+udp://8.8.8.8:53",
"xxx+udp://1.1.1.1:53",
"xxx+udp://[2001:4860:4860::8888]:53",
"xxx+udp://[2606:4700:4700::1111]:53"
]
}
}
]
}
}
}
]
}{
"log": { "loglevel": "debug" },
"inbounds": [
{
// "listen": "127.0.0.1",
"port": 53,
"protocol": "vless",
"settings": {
"clients": [
{
"id": "5783a3e7-e373-51cd-8642-c83782b807c5"
}
],
"decryption": "none"
},
"streamSettings": {
"network": "kcp",
"kcpSettings": {
"mtu": 900
},
"finalmask": {
"udp": [
{
"type": "xdns",
"settings": {
"domains": [
"xxx"
]
}
}
]
}
}
}
],
"outbounds": [
{
"protocol": "freedom"
}
]
} |
|
@LjhAUMEM 是否有把旧版配置 break 掉,resolvers 是否支持多个相同的项(代表选中几率更高),是否是 crypto/rand |
|
旧配置 break 了
支持,不过域名和 ip 不是绑定的,可能出现 resolvers1 的 domain + resolvers4 的 ip
resolverIdx 只在 sendLoop 被修改,是轮询,但 writeto 里的 encode with domain 和这个不是同步 |
报错一下提醒迁移至新配置,能判断下出入站吗,防止位置写错了
轮询改成随机比较好吧 另外 ICMP 有一个这问题 #5879 不知道是不是 ping 被限速了,另开个 PR 也改成多 ip 的形式吧,再支持下掩码,利好 IPv6
|
好像不行
那就得先轮一圈取出活的 dns,再进行随机,现在的轮询碰到第一个活的就可以发了
那是因为要有客户端 echo 服务端才发 reply,多 ip 我晚点看看 |
|
啊还要判断 DNS 是不是活的吗,我觉得直接发就行了,用户既然敢写就默认它是可用的,没回复的话一次性 warn 一下? |
|
那现在针对没写 domains/resolvers 的报错有吗,针对 resolvers 中不是 udp:// 的报错有吗 |
Many public DNS resolvers are rate limited, also their rate limits could change quickly. It'll be hard to update share links quickly. Also, you might have 2 users connected to different ISPs, and some public DNS will work on one ISP and not on another. So if XDNS can drop dead resolvers or resolvers with ping that is too high, it would be great |
单个解析器不管活不活都发,多个自带个检测,这个检测几乎不占额外资源,我觉得挺好
在dial/listen的时候才能看到,我给infra也加一下吧,done |
|
|
|
没有输出是因为不通的数据只在一次dial周期,新dial又是新的,而且这个不通不是说一直不用,如果收到回包或者当前在用的变为不可用都将重新启用这个不通的 也就是处于薛定谔的状态,不太好输出,输出了也不一定保证不会复活 |
|
xicmp 要速度还得推翻现有的 ping 架构,等下 #5887,新版先不带 xicmp 吧, |
Cleaned up resubmission of #5871. Removed unnecessary files, squashed to 2 commits.
Changes
1. XDNS multi-resolver fan-out
Optional
resolversconfig field. When set, the client distributes DNS queries across multiple public resolvers within a single mKCP session for higher throughput.2. mKCP: respect congestion control (per RPRX suggestion in #5871)
The
cwnd *= 20multiplier allowed 20x more in-flight packets than the congestion window, defeating congestion control. Whencongestion: true, the multiplier is now skipped so mKCP doesn't flood low-bandwidth transports like XDNS.Connection timeout increased from 30s to 120s to accommodate DNS tunnel latency.
Test plan
TestParseResolverAddr: resolver address parsingTestResolverModeRoundTrip: mock resolver end-to-endTestMultiResolverDistribution: round-robin verificationTestDirectModeRoundTrip/TestResolverModeServerToClient: bidirectional data