From 21ca5c10b8095c472ad2408afceb47973ee7d8bb Mon Sep 17 00:00:00 2001 From: DHR60 Date: Sat, 31 Jan 2026 10:22:18 +0800 Subject: [PATCH 1/3] Simplify DNS Settings --- v2rayN/ServiceLib/Global.cs | 26 +++++------ v2rayN/ServiceLib/Models/ConfigItems.cs | 5 +-- v2rayN/ServiceLib/Models/V2rayConfig.cs | 2 + v2rayN/ServiceLib/Resx/ResUI.Designer.cs | 45 ++++++++----------- v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx | 11 ++--- v2rayN/ServiceLib/Resx/ResUI.fr.resx | 11 ++--- v2rayN/ServiceLib/Resx/ResUI.hu.resx | 11 ++--- v2rayN/ServiceLib/Resx/ResUI.resx | 11 ++--- v2rayN/ServiceLib/Resx/ResUI.ru.resx | 11 ++--- v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx | 11 ++--- v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx | 11 ++--- .../CoreConfig/Singbox/SingboxDnsService.cs | 10 ++--- .../Singbox/SingboxRoutingService.cs | 19 ++++---- .../CoreConfig/V2ray/V2rayDnsService.cs | 25 ++++++++--- .../ViewModels/DNSSettingViewModel.cs | 15 +++---- .../Views/DNSSettingWindow.axaml | 22 ++------- .../Views/DNSSettingWindow.axaml.cs | 14 +++--- .../Views/RoutingRuleSettingWindow.axaml.cs | 2 +- .../Views/RoutingSettingWindow.axaml.cs | 2 +- v2rayN/v2rayN/Views/DNSSettingWindow.xaml | 24 ++-------- v2rayN/v2rayN/Views/DNSSettingWindow.xaml.cs | 14 +++--- .../Views/RoutingRuleSettingWindow.xaml.cs | 2 +- .../v2rayN/Views/RoutingSettingWindow.xaml.cs | 2 +- 23 files changed, 126 insertions(+), 180 deletions(-) diff --git a/v2rayN/ServiceLib/Global.cs b/v2rayN/ServiceLib/Global.cs index 94a66fa5..f858b00d 100644 --- a/v2rayN/ServiceLib/Global.cs +++ b/v2rayN/ServiceLib/Global.cs @@ -329,13 +329,13 @@ public class Global IPOnDemand ]; - public static readonly List DomainStrategies4Singbox = + public static readonly List DomainStrategies4Sbox = [ - "ipv4_only", - "ipv6_only", + "", "prefer_ipv4", "prefer_ipv6", - "" + "ipv4_only", + "ipv6_only" ]; public static readonly List Fingerprints = @@ -377,23 +377,23 @@ public class Global "" ]; - public static readonly List DomainStrategy4Freedoms = + public static readonly List DomainStrategy = [ "AsIs", "UseIP", + "UseIPv4v6", + "UseIPv6v4", "UseIPv4", "UseIPv6", "" ]; - public static readonly List SingboxDomainStrategy4Out = - [ - "", - "ipv4_only", - "prefer_ipv4", - "prefer_ipv6", - "ipv6_only" - ]; + public static readonly Dictionary DomainStrategy4SboxMap = + DomainStrategy + .Where(s => s.StartsWith("UseIPv4", StringComparison.Ordinal) || s.StartsWith("UseIPv6", StringComparison.Ordinal)) + .ToDictionary( + key => key, + key => key.StartsWith("UseIPv4", StringComparison.Ordinal) ? "prefer_ipv4" : "prefer_ipv6"); public static readonly List DomainDirectDNSAddress = [ diff --git a/v2rayN/ServiceLib/Models/ConfigItems.cs b/v2rayN/ServiceLib/Models/ConfigItems.cs index eeb88deb..04fc955b 100644 --- a/v2rayN/ServiceLib/Models/ConfigItems.cs +++ b/v2rayN/ServiceLib/Models/ConfigItems.cs @@ -265,9 +265,8 @@ public class SimpleDNSItem public string? DirectDNS { get; set; } public string? RemoteDNS { get; set; } public string? BootstrapDNS { get; set; } - public string? RayStrategy4Freedom { get; set; } - public string? SingboxStrategy4Direct { get; set; } - public string? SingboxStrategy4Proxy { get; set; } + public string? Strategy4Freedom { get; set; } + public string? Strategy4Proxy { get; set; } public string? Hosts { get; set; } public string? DirectExpectedIPs { get; set; } } diff --git a/v2rayN/ServiceLib/Models/V2rayConfig.cs b/v2rayN/ServiceLib/Models/V2rayConfig.cs index d9575432..19651950 100644 --- a/v2rayN/ServiceLib/Models/V2rayConfig.cs +++ b/v2rayN/ServiceLib/Models/V2rayConfig.cs @@ -105,6 +105,8 @@ public class Outbounds4Ray public string protocol { get; set; } + public string? targetStrategy { get; set; } + public Outboundsettings4Ray settings { get; set; } public StreamSettings4Ray streamSettings { get; set; } diff --git a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs index bf30c985..6d11f747 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs +++ b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs @@ -2727,6 +2727,15 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Direct Resolution Strategy 的本地化字符串。 + /// + public static string TbDirectResolveStrategy { + get { + return ResourceManager.GetString("TbDirectResolveStrategy", resourceCulture); + } + } + /// /// 查找类似 Display GUI 的本地化字符串。 /// @@ -3222,6 +3231,15 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Remote Resolution Strategy 的本地化字符串。 + /// + public static string TbRemoteResolveStrategy { + get { + return ResourceManager.GetString("TbRemoteResolveStrategy", resourceCulture); + } + } + /// /// 查找类似 Camouflage domain(host) 的本地化字符串。 /// @@ -3357,15 +3375,6 @@ namespace ServiceLib.Resx { } } - /// - /// 查找类似 sing-box Direct Resolution Strategy 的本地化字符串。 - /// - public static string TbSBDirectResolveStrategy { - get { - return ResourceManager.GetString("TbSBDirectResolveStrategy", resourceCulture); - } - } - /// /// 查找类似 sing-box Full Config Template 的本地化字符串。 /// @@ -3384,15 +3393,6 @@ namespace ServiceLib.Resx { } } - /// - /// 查找类似 sing-box Remote Resolution Strategy 的本地化字符串。 - /// - public static string TbSBRemoteResolveStrategy { - get { - return ResourceManager.GetString("TbSBRemoteResolveStrategy", resourceCulture); - } - } - /// /// 查找类似 Encryption method (security) 的本地化字符串。 /// @@ -4419,15 +4419,6 @@ namespace ServiceLib.Resx { } } - /// - /// 查找类似 xray Freedom Resolution Strategy 的本地化字符串。 - /// - public static string TbXrayFreedomStrategy { - get { - return ResourceManager.GetString("TbXrayFreedomStrategy", resourceCulture); - } - } - /// /// 查找类似 The delay: {0} ms, {1} 的本地化字符串。 /// diff --git a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx index 3a720cdd..fc69f970 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx @@ -1422,14 +1422,11 @@ Via proxy — please ensure remote availability - - xray Freedom Resolution Strategy + + Direct Resolution Strategy - - sing-box Direct Resolution Strategy - - - sing-box Remote Resolution Strategy + + Remote Resolution Strategy Add Common DNS Hosts diff --git a/v2rayN/ServiceLib/Resx/ResUI.fr.resx b/v2rayN/ServiceLib/Resx/ResUI.fr.resx index f1710de6..1f3b5096 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fr.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fr.resx @@ -1419,14 +1419,11 @@ Via le proxy ; assurez-vous que le serveur distant est disponible - - Stratégie de résolution xray freedom + + Stratégie de résolution directe - - Stratégie de résolution directe sing-box - - - Stratégie de résolution distante sing-box + + Stratégie de résolution distante Ajouter des hôtes DNS courants diff --git a/v2rayN/ServiceLib/Resx/ResUI.hu.resx b/v2rayN/ServiceLib/Resx/ResUI.hu.resx index 4787f0f1..2e3f4900 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.hu.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.hu.resx @@ -1422,14 +1422,11 @@ Via proxy — please ensure remote availability - - xray Freedom Resolution Strategy + + Direct Resolution Strategy - - sing-box Direct Resolution Strategy - - - sing-box Remote Resolution Strategy + + Remote Resolution Strategy Add Common DNS Hosts diff --git a/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayN/ServiceLib/Resx/ResUI.resx index 6ad8a6e4..f8710615 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.resx @@ -1422,14 +1422,11 @@ Via proxy — please ensure remote availability - - xray Freedom Resolution Strategy + + Direct Resolution Strategy - - sing-box Direct Resolution Strategy - - - sing-box Remote Resolution Strategy + + Remote Resolution Strategy Add Common DNS Hosts diff --git a/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayN/ServiceLib/Resx/ResUI.ru.resx index 3a0f9fef..49e6203a 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.ru.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.ru.resx @@ -1422,14 +1422,11 @@ Via proxy — please ensure remote availability - - Стратегия резолвинга Freedom (Xray) + + Стратегия прямого резолвинга - - Стратегия прямого резолвинга (sing-box) - - - Стратегия удалённого резолвинга (sing-box) + + Стратегия удалённого резолвинга Добавить стандартные записи hosts (DNS) diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx index b37e0ccb..ca6bb05d 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx @@ -1419,14 +1419,11 @@ 通过代理,请确保远程可用 - - xray freedom 解析策略 + + 直连解析策略 - - sing-box 直连解析策略 - - - sing-box 远程解析策略 + + 远程解析策略 添加常用 DNS Hosts diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx index b5acd049..ff0d4388 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx @@ -1419,14 +1419,11 @@ 通过代理,请确保远程可用 - - xray freedom 解析策略 + + 直連解析策略 - - sing-box 直連解析策略 - - - sing-box 遠程解析策略 + + 遠程解析策略 新增常用 DNS Hosts diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs index fa089140..f9c00caa 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs @@ -175,18 +175,18 @@ public partial class CoreConfigSingboxService singboxConfig.dns.rules ??= new List(); singboxConfig.dns.rules.AddRange(new[] - { + { new Rule4Sbox { ip_accept_any = true, server = Global.SingboxHostsDNSTag }, new Rule4Sbox { server = Global.SingboxRemoteDNSTag, - strategy = simpleDNSItem.SingboxStrategy4Proxy.NullIfEmpty(), + strategy = Global.DomainStrategy4SboxMap.GetValueOrDefault(simpleDNSItem.Strategy4Proxy), clash_mode = ERuleMode.Global.ToString() }, new Rule4Sbox { server = Global.SingboxDirectDNSTag, - strategy = simpleDNSItem.SingboxStrategy4Direct.NullIfEmpty(), + strategy = Global.DomainStrategy4SboxMap.GetValueOrDefault(simpleDNSItem.Strategy4Freedom), clash_mode = ERuleMode.Direct.ToString() } }); @@ -309,7 +309,7 @@ public partial class CoreConfigSingboxService if (item.OutboundTag == Global.DirectTag) { rule.server = Global.SingboxDirectDNSTag; - rule.strategy = string.IsNullOrEmpty(simpleDNSItem.SingboxStrategy4Direct) ? null : simpleDNSItem.SingboxStrategy4Direct; + rule.strategy = Global.DomainStrategy4SboxMap.GetValueOrDefault(simpleDNSItem.Strategy4Freedom); if (expectedIPsRegions.Count > 0 && rule.geosite?.Count > 0) { @@ -343,7 +343,7 @@ public partial class CoreConfigSingboxService singboxConfig.dns.rules.Add(rule4Fake); } rule.server = Global.SingboxRemoteDNSTag; - rule.strategy = string.IsNullOrEmpty(simpleDNSItem.SingboxStrategy4Proxy) ? null : simpleDNSItem.SingboxStrategy4Proxy; + rule.strategy = Global.DomainStrategy4SboxMap.GetValueOrDefault(simpleDNSItem.Strategy4Proxy); } singboxConfig.dns.rules.Add(rule); diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRoutingService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRoutingService.cs index 58bcaf99..68d04a7e 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRoutingService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRoutingService.cs @@ -7,21 +7,21 @@ public partial class CoreConfigSingboxService try { singboxConfig.route.final = Global.ProxyTag; - var item = _config.SimpleDNSItem; + var simpleDnsItem = _config.SimpleDNSItem; var defaultDomainResolverTag = Global.SingboxDirectDNSTag; - var directDNSStrategy = item.SingboxStrategy4Direct.IsNullOrEmpty() ? Global.SingboxDomainStrategy4Out.FirstOrDefault() : item.SingboxStrategy4Direct; + var directDnsStrategy = Global.DomainStrategy4SboxMap.GetValueOrDefault(simpleDnsItem.Strategy4Freedom); var rawDNSItem = await AppManager.Instance.GetDNSItem(ECoreType.sing_box); - if (rawDNSItem != null && rawDNSItem.Enabled == true) + if (rawDNSItem is { Enabled: true }) { defaultDomainResolverTag = Global.SingboxLocalDNSTag; - directDNSStrategy = rawDNSItem.DomainStrategy4Freedom.IsNullOrEmpty() ? Global.SingboxDomainStrategy4Out.FirstOrDefault() : rawDNSItem.DomainStrategy4Freedom; + directDnsStrategy = rawDNSItem.DomainStrategy4Freedom.IsNullOrEmpty() ? null : rawDNSItem.DomainStrategy4Freedom; } singboxConfig.route.default_domain_resolver = new() { server = defaultDomainResolverTag, - strategy = directDNSStrategy + strategy = directDnsStrategy }; if (_config.TunModeItem.EnableTun) @@ -73,18 +73,17 @@ public partial class CoreConfigSingboxService var hostsDomains = new List(); var dnsItem = await AppManager.Instance.GetDNSItem(ECoreType.sing_box); - if (dnsItem == null || dnsItem.Enabled == false) + if (dnsItem == null || !dnsItem.Enabled) { - var simpleDNSItem = _config.SimpleDNSItem; - if (!simpleDNSItem.Hosts.IsNullOrEmpty()) + if (!simpleDnsItem.Hosts.IsNullOrEmpty()) { - var userHostsMap = Utils.ParseHostsToDictionary(simpleDNSItem.Hosts); + var userHostsMap = Utils.ParseHostsToDictionary(simpleDnsItem.Hosts); foreach (var kvp in userHostsMap) { hostsDomains.Add(kvp.Key); } } - if (simpleDNSItem.UseSystemHosts == true) + if (simpleDnsItem.UseSystemHosts == true) { var systemHostsMap = Utils.GetSystemHosts(); foreach (var kvp in systemHostsMap) diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs index 6ba45895..2369f1e2 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs @@ -25,25 +25,38 @@ public partial class CoreConfigV2rayService return result; } - var simpleDNSItem = _config.SimpleDNSItem; - var domainStrategy4Freedom = simpleDNSItem?.RayStrategy4Freedom; + var simpleDnsItem = _config.SimpleDNSItem; + var strategy4Freedom = simpleDnsItem?.Strategy4Freedom ?? Global.AsIs; //Outbound Freedom domainStrategy - if (domainStrategy4Freedom.IsNotEmpty()) + if (strategy4Freedom.IsNotEmpty() && strategy4Freedom != Global.AsIs) { var outbound = v2rayConfig.outbounds.FirstOrDefault(t => t is { protocol: "freedom", tag: Global.DirectTag }); if (outbound != null) { outbound.settings = new() { - domainStrategy = domainStrategy4Freedom, + domainStrategy = strategy4Freedom, userLevel = 0 }; } } - await GenDnsServers(node, v2rayConfig, simpleDNSItem); - await GenDnsHosts(v2rayConfig, simpleDNSItem); + var strategy4Proxy = simpleDnsItem?.Strategy4Proxy ?? Global.AsIs; + //Outbound Proxy domainStrategy + if (strategy4Proxy.IsNotEmpty() && strategy4Proxy != Global.AsIs) + { + var xraySupportConfigTypeNames = Global.XraySupportConfigType + .Select(x => x == EConfigType.Hysteria2 ? "hysteria" : Global.ProtocolTypes[x]) + .ToHashSet(); + v2rayConfig.outbounds + .Where(t => xraySupportConfigTypeNames.Contains(t.protocol)) + .ToList() + .ForEach(outbound => outbound.targetStrategy = strategy4Proxy); + } + + await GenDnsServers(node, v2rayConfig, simpleDnsItem); + await GenDnsHosts(v2rayConfig, simpleDnsItem); if (v2rayConfig.routing.domainStrategy == Global.IPIfNonMatch) { diff --git a/v2rayN/ServiceLib/ViewModels/DNSSettingViewModel.cs b/v2rayN/ServiceLib/ViewModels/DNSSettingViewModel.cs index d53b960d..914c3ac7 100644 --- a/v2rayN/ServiceLib/ViewModels/DNSSettingViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/DNSSettingViewModel.cs @@ -9,9 +9,8 @@ public class DNSSettingViewModel : MyReactiveObject [Reactive] public string? DirectDNS { get; set; } [Reactive] public string? RemoteDNS { get; set; } [Reactive] public string? BootstrapDNS { get; set; } - [Reactive] public string? RayStrategy4Freedom { get; set; } - [Reactive] public string? SingboxStrategy4Direct { get; set; } - [Reactive] public string? SingboxStrategy4Proxy { get; set; } + [Reactive] public string? Strategy4Freedom { get; set; } + [Reactive] public string? Strategy4Proxy { get; set; } [Reactive] public string? Hosts { get; set; } [Reactive] public string? DirectExpectedIPs { get; set; } @@ -70,9 +69,8 @@ public class DNSSettingViewModel : MyReactiveObject DirectDNS = item.DirectDNS; RemoteDNS = item.RemoteDNS; BootstrapDNS = item.BootstrapDNS; - RayStrategy4Freedom = item.RayStrategy4Freedom; - SingboxStrategy4Direct = item.SingboxStrategy4Direct; - SingboxStrategy4Proxy = item.SingboxStrategy4Proxy; + Strategy4Freedom = item.Strategy4Freedom; + Strategy4Proxy = item.Strategy4Proxy; Hosts = item.Hosts; DirectExpectedIPs = item.DirectExpectedIPs; @@ -100,9 +98,8 @@ public class DNSSettingViewModel : MyReactiveObject _config.SimpleDNSItem.DirectDNS = DirectDNS; _config.SimpleDNSItem.RemoteDNS = RemoteDNS; _config.SimpleDNSItem.BootstrapDNS = BootstrapDNS; - _config.SimpleDNSItem.RayStrategy4Freedom = RayStrategy4Freedom; - _config.SimpleDNSItem.SingboxStrategy4Direct = SingboxStrategy4Direct; - _config.SimpleDNSItem.SingboxStrategy4Proxy = SingboxStrategy4Proxy; + _config.SimpleDNSItem.Strategy4Freedom = Strategy4Freedom; + _config.SimpleDNSItem.Strategy4Proxy = Strategy4Proxy; _config.SimpleDNSItem.Hosts = Hosts; _config.SimpleDNSItem.DirectExpectedIPs = DirectExpectedIPs; diff --git a/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml b/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml index e5a785b7..b41a0a99 100644 --- a/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml +++ b/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml @@ -105,28 +105,14 @@ Text="{x:Static resx:ResUI.TbBootstrapDNSTips}" TextWrapping="Wrap" /> - - - + Text="{x:Static resx:ResUI.TbDirectResolveStrategy}" /> + Text="{x:Static resx:ResUI.TbRemoteResolveStrategy}" /> btnCancel.Click += (s, e) => Close(); ViewModel = new DNSSettingViewModel(UpdateViewHandler); - cmbRayFreedomDNSStrategy.ItemsSource = Global.DomainStrategy4Freedoms; - cmbSBDirectDNSStrategy.ItemsSource = Global.SingboxDomainStrategy4Out; - cmbSBRemoteDNSStrategy.ItemsSource = Global.SingboxDomainStrategy4Out; + cmbDirectDNSStrategy.ItemsSource = Global.DomainStrategy; + cmbRemoteDNSStrategy.ItemsSource = Global.DomainStrategy; cmbDirectDNS.ItemsSource = Global.DomainDirectDNSAddress; cmbRemoteDNS.ItemsSource = Global.DomainRemoteDNSAddress; cmbBootstrapDNS.ItemsSource = Global.DomainPureIPDNSAddress; cmbDirectExpectedIPs.ItemsSource = Global.ExpectedIPs; - cmbdomainStrategy4FreedomCompatible.ItemsSource = Global.DomainStrategy4Freedoms; - cmbdomainStrategy4OutCompatible.ItemsSource = Global.SingboxDomainStrategy4Out; + cmbdomainStrategy4FreedomCompatible.ItemsSource = Global.DomainStrategy; + cmbdomainStrategy4OutCompatible.ItemsSource = Global.DomainStrategies4Sbox; cmbdomainDNSAddressCompatible.ItemsSource = Global.DomainPureIPDNSAddress; cmbdomainDNSAddress2Compatible.ItemsSource = Global.DomainPureIPDNSAddress; @@ -37,9 +36,8 @@ public partial class DNSSettingWindow : WindowBase this.Bind(ViewModel, vm => vm.DirectDNS, v => v.cmbDirectDNS.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.RemoteDNS, v => v.cmbRemoteDNS.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.BootstrapDNS, v => v.cmbBootstrapDNS.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.RayStrategy4Freedom, v => v.cmbRayFreedomDNSStrategy.SelectedItem).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SingboxStrategy4Direct, v => v.cmbSBDirectDNSStrategy.SelectedItem).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SingboxStrategy4Proxy, v => v.cmbSBRemoteDNSStrategy.SelectedItem).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.Strategy4Freedom, v => v.cmbDirectDNSStrategy.SelectedItem).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.Strategy4Proxy, v => v.cmbRemoteDNSStrategy.SelectedItem).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Hosts, v => v.txtHosts.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.DirectExpectedIPs, v => v.cmbDirectExpectedIPs.Text).DisposeWith(disposables); diff --git a/v2rayN/v2rayN.Desktop/Views/RoutingRuleSettingWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/RoutingRuleSettingWindow.axaml.cs index 076307ec..c2d90a33 100644 --- a/v2rayN/v2rayN.Desktop/Views/RoutingRuleSettingWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/RoutingRuleSettingWindow.axaml.cs @@ -26,7 +26,7 @@ public partial class RoutingRuleSettingWindow : WindowBase { diff --git a/v2rayN/v2rayN.Desktop/Views/RoutingSettingWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/RoutingSettingWindow.axaml.cs index 719acf94..e25f7237 100644 --- a/v2rayN/v2rayN.Desktop/Views/RoutingSettingWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/RoutingSettingWindow.axaml.cs @@ -22,7 +22,7 @@ public partial class RoutingSettingWindow : WindowBase ViewModel = new RoutingSettingViewModel(UpdateViewHandler); cmbdomainStrategy.ItemsSource = Global.DomainStrategies; - cmbdomainStrategy4Singbox.ItemsSource = Global.DomainStrategies4Singbox; + cmbdomainStrategy4Singbox.ItemsSource = Global.DomainStrategies4Sbox; this.WhenActivated(disposables => { diff --git a/v2rayN/v2rayN/Views/DNSSettingWindow.xaml b/v2rayN/v2rayN/Views/DNSSettingWindow.xaml index 7deb7c25..40753a37 100644 --- a/v2rayN/v2rayN/Views/DNSSettingWindow.xaml +++ b/v2rayN/v2rayN/Views/DNSSettingWindow.xaml @@ -133,31 +133,15 @@ Text="{x:Static resx:ResUI.TbBootstrapDNSTips}" TextWrapping="Wrap" /> - - - + Text="{x:Static resx:ResUI.TbDirectResolveStrategy}" /> + Text="{x:Static resx:ResUI.TbRemoteResolveStrategy}" /> vm.DirectDNS, v => v.cmbDirectDNS.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.RemoteDNS, v => v.cmbRemoteDNS.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.BootstrapDNS, v => v.cmbBootstrapDNS.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.RayStrategy4Freedom, v => v.cmbRayFreedomDNSStrategy.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SingboxStrategy4Direct, v => v.cmbSBDirectDNSStrategy.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SingboxStrategy4Proxy, v => v.cmbSBRemoteDNSStrategy.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.Strategy4Freedom, v => v.cmbDirectDNSStrategy.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.Strategy4Proxy, v => v.cmbRemoteDNSStrategy.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Hosts, v => v.txtHosts.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.DirectExpectedIPs, v => v.cmbDirectExpectedIPs.Text).DisposeWith(disposables); diff --git a/v2rayN/v2rayN/Views/RoutingRuleSettingWindow.xaml.cs b/v2rayN/v2rayN/Views/RoutingRuleSettingWindow.xaml.cs index fd0cee6e..e736fb6b 100644 --- a/v2rayN/v2rayN/Views/RoutingRuleSettingWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/RoutingRuleSettingWindow.xaml.cs @@ -18,7 +18,7 @@ public partial class RoutingRuleSettingWindow ViewModel = new RoutingRuleSettingViewModel(routingItem, UpdateViewHandler); cmbdomainStrategy.ItemsSource = Global.DomainStrategies.AppendEmpty(); - cmbdomainStrategy4Singbox.ItemsSource = Global.DomainStrategies4Singbox; + cmbdomainStrategy4Singbox.ItemsSource = Global.DomainStrategies4Sbox; this.WhenActivated(disposables => { diff --git a/v2rayN/v2rayN/Views/RoutingSettingWindow.xaml.cs b/v2rayN/v2rayN/Views/RoutingSettingWindow.xaml.cs index 98ccdc19..a9a781a4 100644 --- a/v2rayN/v2rayN/Views/RoutingSettingWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/RoutingSettingWindow.xaml.cs @@ -17,7 +17,7 @@ public partial class RoutingSettingWindow ViewModel = new RoutingSettingViewModel(UpdateViewHandler); cmbdomainStrategy.ItemsSource = Global.DomainStrategies; - cmbdomainStrategy4Singbox.ItemsSource = Global.DomainStrategies4Singbox; + cmbdomainStrategy4Singbox.ItemsSource = Global.DomainStrategies4Sbox; this.WhenActivated(disposables => { From 45d9db2c5aee1c829acd99a15222b6eede757b8f Mon Sep 17 00:00:00 2001 From: DHR60 Date: Sat, 31 Jan 2026 11:10:31 +0800 Subject: [PATCH 2/3] Add ParallelQuery and ServeStale features --- v2rayN/ServiceLib/Global.cs | 4 +- v2rayN/ServiceLib/Handler/ConfigHandler.cs | 2 + v2rayN/ServiceLib/Models/ConfigItems.cs | 2 + v2rayN/ServiceLib/Models/V2rayConfig.cs | 10 +- v2rayN/ServiceLib/Resx/ResUI.Designer.cs | 18 +++ v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx | 6 + v2rayN/ServiceLib/Resx/ResUI.fr.resx | 6 + v2rayN/ServiceLib/Resx/ResUI.hu.resx | 6 + v2rayN/ServiceLib/Resx/ResUI.resx | 6 + v2rayN/ServiceLib/Resx/ResUI.ru.resx | 6 + v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx | 6 + v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx | 6 + .../CoreConfig/V2ray/V2rayDnsService.cs | 132 ++++++++++-------- .../ViewModels/DNSSettingViewModel.cs | 6 + .../Views/DNSSettingWindow.axaml | 56 ++++++-- .../Views/DNSSettingWindow.axaml.cs | 2 + v2rayN/v2rayN/Views/DNSSettingWindow.xaml | 55 ++++++-- v2rayN/v2rayN/Views/DNSSettingWindow.xaml.cs | 2 + 18 files changed, 234 insertions(+), 97 deletions(-) diff --git a/v2rayN/ServiceLib/Global.cs b/v2rayN/ServiceLib/Global.cs index f858b00d..96fa0a9e 100644 --- a/v2rayN/ServiceLib/Global.cs +++ b/v2rayN/ServiceLib/Global.cs @@ -399,6 +399,7 @@ public class Global [ "https://dns.alidns.com/dns-query", "https://doh.pub/dns-query", + "https://dns.alidns.com/dns-query,https://doh.pub/dns-query", "223.5.5.5", "119.29.29.29", "localhost" @@ -407,8 +408,9 @@ public class Global public static readonly List DomainRemoteDNSAddress = [ "https://cloudflare-dns.com/dns-query", - "https://dns.cloudflare.com/dns-query", "https://dns.google/dns-query", + "https://cloudflare-dns.com/dns-query,https://dns.google/dns-query,8.8.8.8", + "https://dns.cloudflare.com/dns-query", "https://doh.dns.sb/dns-query", "https://doh.opendns.com/dns-query", "https://common.dot.dns.yandex.net", diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs index 5877d012..f9ff4494 100644 --- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -114,6 +114,8 @@ public static class ConfigHandler config.SimpleDNSItem ??= InitBuiltinSimpleDNS(); config.SimpleDNSItem.GlobalFakeIp ??= true; config.SimpleDNSItem.BootstrapDNS ??= Global.DomainPureIPDNSAddress.FirstOrDefault(); + config.SimpleDNSItem.ServeStale ??= false; + config.SimpleDNSItem.ParallelQuery ??= false; config.SpeedTestItem ??= new(); if (config.SpeedTestItem.SpeedTestTimeout < 10) diff --git a/v2rayN/ServiceLib/Models/ConfigItems.cs b/v2rayN/ServiceLib/Models/ConfigItems.cs index 04fc955b..46297e40 100644 --- a/v2rayN/ServiceLib/Models/ConfigItems.cs +++ b/v2rayN/ServiceLib/Models/ConfigItems.cs @@ -267,6 +267,8 @@ public class SimpleDNSItem public string? BootstrapDNS { get; set; } public string? Strategy4Freedom { get; set; } public string? Strategy4Proxy { get; set; } + public bool? ServeStale { get; set; } + public bool? ParallelQuery { get; set; } public string? Hosts { get; set; } public string? DirectExpectedIPs { get; set; } } diff --git a/v2rayN/ServiceLib/Models/V2rayConfig.cs b/v2rayN/ServiceLib/Models/V2rayConfig.cs index 19651950..6a185670 100644 --- a/v2rayN/ServiceLib/Models/V2rayConfig.cs +++ b/v2rayN/ServiceLib/Models/V2rayConfig.cs @@ -3,7 +3,7 @@ namespace ServiceLib.Models; public class V2rayConfig { public Log4Ray log { get; set; } - public Dns4Ray dns { get; set; } + public object dns { get; set; } public List inbounds { get; set; } public List outbounds { get; set; } public Routing4Ray routing { get; set; } @@ -208,12 +208,8 @@ public class Dns4Ray { public Dictionary? hosts { get; set; } public List servers { get; set; } - public string? clientIp { get; set; } - public string? queryStrategy { get; set; } - public bool? disableCache { get; set; } - public bool? disableFallback { get; set; } - public bool? disableFallbackIfMatch { get; set; } - public bool? useSystemHosts { get; set; } + public bool? serveStale { get; set; } + public bool? enableParallelQuery { get; set; } public string? tag { get; set; } } diff --git a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs index 6d11f747..9bfb77a9 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs +++ b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs @@ -3069,6 +3069,15 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Parallel Query 的本地化字符串。 + /// + public static string TbParallelQuery { + get { + return ResourceManager.GetString("TbParallelQuery", resourceCulture); + } + } + /// /// 查找类似 Path 的本地化字符串。 /// @@ -3438,6 +3447,15 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Serve Stale 的本地化字符串。 + /// + public static string TbServeStale { + get { + return ResourceManager.GetString("TbServeStale", resourceCulture); + } + } + /// /// 查找类似 Set system proxy 的本地化字符串。 /// diff --git a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx index fc69f970..70ee0c54 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx @@ -1650,4 +1650,10 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if Certificate fingerprint (SHA-256) + + Serve Stale + + + Parallel Query + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.fr.resx b/v2rayN/ServiceLib/Resx/ResUI.fr.resx index 1f3b5096..6c5e9fef 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fr.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fr.resx @@ -1647,4 +1647,10 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if Certificate fingerprint (SHA-256) + + Serve Stale + + + Parallel Query + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.hu.resx b/v2rayN/ServiceLib/Resx/ResUI.hu.resx index 2e3f4900..aa8c0762 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.hu.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.hu.resx @@ -1650,4 +1650,10 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if Certificate fingerprint (SHA-256) + + Serve Stale + + + Parallel Query + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayN/ServiceLib/Resx/ResUI.resx index f8710615..da17f12f 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.resx @@ -1650,4 +1650,10 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if Certificate fingerprint (SHA-256) + + Serve Stale + + + Parallel Query + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayN/ServiceLib/Resx/ResUI.ru.resx index 49e6203a..da856d97 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.ru.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.ru.resx @@ -1650,4 +1650,10 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if Certificate fingerprint (SHA-256) + + Serve Stale + + + Parallel Query + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx index ca6bb05d..82f1fd47 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx @@ -1647,4 +1647,10 @@ 证书指纹(SHA-256) + + 乐观缓存 + + + 并行查询 + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx index ff0d4388..389f1719 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx @@ -1647,4 +1647,10 @@ Certificate fingerprint (SHA-256) + + Serve Stale + + + Parallel Query + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs index 2369f1e2..2329c395 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs @@ -7,25 +7,35 @@ public partial class CoreConfigV2rayService try { var item = await AppManager.Instance.GetDNSItem(ECoreType.Xray); - if (item != null && item.Enabled == true) + if (item is { Enabled: true }) { var result = await GenDnsCompatible(node, v2rayConfig); - if (v2rayConfig.routing.domainStrategy == Global.IPIfNonMatch) + if (v2rayConfig.routing.domainStrategy != Global.IPIfNonMatch) { - // DNS routing - v2rayConfig.dns.tag = Global.DnsTag; - v2rayConfig.routing.rules.Add(new RulesItem4Ray - { - type = "field", - inboundTag = new List { Global.DnsTag }, - outboundTag = Global.ProxyTag, - }); + return result; } + // DNS routing + var dnsObj = JsonUtils.SerializeToNode(v2rayConfig.dns); + if (dnsObj == null) + { + return result; + } + + dnsObj["tag"] = Global.DnsTag; + v2rayConfig.dns = JsonUtils.Deserialize(JsonUtils.Serialize(dnsObj)); + v2rayConfig.routing.rules.Add(new RulesItem4Ray + { + type = "field", + inboundTag = new List { Global.DnsTag }, + outboundTag = Global.ProxyTag, + }); + return result; } var simpleDnsItem = _config.SimpleDNSItem; + var dnsItem = v2rayConfig.dns is Dns4Ray dns4Ray ? dns4Ray : new Dns4Ray(); var strategy4Freedom = simpleDnsItem?.Strategy4Freedom ?? Global.AsIs; //Outbound Freedom domainStrategy @@ -55,13 +65,16 @@ public partial class CoreConfigV2rayService .ForEach(outbound => outbound.targetStrategy = strategy4Proxy); } - await GenDnsServers(node, v2rayConfig, simpleDnsItem); - await GenDnsHosts(v2rayConfig, simpleDnsItem); + await GenDnsServers(node, dnsItem, simpleDnsItem); + await GenDnsHosts(dnsItem, simpleDnsItem); + + dnsItem.serveStale = simpleDnsItem?.ServeStale is true ? true : null; + dnsItem.enableParallelQuery = simpleDnsItem?.ParallelQuery is true ? true : null; if (v2rayConfig.routing.domainStrategy == Global.IPIfNonMatch) { // DNS routing - v2rayConfig.dns.tag = Global.DnsTag; + dnsItem.tag = Global.DnsTag; v2rayConfig.routing.rules.Add(new RulesItem4Ray { type = "field", @@ -69,6 +82,8 @@ public partial class CoreConfigV2rayService outboundTag = Global.ProxyTag, }); } + + v2rayConfig.dns = dnsItem; } catch (Exception ex) { @@ -77,7 +92,7 @@ public partial class CoreConfigV2rayService return 0; } - private async Task GenDnsServers(ProfileItem? node, V2rayConfig v2rayConfig, SimpleDNSItem simpleDNSItem) + private async Task GenDnsServers(ProfileItem? node, Dns4Ray dnsItem, SimpleDNSItem simpleDNSItem) { static List ParseDnsAddresses(string? dnsInput, string defaultAddress) { @@ -90,7 +105,7 @@ public partial class CoreConfigV2rayService return addresses.Count > 0 ? addresses : new List { defaultAddress }; } - static object CreateDnsServer(string dnsAddress, List domains, List? expectedIPs = null) + static object? CreateDnsServer(string dnsAddress, List domains, List? expectedIPs = null) { var (domain, scheme, port, path) = Utils.ParseUrl(dnsAddress); var domainFinal = dnsAddress; @@ -119,8 +134,8 @@ public partial class CoreConfigV2rayService }); } - var directDNSAddress = ParseDnsAddresses(simpleDNSItem?.DirectDNS, Global.DomainDirectDNSAddress.FirstOrDefault()); - var remoteDNSAddress = ParseDnsAddresses(simpleDNSItem?.RemoteDNS, Global.DomainRemoteDNSAddress.FirstOrDefault()); + var directDNSAddress = ParseDnsAddresses(simpleDNSItem?.DirectDNS, Global.DomainDirectDNSAddress.First()); + var remoteDNSAddress = ParseDnsAddresses(simpleDNSItem?.RemoteDNS, Global.DomainRemoteDNSAddress.First()); var directDomainList = new List(); var directGeositeList = new List(); @@ -130,7 +145,7 @@ public partial class CoreConfigV2rayService var expectedIPs = new List(); var regionNames = new HashSet(); - var bootstrapDNSAddress = ParseDnsAddresses(simpleDNSItem?.BootstrapDNS, Global.DomainPureIPDNSAddress.FirstOrDefault()); + var bootstrapDNSAddress = ParseDnsAddresses(simpleDNSItem?.BootstrapDNS, Global.DomainPureIPDNSAddress.First()); var dnsServerDomains = new List(); foreach (var dns in directDNSAddress) @@ -184,51 +199,48 @@ public partial class CoreConfigV2rayService var routing = await ConfigHandler.GetDefaultRouting(_config); List? rules = null; - if (routing != null) + rules = JsonUtils.Deserialize>(routing.RuleSet) ?? []; + foreach (var item in rules) { - rules = JsonUtils.Deserialize>(routing.RuleSet) ?? []; - foreach (var item in rules) + if (!item.Enabled || item.Domain is null || item.Domain.Count == 0) { - if (!item.Enabled || item.Domain is null || item.Domain.Count == 0) + continue; + } + + if (item.RuleType == ERuleType.Routing) + { + continue; + } + + foreach (var domain in item.Domain) + { + if (domain.StartsWith('#')) { continue; } - if (item.RuleType == ERuleType.Routing) + var normalizedDomain = domain.Replace(Global.RoutingRuleComma, ","); + + if (item.OutboundTag == Global.DirectTag) { - continue; + if (normalizedDomain.StartsWith("geosite:") || normalizedDomain.StartsWith("ext:")) + { + (regionNames.Contains(normalizedDomain) ? expectedDomainList : directGeositeList).Add(normalizedDomain); + } + else + { + directDomainList.Add(normalizedDomain); + } } - - foreach (var domain in item.Domain) + else if (item.OutboundTag != Global.BlockTag) { - if (domain.StartsWith('#')) + if (normalizedDomain.StartsWith("geosite:") || normalizedDomain.StartsWith("ext:")) { - continue; + proxyGeositeList.Add(normalizedDomain); } - - var normalizedDomain = domain.Replace(Global.RoutingRuleComma, ","); - - if (item.OutboundTag == Global.DirectTag) + else { - if (normalizedDomain.StartsWith("geosite:") || normalizedDomain.StartsWith("ext:")) - { - (regionNames.Contains(normalizedDomain) ? expectedDomainList : directGeositeList).Add(normalizedDomain); - } - else - { - directDomainList.Add(normalizedDomain); - } - } - else if (item.OutboundTag != Global.BlockTag) - { - if (normalizedDomain.StartsWith("geosite:") || normalizedDomain.StartsWith("ext:")) - { - proxyGeositeList.Add(normalizedDomain); - } - else - { - proxyDomainList.Add(normalizedDomain); - } + proxyDomainList.Add(normalizedDomain); } } } @@ -257,8 +269,7 @@ public partial class CoreConfigV2rayService } } - v2rayConfig.dns ??= new Dns4Ray(); - v2rayConfig.dns.servers ??= new List(); + dnsItem.servers ??= []; void AddDnsServers(List dnsAddresses, List domains, List? expectedIPs = null) { @@ -266,7 +277,7 @@ public partial class CoreConfigV2rayService { foreach (var dnsAddress in dnsAddresses) { - v2rayConfig.dns.servers.Add(CreateDnsServer(dnsAddress, domains, expectedIPs)); + dnsItem.servers.Add(CreateDnsServer(dnsAddress, domains, expectedIPs)); } } } @@ -288,22 +299,21 @@ public partial class CoreConfigV2rayService || lastRule.Ip?.Contains("0.0.0.0/0") == true); var defaultDnsServers = useDirectDns ? directDNSAddress : remoteDNSAddress; - v2rayConfig.dns.servers.AddRange(defaultDnsServers); + dnsItem.servers.AddRange(defaultDnsServers); return 0; } - private async Task GenDnsHosts(V2rayConfig v2rayConfig, SimpleDNSItem simpleDNSItem) + private async Task GenDnsHosts(Dns4Ray dnsItem, SimpleDNSItem simpleDNSItem) { if (simpleDNSItem.AddCommonHosts == false && simpleDNSItem.UseSystemHosts == false && simpleDNSItem.Hosts.IsNullOrEmpty()) { return await Task.FromResult(0); } - v2rayConfig.dns ??= new Dns4Ray(); - v2rayConfig.dns.hosts ??= new Dictionary(); + dnsItem.hosts ??= new Dictionary(); if (simpleDNSItem.AddCommonHosts == true) { - v2rayConfig.dns.hosts = Global.PredefinedHosts.ToDictionary( + dnsItem.hosts = Global.PredefinedHosts.ToDictionary( kvp => kvp.Key, kvp => (object)kvp.Value ); @@ -312,7 +322,7 @@ public partial class CoreConfigV2rayService if (simpleDNSItem.UseSystemHosts == true) { var systemHosts = Utils.GetSystemHosts(); - var normalHost = v2rayConfig?.dns?.hosts; + var normalHost = dnsItem.hosts; if (normalHost != null && systemHosts?.Count > 0) { @@ -329,7 +339,7 @@ public partial class CoreConfigV2rayService foreach (var kvp in userHostsMap) { - v2rayConfig.dns.hosts[kvp.Key] = kvp.Value; + dnsItem.hosts[kvp.Key] = kvp.Value; } } return await Task.FromResult(0); diff --git a/v2rayN/ServiceLib/ViewModels/DNSSettingViewModel.cs b/v2rayN/ServiceLib/ViewModels/DNSSettingViewModel.cs index 914c3ac7..00178d17 100644 --- a/v2rayN/ServiceLib/ViewModels/DNSSettingViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/DNSSettingViewModel.cs @@ -13,6 +13,8 @@ public class DNSSettingViewModel : MyReactiveObject [Reactive] public string? Strategy4Proxy { get; set; } [Reactive] public string? Hosts { get; set; } [Reactive] public string? DirectExpectedIPs { get; set; } + [Reactive] public bool? ParallelQuery { get; set; } + [Reactive] public bool? ServeStale { get; set; } [Reactive] public bool UseSystemHostsCompatible { get; set; } [Reactive] public string DomainStrategy4FreedomCompatible { get; set; } @@ -73,6 +75,8 @@ public class DNSSettingViewModel : MyReactiveObject Strategy4Proxy = item.Strategy4Proxy; Hosts = item.Hosts; DirectExpectedIPs = item.DirectExpectedIPs; + ParallelQuery = item.ParallelQuery; + ServeStale = item.ServeStale; var item1 = await AppManager.Instance.GetDNSItem(ECoreType.Xray); RayCustomDNSEnableCompatible = item1.Enabled; @@ -102,6 +106,8 @@ public class DNSSettingViewModel : MyReactiveObject _config.SimpleDNSItem.Strategy4Proxy = Strategy4Proxy; _config.SimpleDNSItem.Hosts = Hosts; _config.SimpleDNSItem.DirectExpectedIPs = DirectExpectedIPs; + _config.SimpleDNSItem.ParallelQuery = ParallelQuery; + _config.SimpleDNSItem.ServeStale = ServeStale; if (NormalDNSCompatible.IsNotEmpty()) { diff --git a/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml b/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml index b41a0a99..0f839c58 100644 --- a/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml +++ b/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml @@ -132,19 +132,32 @@ Width="200" Margin="{StaticResource Margin4}" PlaceholderText="Default" /> - + + Text="{x:Static resx:ResUI.TbParallelQuery}" /> + + + @@ -155,7 +168,7 @@ x:Name="gridAdvancedDNSSettings" Margin="{StaticResource Margin8}" ColumnDefinitions="Auto,Auto,*" - RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,*"> + RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,*"> + Text="{x:Static resx:ResUI.TbAddCommonDNSHosts}" /> + + + this.Bind(ViewModel, vm => vm.Strategy4Proxy, v => v.cmbRemoteDNSStrategy.SelectedItem).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Hosts, v => v.txtHosts.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.DirectExpectedIPs, v => v.cmbDirectExpectedIPs.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.ParallelQuery, v => v.togParallelQuery.IsChecked).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.ServeStale, v => v.togServeStale.IsChecked).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables); diff --git a/v2rayN/v2rayN/Views/DNSSettingWindow.xaml b/v2rayN/v2rayN/Views/DNSSettingWindow.xaml index 40753a37..0a79ed8f 100644 --- a/v2rayN/v2rayN/Views/DNSSettingWindow.xaml +++ b/v2rayN/v2rayN/Views/DNSSettingWindow.xaml @@ -171,13 +171,27 @@ Margin="{StaticResource Margin8}" VerticalAlignment="Center" Style="{StaticResource ToolbarTextBlock}" - Text="{x:Static resx:ResUI.TbAddCommonDNSHosts}" /> + Text="{x:Static resx:ResUI.TbParallelQuery}" /> + + + @@ -191,6 +205,7 @@ + @@ -229,15 +244,29 @@ Margin="{StaticResource Margin8}" VerticalAlignment="Center" Style="{StaticResource ToolbarTextBlock}" - Text="{x:Static resx:ResUI.TbFakeIP}" /> + Text="{x:Static resx:ResUI.TbAddCommonDNSHosts}" /> + + + vm.Strategy4Proxy, v => v.cmbRemoteDNSStrategy.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Hosts, v => v.txtHosts.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.DirectExpectedIPs, v => v.cmbDirectExpectedIPs.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.ParallelQuery, v => v.togParallelQuery.IsChecked).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.ServeStale, v => v.togServeStale.IsChecked).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables); From 1f7609cc2219bf44d76dec1315a3b962bbba418c Mon Sep 17 00:00:00 2001 From: DHR60 Date: Sat, 31 Jan 2026 11:44:11 +0800 Subject: [PATCH 3/3] Fix --- v2rayN/ServiceLib/Common/Utils.cs | 12 ++++++++++++ v2rayN/ServiceLib/Global.cs | 7 ------- .../Services/CoreConfig/Singbox/SingboxDnsService.cs | 8 ++++---- .../CoreConfig/Singbox/SingboxRoutingService.cs | 2 +- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/v2rayN/ServiceLib/Common/Utils.cs b/v2rayN/ServiceLib/Common/Utils.cs index 0f4feda5..22d10304 100644 --- a/v2rayN/ServiceLib/Common/Utils.cs +++ b/v2rayN/ServiceLib/Common/Utils.cs @@ -462,6 +462,18 @@ public class Utils return (domain, port); } + public static string? DomainStrategy4Sbox(string? strategy) + { + return strategy switch + { + not null when strategy.StartsWith("UseIPv4") => "prefer_ipv4", + not null when strategy.StartsWith("UseIPv6") => "prefer_ipv6", + not null when strategy.StartsWith("ForceIPv4") => "ipv4_only", + not null when strategy.StartsWith("ForceIPv6") => "ipv6_only", + _ => null + }; + } + #endregion Conversion Functions #region Data Checks diff --git a/v2rayN/ServiceLib/Global.cs b/v2rayN/ServiceLib/Global.cs index 96fa0a9e..a902bbdd 100644 --- a/v2rayN/ServiceLib/Global.cs +++ b/v2rayN/ServiceLib/Global.cs @@ -388,13 +388,6 @@ public class Global "" ]; - public static readonly Dictionary DomainStrategy4SboxMap = - DomainStrategy - .Where(s => s.StartsWith("UseIPv4", StringComparison.Ordinal) || s.StartsWith("UseIPv6", StringComparison.Ordinal)) - .ToDictionary( - key => key, - key => key.StartsWith("UseIPv4", StringComparison.Ordinal) ? "prefer_ipv4" : "prefer_ipv6"); - public static readonly List DomainDirectDNSAddress = [ "https://dns.alidns.com/dns-query", diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs index f9c00caa..6079b2fb 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs @@ -180,13 +180,13 @@ public partial class CoreConfigSingboxService new Rule4Sbox { server = Global.SingboxRemoteDNSTag, - strategy = Global.DomainStrategy4SboxMap.GetValueOrDefault(simpleDNSItem.Strategy4Proxy), + strategy = Utils.DomainStrategy4Sbox(simpleDNSItem.Strategy4Proxy), clash_mode = ERuleMode.Global.ToString() }, new Rule4Sbox { server = Global.SingboxDirectDNSTag, - strategy = Global.DomainStrategy4SboxMap.GetValueOrDefault(simpleDNSItem.Strategy4Freedom), + strategy = Utils.DomainStrategy4Sbox(simpleDNSItem.Strategy4Freedom), clash_mode = ERuleMode.Direct.ToString() } }); @@ -309,7 +309,7 @@ public partial class CoreConfigSingboxService if (item.OutboundTag == Global.DirectTag) { rule.server = Global.SingboxDirectDNSTag; - rule.strategy = Global.DomainStrategy4SboxMap.GetValueOrDefault(simpleDNSItem.Strategy4Freedom); + rule.strategy = Utils.DomainStrategy4Sbox(simpleDNSItem.Strategy4Freedom); if (expectedIPsRegions.Count > 0 && rule.geosite?.Count > 0) { @@ -343,7 +343,7 @@ public partial class CoreConfigSingboxService singboxConfig.dns.rules.Add(rule4Fake); } rule.server = Global.SingboxRemoteDNSTag; - rule.strategy = Global.DomainStrategy4SboxMap.GetValueOrDefault(simpleDNSItem.Strategy4Proxy); + rule.strategy = Utils.DomainStrategy4Sbox(simpleDNSItem.Strategy4Proxy); } singboxConfig.dns.rules.Add(rule); diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRoutingService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRoutingService.cs index 68d04a7e..9424df78 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRoutingService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRoutingService.cs @@ -10,7 +10,7 @@ public partial class CoreConfigSingboxService var simpleDnsItem = _config.SimpleDNSItem; var defaultDomainResolverTag = Global.SingboxDirectDNSTag; - var directDnsStrategy = Global.DomainStrategy4SboxMap.GetValueOrDefault(simpleDnsItem.Strategy4Freedom); + var directDnsStrategy = Utils.DomainStrategy4Sbox(simpleDnsItem.Strategy4Freedom); var rawDNSItem = await AppManager.Instance.GetDNSItem(ECoreType.sing_box); if (rawDNSItem is { Enabled: true })