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/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/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..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(); @@ -244,6 +250,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 +261,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..021fee2f 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs @@ -70,18 +70,36 @@ 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 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) { - // DNS routing - dnsItem.tag = Global.DnsTag; - _coreConfig.routing.rules.Add(new RulesItem4Ray + _coreConfig.routing.rules.Add(new() { type = "field", - inboundTag = new List { Global.DnsTag }, - outboundTag = Global.ProxyTag, + inboundTag = directDnsTags, + outboundTag = Global.DirectTag, }); } + var finalRule = BuildFinalRule(); + dnsItem.tag = Global.DnsTag; + _coreConfig.routing.rules.Add(new() + { + type = "field", + inboundTag = [Global.DnsTag], + outboundTag = finalRule.outboundTag, + balancerTag = finalRule.balancerTag, + }); + _coreConfig.dns = dnsItem; } catch (Exception ex) @@ -93,45 +111,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,35 +231,102 @@ 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)); - } - } - } + 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); } - 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; - var defaultDnsServers = useDirectDns ? directDNSAddress : remoteDNSAddress; - dnsItem.servers.AddRange(defaultDnsServers); + 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) + { + 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) + { + 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 DnsServer4Ray 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 dnsServer; + } + + void AddDnsServers(List dnsAddresses, List domains, bool isDirectDns = false, List? expectedIPs = null) + { + if (domains.Count <= 0) + { + return; + } + foreach (var dnsAddress in dnsAddresses) + { + 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); + } + } } 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..76969297 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayRoutingService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayRoutingService.cs @@ -181,4 +181,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; + } }