diff --git a/v2rayN/ServiceLib/Common/JsonUtils.cs b/v2rayN/ServiceLib/Common/JsonUtils.cs index 773ba79c..6954e124 100644 --- a/v2rayN/ServiceLib/Common/JsonUtils.cs +++ b/v2rayN/ServiceLib/Common/JsonUtils.cs @@ -128,5 +128,8 @@ public class JsonUtils /// /// /// - public static JsonNode? SerializeToNode(object? obj) => JsonSerializer.SerializeToNode(obj); + public static JsonNode? SerializeToNode(object? obj, JsonSerializerOptions? options = null) + { + return JsonSerializer.SerializeToNode(obj, options); + } } diff --git a/v2rayN/ServiceLib/Global.cs b/v2rayN/ServiceLib/Global.cs index 4888ab6a..11acde71 100644 --- a/v2rayN/ServiceLib/Global.cs +++ b/v2rayN/ServiceLib/Global.cs @@ -76,6 +76,13 @@ public class Global public const int SpeedTestPageSize = 1000; public const string LinuxBash = "/bin/bash"; + public const string SingboxDirectDNSTag = "direct_dns"; + public const string SingboxRemoteDNSTag = "remote_dns"; + public const string SingboxOutboundResolverTag = "outbound_resolver"; + public const string SingboxFinalResolverTag = "final_resolver"; + public const string SingboxHostsDNSTag = "hosts_dns"; + public const string SingboxFakeDNSTag = "fake_dns"; + public static readonly List IEProxyProtocols = [ "{ip}:{http_port}", @@ -351,25 +358,42 @@ public class Global public static readonly List SingboxDomainStrategy4Out = [ - "ipv4_only", + "", + "ipv4_only", "prefer_ipv4", "prefer_ipv6", - "ipv6_only", - "" + "ipv6_only" ]; - public static readonly List DomainDNSAddress = + public static readonly List DomainDirectDNSAddress = [ - "223.5.5.5", - "223.6.6.6", + "https://dns.alidns.com/dns-query", + "https://doh.pub/dns-query", + "223.5.5.5", + "119.29.29.29", "localhost" ]; - public static readonly List SingboxDomainDNSAddress = + public static readonly List DomainRemoteDNSAddress = + [ + "https://cloudflare-dns.com/dns-query", + "https://dns.cloudflare.com/dns-query", + "https://dns.google/dns-query", + "https://doh.dns.sb/dns-query", + "https://doh.opendns.com/dns-query", + "https://common.dot.dns.yandex.net", + "8.8.8.8", + "1.1.1.1", + "185.222.222.222", + "208.67.222.222", + "77.88.8.8" + ]; + + public static readonly List DomainPureIPDNSAddress = [ "223.5.5.5", - "223.6.6.6", - "dhcp://auto" + "119.29.29.29", + "localhost" ]; public static readonly List Languages = @@ -539,5 +563,30 @@ public class Global BlockTag ]; + public static readonly Dictionary> PredefinedHosts = new() + { + { "dns.google", new List { "8.8.8.8", "8.8.4.4", "2001:4860:4860::8888", "2001:4860:4860::8844" } }, + { "dns.alidns.com", new List { "223.5.5.5", "223.6.6.6", "2400:3200::1", "2400:3200:baba::1" } }, + { "one.one.one.one", new List { "1.1.1.1", "1.0.0.1", "2606:4700:4700::1111", "2606:4700:4700::1001" } }, + { "1dot1dot1dot1.cloudflare-dns.com", new List { "1.1.1.1", "1.0.0.1", "2606:4700:4700::1111", "2606:4700:4700::1001" } }, + { "cloudflare-dns.com", new List { "104.16.249.249", "104.16.248.249", "2606:4700::6810:f8f9", "2606:4700::6810:f9f9" } }, + { "dns.cloudflare.com", new List { "104.16.132.229", "104.16.133.229", "2606:4700::6810:84e5", "2606:4700::6810:85e5" } }, + { "dot.pub", new List { "1.12.12.12", "120.53.53.53" } }, + { "dns.quad9.net", new List { "9.9.9.9", "149.112.112.112", "2620:fe::fe", "2620:fe::9" } }, + { "dns.yandex.net", new List { "77.88.8.8", "77.88.8.1", "2a02:6b8::feed:0ff", "2a02:6b8:0:1::feed:0ff" } }, + { "dns.sb", new List { "185.222.222.222", "2a09::" } }, + { "dns.umbrella.com", new List { "208.67.220.220", "208.67.222.222", "2620:119:35::35", "2620:119:53::53" } }, + { "dns.sse.cisco.com", new List { "208.67.220.220", "208.67.222.222", "2620:119:35::35", "2620:119:53::53" } }, + { "engage.cloudflareclient.com", new List { "162.159.192.1", "2606:4700:d0::a29f:c001" } } + }; + + public static readonly List ExpectedIPs = + [ + "geoip:cn", + "geoip:ir", + "geoip:ru", + "" + ]; + #endregion const } diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs index 6c93d868..998c2427 100644 --- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -112,6 +112,11 @@ public class ConfigHandler config.ConstItem ??= new ConstItem(); + if (config.SimpleDNSItem == null) + { + InitBuiltinSimpleDNS(config); + } + config.SpeedTestItem ??= new(); if (config.SpeedTestItem.SpeedTestTimeout < 10) { @@ -2094,18 +2099,38 @@ public class ConfigHandler /// /// Initialize built-in DNS configurations /// Creates default DNS items for V2Ray and sing-box + /// Also checks existing DNS items and disables those with empty NormalDNS /// /// Current configuration /// 0 if successful public static async Task InitBuiltinDNS(Config config) { var items = await AppHandler.Instance.DNSItems(); + + // Check existing DNS items and disable those with empty NormalDNS + var needsUpdate = false; + foreach (var existingItem in items) + { + if (existingItem.NormalDNS.IsNullOrEmpty() && existingItem.Enabled) + { + existingItem.Enabled = false; + needsUpdate = true; + } + } + + // Update items if any changes were made + if (needsUpdate) + { + await SQLiteHelper.Instance.UpdateAllAsync(items); + } + if (items.Count <= 0) { var item = new DNSItem() { Remarks = "V2ray", CoreType = ECoreType.Xray, + Enabled = false, }; await SaveDNSItems(config, item); @@ -2113,6 +2138,7 @@ public class ConfigHandler { Remarks = "sing-box", CoreType = ECoreType.sing_box, + Enabled = false, }; await SaveDNSItems(config, item2); } @@ -2184,6 +2210,38 @@ public class ConfigHandler #endregion DNS + #region Simple DNS + + public static int InitBuiltinSimpleDNS(Config config) + { + config.SimpleDNSItem = new SimpleDNSItem() + { + UseSystemHosts = false, + AddCommonHosts = true, + FakeIP = false, + BlockBindingQuery = true, + DirectDNS = Global.DomainDirectDNSAddress.FirstOrDefault(), + RemoteDNS = Global.DomainRemoteDNSAddress.FirstOrDefault(), + SingboxOutboundsResolveDNS = Global.DomainDirectDNSAddress.FirstOrDefault(), + SingboxFinalResolveDNS = Global.DomainPureIPDNSAddress.FirstOrDefault() + }; + return 0; + } + + public static async Task GetExternalSimpleDNSItem(string url) + { + var downloadHandle = new DownloadService(); + var templateContent = await downloadHandle.TryDownloadString(url, true, ""); + if (templateContent.IsNullOrEmpty()) + return null; + var template = JsonUtils.Deserialize(templateContent); + if (template == null) + return null; + return template; + } + + #endregion Simple DNS + #region Regional Presets /// @@ -2205,6 +2263,8 @@ public class ConfigHandler await SQLiteHelper.Instance.DeleteAllAsync(); await InitBuiltinDNS(config); + InitBuiltinSimpleDNS(config); + return true; case EPresetType.Russia: @@ -2215,6 +2275,8 @@ public class ConfigHandler await SaveDNSItems(config, await GetExternalDNSItem(ECoreType.Xray, Global.DNSTemplateSources[1] + "v2ray.json")); await SaveDNSItems(config, await GetExternalDNSItem(ECoreType.sing_box, Global.DNSTemplateSources[1] + "sing_box.json")); + config.SimpleDNSItem = await GetExternalSimpleDNSItem(Global.DNSTemplateSources[1] + "simple_dns.json"); + return true; case EPresetType.Iran: @@ -2225,6 +2287,8 @@ public class ConfigHandler await SaveDNSItems(config, await GetExternalDNSItem(ECoreType.Xray, Global.DNSTemplateSources[2] + "v2ray.json")); await SaveDNSItems(config, await GetExternalDNSItem(ECoreType.sing_box, Global.DNSTemplateSources[2] + "sing_box.json")); + config.SimpleDNSItem = await GetExternalSimpleDNSItem(Global.DNSTemplateSources[2] + "simple_dns.json"); + return true; } diff --git a/v2rayN/ServiceLib/Models/Config.cs b/v2rayN/ServiceLib/Models/Config.cs index 15996608..91b49b29 100644 --- a/v2rayN/ServiceLib/Models/Config.cs +++ b/v2rayN/ServiceLib/Models/Config.cs @@ -48,6 +48,7 @@ public class Config public List Inbound { get; set; } public List GlobalHotkeys { get; set; } public List CoreTypeItem { get; set; } + public SimpleDNSItem SimpleDNSItem { get; set; } #endregion other entities } diff --git a/v2rayN/ServiceLib/Models/ConfigItems.cs b/v2rayN/ServiceLib/Models/ConfigItems.cs index c6254b9f..998b49f1 100644 --- a/v2rayN/ServiceLib/Models/ConfigItems.cs +++ b/v2rayN/ServiceLib/Models/ConfigItems.cs @@ -253,3 +253,21 @@ public class WindowSizeItem public int Width { get; set; } public int Height { get; set; } } + +[Serializable] +public class SimpleDNSItem +{ + public bool? UseSystemHosts { get; set; } + public bool? AddCommonHosts { get; set; } + public bool? FakeIP { get; set; } + public bool? BlockBindingQuery { get; set; } + public string? DirectDNS { get; set; } + public string? RemoteDNS { get; set; } + public string? SingboxOutboundsResolveDNS { get; set; } + public string? SingboxFinalResolveDNS { get; set; } + public string? RayStrategy4Freedom { get; set; } + public string? SingboxStrategy4Direct { get; set; } + public string? SingboxStrategy4Proxy { get; set; } + public string? Hosts { get; set; } + public string? DirectExpectedIPs { get; set; } +} diff --git a/v2rayN/ServiceLib/Models/DNSItem.cs b/v2rayN/ServiceLib/Models/DNSItem.cs index 59ab9c9b..9474d906 100644 --- a/v2rayN/ServiceLib/Models/DNSItem.cs +++ b/v2rayN/ServiceLib/Models/DNSItem.cs @@ -9,7 +9,7 @@ public class DNSItem public string Id { get; set; } public string Remarks { get; set; } - public bool Enabled { get; set; } = true; + public bool Enabled { get; set; } = false; public ECoreType CoreType { get; set; } public bool UseSystemHosts { get; set; } public string? NormalDNS { get; set; } diff --git a/v2rayN/ServiceLib/Models/SingboxConfig.cs b/v2rayN/ServiceLib/Models/SingboxConfig.cs index c63acc4a..cbaf77e6 100644 --- a/v2rayN/ServiceLib/Models/SingboxConfig.cs +++ b/v2rayN/ServiceLib/Models/SingboxConfig.cs @@ -36,6 +36,7 @@ public class Dns4Sbox public class Route4Sbox { + public Rule4Sbox? default_domain_resolver { get; set; } // or string public bool? auto_detect_interface { get; set; } public List rules { get; set; } public List? rule_set { get; set; } @@ -75,7 +76,7 @@ public class Rule4Sbox public string? strategy { get; set; } public List? sniffer { get; set; } public string? rcode { get; set; } - public List? query_type { get; set; } + public List? query_type { get; set; } public List? answer { get; set; } public List? ns { get; set; } public List? extra { get; set; } @@ -237,7 +238,7 @@ public class Server4Sbox : BaseServer4Sbox public string? path { get; set; } public Headers4Sbox? headers { get; set; } // public List? path { get; set; } // hosts - public Dictionary? predefined { get; set; } + public Dictionary>? predefined { get; set; } // Deprecated public string? address { get; set; } public string? address_resolver { get; set; } diff --git a/v2rayN/ServiceLib/Models/V2rayConfig.cs b/v2rayN/ServiceLib/Models/V2rayConfig.cs index e7cd3722..354e33dc 100644 --- a/v2rayN/ServiceLib/Models/V2rayConfig.cs +++ b/v2rayN/ServiceLib/Models/V2rayConfig.cs @@ -5,7 +5,7 @@ namespace ServiceLib.Models; public class V2rayConfig { public Log4Ray log { get; set; } - public object dns { get; set; } + public Dns4Ray dns { get; set; } public List inbounds { get; set; } public List outbounds { get; set; } public Routing4Ray routing { get; set; } @@ -203,7 +203,8 @@ public class Response4Ray public class Dns4Ray { - public List servers { get; set; } + public Dictionary>? hosts { get; set; } + public List servers { get; set; } } public class DnsServer4Ray @@ -211,6 +212,8 @@ public class DnsServer4Ray public string? address { get; set; } public List? domains { get; set; } public bool? skipFallback { get; set; } + public List? expectedIPs { get; set; } + public List? unexpectedIPs { get; set; } } public class Routing4Ray diff --git a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs index ca554860..31405d61 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs +++ b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs @@ -2220,6 +2220,15 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Add Common DNS Hosts 的本地化字符串。 + /// + public static string TbAddCommonDNSHosts { + get { + return ResourceManager.GetString("TbAddCommonDNSHosts", resourceCulture); + } + } + /// /// 查找类似 Address 的本地化字符串。 /// @@ -2256,6 +2265,15 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Apply to Proxy Domains Only 的本地化字符串。 + /// + public static string TbApplyProxyDomainsOnly { + get { + return ResourceManager.GetString("TbApplyProxyDomainsOnly", resourceCulture); + } + } + /// /// 查找类似 Auto refresh 的本地化字符串。 /// @@ -2283,6 +2301,15 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Block SVCB and HTTPS Queries 的本地化字符串。 + /// + public static string TbBlockSVCBHTTPSQueries { + get { + return ResourceManager.GetString("TbBlockSVCBHTTPSQueries", resourceCulture); + } + } + /// /// 查找类似 Browse 的本地化字符串。 /// @@ -2337,6 +2364,24 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Enable Custom DNS 的本地化字符串。 + /// + public static string TbCustomDNSEnable { + get { + return ResourceManager.GetString("TbCustomDNSEnable", resourceCulture); + } + } + + /// + /// 查找类似 Custom DNS Enabled, This Page's Settings Invalid 的本地化字符串。 + /// + public static string TbCustomDNSEnabledPageInvalid { + get { + return ResourceManager.GetString("TbCustomDNSEnabledPageInvalid", resourceCulture); + } + } + /// /// 查找类似 Display GUI 的本地化字符串。 /// @@ -2355,6 +2400,15 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 DNS Hosts: ("domain1 ip1 ip2" per line) 的本地化字符串。 + /// + public static string TbDNSHostsConfig { + get { + return ResourceManager.GetString("TbDNSHostsConfig", resourceCulture); + } + } + /// /// 查找类似 Supports DNS Object; Click to view documentation 的本地化字符串。 /// @@ -2400,6 +2454,15 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Domestic DNS 的本地化字符串。 + /// + public static string TbDomesticDNS { + get { + return ResourceManager.GetString("TbDomesticDNS", resourceCulture); + } + } + /// /// 查找类似 Edit 的本地化字符串。 /// @@ -2418,6 +2481,15 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 FakeIP 的本地化字符串。 + /// + public static string TbFakeIP { + get { + return ResourceManager.GetString("TbFakeIP", resourceCulture); + } + } + /// /// 查找类似 Fingerprint 的本地化字符串。 /// @@ -2616,6 +2688,15 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Prevent DNS Leaks 的本地化字符串。 + /// + public static string TbPreventDNSLeaks { + get { + return ResourceManager.GetString("TbPreventDNSLeaks", resourceCulture); + } + } + /// /// 查找类似 Private Key 的本地化字符串。 /// @@ -2652,6 +2733,15 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Remote DNS 的本地化字符串。 + /// + public static string TbRemoteDNS { + get { + return ResourceManager.GetString("TbRemoteDNS", resourceCulture); + } + } + /// /// 查找类似 Camouflage domain(host) 的本地化字符串。 /// @@ -2760,6 +2850,69 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 sing-box Direct Resolution Strategy 的本地化字符串。 + /// + public static string TbSBDirectResolveStrategy { + get { + return ResourceManager.GetString("TbSBDirectResolveStrategy", resourceCulture); + } + } + + /// + /// 查找类似 The sing-box DoH resolution server can be overwritten 的本地化字符串。 + /// + public static string TbSBDoHOverride { + get { + return ResourceManager.GetString("TbSBDoHOverride", resourceCulture); + } + } + + /// + /// 查找类似 sing-box DoH Resolver Server 的本地化字符串。 + /// + public static string TbSBDoHResolverServer { + get { + return ResourceManager.GetString("TbSBDoHResolverServer", resourceCulture); + } + } + + /// + /// 查找类似 Fallback DNS Resolution, Suggest IP 的本地化字符串。 + /// + public static string TbSBFallbackDNSResolve { + get { + return ResourceManager.GetString("TbSBFallbackDNSResolve", resourceCulture); + } + } + + /// + /// 查找类似 Resolve Outbound Domains 的本地化字符串。 + /// + public static string TbSBOutboundDomainResolve { + get { + return ResourceManager.GetString("TbSBOutboundDomainResolve", resourceCulture); + } + } + + /// + /// 查找类似 Outbound DNS Resolution (sing-box) 的本地化字符串。 + /// + public static string TbSBOutboundsResolverDNS { + get { + return ResourceManager.GetString("TbSBOutboundsResolverDNS", resourceCulture); + } + } + + /// + /// 查找类似 sing-box Remote Resolution Strategy 的本地化字符串。 + /// + public static string TbSBRemoteResolveStrategy { + get { + return ResourceManager.GetString("TbSBRemoteResolveStrategy", resourceCulture); + } + } + /// /// 查找类似 Encryption method (security) 的本地化字符串。 /// @@ -3705,6 +3858,33 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Validate Regional Domain IPs 的本地化字符串。 + /// + public static string TbValidateDirectExpectedIPs { + get { + return ResourceManager.GetString("TbValidateDirectExpectedIPs", resourceCulture); + } + } + + /// + /// 查找类似 When configured, validates IPs returned for regional domains (e.g., geosite:cn), returning only expected IPs 的本地化字符串。 + /// + public static string TbValidateDirectExpectedIPsDesc { + get { + return ResourceManager.GetString("TbValidateDirectExpectedIPsDesc", resourceCulture); + } + } + + /// + /// 查找类似 xray Freedom Resolution Strategy 的本地化字符串。 + /// + public static string TbXrayFreedomResolveStrategy { + get { + return ResourceManager.GetString("TbXrayFreedomResolveStrategy", resourceCulture); + } + } + /// /// 查找类似 The delay: {0} ms, {1} 的本地化字符串。 /// @@ -3714,6 +3894,24 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Advanced DNS Settings 的本地化字符串。 + /// + public static string ThAdvancedDNSSettings { + get { + return ResourceManager.GetString("ThAdvancedDNSSettings", resourceCulture); + } + } + + /// + /// 查找类似 Basic DNS Settings 的本地化字符串。 + /// + public static string ThBasicDNSSettings { + get { + return ResourceManager.GetString("ThBasicDNSSettings", resourceCulture); + } + } + /// /// 查找类似 Active 的本地化字符串。 /// diff --git a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx index bd2cb887..24625798 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx @@ -1404,4 +1404,70 @@ Add [Anytls] Configuration + + Remote DNS + + + Domestic DNS + + + Outbound DNS Resolution (sing-box) + + + Resolve Outbound Domains + + + sing-box DoH Resolver Server + + + Fallback DNS Resolution, Suggest IP + + + xray Freedom Resolution Strategy + + + sing-box Direct Resolution Strategy + + + sing-box Remote Resolution Strategy + + + Add Common DNS Hosts + + + The sing-box DoH resolution server can be overwritten + + + FakeIP + + + Block SVCB and HTTPS Queries + + + Prevent DNS Leaks + + + DNS Hosts: ("domain1 ip1 ip2" per line) + + + Apply to Proxy Domains Only + + + Basic DNS Settings + + + Advanced DNS Settings + + + Validate Regional Domain IPs + + + When configured, validates IPs returned for regional domains (e.g., geosite:cn), returning only expected IPs + + + Enable Custom DNS + + + Custom DNS Enabled, This Page's Settings Invalid + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.hu.resx b/v2rayN/ServiceLib/Resx/ResUI.hu.resx index 0fc30153..14df7b0e 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.hu.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.hu.resx @@ -1404,4 +1404,70 @@ [Anytls] konfiguráció hozzáadása + + Remote DNS + + + Domestic DNS + + + Outbound DNS Resolution (sing-box) + + + Resolve Outbound Domains + + + sing-box DoH Resolver Server + + + Fallback DNS Resolution, Suggest IP + + + xray Freedom Resolution Strategy + + + sing-box Direct Resolution Strategy + + + sing-box Remote Resolution Strategy + + + Add Common DNS Hosts + + + The sing-box DoH resolution server can be overwritten + + + FakeIP + + + Block SVCB and HTTPS Queries + + + Prevent DNS Leaks + + + DNS Hosts: ("domain1 ip1 ip2" per line) + + + Apply to Proxy Domains Only + + + Basic DNS Settings + + + Advanced DNS Settings + + + Validate Regional Domain IPs + + + When configured, validates IPs returned for regional domains (e.g., geosite:cn), returning only expected IPs + + + Enable Custom DNS + + + Custom DNS Enabled, This Page's Settings Invalid + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayN/ServiceLib/Resx/ResUI.resx index 7ecfcc98..e44f9098 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.resx @@ -1404,4 +1404,70 @@ Add [Anytls] Configuration + + Remote DNS + + + Domestic DNS + + + Outbound DNS Resolution (sing-box) + + + Resolve Outbound Domains + + + sing-box DoH Resolver Server + + + Fallback DNS Resolution, Suggest IP + + + xray Freedom Resolution Strategy + + + sing-box Direct Resolution Strategy + + + sing-box Remote Resolution Strategy + + + Add Common DNS Hosts + + + The sing-box DoH resolution server can be overwritten + + + FakeIP + + + Block SVCB and HTTPS Queries + + + Prevent DNS Leaks + + + DNS Hosts: ("domain1 ip1 ip2" per line) + + + Apply to Proxy Domains Only + + + Basic DNS Settings + + + Advanced DNS Settings + + + Validate Regional Domain IPs + + + When configured, validates IPs returned for regional domains (e.g., geosite:cn), returning only expected IPs + + + Enable Custom DNS + + + Custom DNS Enabled, This Page's Settings Invalid + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayN/ServiceLib/Resx/ResUI.ru.resx index 25f17e0d..c8c2894a 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.ru.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.ru.resx @@ -1,17 +1,17 @@ - @@ -807,9 +807,6 @@ Вверх (U) - - Переместить вверх/вниз - Фильтр, поддерживает regex @@ -969,6 +966,9 @@ URL для тестирования скорости + + Переместить вверх/вниз + PublicKey @@ -1404,4 +1404,70 @@ Добавить сервер [Anytls] + + Remote DNS + + + Domestic DNS + + + Outbound DNS Resolution (sing-box) + + + Resolve Outbound Domains + + + sing-box DoH Resolver Server + + + Fallback DNS Resolution, Suggest IP + + + xray Freedom Resolution Strategy + + + sing-box Direct Resolution Strategy + + + sing-box Remote Resolution Strategy + + + Add Common DNS Hosts + + + The sing-box DoH resolution server can be overwritten + + + FakeIP + + + Block SVCB and HTTPS Queries + + + Prevent DNS Leaks + + + DNS Hosts: ("domain1 ip1 ip2" per line) + + + Apply to Proxy Domains Only + + + Basic DNS Settings + + + Advanced DNS Settings + + + Validate Regional Domain IPs + + + When configured, validates IPs returned for regional domains (e.g., geosite:cn), returning only expected IPs + + + Enable Custom DNS + + + Custom DNS Enabled, This Page's Settings Invalid + \ 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 95d69e9b..f9c5a02b 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx @@ -1401,4 +1401,70 @@ 添加 [Anytls] 配置文件 + + 远程 DNS + + + 直连 DNS + + + 出站 DNS 解析(sing-box) + + + 解析出站域名 + + + sing-box DoH 解析服务器 + + + 兜底解析其他 DNS 域名,建议设为 ip + + + xray freedom 解析策略 + + + sing-box 直连解析策略 + + + sing-box 远程解析策略 + + + 添加常用 DNS Hosts + + + 开启后可覆盖 sing-box DoH 解析服务器 + + + FakeIP + + + 阻止 SVCB 和 HTTPS 查询 + + + 避免 DNS 泄漏 + + + DNS Hosts:(“域名1 ip1 ip2” 一行一个) + + + 仅对代理域名生效 + + + DNS 基础设置 + + + DNS 进阶设置 + + + 校验相应地区域名 IP + + + 配置后,会对相应地区域名(如 geosite:cn)的返回 IP 进行校验,仅返回期望 IP + + + 启用自定义 DNS + + + 自定义 DNS 已启用,此页面配置将无效 + \ 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 0b23885c..429e4637 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx @@ -1401,4 +1401,70 @@ 新增 [Anytls] 設定檔 + + Remote DNS + + + Domestic DNS + + + Outbound DNS Resolution (sing-box) + + + Resolve Outbound Domains + + + sing-box DoH Resolver Server + + + Fallback DNS Resolution, Suggest IP + + + xray Freedom Resolution Strategy + + + sing-box Direct Resolution Strategy + + + sing-box Remote Resolution Strategy + + + Add Common DNS Hosts + + + The sing-box DoH resolution server can be overwritten + + + FakeIP + + + Block SVCB and HTTPS Queries + + + Prevent DNS Leaks + + + DNS Hosts: ("domain1 ip1 ip2" per line) + + + Apply to Proxy Domains Only + + + Basic DNS Settings + + + Advanced DNS Settings + + + Validate Regional Domain IPs + + + When configured, validates IPs returned for regional domains (e.g., geosite:cn), returning only expected IPs + + + Enable Custom DNS + + + Custom DNS Enabled, This Page's Settings Invalid + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs index 80b89654..aa4b4322 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs @@ -1,7 +1,9 @@ +using System.Collections.Generic; using System.Data; using System.Net; using System.Net.NetworkInformation; using System.Reactive; +using System.Text; using DynamicData; using ServiceLib.Models; @@ -73,7 +75,7 @@ public class CoreConfigSingboxService await GenRouting(singboxConfig); - await GenDns(node, singboxConfig); + await GenDns(singboxConfig); await GenExperimental(singboxConfig); @@ -243,20 +245,20 @@ public class CoreConfigSingboxService singboxConfig.route.rules.Add(rule); } - await GenDnsDomains(null, singboxConfig, null); - //var dnsServer = singboxConfig.dns?.servers.FirstOrDefault(); - //if (dnsServer != null) - //{ - // dnsServer.detour = singboxConfig.route.rules.LastOrDefault()?.outbound; - //} - //var dnsRule = singboxConfig.dns?.rules.Where(t => t.outbound != null).FirstOrDefault(); - //if (dnsRule != null) - //{ - // singboxConfig.dns.rules = []; - // singboxConfig.dns.rules.Add(dnsRule); - //} + var rawDNSItem = await AppHandler.Instance.GetDNSItem(ECoreType.sing_box); + if (rawDNSItem != null && rawDNSItem.Enabled == true) + { + await GenDnsDomainsCompatible(singboxConfig, rawDNSItem); + } + else + { + await GenDnsDomains(singboxConfig, _config.SimpleDNSItem); + } + singboxConfig.route.default_domain_resolver = new() + { + server = Global.SingboxFinalResolverTag + }; - //ret.Msg =string.Format(ResUI.SuccessfulConfiguration"), node.getSummary()); ret.Success = true; ret.Data = JsonUtils.Serialize(singboxConfig); return ret; @@ -315,7 +317,19 @@ public class CoreConfigSingboxService await GenOutbound(node, singboxConfig.outbounds.First()); } await GenMoreOutbounds(node, singboxConfig); - await GenDnsDomains(null, singboxConfig, null); + var item = await AppHandler.Instance.GetDNSItem(ECoreType.sing_box); + if (item != null && item.Enabled == true) + { + await GenDnsDomainsCompatible(singboxConfig, item); + } + else + { + await GenDnsDomains(singboxConfig, _config.SimpleDNSItem); + } + singboxConfig.route.default_domain_resolver = new() + { + server = Global.SingboxFinalResolverTag + }; singboxConfig.route.rules.Clear(); singboxConfig.inbounds.Clear(); @@ -417,7 +431,7 @@ public class CoreConfigSingboxService } await GenOutboundsList(proxyProfiles, singboxConfig); - await GenDns(null, singboxConfig); + await GenDns(singboxConfig); await ConvertGeo2Ruleset(singboxConfig); ret.Success = true; @@ -648,17 +662,6 @@ public class CoreConfigSingboxService outbound.server_port = node.Port; outbound.type = Global.ProtocolTypes[node.ConfigType]; - if (Utils.IsDomain(node.Address)) - { - var item = await AppHandler.Instance.GetDNSItem(ECoreType.sing_box); - var localDnsAddress = string.IsNullOrEmpty(item?.DomainDNSAddress) ? Global.SingboxDomainDNSAddress.FirstOrDefault() : item?.DomainDNSAddress; - outbound.domain_resolver = new() - { - server = localDnsAddress.StartsWith("tag://") ? localDnsAddress.Substring(6) : "local_resolver", - strategy = string.IsNullOrEmpty(item?.DomainStrategy4Freedom) ? null : item?.DomainStrategy4Freedom - }; - } - switch (node.ConfigType) { case EConfigType.VMess: @@ -793,17 +796,6 @@ public class CoreConfigSingboxService endpoint.address = Utils.String2List(node.RequestHost); endpoint.type = Global.ProtocolTypes[node.ConfigType]; - if (Utils.IsDomain(node.Address)) - { - var item = await AppHandler.Instance.GetDNSItem(ECoreType.sing_box); - var localDnsAddress = string.IsNullOrEmpty(item?.DomainDNSAddress) ? Global.SingboxDomainDNSAddress.FirstOrDefault() : item?.DomainDNSAddress; - endpoint.domain_resolver = new() - { - server = localDnsAddress.StartsWith("tag://") ? localDnsAddress.Substring(6) : "local_resolver", - strategy = string.IsNullOrEmpty(item?.DomainStrategy4Freedom) ? null : item?.DomainStrategy4Freedom - }; - } - switch (node.ConfigType) { case EConfigType.WireGuard: @@ -1250,6 +1242,22 @@ public class CoreConfigSingboxService try { singboxConfig.route.final = Global.ProxyTag; + var item = _config.SimpleDNSItem; + + var defaultDomainResolverTag = Global.SingboxOutboundResolverTag; + var directDNSStrategy = item.SingboxStrategy4Direct.IsNullOrEmpty() ? Global.SingboxDomainStrategy4Out.FirstOrDefault() : item.SingboxStrategy4Direct; + + var rawDNSItem = await AppHandler.Instance.GetDNSItem(ECoreType.sing_box); + if (rawDNSItem != null && rawDNSItem.Enabled == true) + { + defaultDomainResolverTag = Global.SingboxFinalResolverTag; + directDNSStrategy = rawDNSItem.DomainStrategy4Freedom.IsNullOrEmpty() ? Global.SingboxDomainStrategy4Out.FirstOrDefault() : rawDNSItem.DomainStrategy4Freedom; + } + singboxConfig.route.default_domain_resolver = new() + { + server = defaultDomainResolverTag, + strategy = directDNSStrategy + }; if (_config.TunModeItem.EnableTun) { @@ -1330,14 +1338,14 @@ public class CoreConfigSingboxService if (routing != null) { var rules = JsonUtils.Deserialize>(routing.RuleSet); - foreach (var item in rules ?? []) + foreach (var item1 in rules ?? []) { - if (item.Enabled) + if (item1.Enabled) { - await GenRoutingUserRule(item, singboxConfig); - if (item.Ip != null && item.Ip.Count > 0) + await GenRoutingUserRule(item1, singboxConfig); + if (item1.Ip != null && item1.Ip.Count > 0) { - ipRules.Add(item); + ipRules.Add(item1); } } } @@ -1345,9 +1353,9 @@ public class CoreConfigSingboxService if (_config.RoutingBasicItem.DomainStrategy == "IPIfNonMatch") { singboxConfig.route.rules.Add(resolveRule); - foreach (var item in ipRules) + foreach (var item2 in ipRules) { - await GenRoutingUserRule(item, singboxConfig); + await GenRoutingUserRule(item2, singboxConfig); } } } @@ -1590,7 +1598,278 @@ public class CoreConfigSingboxService return server.tag; } - private async Task GenDns(ProfileItem? node, SingboxConfig singboxConfig) + private async Task GenDns(SingboxConfig singboxConfig) + { + try + { + var item = await AppHandler.Instance.GetDNSItem(ECoreType.sing_box); + if (item != null && item.Enabled == true) + { + return await GenDnsCompatible(singboxConfig); + } + + var simpleDNSItem = _config.SimpleDNSItem; + await GenDnsServers(singboxConfig, simpleDNSItem); + await GenDnsRules(singboxConfig, simpleDNSItem); + + singboxConfig.dns ??= new Dns4Sbox(); + singboxConfig.dns.independent_cache = true; + + var routing = await ConfigHandler.GetDefaultRouting(_config); + var useDirectDns = false; + if (routing != null) + { + 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); + } + singboxConfig.dns.final = useDirectDns ? Global.SingboxDirectDNSTag : Global.SingboxRemoteDNSTag; + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } + return 0; + } + + private async Task GenDnsServers(SingboxConfig singboxConfig, SimpleDNSItem simpleDNSItem) + { + var finalDns = await GenDnsDomains(singboxConfig, simpleDNSItem); + + var directDns = ParseDnsAddress(simpleDNSItem.DirectDNS); + directDns.tag = Global.SingboxDirectDNSTag; + directDns.domain_resolver = Global.SingboxFinalResolverTag; + + var remoteDns = ParseDnsAddress(simpleDNSItem.RemoteDNS); + remoteDns.tag = Global.SingboxRemoteDNSTag; + remoteDns.detour = Global.ProxyTag; + remoteDns.domain_resolver = Global.SingboxFinalResolverTag; + + var resolverDns = ParseDnsAddress(simpleDNSItem.SingboxOutboundsResolveDNS); + resolverDns.tag = Global.SingboxOutboundResolverTag; + resolverDns.domain_resolver = Global.SingboxFinalResolverTag; + + var hostsDns = new Server4Sbox + { + tag = Global.SingboxHostsDNSTag, + type = "hosts", + }; + if (simpleDNSItem.AddCommonHosts == true) + { + hostsDns.predefined = Global.PredefinedHosts; + } + var userHostsMap = simpleDNSItem.Hosts? + .Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries) + .Where(line => !string.IsNullOrWhiteSpace(line)) + .Where(line => line.Contains(' ')) + .ToDictionary( + line => + { + var parts = line.Trim().Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); + return parts[0]; + }, + line => + { + var parts = line.Trim().Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); + var values = parts.Skip(1).ToList(); + return values; + } + ); + + if (userHostsMap != null) + { + foreach (var kvp in userHostsMap) + { + hostsDns.predefined[kvp.Key] = kvp.Value; + } + } + + foreach (var host in hostsDns.predefined) + { + if (finalDns.server == host.Key) + { + finalDns.domain_resolver = Global.SingboxHostsDNSTag; + } + if (remoteDns.server == host.Key) + { + remoteDns.domain_resolver = Global.SingboxHostsDNSTag; + } + if (resolverDns.server == host.Key) + { + resolverDns.domain_resolver = Global.SingboxHostsDNSTag; + } + if (directDns.server == host.Key) + { + directDns.domain_resolver = Global.SingboxHostsDNSTag; + } + } + + singboxConfig.dns ??= new Dns4Sbox(); + singboxConfig.dns.servers ??= new List(); + singboxConfig.dns.servers.Add(remoteDns); + singboxConfig.dns.servers.Add(directDns); + singboxConfig.dns.servers.Add(resolverDns); + singboxConfig.dns.servers.Add(hostsDns); + + // fake ip + if (simpleDNSItem.FakeIP == true) + { + var fakeip = new Server4Sbox + { + tag = Global.SingboxFakeDNSTag, + type = "fakeip", + inet4_range = "198.18.0.0/15", + inet6_range = "fc00::/18", + }; + singboxConfig.dns.servers.Add(fakeip); + } + + return await Task.FromResult(0); + } + + private async Task GenDnsDomains(SingboxConfig singboxConfig, SimpleDNSItem? simpleDNSItem) + { + var finalDns = ParseDnsAddress(simpleDNSItem.SingboxFinalResolveDNS); + finalDns.tag = Global.SingboxFinalResolverTag; + singboxConfig.dns ??= new Dns4Sbox(); + singboxConfig.dns.servers ??= new List(); + singboxConfig.dns.servers.Add(finalDns); + return await Task.FromResult(finalDns); + } + + private async Task GenDnsRules(SingboxConfig singboxConfig, SimpleDNSItem simpleDNSItem) + { + singboxConfig.dns ??= new Dns4Sbox(); + 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.IsNullOrEmpty() ? null : simpleDNSItem.SingboxStrategy4Proxy, + clash_mode = ERuleMode.Global.ToString() + }, + new Rule4Sbox + { + server = Global.SingboxDirectDNSTag, + strategy = simpleDNSItem.SingboxStrategy4Direct.IsNullOrEmpty() ? null : simpleDNSItem.SingboxStrategy4Direct, + clash_mode = ERuleMode.Direct.ToString() + } + }); + + if (simpleDNSItem.BlockBindingQuery == true) + { + singboxConfig.dns.rules.Add(new() + { + query_type = new List { 64, 65 }, + action = "predefined", + rcode = "NOTIMP" + }); + } + + var routing = await ConfigHandler.GetDefaultRouting(_config); + if (routing == null) + return 0; + + var rules = JsonUtils.Deserialize>(routing.RuleSet) ?? []; + var expectedIPCidr = new List(); + var expectedIPsRegions = new List(); + var regionNames = new HashSet(); + + if (!string.IsNullOrEmpty(simpleDNSItem?.DirectExpectedIPs)) + { + var ipItems = simpleDNSItem.DirectExpectedIPs + .Split(new[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries) + .Select(s => s.Trim()) + .Where(s => !string.IsNullOrEmpty(s)) + .ToList(); + + foreach (var ip in ipItems) + { + if (ip.StartsWith("geoip:", StringComparison.OrdinalIgnoreCase)) + { + var region = ip["geoip:".Length..]; + if (!string.IsNullOrEmpty(region)) + { + expectedIPsRegions.Add(region); + regionNames.Add(region); + regionNames.Add($"geolocation-{region}"); + regionNames.Add($"tld-{region}"); + } + } + else + { + expectedIPCidr.Add(ip); + } + } + } + + foreach (var item in rules) + { + if (!item.Enabled || item.Domain is null || item.Domain.Count == 0) + { + continue; + } + + var rule = new Rule4Sbox(); + var validDomains = item.Domain.Count(it => ParseV2Domain(it, rule)); + if (validDomains <= 0) + { + continue; + } + + if (item.OutboundTag == Global.DirectTag) + { + rule.server = Global.SingboxDirectDNSTag; + rule.strategy = string.IsNullOrEmpty(simpleDNSItem.SingboxStrategy4Direct) ? null : simpleDNSItem.SingboxStrategy4Direct; + + if (expectedIPsRegions.Count > 0 && rule.geosite?.Count > 0) + { + var geositeSet = new HashSet(rule.geosite); + if (regionNames.Intersect(geositeSet).Any()) + { + if (expectedIPsRegions.Count > 0) + { + rule.geoip = expectedIPsRegions; + } + if (expectedIPCidr.Count > 0) + { + rule.ip_cidr = expectedIPCidr; + } + } + } + } + else if (item.OutboundTag == Global.ProxyTag) + { + if (simpleDNSItem.FakeIP == true) + { + var rule4Fake = JsonUtils.DeepCopy(rule); + rule4Fake.server = Global.SingboxFakeDNSTag; + singboxConfig.dns.rules.Add(rule4Fake); + } + rule.server = Global.SingboxRemoteDNSTag; + rule.strategy = string.IsNullOrEmpty(simpleDNSItem.SingboxStrategy4Proxy) ? null : simpleDNSItem.SingboxStrategy4Proxy; + } + else if (item.OutboundTag == Global.BlockTag) + { + rule.action = "predefined"; + rule.rcode = "NOERROR"; + rule.answer = new List { "A" }; + } + + singboxConfig.dns.rules.Add(rule); + } + + return 0; + } + + private async Task GenDnsCompatible(SingboxConfig singboxConfig) { try { @@ -1614,11 +1893,11 @@ public class CoreConfigSingboxService if (dns4Sbox.servers != null && dns4Sbox.servers.Count > 0 && dns4Sbox.servers.First().address.IsNullOrEmpty()) { - await GenDnsDomains(node, singboxConfig, item); + await GenDnsDomainsCompatible(singboxConfig, item); } else { - await GenDnsDomainsLegacy(node, singboxConfig, item); + await GenDnsDomainsLegacyCompatible(singboxConfig, item); } } catch (Exception ex) @@ -1628,86 +1907,35 @@ public class CoreConfigSingboxService return 0; } - private async Task GenDnsDomains(ProfileItem? node, SingboxConfig singboxConfig, DNSItem? dNSItem) + private async Task GenDnsDomainsCompatible(SingboxConfig singboxConfig, DNSItem? dNSItem) { var dns4Sbox = singboxConfig.dns ?? new(); dns4Sbox.servers ??= []; dns4Sbox.rules ??= []; - var tag = "local_resolver"; - var localDnsAddress = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.SingboxDomainDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress; + var tag = Global.SingboxFinalResolverTag; + var localDnsAddress = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress; - if (localDnsAddress.StartsWith("tag://")) - { - tag = localDnsAddress.Substring(6); + var localDnsServer = ParseDnsAddress(localDnsAddress); + localDnsServer.tag = tag; - var localDnsTag = "local_local"; - - dns4Sbox.servers.Add(new() - { - tag = localDnsTag, - type = "local" - }); - - dns4Sbox.rules.Insert(0, new() - { - server = localDnsTag, - clash_mode = ERuleMode.Direct.ToString() - }); - } - else - { - var (dnsType, dnsHost, dnsPort, dnsPath) = ParseDnsAddress(localDnsAddress); - - dns4Sbox.servers.Add(new() - { - tag = tag, - type = dnsType, - server = dnsHost, - Interface = dnsType == "dhcp" ? dnsHost : null, - server_port = dnsPort, - path = dnsPath - }); - - dns4Sbox.rules.Insert(0, new() - { - server = tag, - clash_mode = ERuleMode.Direct.ToString() - }); - } - - dns4Sbox.rules.Insert(0, new() - { - server = dns4Sbox.servers.Where(t => t.detour == Global.ProxyTag).Select(t => t.tag).FirstOrDefault() ?? "remote", - clash_mode = ERuleMode.Global.ToString() - }); - - //Tun2SocksAddress - if (_config.TunModeItem.EnableTun && node?.ConfigType == EConfigType.SOCKS && Utils.IsDomain(node?.Sni)) - { - dns4Sbox.rules.Insert(0, new() - { - server = tag, - domain = [node?.Sni], - strategy = string.IsNullOrEmpty(dNSItem?.DomainStrategy4Freedom) ? null : dNSItem?.DomainStrategy4Freedom - }); - } + dns4Sbox.servers.Add(localDnsServer); singboxConfig.dns = dns4Sbox; return await Task.FromResult(0); } - private async Task GenDnsDomainsLegacy(ProfileItem? node, SingboxConfig singboxConfig, DNSItem? dNSItem) + private async Task GenDnsDomainsLegacyCompatible(SingboxConfig singboxConfig, DNSItem? dNSItem) { var dns4Sbox = singboxConfig.dns ?? new(); dns4Sbox.servers ??= []; dns4Sbox.rules ??= []; - var tag = "local_local"; + var tag = Global.SingboxFinalResolverTag; dns4Sbox.servers.Add(new() { tag = tag, - address = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.SingboxDomainDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress, + address = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress, detour = Global.DirectTag, strategy = string.IsNullOrEmpty(dNSItem?.DomainStrategy4Freedom) ? null : dNSItem?.DomainStrategy4Freedom, }); @@ -1736,103 +1964,100 @@ public class CoreConfigSingboxService }); } - //Tun2SocksAddress - if (_config.TunModeItem.EnableTun && node?.ConfigType == EConfigType.SOCKS && Utils.IsDomain(node?.Sni)) - { - dns4Sbox.rules.Insert(0, new() - { - server = tag, - domain = [node?.Sni] - }); - } - singboxConfig.dns = dns4Sbox; return await Task.FromResult(0); } - private (string type, string? host, int? port, string? path) ParseDnsAddress(string address) + private static Server4Sbox? ParseDnsAddress(string address) { - string type = "udp"; - string? host = null; - int? port = null; - string? path = null; - - if (address is "local" or "localhost") + var addressFirst = address?.Split(address.Contains(',') ? ',' : ';').FirstOrDefault()?.Trim(); + if (string.IsNullOrEmpty(addressFirst)) { - return ("local", null, null, null); + return null; } - if (address.StartsWith("dhcp://", StringComparison.OrdinalIgnoreCase)) + var server = new Server4Sbox(); + + if (addressFirst is "local" or "localhost") { - string interface_name = address.Substring(7); - return ("dhcp", interface_name == "auto" ? null : interface_name, null, null); + server.type = "local"; + return server; } - if (!address.Contains("://")) + if (addressFirst.StartsWith("dhcp://", StringComparison.OrdinalIgnoreCase)) + { + var interface_name = addressFirst.Substring(7); + server.type = "dhcp"; + server.Interface = interface_name == "auto" ? null : interface_name; + return server; + } + + if (!addressFirst.Contains("://")) { // udp dns - host = address; - return (type, host, port, path); + server.type = "udp"; + server.server = addressFirst; + return server; } try { - int protocolEndIndex = address.IndexOf("://", StringComparison.Ordinal); - type = address.Substring(0, protocolEndIndex).ToLower(); + var protocolEndIndex = addressFirst.IndexOf("://", StringComparison.Ordinal); + server.type = addressFirst.Substring(0, protocolEndIndex).ToLower(); - var uri = new Uri(address); - host = uri.Host; + var uri = new Uri(addressFirst); + server.server = uri.Host; if (!uri.IsDefaultPort) { - port = uri.Port; + server.server_port = uri.Port; } - if ((type == "https" || type == "h3") && !string.IsNullOrEmpty(uri.AbsolutePath) && uri.AbsolutePath != "/") + if ((server.type == "https" || server.type == "h3") && !string.IsNullOrEmpty(uri.AbsolutePath) && uri.AbsolutePath != "/") { - path = uri.AbsolutePath; + server.path = uri.AbsolutePath; } } catch (UriFormatException) { - int protocolEndIndex = address.IndexOf("://", StringComparison.Ordinal); + var protocolEndIndex = addressFirst.IndexOf("://", StringComparison.Ordinal); if (protocolEndIndex > 0) { - type = address.Substring(0, protocolEndIndex).ToLower(); - string remaining = address.Substring(protocolEndIndex + 3); + server.type = addressFirst.Substring(0, protocolEndIndex).ToLower(); + var remaining = addressFirst.Substring(protocolEndIndex + 3); - int portIndex = remaining.IndexOf(':'); - int pathIndex = remaining.IndexOf('/'); + var portIndex = remaining.IndexOf(':'); + var pathIndex = remaining.IndexOf('/'); if (portIndex > 0) { - host = remaining.Substring(0, portIndex); - string portPart = pathIndex > portIndex + server.server = remaining.Substring(0, portIndex); + var portPart = pathIndex > portIndex ? remaining.Substring(portIndex + 1, pathIndex - portIndex - 1) : remaining.Substring(portIndex + 1); - if (int.TryParse(portPart, out int parsedPort)) + if (int.TryParse(portPart, out var parsedPort)) { - port = parsedPort; + server.server_port = parsedPort; } } else if (pathIndex > 0) { - host = remaining.Substring(0, pathIndex); + server.server = remaining.Substring(0, pathIndex); } else { - host = remaining; + server.server = remaining; } - if (pathIndex > 0 && (type == "https" || type == "h3")) + if (pathIndex > 0 && (server.type == "https" || server.type == "h3")) { - path = remaining.Substring(pathIndex); + server.path = remaining.Substring(pathIndex); } } } - return (type, host, port, path); + return server; } private async Task GenExperimental(SingboxConfig singboxConfig) @@ -1852,7 +2077,8 @@ public class CoreConfigSingboxService singboxConfig.experimental.cache_file = new CacheFile4Sbox() { enabled = true, - path = Utils.GetBinPath("cache.db") + path = Utils.GetBinPath("cache.db"), + store_fakeip = _config.SimpleDNSItem.FakeIP == true }; } @@ -1873,13 +2099,15 @@ public class CoreConfigSingboxService //convert route geosite & geoip to ruleset foreach (var rule in singboxConfig.route.rules.Where(t => t.geosite?.Count > 0).ToList() ?? []) { - rule.rule_set = rule?.geosite?.Select(t => $"{geosite}-{t}").ToList(); + rule.rule_set ??= new List(); + rule.rule_set.AddRange(rule?.geosite?.Select(t => $"{geosite}-{t}").ToList()); rule.geosite = null; AddRuleSets(ruleSets, rule.rule_set); } foreach (var rule in singboxConfig.route.rules.Where(t => t.geoip?.Count > 0).ToList() ?? []) { - rule.rule_set = rule?.geoip?.Select(t => $"{geoip}-{t}").ToList(); + rule.rule_set ??= new List(); + rule.rule_set.AddRange(rule?.geoip?.Select(t => $"{geoip}-{t}").ToList()); rule.geoip = null; AddRuleSets(ruleSets, rule.rule_set); } @@ -1887,12 +2115,14 @@ public class CoreConfigSingboxService //convert dns geosite & geoip to ruleset foreach (var rule in singboxConfig.dns?.rules.Where(t => t.geosite?.Count > 0).ToList() ?? []) { - rule.rule_set = rule?.geosite?.Select(t => $"{geosite}-{t}").ToList(); + rule.rule_set ??= new List(); + rule.rule_set.AddRange(rule?.geosite?.Select(t => $"{geosite}-{t}").ToList()); rule.geosite = null; } foreach (var rule in singboxConfig.dns?.rules.Where(t => t.geoip?.Count > 0).ToList() ?? []) { - rule.rule_set = rule?.geoip?.Select(t => $"{geoip}-{t}").ToList(); + rule.rule_set ??= new List(); + rule.rule_set.AddRange(rule?.geoip?.Select(t => $"{geoip}-{t}").ToList()); rule.geoip = null; } foreach (var dnsRule in singboxConfig.dns?.rules.Where(t => t.rule_set?.Count > 0).ToList() ?? []) diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs index 60b4df3e..1986be14 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs @@ -1,6 +1,8 @@ using System.Net; using System.Net.NetworkInformation; +using System.Text.Json; using System.Text.Json.Nodes; +using System.Text.Json.Serialization; namespace ServiceLib.Services.CoreConfig; @@ -1134,6 +1136,264 @@ public class CoreConfigV2rayService } private async Task GenDns(ProfileItem? node, V2rayConfig v2rayConfig) + { + try + { + var item = await AppHandler.Instance.GetDNSItem(ECoreType.Xray); + if (item != null && item.Enabled == true) + { + return await GenDnsCompatible(node, v2rayConfig); + } + var simpleDNSItem = _config.SimpleDNSItem; + var domainStrategy4Freedom = simpleDNSItem?.RayStrategy4Freedom; + + //Outbound Freedom domainStrategy + if (domainStrategy4Freedom.IsNotEmpty()) + { + var outbound = v2rayConfig.outbounds.FirstOrDefault(t => t is { protocol: "freedom", tag: Global.DirectTag }); + if (outbound != null) + { + outbound.settings = new() + { + domainStrategy = domainStrategy4Freedom, + userLevel = 0 + }; + } + } + + await GenDnsServers(node, v2rayConfig, simpleDNSItem); + await GenDnsHosts(v2rayConfig, simpleDNSItem); + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } + return 0; + } + + private async Task GenDnsServers(ProfileItem? node, V2rayConfig v2rayConfig, SimpleDNSItem 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 dnsServer = new DnsServer4Ray + { + address = dnsAddress, + 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.FirstOrDefault()); + var remoteDNSAddress = ParseDnsAddresses(simpleDNSItem?.RemoteDNS, Global.DomainRemoteDNSAddress.FirstOrDefault()); + + var directDomainList = new List(); + var directGeositeList = new List(); + var proxyDomainList = new List(); + var proxyGeositeList = new List(); + var expectedDomainList = new List(); + var expectedIPs = new List(); + var regionNames = new HashSet(); + + if (!string.IsNullOrEmpty(simpleDNSItem?.DirectExpectedIPs)) + { + expectedIPs = simpleDNSItem.DirectExpectedIPs + .Split(new[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries) + .Select(s => s.Trim()) + .Where(s => !string.IsNullOrEmpty(s)) + .ToList(); + + foreach (var ip in expectedIPs) + { + if (ip.StartsWith("geoip:", StringComparison.OrdinalIgnoreCase)) + { + var region = ip["geoip:".Length..]; + if (!string.IsNullOrEmpty(region)) + { + regionNames.Add($"geosite:{region}"); + regionNames.Add($"geosite:geolocation-{region}"); + regionNames.Add($"geosite:tld-{region}"); + } + } + } + } + + var routing = await ConfigHandler.GetDefaultRouting(_config); + List? rules = null; + if (routing != null) + { + rules = JsonUtils.Deserialize>(routing.RuleSet) ?? []; + foreach (var item in rules) + { + if (!item.Enabled || item.Domain is null || item.Domain.Count == 0) + { + continue; + } + + foreach (var domain in item.Domain) + { + if (domain.StartsWith('#')) + continue; + var normalizedDomain = domain.Replace(Global.RoutingRuleComma, ","); + + if (item.OutboundTag == Global.DirectTag) + { + if (normalizedDomain.StartsWith("geosite:")) + { + (regionNames.Contains(normalizedDomain) ? expectedDomainList : directGeositeList).Add(normalizedDomain); + } + else + { + directDomainList.Add(normalizedDomain); + } + } + else if (item.OutboundTag == Global.ProxyTag) + { + if (normalizedDomain.StartsWith("geosite:")) + { + proxyGeositeList.Add(normalizedDomain); + } + else + { + proxyDomainList.Add(normalizedDomain); + } + } + } + } + } + + if (Utils.IsDomain(node?.Address)) + { + directDomainList.Add(node.Address); + } + + if (node?.Subid is not null) + { + var subItem = await AppHandler.Instance.GetSubItem(node.Subid); + if (subItem is not null) + { + foreach (var profile in new[] { subItem.PrevProfile, subItem.NextProfile }) + { + var profileNode = await AppHandler.Instance.GetProfileItemViaRemarks(profile); + if (profileNode is not null && + profileNode.ConfigType is not (EConfigType.Custom or EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls) && + Utils.IsDomain(profileNode.Address)) + { + directDomainList.Add(profileNode.Address); + } + } + } + } + + v2rayConfig.dns ??= new Dns4Ray(); + v2rayConfig.dns.servers ??= new List(); + + void AddDnsServers(List dnsAddresses, List domains, List? expectedIPs = null) + { + if (domains.Count > 0) + { + foreach (var dnsAddress in dnsAddresses) + { + v2rayConfig.dns.servers.Add(CreateDnsServer(dnsAddress, domains, expectedIPs)); + } + } + } + + AddDnsServers(remoteDNSAddress, proxyDomainList); + AddDnsServers(directDNSAddress, directDomainList); + AddDnsServers(remoteDNSAddress, proxyGeositeList); + AddDnsServers(directDNSAddress, directGeositeList); + AddDnsServers(directDNSAddress, expectedDomainList, expectedIPs); + + 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 defaultDnsServers = useDirectDns ? directDNSAddress : remoteDNSAddress; + v2rayConfig.dns.servers.AddRange(defaultDnsServers); + + return 0; + } + + private async Task GenDnsHosts(V2rayConfig v2rayConfig, 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>(); + if (simpleDNSItem.AddCommonHosts == true) + { + v2rayConfig.dns.hosts = Global.PredefinedHosts; + } + + if (simpleDNSItem.UseSystemHosts == true) + { + var systemHosts = Utils.GetSystemHosts(); + if (systemHosts.Count > 0) + { + var normalHost = v2rayConfig.dns.hosts; + if (normalHost != null) + { + foreach (var host in systemHosts) + { + if (normalHost[host.Key] != null) + { + continue; + } + normalHost[host.Key] = new List { host.Value }; + } + } + } + } + + var userHostsMap = simpleDNSItem.Hosts? + .Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries) + .Where(line => !string.IsNullOrWhiteSpace(line)) + .Where(line => line.Contains(' ')) + .ToDictionary( + line => + { + var parts = line.Trim().Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); + return parts[0]; + }, + line => + { + var parts = line.Trim().Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); + var values = parts.Skip(1).ToList(); + return values; + } + ); + + if (userHostsMap != null) + { + foreach (var kvp in userHostsMap) + { + v2rayConfig.dns.hosts[kvp.Key] = kvp.Value; + } + } + return await Task.FromResult(0); + } + + private async Task GenDnsCompatible(ProfileItem? node, V2rayConfig v2rayConfig) { try { @@ -1176,22 +1436,33 @@ public class CoreConfigV2rayService var systemHosts = Utils.GetSystemHosts(); if (systemHosts.Count > 0) { - var normalHost = obj["hosts"]; - if (normalHost != null) + var normalHost1 = obj["hosts"]; + if (normalHost1 != null) { foreach (var host in systemHosts) { - if (normalHost[host.Key] != null) + if (normalHost1[host.Key] != null) continue; - normalHost[host.Key] = host.Value; + normalHost1[host.Key] = host.Value; } } } } + var normalHost = obj["hosts"]; + if (normalHost != null) + { + foreach (var hostProp in normalHost.AsObject().ToList()) + { + if (hostProp.Value is JsonValue value && value.TryGetValue(out var ip)) + { + normalHost[hostProp.Key] = new JsonArray(ip); + } + } + } - await GenDnsDomains(node, obj, item); + await GenDnsDomainsCompatible(node, obj, item); - v2rayConfig.dns = obj; + v2rayConfig.dns = JsonUtils.Deserialize(obj.ToJsonString()); } catch (Exception ex) { @@ -1200,7 +1471,7 @@ public class CoreConfigV2rayService return 0; } - private async Task GenDnsDomains(ProfileItem? node, JsonNode dns, DNSItem? dNSItem) + private async Task GenDnsDomainsCompatible(ProfileItem? node, JsonNode dns, DNSItem? dNSItem) { if (node == null) { @@ -1223,7 +1494,6 @@ public class CoreConfigV2rayService && prevNode.ConfigType != EConfigType.Custom && prevNode.ConfigType != EConfigType.Hysteria2 && prevNode.ConfigType != EConfigType.TUIC - && prevNode.ConfigType != EConfigType.Anytls && Utils.IsDomain(prevNode.Address)) { domainList.Add(prevNode.Address); @@ -1235,7 +1505,6 @@ public class CoreConfigV2rayService && nextNode.ConfigType != EConfigType.Custom && nextNode.ConfigType != EConfigType.Hysteria2 && nextNode.ConfigType != EConfigType.TUIC - && nextNode.ConfigType != EConfigType.Anytls && Utils.IsDomain(nextNode.Address)) { domainList.Add(nextNode.Address); @@ -1245,7 +1514,7 @@ public class CoreConfigV2rayService { var dnsServer = new DnsServer4Ray() { - address = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.DomainDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress, + address = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress, skipFallback = true, domains = domainList }; diff --git a/v2rayN/ServiceLib/ViewModels/DNSSettingViewModel.cs b/v2rayN/ServiceLib/ViewModels/DNSSettingViewModel.cs index 1d6829b8..e6e985ca 100644 --- a/v2rayN/ServiceLib/ViewModels/DNSSettingViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/DNSSettingViewModel.cs @@ -6,39 +6,52 @@ namespace ServiceLib.ViewModels; public class DNSSettingViewModel : MyReactiveObject { - [Reactive] public bool UseSystemHosts { get; set; } - [Reactive] public string DomainStrategy4Freedom { get; set; } - [Reactive] public string DomainDNSAddress { get; set; } - [Reactive] public string NormalDNS { get; set; } + [Reactive] public bool? UseSystemHosts { get; set; } + [Reactive] public bool? AddCommonHosts { get; set; } + [Reactive] public bool? FakeIP { get; set; } + [Reactive] public bool? BlockBindingQuery { get; set; } + [Reactive] public string? DirectDNS { get; set; } + [Reactive] public string? RemoteDNS { get; set; } + [Reactive] public string? SingboxOutboundsResolveDNS { get; set; } + [Reactive] public string? SingboxFinalResolveDNS { get; set; } + [Reactive] public string? RayStrategy4Freedom { get; set; } + [Reactive] public string? SingboxStrategy4Direct { get; set; } + [Reactive] public string? SingboxStrategy4Proxy { get; set; } + [Reactive] public string? Hosts { get; set; } + [Reactive] public string? DirectExpectedIPs { get; set; } - [Reactive] public string DomainStrategy4Freedom2 { get; set; } - [Reactive] public string DomainDNSAddress2 { get; set; } - [Reactive] public string NormalDNS2 { get; set; } - [Reactive] public string TunDNS2 { get; set; } + [Reactive] public bool UseSystemHostsCompatible { get; set; } + [Reactive] public string DomainStrategy4FreedomCompatible { get; set; } + [Reactive] public string DomainDNSAddressCompatible { get; set; } + [Reactive] public string NormalDNSCompatible { get; set; } + + [Reactive] public string DomainStrategy4Freedom2Compatible { get; set; } + [Reactive] public string DomainDNSAddress2Compatible { get; set; } + [Reactive] public string NormalDNS2Compatible { get; set; } + [Reactive] public string TunDNS2Compatible { get; set; } + [Reactive] public bool RayCustomDNSEnableCompatible { get; set; } + [Reactive] public bool SBCustomDNSEnableCompatible { get; set; } public ReactiveCommand SaveCmd { get; } - public ReactiveCommand ImportDefConfig4V2rayCmd { get; } - public ReactiveCommand ImportDefConfig4SingboxCmd { get; } + public ReactiveCommand ImportDefConfig4V2rayCompatibleCmd { get; } + public ReactiveCommand ImportDefConfig4SingboxCompatibleCmd { get; } public DNSSettingViewModel(Func>? updateView) { _config = AppHandler.Instance.Config; _updateView = updateView; - SaveCmd = ReactiveCommand.CreateFromTask(async () => - { - await SaveSettingAsync(); - }); + SaveCmd = ReactiveCommand.CreateFromTask(SaveSettingAsync); - ImportDefConfig4V2rayCmd = ReactiveCommand.CreateFromTask(async () => + ImportDefConfig4V2rayCompatibleCmd = ReactiveCommand.CreateFromTask(async () => { - NormalDNS = EmbedUtils.GetEmbedText(Global.DNSV2rayNormalFileName); + NormalDNSCompatible = EmbedUtils.GetEmbedText(Global.DNSV2rayNormalFileName); await Task.CompletedTask; }); - ImportDefConfig4SingboxCmd = ReactiveCommand.CreateFromTask(async () => + ImportDefConfig4SingboxCompatibleCmd = ReactiveCommand.CreateFromTask(async () => { - NormalDNS2 = EmbedUtils.GetEmbedText(Global.DNSSingboxNormalFileName); - TunDNS2 = EmbedUtils.GetEmbedText(Global.TunSingboxDNSFileName); + NormalDNS2Compatible = EmbedUtils.GetEmbedText(Global.DNSSingboxNormalFileName); + TunDNS2Compatible = EmbedUtils.GetEmbedText(Global.TunSingboxDNSFileName); await Task.CompletedTask; }); @@ -47,48 +60,80 @@ public class DNSSettingViewModel : MyReactiveObject private async Task Init() { - var item = await AppHandler.Instance.GetDNSItem(ECoreType.Xray); + _config = AppHandler.Instance.Config; + var item = _config.SimpleDNSItem; UseSystemHosts = item.UseSystemHosts; - DomainStrategy4Freedom = item?.DomainStrategy4Freedom ?? string.Empty; - DomainDNSAddress = item?.DomainDNSAddress ?? string.Empty; - NormalDNS = item?.NormalDNS ?? string.Empty; + AddCommonHosts = item.AddCommonHosts; + FakeIP = item.FakeIP; + BlockBindingQuery = item.BlockBindingQuery; + DirectDNS = item.DirectDNS; + RemoteDNS = item.RemoteDNS; + RayStrategy4Freedom = item.RayStrategy4Freedom; + SingboxOutboundsResolveDNS = item.SingboxOutboundsResolveDNS; + SingboxFinalResolveDNS = item.SingboxFinalResolveDNS; + SingboxStrategy4Direct = item.SingboxStrategy4Direct; + SingboxStrategy4Proxy = item.SingboxStrategy4Proxy; + Hosts = item.Hosts; + DirectExpectedIPs = item.DirectExpectedIPs; + + var item1 = await AppHandler.Instance.GetDNSItem(ECoreType.Xray); + RayCustomDNSEnableCompatible = item1.Enabled; + UseSystemHostsCompatible = item1.UseSystemHosts; + DomainStrategy4FreedomCompatible = item1?.DomainStrategy4Freedom ?? string.Empty; + DomainDNSAddressCompatible = item1?.DomainDNSAddress ?? string.Empty; + NormalDNSCompatible = item1?.NormalDNS ?? string.Empty; var item2 = await AppHandler.Instance.GetDNSItem(ECoreType.sing_box); - DomainStrategy4Freedom2 = item2?.DomainStrategy4Freedom ?? string.Empty; - DomainDNSAddress2 = item2?.DomainDNSAddress ?? string.Empty; - NormalDNS2 = item2?.NormalDNS ?? string.Empty; - TunDNS2 = item2?.TunDNS ?? string.Empty; + SBCustomDNSEnableCompatible = item2.Enabled; + DomainStrategy4Freedom2Compatible = item2?.DomainStrategy4Freedom ?? string.Empty; + DomainDNSAddress2Compatible = item2?.DomainDNSAddress ?? string.Empty; + NormalDNS2Compatible = item2?.NormalDNS ?? string.Empty; + TunDNS2Compatible = item2?.TunDNS ?? string.Empty; } private async Task SaveSettingAsync() { - if (NormalDNS.IsNotEmpty()) + _config.SimpleDNSItem.UseSystemHosts = UseSystemHosts; + _config.SimpleDNSItem.AddCommonHosts = AddCommonHosts; + _config.SimpleDNSItem.FakeIP = FakeIP; + _config.SimpleDNSItem.BlockBindingQuery = BlockBindingQuery; + _config.SimpleDNSItem.DirectDNS = DirectDNS; + _config.SimpleDNSItem.RemoteDNS = RemoteDNS; + _config.SimpleDNSItem.RayStrategy4Freedom = RayStrategy4Freedom; + _config.SimpleDNSItem.SingboxOutboundsResolveDNS = SingboxOutboundsResolveDNS; + _config.SimpleDNSItem.SingboxFinalResolveDNS = SingboxFinalResolveDNS; + _config.SimpleDNSItem.SingboxStrategy4Direct = SingboxStrategy4Direct; + _config.SimpleDNSItem.SingboxStrategy4Proxy = SingboxStrategy4Proxy; + _config.SimpleDNSItem.Hosts = Hosts; + _config.SimpleDNSItem.DirectExpectedIPs = DirectExpectedIPs; + + if (NormalDNSCompatible.IsNotEmpty()) { - var obj = JsonUtils.ParseJson(NormalDNS); + var obj = JsonUtils.ParseJson(NormalDNSCompatible); if (obj != null && obj["servers"] != null) { } else { - if (NormalDNS.Contains('{') || NormalDNS.Contains('}')) + if (NormalDNSCompatible.Contains('{') || NormalDNSCompatible.Contains('}')) { NoticeHandler.Instance.Enqueue(ResUI.FillCorrectDNSText); return; } } } - if (NormalDNS2.IsNotEmpty()) + if (NormalDNS2Compatible.IsNotEmpty()) { - var obj2 = JsonUtils.Deserialize(NormalDNS2); + var obj2 = JsonUtils.Deserialize(NormalDNS2Compatible); if (obj2 == null) { NoticeHandler.Instance.Enqueue(ResUI.FillCorrectDNSText); return; } } - if (TunDNS2.IsNotEmpty()) + if (TunDNS2Compatible.IsNotEmpty()) { - var obj2 = JsonUtils.Deserialize(TunDNS2); + var obj2 = JsonUtils.Deserialize(TunDNS2Compatible); if (obj2 == null) { NoticeHandler.Instance.Enqueue(ResUI.FillCorrectDNSText); @@ -96,21 +141,26 @@ public class DNSSettingViewModel : MyReactiveObject } } - var item = await AppHandler.Instance.GetDNSItem(ECoreType.Xray); - item.DomainStrategy4Freedom = DomainStrategy4Freedom; - item.DomainDNSAddress = DomainDNSAddress; - item.UseSystemHosts = UseSystemHosts; - item.NormalDNS = NormalDNS; - await ConfigHandler.SaveDNSItems(_config, item); + var item1 = await AppHandler.Instance.GetDNSItem(ECoreType.Xray); + item1.Enabled = RayCustomDNSEnableCompatible; + item1.DomainStrategy4Freedom = DomainStrategy4FreedomCompatible; + item1.DomainDNSAddress = DomainDNSAddressCompatible; + item1.UseSystemHosts = UseSystemHostsCompatible; + item1.NormalDNS = NormalDNSCompatible; + await ConfigHandler.SaveDNSItems(_config, item1); var item2 = await AppHandler.Instance.GetDNSItem(ECoreType.sing_box); - item2.DomainStrategy4Freedom = DomainStrategy4Freedom2; - item2.DomainDNSAddress = DomainDNSAddress2; - item2.NormalDNS = JsonUtils.Serialize(JsonUtils.ParseJson(NormalDNS2)); - item2.TunDNS = JsonUtils.Serialize(JsonUtils.ParseJson(TunDNS2)); + item2.Enabled = RayCustomDNSEnableCompatible; + item2.DomainStrategy4Freedom = DomainStrategy4Freedom2Compatible; + item2.DomainDNSAddress = DomainDNSAddress2Compatible; + item2.NormalDNS = JsonUtils.Serialize(JsonUtils.ParseJson(NormalDNS2Compatible)); + item2.TunDNS = JsonUtils.Serialize(JsonUtils.ParseJson(TunDNS2Compatible)); await ConfigHandler.SaveDNSItems(_config, item2); - NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess); - _ = _updateView?.Invoke(EViewAction.CloseWindow, null); + await ConfigHandler.SaveConfig(_config); + if (_updateView != null) + { + await _updateView(EViewAction.CloseWindow, null); + } } } diff --git a/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml b/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml index efdf4eda..23ebe9fb 100644 --- a/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml +++ b/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml @@ -2,6 +2,7 @@ x:Class="v2rayN.Desktop.Views.DNSSettingWindow" xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:ctrls="clr-namespace:v2rayN.Desktop.Controls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib" @@ -35,25 +36,298 @@ - - - + + + + - - - - - - -