Skip to content

DNS模块:域名命中多个dnsServerObject规则时,缺少idx排序导致行为混沌不可预测 #4508

@Meo597

Description

@Meo597

完整性要求

  • 我保证阅读了文档,了解所有我编写的配置文件项的含义,而不是大量堆砌看似有用的选项或默认值。
  • 我提供了完整的配置文件和日志,而不是出于自己的判断只给出截取的部分。
  • 我搜索了 issues, 没有发现已提出的类似问题。
  • 问题在 Release 最新的版本上可以成功复现

描述

根据文档说明
https://xtls.github.io/config/dns.html#dns-%E5%A4%84%E7%90%86%E6%B5%81%E7%A8%8B

没有命中 hosts,但命中了某(几)个 DNS 服务器中的 domains 域名列表,则按照命中的规则的优先级,依次使用该规则对应的 DNS 服务器进行查询。

这里的依次,究竟是什么次序?本来想当然以为是配置文件中dnsServerObject的次序,也就是代码片段中的idx。
但我昨天无意间发现他非常具有混沌性,dns配置不动,修改路由规则或者有时候重启会导致其解析顺序不一样。
debug了一下,发现是缺少排序逻辑。


修复建议:
sortClients()应在272行根据idx整理clients,使其顺序跟配置文件中的顺序相符,以符合配置文件预期

func (s *DNS) sortClients(domain string) []*Client {
	clients := make([]*Client, 0, len(s.clients))
	clientUsed := make([]bool, len(s.clients))
	clientNames := make([]string, 0, len(s.clients))
	domainRules := []string{}

	// Priority domain matching
	hasMatch := false
	for _, match := range s.domainMatcher.Match(domain) {
		info := s.matcherInfos[match]
		client := s.clients[info.clientIdx]
		domainRule := client.domains[info.domainRuleIdx]
		domainRules = append(domainRules, fmt.Sprintf("%s(DNS idx:%d)", domainRule, info.clientIdx))
		if clientUsed[info.clientIdx] {
			continue
		}
		clientUsed[info.clientIdx] = true
		clients = append(clients, client)
		clientNames = append(clientNames, client.Name())
		hasMatch = true
	}

// 272行:在这里加sort by idx asc

	if !(s.disableFallback || s.disableFallbackIfMatch && hasMatch) {
		// Default round-robin query
		for idx, client := range s.clients {
			if clientUsed[idx] || client.skipFallback {
				continue
			}
			clientUsed[idx] = true
			clients = append(clients, client)
			clientNames = append(clientNames, client.Name())
		}
	}

重现方式

dig www.google.fr

客户端配置


{
  "dns": {
    "servers": [
      {
        // 这个IP将会走法国节点
        "address": "208.67.222.222",
        "port": 53,
        "skipFallback": true,
        "domains": ["domain:fr", "geosite:category-ai-chat-!cn", "geosite:disney", "geosite:netflix", "geosite:primevideo", "geosite:spotify"]
      },
      {
        // 省略etc...
      },
      {
        "address": "1.1.1.1",
        "port": 53,
        "skipFallback": true,
        "domains": ["geosite:google", "geosite:google-cn"]
      },
      {
        "address": "8.8.8.8",
        "port": 53,
        "skipFallback": true,
        "domains": ["geosite:google", "geosite:google-cn"]
      },
      // 省略chinaDNS....
      "8.8.8.8",
      "1.1.1.1"
    ]
  }
}

服务端配置

N/A

客户端日志


2025/03/19 20:45:57.559075 [Debug] app/dns: domain [www.google.fr ](http://www.google.fr/)matches following rules: [geosite:google(DNS idx:2) geosite:google(DNS idx:3) geosite:geolocation-!cn(DNS idx:6) domain:fr(DNS idx:0)]
2025/03/19 20:45:57.559176 [Debug] app/dns: domain [www.google.fr ](http://www.google.fr/)will use DNS in order: [UDP:1.1.1.1:53 UDP:8.8.8.8:53 UDP:1.1.1.1:53 UDP:208.67.222.222:53 UDP:8.8.8.8:53 UDP:8.8.8.8:53 UDP:1.1.1.1:53]
2025/03/19 20:45:57.559213 [Debug] app/dns: UDP:1.1.1.1:53 cache HIT [www.google.fr ](http://www.google.fr/)-> [172.217.25.163]

服务端日志

N/A

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions