-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Description
验证步骤
- 我已经阅读了 文档,确认了该功能没有实现
- 我已在 Issue Tracker 中寻找过我要提出的功能请求,并且没有找到
- 我是中文用户,而非其他语言用户
描述
根据代码分析,这是因为 metadata.DNSMode 的设置基于全局 DNS 配置,而不是检查具体域名是否真的被分配了 fake IP。
问题原因
当你配置了 fake-ip-filter 后,虽然被过滤的域名确实不会被分配 fake IP,但在连接元数据处理阶段,DNSMode 的设置逻辑有问题: 1
在 preHandleMetadata 函数中,当通过 IP 反查到域名时:
- 首先设置
metadata.DNSMode = C.DNSMapping(311行) - 然后只要
resolver.FakeIPEnabled()返回 true(即全局启用了 fake-ip 模式),就直接设置metadata.DNSMode = C.DNSFakeIP(314行)
关键问题:这里的判断使用的是 resolver.FakeIPEnabled(),这只检查全局是否启用了 fake-ip 模式,而没有检查当前域名是否真的在 fake IP 池中或是否被 fake-ip-filter 排除。 2
fake-ip-filter 的实际工作原理
fake-ip-filter 确实在 DNS 解析阶段生效了: 3
在 withFakeIP 中间件中,如果域名被 fakePool.ShouldSkipped(host) 判定为应该跳过(156行),就会调用 next(ctx, r) 继续走正常的 DNS 解析流程,不会分配 fake IP。 4
总结
这是一个设计上的不一致:
- DNS 解析层面:fake-ip-filter 正确工作,被过滤的域名不会被分配 fake IP
- 元数据记录层面:DNSMode 只反映全局配置(是否启用 fake-ip),不反映实际解析方式
因此即使域名被 fake-ip-filter 排除了,WebUI 日志中的 dnsmode 仍然显示为 fakeip,因为它显示的是全局配置状态,而不是该域名的实际解析方式。
Notes
要准确判断一个域名是否真的使用了 fake IP,需要检查该 IP 是否真的在 fake IP 池中,而不是仅看 DNSMode 字段。这个 DNSMode 字段更多反映的是配置的 enhanced-mode,而不是实际的解析行为。
Citations
File: tunnel/tunnel.go (L305-330)
func preHandleMetadata(metadata *C.Metadata) error {
// preprocess enhanced-mode metadata
if needLookupIP(metadata) {
host, exist := resolver.FindHostByIP(metadata.DstIP)
if exist {
metadata.Host = host
metadata.DNSMode = C.DNSMapping
if resolver.FakeIPEnabled() {
metadata.DstIP = netip.Addr{}
metadata.DNSMode = C.DNSFakeIP
} else if node, ok := resolver.DefaultHosts.Search(host, false); ok {
// redir-host should lookup the hosts
metadata.DstIP, _ = node.RandIP()
} else if node != nil && node.IsDomain {
metadata.Host = node.Domain
}
} else if resolver.IsFakeIP(metadata.DstIP) {
return fmt.Errorf("fake DNS record %s missing", metadata.DstIP)
}
} else if node, ok := resolver.DefaultHosts.Search(metadata.Host, true); ok {
// try use domain mapping
metadata.Host = node.Domain
}
return nil
}File: dns/enhancer.go (L17-19)
func (h *ResolverEnhancer) FakeIPEnabled() bool {
return h.mode == C.DNSFakeIP
}File: dns/middleware.go (L150-185)
func withFakeIP(fakePool *fakeip.Pool) middleware {
return func(next handler) handler {
return func(ctx *context.DNSContext, r *D.Msg) (*D.Msg, error) {
q := r.Question[0]
host := strings.TrimRight(q.Name, ".")
if fakePool.ShouldSkipped(host) {
return next(ctx, r)
}
switch q.Qtype {
case D.TypeAAAA, D.TypeSVCB, D.TypeHTTPS:
return handleMsgWithEmptyAnswer(r), nil
}
if q.Qtype != D.TypeA {
return next(ctx, r)
}
rr := &D.A{}
rr.Hdr = D.RR_Header{Name: q.Name, Rrtype: D.TypeA, Class: D.ClassINET, Ttl: dnsDefaultTTL}
ip := fakePool.Lookup(host)
rr.A = ip.AsSlice()
msg := r.Copy()
msg.Answer = []D.RR{rr}
ctx.SetType(context.DNSTypeFakeIP)
setMsgTTL(msg, 1)
msg.SetRcode(r, D.RcodeSuccess)
msg.Authoritative = true
msg.RecursionAvailable = true
return msg, nil
}
}
}File: component/fakeip/pool.go (L69-85)
// ShouldSkipped return if domain should be skipped
func (p *Pool) ShouldSkipped(domain string) bool {
should := p.shouldSkipped(domain)
if p.mode == C.FilterWhiteList {
return !should
}
return should
}
func (p *Pool) shouldSkipped(domain string) bool {
for _, matcher := range p.host {
if matcher.MatchDomain(domain) {
return true
}
}
return false
}如果deepwiki分析错误或者这是预期行为,抱歉打扰