fix: dedupe direct UDP session dials and skip self as DNS fallback - #126
Merged
Conversation
- directUDPRelay raced between the map lookup and insert: concurrent datagrams for the same flow dialed twice, orphaning one socket and letting its cleanup evict the LIVE map entry every ~2 minutes; add a directUDPFactory in-flight dedup (mirroring the proxied path) and make the read loop delete only its own session - the DNS forward server used 127.0.0.1 as a fallback upstream when TUN set it as the system DNS, recursing into itself while the builtin servers were unreachable; filter out its own listen address
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
背景
修复两个问题:
1. direct UDP 会话重复拨号导致连接泄漏
directUDPRelay在 map 查找与插入之间存在竞态窗口:同一 (client, target) 流的并发 datagram 会重复 dial,产生孤儿 socket 及其 read goroutine(最多泄漏至 2 分钟读超时);更严重的是孤儿 readLoop 退出时无条件删除 map 条目,会误删 LIVE 会话,导致长连接流(QUIC、游戏、VoIP)每 ~2 分钟 churn 一个新 socket。修复:
directUDPFactoryin-flight 去重(与 proxied 路径的udpExchangeFactory对称),保证每个流恰好一个 socket + 一个 readLoopclosing标志,服务关闭期间新建的 socket 立即关闭,不泄漏2. DNS forward 服务器以自身作为 fallback 上游造成递归
TUN 模式 +
EnableForwardDNS时系统 DNS 被设置为 127.0.0.1(即本 forward 服务器自身)。当 builtin DNS 服务器全部不可达时,fallback 会递归回自身(查询 -> fallback -> 127.0.0.1:53 -> 同一查询),堆积 goroutine 和 UDP socket 直到查询超时。修复:
systemDNSServers()过滤掉指向自身监听地址的条目。测试
TestDirectUDPRelayConcurrentDial:验证并发 datagram 只拨号一次TestDirectUDPRelayStaleReadLoopKeepsLiveEntry:验证孤儿 readLoop 退出不误删 LIVE 条目(含正向对照)TestForwardQuerySkipsSelfAsUpstream:验证系统 DNS 中的自身条目被过滤go test ./client/dns/...与go test ./client/proxy/ -run DirectUDP全部通过