From d9201157c8a22cde98246570483b27a6b5bb7b98 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Wed, 4 Mar 2026 18:58:59 +0800 Subject: [PATCH 1/4] Bug fix https://github.com/2dust/v2rayN/issues/8875 --- v2rayN/v2rayN/Views/DNSSettingWindow.xaml.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/v2rayN/v2rayN/Views/DNSSettingWindow.xaml.cs b/v2rayN/v2rayN/Views/DNSSettingWindow.xaml.cs index 90999417..2710b102 100644 --- a/v2rayN/v2rayN/Views/DNSSettingWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/DNSSettingWindow.xaml.cs @@ -31,13 +31,13 @@ public partial class DNSSettingWindow this.Bind(ViewModel, vm => vm.AddCommonHosts, v => v.togAddCommonHosts.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.FakeIP, v => v.togFakeIP.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.BlockBindingQuery, v => v.togBlockBindingQuery.IsChecked).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.DirectDNS, v => v.cmbDirectDNS.SelectedItem).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.RemoteDNS, v => v.cmbRemoteDNS.SelectedItem).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.BootstrapDNS, v => v.cmbBootstrapDNS.SelectedItem).DisposeWith(disposables); + 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.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.SelectedItem).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); From 81da72bb39035317f6415962429a549317c21ed8 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Wed, 4 Mar 2026 19:11:31 +0800 Subject: [PATCH 2/4] Fix https://github.com/2dust/v2rayN/issues/8881 --- v2rayN/ServiceLib/Handler/ConfigHandler.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs index 0153f6a1..de4c774d 100644 --- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -1332,6 +1332,7 @@ public static class ConfigHandler public static async Task RemoveInvalidServerResult(Config config, string subid) { var lstModel = await AppManager.Instance.ProfileModels(subid, ""); + lstModel.RemoveAll(t => t.ConfigType.IsComplexType()); if (lstModel is { Count: <= 0 }) { return -1; From b8f7cc0768ec2e46315887f25744bc56abb267f0 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Wed, 4 Mar 2026 12:13:06 +0000 Subject: [PATCH 3/4] Fix hosts matching (#8890) * Fix hosts matching * Fix hosts resolve rule * Fix --- .../CoreConfig/Singbox/SingboxDnsService.cs | 98 +++++++++++++------ .../Singbox/SingboxRoutingService.cs | 58 ++++++++++- 2 files changed, 123 insertions(+), 33 deletions(-) diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs index 7a5074bb..afab27d9 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs @@ -93,7 +93,23 @@ public partial class CoreConfigSingboxService foreach (var kvp in Utils.ParseHostsToDictionary(simpleDnsItem.Hosts)) { - hostsDns.predefined[kvp.Key] = kvp.Value.Where(Utils.IsIpAddress).ToList(); + // only allow full match + // like example.com and full:example.com, + // but not domain:example.com, keyword:example.com or regex:example.com etc. + var testRule = new Rule4Sbox(); + if (!ParseV2Domain(kvp.Key, testRule)) + { + continue; + } + if (testRule.domain_keyword?.Count > 0 && !kvp.Key.Contains(':')) + { + testRule.domain = testRule.domain_keyword; + testRule.domain_keyword = null; + } + if (testRule.domain?.Count == 1) + { + hostsDns.predefined[testRule.domain.First()] = kvp.Value.Where(Utils.IsIpAddress).ToList(); + } } foreach (var host in hostsDns.predefined) @@ -179,44 +195,66 @@ public partial class CoreConfigSingboxService foreach (var kvp in Utils.ParseHostsToDictionary(simpleDnsItem.Hosts)) { var predefined = kvp.Value.First(); - if (predefined.IsNullOrEmpty() || Utils.IsIpAddress(predefined)) + if (predefined.IsNullOrEmpty()) { continue; } - if (predefined.StartsWith('#') && int.TryParse(predefined.AsSpan(1), out var rcode)) + var rule = new Rule4Sbox() { - // xray syntactic sugar for predefined - // etc. #0 -> NOERROR - _coreConfig.dns.rules.Add(new() - { - query_type = [1, 28], - domain = [kvp.Key], - action = "predefined", - rcode = rcode switch - { - 0 => "NOERROR", - 1 => "FORMERR", - 2 => "SERVFAIL", - 3 => "NXDOMAIN", - 4 => "NOTIMP", - 5 => "REFUSED", - _ => "NOERROR", - }, - }); - continue; - } - // CNAME record - Rule4Sbox rule = new() - { - query_type = [1, 28], + query_type = [1, 5, 28], // A, CNAME and AAAA action = "predefined", rcode = "NOERROR", - answer = [$"*. IN CNAME {predefined}."], }; - if (ParseV2Domain(kvp.Key, rule)) + if (!ParseV2Domain(kvp.Key, rule)) { - _coreConfig.dns.rules.Add(rule); + continue; } + // see: https://xtls.github.io/en/config/dns.html#dnsobject + // The matching format (domain:, full:, etc.) is the same as the domain + // in the commonly used Routing System. The difference is that without a prefix, + // it defaults to using the full: prefix (similar to the common hosts file syntax). + if (rule.domain_keyword?.Count > 0 && !kvp.Key.Contains(':')) + { + rule.domain = rule.domain_keyword; + rule.domain_keyword = null; + } + // example.com #0 -> example.com with NOERROR + if (predefined.StartsWith('#') && int.TryParse(predefined.AsSpan(1), out var rcode)) + { + rule.rcode = rcode switch + { + 0 => "NOERROR", + 1 => "FORMERR", + 2 => "SERVFAIL", + 3 => "NXDOMAIN", + 4 => "NOTIMP", + 5 => "REFUSED", + _ => "NOERROR", + }; + } + else if (Utils.IsDomain(predefined)) + { + // example.com CNAME target.com -> example.com with CNAME target.com + rule.answer = new List { $"*. IN CNAME {predefined}." }; + } + else if (Utils.IsIpAddress(predefined) && (rule.domain?.Count ?? 0) == 0) + { + // not full match, but an IP address, treat it as predefined answer + if (Utils.IsIpv6(predefined)) + { + rule.answer = new List { $"*. IN AAAA {predefined}" }; + + } + else + { + rule.answer = new List { $"*. IN A {predefined}" }; + } + } + else + { + continue; + } + _coreConfig.dns.rules.Add(rule); } if (simpleDnsItem.BlockBindingQuery == true) diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRoutingService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRoutingService.cs index 9bcb9adf..45620c41 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRoutingService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxRoutingService.cs @@ -84,11 +84,58 @@ public partial class CoreConfigSingboxService } if (hostsDomains.Count > 0) { - _coreConfig.route.rules.Add(new() + var hostsResolveRule = new Rule4Sbox { action = "resolve", - domain = hostsDomains, - }); + }; + var hostsCounter = 0; + foreach (var host in hostsDomains) + { + var domainRule = new Rule4Sbox(); + if (!ParseV2Domain(host, domainRule)) + { + continue; + } + if (domainRule.domain_keyword?.Count > 0 && !host.Contains(':')) + { + domainRule.domain = domainRule.domain_keyword; + domainRule.domain_keyword = null; + } + if (domainRule.domain?.Count > 0) + { + hostsResolveRule.domain ??= []; + hostsResolveRule.domain.AddRange(domainRule.domain); + hostsCounter++; + } + else if (domainRule.domain_keyword?.Count > 0) + { + hostsResolveRule.domain_keyword ??= []; + hostsResolveRule.domain_keyword.AddRange(domainRule.domain_keyword); + hostsCounter++; + } + else if (domainRule.domain_suffix?.Count > 0) + { + hostsResolveRule.domain_suffix ??= []; + hostsResolveRule.domain_suffix.AddRange(domainRule.domain_suffix); + hostsCounter++; + } + else if (domainRule.domain_regex?.Count > 0) + { + hostsResolveRule.domain_regex ??= []; + hostsResolveRule.domain_regex.AddRange(domainRule.domain_regex); + hostsCounter++; + } + else if (domainRule.geosite?.Count > 0) + { + hostsResolveRule.geosite ??= []; + hostsResolveRule.geosite.AddRange(domainRule.geosite); + hostsCounter++; + } + } + if (hostsCounter > 0) + { + _coreConfig.route.rules.Add(hostsResolveRule); + } } _coreConfig.route.rules.Add(new() @@ -355,6 +402,11 @@ public partial class CoreConfigSingboxService rule.domain_keyword ??= []; rule.domain_keyword?.Add(domain.Substring(8)); } + else if (domain.StartsWith("dotless:")) + { + rule.domain_keyword ??= []; + rule.domain_keyword?.Add(domain.Substring(8)); + } else { rule.domain_keyword ??= []; From c0aa829abb90688dcabb66acdb5b1ef7c0fc7e91 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Thu, 5 Mar 2026 09:49:46 +0800 Subject: [PATCH 4/4] up 7.19.2 --- v2rayN/Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2rayN/Directory.Build.props b/v2rayN/Directory.Build.props index e7dffdd2..6c744bb5 100644 --- a/v2rayN/Directory.Build.props +++ b/v2rayN/Directory.Build.props @@ -1,7 +1,7 @@ - 7.19.1 + 7.19.2