From 12a46990b50dc7bf951dc57037d8adbd91250ed0 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Wed, 25 Feb 2026 10:18:47 +0800 Subject: [PATCH 1/3] Fix routing --- .../Singbox/SingboxOutboundService.cs | 9 +- .../V2ray/CoreConfigV2rayService.cs | 3 + .../CoreConfig/V2ray/V2rayDnsService.cs | 122 +++++++++--------- .../CoreConfig/V2ray/V2rayRoutingService.cs | 25 ++++ 4 files changed, 96 insertions(+), 63 deletions(-) diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs index 23b331f6..ac4bfd33 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs @@ -20,10 +20,13 @@ public partial class CoreConfigSingboxService { proxyOutboundList.AddRange(BuildGroupProxyOutbounds(baseTagName)); } - var proxyTags = proxyOutboundList.Where(n => n.tag.StartsWith(Global.ProxyTag)).Select(n => n.tag).ToList(); - if (proxyTags.Count > 1) + if (withSelector) { - proxyOutboundList.InsertRange(0, BuildSelectorOutbounds(proxyTags, baseTagName)); + var proxyTags = proxyOutboundList.Where(n => n.tag.StartsWith(Global.ProxyTag)).Select(n => n.tag).ToList(); + if (proxyTags.Count > 1) + { + proxyOutboundList.InsertRange(0, BuildSelectorOutbounds(proxyTags, baseTagName)); + } } return proxyOutboundList; } diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs index a9f6e053..579b5f90 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs @@ -244,6 +244,7 @@ public partial class CoreConfigV2rayService(CoreConfigContext context) GenLog(); GenOutbounds(); + _coreConfig.routing.domainStrategy = Global.AsIs; _coreConfig.routing.rules.Clear(); _coreConfig.inbounds.Clear(); _coreConfig.inbounds.Add(new() @@ -254,6 +255,8 @@ public partial class CoreConfigV2rayService(CoreConfigContext context) protocol = EInboundProtocol.mixed.ToString(), }); + _coreConfig.routing.rules.Add(BuildFinalRule()); + ret.Msg = string.Format(ResUI.SuccessfulConfiguration, ""); ret.Success = true; ret.Data = JsonUtils.Serialize(_coreConfig); diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs index 10cd905b..aec724b0 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs @@ -70,17 +70,16 @@ public partial class CoreConfigV2rayService dnsItem.serveStale = simpleDnsItem?.ServeStale is true ? true : null; dnsItem.enableParallelQuery = simpleDnsItem?.ParallelQuery is true ? true : null; - if (_coreConfig.routing.domainStrategy == Global.IPIfNonMatch) + // DNS routing + var finalRule = BuildFinalRule(); + dnsItem.tag = Global.DnsTag; + _coreConfig.routing.rules.Add(new() { - // DNS routing - dnsItem.tag = Global.DnsTag; - _coreConfig.routing.rules.Add(new RulesItem4Ray - { - type = "field", - inboundTag = new List { Global.DnsTag }, - outboundTag = Global.ProxyTag, - }); - } + type = "field", + inboundTag = [Global.DnsTag], + outboundTag = finalRule.outboundTag, + balancerTag = finalRule.balancerTag + }); _coreConfig.dns = dnsItem; } @@ -93,45 +92,6 @@ public partial class CoreConfigV2rayService private void FillDnsServers(Dns4Ray dnsItem) { var simpleDNSItem = context.SimpleDnsItem; - static List ParseDnsAddresses(string? dnsInput, string defaultAddress) - { - var addresses = dnsInput?.Split(dnsInput.Contains(',') ? ',' : ';') - .Select(addr => addr.Trim()) - .Where(addr => !string.IsNullOrEmpty(addr)) - .Select(addr => addr.StartsWith("dhcp", StringComparison.OrdinalIgnoreCase) ? "localhost" : addr) - .Distinct() - .ToList() ?? new List { defaultAddress }; - return addresses.Count > 0 ? addresses : new List { defaultAddress }; - } - - static object? CreateDnsServer(string dnsAddress, List domains, List? expectedIPs = null) - { - var (domain, scheme, port, path) = Utils.ParseUrl(dnsAddress); - var domainFinal = dnsAddress; - int? portFinal = null; - if (scheme.IsNullOrEmpty() || scheme.StartsWith("udp", StringComparison.OrdinalIgnoreCase)) - { - domainFinal = domain; - portFinal = port > 0 ? port : null; - } - else if (scheme.StartsWith("tcp", StringComparison.OrdinalIgnoreCase)) - { - domainFinal = scheme + "://" + domain; - portFinal = port > 0 ? port : null; - } - var dnsServer = new DnsServer4Ray - { - address = domainFinal, - port = portFinal, - skipFallback = true, - domains = domains.Count > 0 ? domains : null, - expectedIPs = expectedIPs?.Count > 0 ? expectedIPs : null - }; - return JsonUtils.SerializeToNode(dnsServer, new JsonSerializerOptions - { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }); - } var directDNSAddress = ParseDnsAddresses(simpleDNSItem?.DirectDNS, Global.DomainDirectDNSAddress.First()); var remoteDNSAddress = ParseDnsAddresses(simpleDNSItem?.RemoteDNS, Global.DomainRemoteDNSAddress.First()); @@ -252,17 +212,6 @@ public partial class CoreConfigV2rayService dnsItem.servers ??= []; - void AddDnsServers(List dnsAddresses, List domains, List? expectedIPs = null) - { - if (domains.Count > 0) - { - foreach (var dnsAddress in dnsAddresses) - { - dnsItem.servers.Add(CreateDnsServer(dnsAddress, domains, expectedIPs)); - } - } - } - AddDnsServers(remoteDNSAddress, proxyDomainList); AddDnsServers(directDNSAddress, directDomainList); AddDnsServers(remoteDNSAddress, proxyGeositeList); @@ -281,6 +230,59 @@ public partial class CoreConfigV2rayService var defaultDnsServers = useDirectDns ? directDNSAddress : remoteDNSAddress; dnsItem.servers.AddRange(defaultDnsServers); + return; + + static List ParseDnsAddresses(string? dnsInput, string defaultAddress) + { + var addresses = dnsInput?.Split(dnsInput.Contains(',') ? ',' : ';') + .Select(addr => addr.Trim()) + .Where(addr => !string.IsNullOrEmpty(addr)) + .Select(addr => addr.StartsWith("dhcp", StringComparison.OrdinalIgnoreCase) ? "localhost" : addr) + .Distinct() + .ToList() ?? [defaultAddress]; + return addresses.Count > 0 ? addresses : new List { defaultAddress }; + } + + static object? CreateDnsServer(string dnsAddress, List domains, List? expectedIPs = null) + { + var (domain, scheme, port, path) = Utils.ParseUrl(dnsAddress); + var domainFinal = dnsAddress; + int? portFinal = null; + if (scheme.IsNullOrEmpty() || scheme.StartsWith("udp", StringComparison.OrdinalIgnoreCase)) + { + domainFinal = domain; + portFinal = port > 0 ? port : null; + } + else if (scheme.StartsWith("tcp", StringComparison.OrdinalIgnoreCase)) + { + domainFinal = scheme + "://" + domain; + portFinal = port > 0 ? port : null; + } + var dnsServer = new DnsServer4Ray + { + address = domainFinal, + port = portFinal, + skipFallback = true, + domains = domains.Count > 0 ? domains : null, + expectedIPs = expectedIPs?.Count > 0 ? expectedIPs : null + }; + return JsonUtils.SerializeToNode(dnsServer, new JsonSerializerOptions + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }); + } + + void AddDnsServers(List dnsAddresses, List domains, List? expectedIPs = null) + { + if (domains.Count <= 0) + { + return; + } + foreach (var dnsAddress in dnsAddresses) + { + dnsItem.servers.Add(CreateDnsServer(dnsAddress, domains, expectedIPs)); + } + } } private void FillDnsHosts(Dns4Ray dnsItem) diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayRoutingService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayRoutingService.cs index 1d3d1a37..0c3b4570 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayRoutingService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayRoutingService.cs @@ -45,6 +45,7 @@ public partial class CoreConfigV2rayService rulesItem.outboundTag = null; } } + _coreConfig.routing.rules.Add(BuildFinalRule()); } } catch (Exception ex) @@ -181,4 +182,28 @@ public partial class CoreConfigV2rayService return tag; } + + private RulesItem4Ray BuildFinalRule() + { + var finalRule = new RulesItem4Ray() + { + type = "field", + network = "tcp,udp", + outboundTag = Global.ProxyTag, + }; + var balancer = + _coreConfig?.routing?.balancers?.FirstOrDefault(b => b.tag == Global.ProxyTag + Global.BalancerTagSuffix, null); + var domainStrategy = _coreConfig.routing?.domainStrategy ?? Global.AsIs; + if (balancer is not null) + { + finalRule.outboundTag = null; + finalRule.balancerTag = balancer.tag; + } + if (domainStrategy == Global.IPIfNonMatch) + { + finalRule.network = null; + finalRule.ip = ["0.0.0.0/0", "::/0"]; + } + return finalRule; + } } From d6ee3a4f381eafc6dbba8de32f30b4d6b3b1c202 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Wed, 25 Feb 2026 11:00:42 +0800 Subject: [PATCH 2/3] Fix DNS routing --- v2rayN/ServiceLib/Global.cs | 1 + v2rayN/ServiceLib/Models/V2rayConfig.cs | 6 -- .../CoreConfig/V2ray/V2rayDnsService.cs | 64 +++++++++++++++---- 3 files changed, 52 insertions(+), 19 deletions(-) diff --git a/v2rayN/ServiceLib/Global.cs b/v2rayN/ServiceLib/Global.cs index 90721217..8469cb76 100644 --- a/v2rayN/ServiceLib/Global.cs +++ b/v2rayN/ServiceLib/Global.cs @@ -49,6 +49,7 @@ public class Global public const string DirectTag = "direct"; public const string BlockTag = "block"; public const string DnsTag = "dns-module"; + public const string DirectDnsTag = "direct-dns"; public const string BalancerTagSuffix = "-round"; public const string StreamSecurity = "tls"; public const string StreamSecurityReality = "reality"; diff --git a/v2rayN/ServiceLib/Models/V2rayConfig.cs b/v2rayN/ServiceLib/Models/V2rayConfig.cs index 80baf43b..2393d5fb 100644 --- a/v2rayN/ServiceLib/Models/V2rayConfig.cs +++ b/v2rayN/ServiceLib/Models/V2rayConfig.cs @@ -220,12 +220,6 @@ public class DnsServer4Ray public List? domains { get; set; } public bool? skipFallback { get; set; } public List? expectedIPs { get; set; } - public List? unexpectedIPs { get; set; } - public string? clientIp { get; set; } - public string? queryStrategy { get; set; } - public int? timeoutMs { get; set; } - public bool? disableCache { get; set; } - public bool? finalQuery { get; set; } public string? tag { get; set; } } diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs index aec724b0..eb1b24ff 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs @@ -71,6 +71,25 @@ public partial class CoreConfigV2rayService dnsItem.enableParallelQuery = simpleDnsItem?.ParallelQuery is true ? true : null; // DNS routing + var directDnsTags = dnsItem.servers + .Select(server => + { + var tagNode = (server as JsonObject)?["tag"]; + return tagNode is JsonValue value && value.TryGetValue(out var tag) ? tag : null; + }) + .Where(tag => tag is not null && tag.StartsWith(Global.DirectDnsTag, StringComparison.Ordinal)) + .Select(tag => tag!) + .ToList(); + if (directDnsTags.Count > 0) + { + _coreConfig.routing.rules.Add(new() + { + type = "field", + inboundTag = directDnsTags, + outboundTag = Global.DirectTag, + }); + } + var finalRule = BuildFinalRule(); dnsItem.tag = Global.DnsTag; _coreConfig.routing.rules.Add(new() @@ -78,7 +97,7 @@ public partial class CoreConfigV2rayService type = "field", inboundTag = [Global.DnsTag], outboundTag = finalRule.outboundTag, - balancerTag = finalRule.balancerTag + balancerTag = finalRule.balancerTag, }); _coreConfig.dns = dnsItem; @@ -212,11 +231,13 @@ public partial class CoreConfigV2rayService dnsItem.servers ??= []; + var directDnsTagIndex = 1; + AddDnsServers(remoteDNSAddress, proxyDomainList); - AddDnsServers(directDNSAddress, directDomainList); + AddDnsServers(directDNSAddress, directDomainList, true); AddDnsServers(remoteDNSAddress, proxyGeositeList); - AddDnsServers(directDNSAddress, directGeositeList); - AddDnsServers(directDNSAddress, expectedDomainList, expectedIPs); + AddDnsServers(directDNSAddress, directGeositeList, true); + AddDnsServers(directDNSAddress, expectedDomainList, true, expectedIPs); if (dnsServerDomains.Count > 0) { AddDnsServers(bootstrapDNSAddress, dnsServerDomains); @@ -228,8 +249,21 @@ public partial class CoreConfigV2rayService || lastRule.Network == "tcp,udp" || lastRule.Ip?.Contains("0.0.0.0/0") == true); - var defaultDnsServers = useDirectDns ? directDNSAddress : remoteDNSAddress; - dnsItem.servers.AddRange(defaultDnsServers); + if (!useDirectDns) + { + dnsItem.servers.AddRange(remoteDNSAddress); + } + else + { + foreach (var dns in directDNSAddress) + { + var dnsServer = CreateDnsServer(dns, []); + dnsServer.tag = $"{Global.DirectDnsTag}-{directDnsTagIndex++}"; + dnsServer.skipFallback = false; + dnsItem.servers.Add(JsonUtils.SerializeToNode(dnsServer, + new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull })); + } + } return; static List ParseDnsAddresses(string? dnsInput, string defaultAddress) @@ -243,7 +277,7 @@ public partial class CoreConfigV2rayService return addresses.Count > 0 ? addresses : new List { defaultAddress }; } - static object? CreateDnsServer(string dnsAddress, List domains, List? expectedIPs = null) + static DnsServer4Ray CreateDnsServer(string dnsAddress, List domains, List? expectedIPs = null) { var (domain, scheme, port, path) = Utils.ParseUrl(dnsAddress); var domainFinal = dnsAddress; @@ -266,13 +300,10 @@ public partial class CoreConfigV2rayService domains = domains.Count > 0 ? domains : null, expectedIPs = expectedIPs?.Count > 0 ? expectedIPs : null }; - return JsonUtils.SerializeToNode(dnsServer, new JsonSerializerOptions - { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }); + return dnsServer; } - void AddDnsServers(List dnsAddresses, List domains, List? expectedIPs = null) + void AddDnsServers(List dnsAddresses, List domains, bool isDirectDns = false, List? expectedIPs = null) { if (domains.Count <= 0) { @@ -280,7 +311,14 @@ public partial class CoreConfigV2rayService } foreach (var dnsAddress in dnsAddresses) { - dnsItem.servers.Add(CreateDnsServer(dnsAddress, domains, expectedIPs)); + var dnsServer = CreateDnsServer(dnsAddress, domains, expectedIPs); + if (isDirectDns) + { + dnsServer.tag = $"{Global.DirectDnsTag}-{directDnsTagIndex++}"; + } + var dnsServerNode = JsonUtils.SerializeToNode(dnsServer, + new JsonSerializerOptions { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }); + dnsItem.servers.Add(dnsServerNode); } } } From 152e3dc4aaf008cf2cd5ded0dc715b91251b7899 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Thu, 26 Feb 2026 16:11:15 +0800 Subject: [PATCH 3/3] Fix --- .../CoreConfig/Singbox/SingboxDnsService.cs | 14 +++++++++----- .../CoreConfig/V2ray/CoreConfigV2rayService.cs | 6 ++++++ .../Services/CoreConfig/V2ray/V2rayDnsService.cs | 16 +++++++++++----- .../CoreConfig/V2ray/V2rayRoutingService.cs | 1 - 4 files changed, 26 insertions(+), 11 deletions(-) diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs index b4ddaf7f..03f4e80c 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs @@ -26,11 +26,15 @@ public partial class CoreConfigSingboxService { var rules = JsonUtils.Deserialize>(routing.RuleSet) ?? []; - useDirectDns = rules?.LastOrDefault() is { } lastRule && - lastRule.OutboundTag == Global.DirectTag && - (lastRule.Port == "0-65535" || - lastRule.Network == "tcp,udp" || - lastRule.Ip?.Contains("0.0.0.0/0") == true); + if (rules?.LastOrDefault() is { } lastRule && lastRule.OutboundTag == Global.DirectTag) + { + var noDomain = lastRule.Domain == null || lastRule.Domain.Count == 0; + var noProcess = lastRule.Process == null || lastRule.Process.Count == 0; + var isAnyIp = lastRule.Ip == null || lastRule.Ip.Count == 0 || lastRule.Ip.Contains("0.0.0.0/0"); + var isAnyPort = string.IsNullOrEmpty(lastRule.Port) || lastRule.Port == "0-65535"; + var isAnyNetwork = string.IsNullOrEmpty(lastRule.Network) || lastRule.Network == "tcp,udp"; + useDirectDns = noDomain && noProcess && isAnyIp && isAnyPort && isAnyNetwork; + } } _coreConfig.dns.final = useDirectDns ? Global.SingboxDirectDNSTag : Global.SingboxRemoteDNSTag; var simpleDnsItem = context.SimpleDnsItem; diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs index 579b5f90..4d5076a3 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs @@ -60,6 +60,12 @@ public partial class CoreConfigV2rayService(CoreConfigContext context) GenStatistic(); + var finalRule = BuildFinalRule(); + if (!string.IsNullOrEmpty(finalRule?.balancerTag)) + { + _coreConfig.routing.rules.Add(finalRule); + } + ret.Msg = string.Format(ResUI.SuccessfulConfiguration, ""); ret.Success = true; ret.Data = ApplyFullConfigTemplate(); diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs index eb1b24ff..021fee2f 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs @@ -243,11 +243,17 @@ public partial class CoreConfigV2rayService AddDnsServers(bootstrapDNSAddress, dnsServerDomains); } - var useDirectDns = rules?.LastOrDefault() is { } lastRule - && lastRule.OutboundTag == Global.DirectTag - && (lastRule.Port == "0-65535" - || lastRule.Network == "tcp,udp" - || lastRule.Ip?.Contains("0.0.0.0/0") == true); + var useDirectDns = false; + + if (rules?.LastOrDefault() is { } lastRule && lastRule.OutboundTag == Global.DirectTag) + { + var noDomain = lastRule.Domain == null || lastRule.Domain.Count == 0; + var noProcess = lastRule.Process == null || lastRule.Process.Count == 0; + var isAnyIp = lastRule.Ip == null || lastRule.Ip.Count == 0 || lastRule.Ip.Contains("0.0.0.0/0"); + var isAnyPort = string.IsNullOrEmpty(lastRule.Port) || lastRule.Port == "0-65535"; + var isAnyNetwork = string.IsNullOrEmpty(lastRule.Network) || lastRule.Network == "tcp,udp"; + useDirectDns = noDomain && noProcess && isAnyIp && isAnyPort && isAnyNetwork; + } if (!useDirectDns) { diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayRoutingService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayRoutingService.cs index 0c3b4570..76969297 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayRoutingService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayRoutingService.cs @@ -45,7 +45,6 @@ public partial class CoreConfigV2rayService rulesItem.outboundTag = null; } } - _coreConfig.routing.rules.Add(BuildFinalRule()); } } catch (Exception ex)