Improve DNS, try fix

https://github.com/2dust/v2rayNG/issues/5828
This commit is contained in:
2dust committed 2026-07-05 17:44:47 +08:00
1 parent 318afdbafc
commit 5369303e18
3 files changed
+231 -7

No files matched your search

@@ -43,11 +43,13 @@ object CoreConfigContextBuilder {
// Step 2: Resolve all non-builtin routing outbound tags.
val routingResolvedOutbounds = resolveRoutingOutbounds()
val routingDomainRules = collectRoutingDomainRulesForDns()
return CoreConfigContext(
context = context,
guid = guid,
resolvedOutbounds = listOf(primaryResolvedOutbound) + routingResolvedOutbounds,
routingDomainRules = routingDomainRules,
)
}
@@ -214,4 +216,34 @@ object CoreConfigContextBuilder {
return listOf(config)
}
}
/**
* Collect enabled routing domain rules in original order for DNS segmentation.
*
* outbounds are normalized into three tags only: proxy / direct / block.
*/
private fun collectRoutingDomainRulesForDns(): List<CoreConfigContext.RoutingDomainRule> {
val rulesetItems = MmkvManager.decodeRoutingRulesets() ?: return emptyList()
val result = mutableListOf<CoreConfigContext.RoutingDomainRule>()
rulesetItems
.asSequence()
.filter { it.enabled }
.filter { !it.domain.isNullOrEmpty() }
.forEach { rule ->
val normalizedOutboundTag = when (rule.outboundTag) {
AppConfig.TAG_DIRECT -> AppConfig.TAG_DIRECT
AppConfig.TAG_BLOCKED -> AppConfig.TAG_BLOCKED
else -> AppConfig.TAG_PROXY
}
result.add(
CoreConfigContext.RoutingDomainRule(
domain = rule.domain.orEmpty(),
outboundTag = normalizedOutboundTag
)
)
}
return result
}
}
@@ -166,8 +166,8 @@ object CoreConfigManager {
// User routing rules (policyGroupBalancerTags rewrites TAG_PROXY→balancer when main is POLICYGROUP).
configureRouting(configContext, v2rayConfig, policyGroupBalancerTags)
configureFakeDns(v2rayConfig)
configureDns(v2rayConfig, policyGroupBalancerTags)
configureLocalDns(v2rayConfig)
configureDns(configContext, v2rayConfig, policyGroupBalancerTags)
configureLocalDns(configContext, v2rayConfig)
configureRootModeDns(v2rayConfig)
// (added by getDns / getCustomLocalDns) to use the balancer, then add
@@ -570,16 +570,20 @@ object CoreConfigManager {
/**
* Configure local DNS inbounds, outbounds, and routing rules.
*/
private fun configureLocalDns(v2rayConfig: V2rayConfig) {
private fun configureLocalDns(configContext: CoreConfigContext, v2rayConfig: V2rayConfig) {
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_LOCAL_DNS_ENABLED) != true) {
return
}
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_FAKE_DNS_ENABLED) == true) {
val geositeCn = arrayListOf(AppConfig.GEOSITE_CN)
val proxyDomain = collectUserRuleDomainsByTag(AppConfig.TAG_PROXY)
val directDomain = collectUserRuleDomainsByTag(AppConfig.TAG_DIRECT)
val finalDomain = geositeCn.plus(proxyDomain).plus(directDomain).distinct()
val routingDomains = configContext.routingDomainRules
.asSequence()
.filter { it.outboundTag != AppConfig.TAG_BLOCKED }
.flatMap { it.domain.asSequence() }
.toList()
.distinct()
val finalDomain = geositeCn + routingDomains
// fakedns with all domains to make it always top priority
v2rayConfig.dns?.servers?.add(
0,
@@ -668,6 +672,7 @@ object CoreConfigManager {
}
}
/*
/**
* Configure DNS servers, hosts, and DNS routing rules.
*/
@@ -807,7 +812,188 @@ object CoreConfigManager {
)
}
}
*/
/**
* Configure DNS servers, hosts, and DNS routing rules.
*/
private fun configureDns(
configContext: CoreConfigContext,
v2rayConfig: V2rayConfig,
policyGroupBalancerTags: Map<String, String>,
) {
val servers = ArrayList<Any>()
val remoteDns = SettingsManager.getRemoteDnsServers()
val domesticDns = SettingsManager.getDomesticDnsServers()
remoteDns.forEach { servers.add(it) }
val hosts = buildDnsHostsFromRoutingRules(configContext)
val cnDomesticDnsTags = buildDnsCnModeFromRoutingRules(configContext, servers, domesticDns)
val domesticDnsTags = buildDnsFromRoutingRules(
configContext = configContext,
servers = servers,
remoteDns = remoteDns,
domesticDns = domesticDns
)
domesticDnsTags.addAll(cnDomesticDnsTags)
v2rayConfig.dns = V2rayConfig.DnsBean(
servers = servers,
hosts = hosts,
tag = AppConfig.TAG_DNS,
enableParallelQuery = if ((domesticDns.size + remoteDns.size) > 2) true else null
)
if (domesticDnsTags.isNotEmpty()) {
v2rayConfig.routing.rules.add(
V2rayConfig.RoutingBean.RulesBean(
outboundTag = AppConfig.TAG_DIRECT,
inboundTag = ArrayList(domesticDnsTags),
domain = null
)
)
}
val dnsProxyBalancerTag = policyGroupBalancerTags[AppConfig.TAG_PROXY]
if (dnsProxyBalancerTag != null) {
v2rayConfig.routing.rules.add(
V2rayConfig.RoutingBean.RulesBean(
balancerTag = dnsProxyBalancerTag,
inboundTag = arrayListOf(AppConfig.TAG_DNS),
domain = null
)
)
} else {
v2rayConfig.routing.rules.add(
V2rayConfig.RoutingBean.RulesBean(
outboundTag = AppConfig.TAG_PROXY,
inboundTag = arrayListOf(AppConfig.TAG_DNS),
domain = null
)
)
}
}
private fun buildDnsHostsFromRoutingRules(configContext: CoreConfigContext): MutableMap<String, Any> {
val hosts = mutableMapOf<String, Any>()
val blockDomains = configContext.routingDomainRules
.asSequence()
.filter { it.outboundTag == AppConfig.TAG_BLOCKED }
.flatMap { it.domain.asSequence() }
.toList()
if (blockDomains.isNotEmpty()) {
hosts.putAll(blockDomains.map { it to AppConfig.LOOPBACK })
}
hosts[AppConfig.GOOGLEAPIS_CN_DOMAIN] = AppConfig.GOOGLEAPIS_COM_DOMAIN
hosts[AppConfig.DNS_ALIDNS_DOMAIN] = AppConfig.DNS_ALIDNS_ADDRESSES
hosts[AppConfig.DNS_CISCO_SSE_DOMAIN] = AppConfig.DNS_CISCO_SSE_ADDRESSES
hosts[AppConfig.DNS_CISCO_UMBRELLA_DOMAIN] = AppConfig.DNS_CISCO_UMBRELLA_ADDRESSES
hosts[AppConfig.DNS_CLOUDFLARE_ONE_DOMAIN] = AppConfig.DNS_CLOUDFLARE_ONE_ADDRESSES
hosts[AppConfig.DNS_CLOUDFLARE_ONEDOT_DNS_DOMAIN] = AppConfig.DNS_CLOUDFLARE_ONEDOT_DNS_ADDRESSES
hosts[AppConfig.DNS_CLOUDFLARE_DNS_COM_DOMAIN] = AppConfig.DNS_CLOUDFLARE_DNS_COM_ADDRESSES
hosts[AppConfig.DNS_CLOUDFLARE_DNS_DOMAIN] = AppConfig.DNS_CLOUDFLARE_DNS_ADDRESSES
hosts[AppConfig.DNS_CLOUDFLARE_WARP_DOMAIN] = AppConfig.DNS_CLOUDFLARE_WARP_ADDRESSES
hosts[AppConfig.DNS_DNSPOD_DOH_DOMAIN] = AppConfig.DNS_DNSPOD_DOH_ADDRESSES
hosts[AppConfig.DNS_DNSPOD_DOT_DOMAIN] = AppConfig.DNS_DNSPOD_DOT_ADDRESSES
hosts[AppConfig.DNS_GOOGLE_DOMAIN] = AppConfig.DNS_GOOGLE_ADDRESSES
hosts[AppConfig.DNS_QUAD9_DOMAIN] = AppConfig.DNS_QUAD9_ADDRESSES
hosts[AppConfig.DNS_SB_DOMAIN] = AppConfig.DNS_SB_ADDRESSES
hosts[AppConfig.DNS_YANDEX_DOMAIN] = AppConfig.DNS_YANDEX_ADDRESSES
val userHosts = MmkvManager.decodeSettingsString(AppConfig.PREF_DNS_HOSTS)
if (userHosts.isNotNullEmpty()) {
val userHostsMap = userHosts?.split(",").orEmpty()
.filter { it.isNotEmpty() }
.filter { it.contains(":") }
.associate { it.split(":").let { (k, v) -> k to v } }
hosts.putAll(userHostsMap)
}
return hosts
}
private fun buildDnsCnModeFromRoutingRules(configContext: CoreConfigContext, servers: ArrayList<Any>, domesticDns: List<String>, ): List<String> {
val cnRegionFilter = { domain: String ->
domain.startsWith("geosite:") && (domain.endsWith("-cn") || domain.endsWith("@cn"))
|| domain == AppConfig.GEOSITE_CN
}
val isCnRoutingMode = configContext.routingDomainRules
.asSequence()
.filter { it.outboundTag == AppConfig.TAG_DIRECT }
.flatMap { it.domain.asSequence() }
.any { it == AppConfig.GEOSITE_CN }
if (!isCnRoutingMode) {
return emptyList()
}
val geoipCn = arrayListOf(AppConfig.GEOIP_CN)
val cnDomains = configContext.routingDomainRules
.asSequence()
.filter { it.outboundTag == AppConfig.TAG_DIRECT }
.flatMap { it.domain.asSequence() }
.filter { cnRegionFilter(it) }
.toList()
if (cnDomains.isEmpty()) {
return emptyList()
}
val cnDomesticDnsTags = mutableListOf<String>()
domesticDns.forEachIndexed { index, address ->
val cnDomesticDnsTag = "${AppConfig.TAG_DOMESTIC_DNS}_cn_expect_${index}"
servers.add(
V2rayConfig.DnsBean.ServersBean(
address = address,
domains = cnDomains,
expectIPs = geoipCn,
skipFallback = true,
tag = cnDomesticDnsTag
)
)
cnDomesticDnsTags.add(cnDomesticDnsTag)
}
return cnDomesticDnsTags
}
private fun buildDnsFromRoutingRules(
configContext: CoreConfigContext,
servers: ArrayList<Any>,
remoteDns: List<String>,
domesticDns: List<String>,
): MutableList<String> {
val domesticDnsTags = mutableListOf<String>()
configContext.routingDomainRules.forEachIndexed { ruleIndex, rule ->
when (rule.outboundTag) {
AppConfig.TAG_DIRECT -> {
domesticDns.forEachIndexed { dnsIndex, address ->
val tag = "${AppConfig.TAG_DOMESTIC_DNS}_${ruleIndex}_$dnsIndex"
servers.add(
V2rayConfig.DnsBean.ServersBean(
address = address,
domains = rule.domain,
skipFallback = true,
tag = tag
)
)
domesticDnsTags.add(tag)
}
}
AppConfig.TAG_BLOCKED -> Unit
else -> {
servers.add(
V2rayConfig.DnsBean.ServersBean(
address = remoteDns.first(),
domains = rule.domain,
)
)
}
}
}
return domesticDnsTags
}
//endregion
@@ -9,6 +9,7 @@ data class CoreConfigContext(
val guid: String,
val isCustom: Boolean = false,
val resolvedOutbounds: List<ResolvedOutbound> = emptyList(),
val routingDomainRules: List<RoutingDomainRule> = emptyList(),
) {
data class ResolvedOutbound(
val tag: String,
@@ -16,4 +17,9 @@ data class CoreConfigContext(
val resolvedProfiles: List<ProfileItem>,
val resolvedType: CoreResolvedType,
)
}
data class RoutingDomainRule(
val domain: List<String>,
val outboundTag: String,
)
}