Skip to content

[Feature]希望 fake-ip-filter添加后webui日志里能显示对应域名dnsmode为正常dns #2322

@fatelove42

Description

@fatelove42

验证步骤

  • 我已经阅读了 文档,确认了该功能没有实现
  • 我已在 Issue Tracker 中寻找过我要提出的功能请求,并且没有找到
  • 我是中文用户,而非其他语言用户

描述

根据代码分析,这是因为 metadata.DNSMode 的设置基于全局 DNS 配置,而不是检查具体域名是否真的被分配了 fake IP

问题原因

当你配置了 fake-ip-filter 后,虽然被过滤的域名确实不会被分配 fake IP,但在连接元数据处理阶段,DNSMode 的设置逻辑有问题: 1

preHandleMetadata 函数中,当通过 IP 反查到域名时:

  1. 首先设置 metadata.DNSMode = C.DNSMapping(311行)
  2. 然后只要 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分析错误或者这是预期行为,抱歉打扰

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions