From 922cf54d934a103cb5aa874303a47b8d797a97c6 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Thu, 7 Aug 2025 17:37:48 +0800 Subject: [PATCH 01/54] Revert "Temporary addition to support proper use of sing-box v1.12" This reverts commit 508eb24fc3a8ad03a249332446da98754d005bbe. --- v2rayN/ServiceLib/Handler/CoreHandler.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/v2rayN/ServiceLib/Handler/CoreHandler.cs b/v2rayN/ServiceLib/Handler/CoreHandler.cs index a0e20d07..f0c23c69 100644 --- a/v2rayN/ServiceLib/Handler/CoreHandler.cs +++ b/v2rayN/ServiceLib/Handler/CoreHandler.cs @@ -25,8 +25,6 @@ public class CoreHandler Environment.SetEnvironmentVariable(Global.V2RayLocalAsset, Utils.GetBinPath(""), EnvironmentVariableTarget.Process); Environment.SetEnvironmentVariable(Global.XrayLocalAsset, Utils.GetBinPath(""), EnvironmentVariableTarget.Process); Environment.SetEnvironmentVariable(Global.XrayLocalCert, Utils.GetBinPath(""), EnvironmentVariableTarget.Process); - // TODO Temporary addition to support proper use of sing-box v1.12 - Environment.SetEnvironmentVariable("ENABLE_DEPRECATED_SPECIAL_OUTBOUNDS", "true", EnvironmentVariableTarget.Process); //Copy the bin folder to the storage location (for init) if (Environment.GetEnvironmentVariable(Global.LocalAppData) == "1") From b3acb89c29f1dad3f686c1209465f6abe698d221 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Sun, 6 Apr 2025 21:34:23 +0800 Subject: [PATCH 02/54] Migrating to singbox 1.11 support --- v2rayN/ServiceLib/Models/SingboxConfig.cs | 32 +++++++++- .../Sample/SingboxSampleClientConfig | 14 +---- v2rayN/ServiceLib/Sample/tun_singbox_rules | 4 +- .../CoreConfig/CoreConfigSingboxService.cs | 63 ++++++++++++------- 4 files changed, 74 insertions(+), 39 deletions(-) diff --git a/v2rayN/ServiceLib/Models/SingboxConfig.cs b/v2rayN/ServiceLib/Models/SingboxConfig.cs index 9ea1157d..cbb2cb29 100644 --- a/v2rayN/ServiceLib/Models/SingboxConfig.cs +++ b/v2rayN/ServiceLib/Models/SingboxConfig.cs @@ -67,6 +67,9 @@ public class Rule4Sbox public List? process_name { get; set; } public List? rule_set { get; set; } public List? rules { get; set; } + public string? action { get; set; } + public string? strategy { get; set; } + public List? sniffer { get; set; } } [Serializable] @@ -76,7 +79,6 @@ public class Inbound4Sbox public string tag { get; set; } public string listen { get; set; } public int? listen_port { get; set; } - public string? domain_strategy { get; set; } public string interface_name { get; set; } public List? address { get; set; } public int? mtu { get; set; } @@ -84,8 +86,6 @@ public class Inbound4Sbox public bool? strict_route { get; set; } public bool? endpoint_independent_nat { get; set; } public string? stack { get; set; } - public bool? sniff { get; set; } - public bool? sniff_override_destination { get; set; } public List users { get; set; } } @@ -136,6 +136,32 @@ public class Outbound4Sbox public bool? interrupt_exist_connections { get; set; } } +public class Endpoints4Sbox +{ + public string type { get; set; } + public string tag { get; set; } + public bool? system { get; set; } + public string? name { get; set; } + public int? mtu { get; set; } + public List address { get; set; } + public string private_key { get; set; } + public int listen_port { get; set; } + public string? udp_timeout { get; set; } + public int? workers { get; set; } + public List peers { get; set; } +} + +public class Peer4Sbox +{ + public string address { get; set; } + public int port { get; set; } + public string public_key { get; set; } + public string? pre_shared_key { get; set; } + public List allowed_ips { get; set; } + public int? persistent_keepalive_interval { get; set; } + public List reserved { get; set; } +} + public class Tls4Sbox { public bool enabled { get; set; } diff --git a/v2rayN/ServiceLib/Sample/SingboxSampleClientConfig b/v2rayN/ServiceLib/Sample/SingboxSampleClientConfig index f88422a1..b07fd72c 100644 --- a/v2rayN/ServiceLib/Sample/SingboxSampleClientConfig +++ b/v2rayN/ServiceLib/Sample/SingboxSampleClientConfig @@ -1,4 +1,4 @@ -{ +{ "log": { "level": "debug", "timestamp": true @@ -14,22 +14,10 @@ { "type": "direct", "tag": "direct" - }, - { - "type": "block", - "tag": "block" - }, - { - "tag": "dns_out", - "type": "dns" } ], "route": { "rules": [ - { - "protocol": [ "dns" ], - "outbound": "dns_out" - } ] } } \ No newline at end of file diff --git a/v2rayN/ServiceLib/Sample/tun_singbox_rules b/v2rayN/ServiceLib/Sample/tun_singbox_rules index df1dc4ec..a4276134 100644 --- a/v2rayN/ServiceLib/Sample/tun_singbox_rules +++ b/v2rayN/ServiceLib/Sample/tun_singbox_rules @@ -8,13 +8,13 @@ 139, 5353 ], - "outbound": "block" + "action": "reject" }, { "ip_cidr": [ "224.0.0.0/3", "ff00::/8" ], - "outbound": "block" + "action": "reject" } ] \ No newline at end of file diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs index 79e4c0a2..a353915c 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs @@ -1,6 +1,7 @@ using System.Data; using System.Net; using System.Net.NetworkInformation; +using DynamicData; namespace ServiceLib.Services.CoreConfig; @@ -534,15 +535,6 @@ public class CoreConfigSingboxService singboxConfig.inbounds.Add(inbound); inbound.listen_port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks); - inbound.sniff = _config.Inbound.First().SniffingEnabled; - inbound.sniff_override_destination = _config.Inbound.First().RouteOnly ? false : _config.Inbound.First().SniffingEnabled; - inbound.domain_strategy = _config.RoutingBasicItem.DomainStrategy4Singbox.IsNullOrEmpty() ? null : _config.RoutingBasicItem.DomainStrategy4Singbox; - - var routing = await ConfigHandler.GetDefaultRouting(_config); - if (routing.DomainStrategy4Singbox.IsNotEmpty()) - { - inbound.domain_strategy = routing.DomainStrategy4Singbox; - } if (_config.Inbound.First().SecondLocalPortEnabled) { @@ -587,8 +579,6 @@ public class CoreConfigSingboxService tunInbound.mtu = _config.TunModeItem.Mtu; tunInbound.strict_route = _config.TunModeItem.StrictRoute; tunInbound.stack = _config.TunModeItem.Stack; - tunInbound.sniff = _config.Inbound.First().SniffingEnabled; - //tunInbound.sniff_override_destination = _config.inbound.First().routeOnly ? false : _config.inbound.First().sniffingEnabled; if (_config.TunModeItem.EnableIPv6Address == false) { tunInbound.address = ["172.18.0.1/30"]; @@ -1115,8 +1105,6 @@ public class CoreConfigSingboxService { try { - var dnsOutbound = "dns_out"; - if (_config.TunModeItem.EnableTun) { singboxConfig.route.auto_detect_interface = true; @@ -1131,7 +1119,7 @@ public class CoreConfigSingboxService singboxConfig.route.rules.Add(new() { port = new() { 53 }, - outbound = dnsOutbound, + action = "hijack-dns", process_name = lstDnsExe }); @@ -1142,13 +1130,26 @@ public class CoreConfigSingboxService }); } - if (!_config.Inbound.First().SniffingEnabled) + if (_config.Inbound.First().SniffingEnabled) { singboxConfig.route.rules.Add(new() { - port = [53], - network = ["udp"], - outbound = dnsOutbound + action = "sniff", + sniffer = new() { "dns", _config.Inbound.First().DestOverride } + }); + singboxConfig.route.rules.Add(new() + { + protocol = new() { "dns" }, + action = "hijack-dns" + }); + } + else + { + singboxConfig.route.rules.Add(new() + { + port = new() { 53 }, + network = new() { "udp" }, + action = "hijack-dns" }); } @@ -1163,6 +1164,21 @@ public class CoreConfigSingboxService clash_mode = ERuleMode.Global.ToString() }); + if (!(_config.Inbound.First().RouteOnly || _config.TunModeItem.EnableTun)) + { + var domainStrategy = _config.RoutingBasicItem.DomainStrategy4Singbox.IsNullOrEmpty() ? null : _config.RoutingBasicItem.DomainStrategy4Singbox; + var defaultRouting = await ConfigHandler.GetDefaultRouting(_config); + if (defaultRouting.DomainStrategy4Singbox.IsNotEmpty()) + { + domainStrategy = defaultRouting.DomainStrategy4Singbox; + } + singboxConfig.route.rules.Add(new() + { + action = "resolve", + strategy = domainStrategy + }); + } + var routing = await ConfigHandler.GetDefaultRouting(_config); if (routing != null) { @@ -1222,10 +1238,15 @@ public class CoreConfigSingboxService item.OutboundTag = await GenRoutingUserRuleOutbound(item.OutboundTag, singboxConfig); var rules = singboxConfig.route.rules; - var rule = new Rule4Sbox() + var rule = new Rule4Sbox(); + if (item.OutboundTag == "block") { - outbound = item.OutboundTag, - }; + rule.action = "reject"; + } + else + { + rule.outbound = item.OutboundTag; + } if (item.Port.IsNotEmpty()) { From ea55dfb6c52500a8764cb7d9e7b520274911460a Mon Sep 17 00:00:00 2001 From: DHR60 Date: Sun, 6 Apr 2025 23:01:16 +0800 Subject: [PATCH 03/54] Removes unnecessary sniffer --- .../ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs index a353915c..6f5e3008 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs @@ -1134,8 +1134,7 @@ public class CoreConfigSingboxService { singboxConfig.route.rules.Add(new() { - action = "sniff", - sniffer = new() { "dns", _config.Inbound.First().DestOverride } + action = "sniff" }); singboxConfig.route.rules.Add(new() { From d1928d80c77772132fe156f33c05ad7479821b1d Mon Sep 17 00:00:00 2001 From: DHR60 Date: Mon, 7 Apr 2025 19:24:30 +0800 Subject: [PATCH 04/54] Migrating to singbox 1.12 support --- v2rayN/ServiceLib/Models/SingboxConfig.cs | 19 ++-- v2rayN/ServiceLib/Sample/dns_singbox_normal | 13 ++- v2rayN/ServiceLib/Sample/tun_singbox_dns | 13 ++- .../CoreConfig/CoreConfigSingboxService.cs | 90 +++++++++++++++---- 4 files changed, 88 insertions(+), 47 deletions(-) diff --git a/v2rayN/ServiceLib/Models/SingboxConfig.cs b/v2rayN/ServiceLib/Models/SingboxConfig.cs index cbb2cb29..1d3249c9 100644 --- a/v2rayN/ServiceLib/Models/SingboxConfig.cs +++ b/v2rayN/ServiceLib/Models/SingboxConfig.cs @@ -29,7 +29,6 @@ public class Dns4Sbox public bool? independent_cache { get; set; } public bool? reverse_mapping { get; set; } public string? client_subnet { get; set; } - public Fakeip4Sbox? fakeip { get; set; } } public class Route4Sbox @@ -134,6 +133,7 @@ public class Outbound4Sbox public HyObfs4Sbox? obfs { get; set; } public List? outbounds { get; set; } public bool? interrupt_exist_connections { get; set; } + public Rule4Sbox? domain_resolver { get; set; } } public class Endpoints4Sbox @@ -220,12 +220,14 @@ public class HyObfs4Sbox public class Server4Sbox { public string? tag { get; set; } - public string? address { get; set; } - public string? address_resolver { get; set; } - public string? address_strategy { get; set; } - public string? strategy { get; set; } public string? detour { get; set; } + public string? inet4_range { get; set; } + public string? inet6_range { get; set; } public string? client_subnet { get; set; } + public string? type { get; set; } + public string? server { get; set; } + public string? server_resolver { get; set; } + //public string? interface { get; set; } } public class Experimental4Sbox @@ -255,13 +257,6 @@ public class Stats4Sbox public List? users { get; set; } } -public class Fakeip4Sbox -{ - public bool enabled { get; set; } - public string inet4_range { get; set; } - public string inet6_range { get; set; } -} - public class CacheFile4Sbox { public bool enabled { get; set; } diff --git a/v2rayN/ServiceLib/Sample/dns_singbox_normal b/v2rayN/ServiceLib/Sample/dns_singbox_normal index 0921fe64..13aa7ead 100644 --- a/v2rayN/ServiceLib/Sample/dns_singbox_normal +++ b/v2rayN/ServiceLib/Sample/dns_singbox_normal @@ -2,19 +2,16 @@ "servers": [ { "tag": "remote", - "address": "tcp://8.8.8.8", + "type": "tcp", + "server": "8.8.8.8", "strategy": "prefer_ipv4", "detour": "proxy" }, { "tag": "local", - "address": "223.5.5.5", - "strategy": "prefer_ipv4", - "detour": "direct" - }, - { - "tag": "block", - "address": "rcode://success" + "type": "udp", + "server": "223.5.5.5", + "strategy": "prefer_ipv4" } ], "rules": [ diff --git a/v2rayN/ServiceLib/Sample/tun_singbox_dns b/v2rayN/ServiceLib/Sample/tun_singbox_dns index d8ca9808..e20c5cd5 100644 --- a/v2rayN/ServiceLib/Sample/tun_singbox_dns +++ b/v2rayN/ServiceLib/Sample/tun_singbox_dns @@ -2,19 +2,16 @@ "servers": [ { "tag": "remote", - "address": "tcp://8.8.8.8", + "type": "tcp", + "server": "8.8.8.8", "strategy": "prefer_ipv4", "detour": "proxy" }, { "tag": "local", - "address": "223.5.5.5", - "strategy": "prefer_ipv4", - "detour": "direct" - }, - { - "tag": "block", - "address": "rcode://success" + "type": "udp", + "server": "223.5.5.5", + "strategy": "prefer_ipv4" } ], "rules": [ diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs index 6f5e3008..0690f87a 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs @@ -2,6 +2,7 @@ using System.Data; using System.Net; using System.Net.NetworkInformation; using DynamicData; +using ServiceLib.Models; namespace ServiceLib.Services.CoreConfig; @@ -611,6 +612,16 @@ public class CoreConfigSingboxService outbound.server_port = node.Port; outbound.type = Global.ProtocolTypes[node.ConfigType]; + if (Utils.IsDomain(node.Address)) + { + outbound.domain_resolver = new() + { + server = "local_local", + // TODO + //strategy = string.IsNullOrEmpty(dNSItem?.DomainStrategy4Freedom) ? null : dNSItem?.DomainStrategy4Freedom + }; + } + switch (node.ConfigType) { case EConfigType.VMess: @@ -1453,17 +1464,71 @@ public class CoreConfigSingboxService dns4Sbox.rules ??= []; var tag = "local_local"; + var localDnsAddress = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.SingboxDomainDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress; + string? localDnsType = null; + //string? dhcpDnsInterface = null; + if (localDnsAddress == "local") + { + localDnsType = "local"; + localDnsAddress = null; + } + else if (localDnsAddress.StartsWith("dhcp")) + { + localDnsType = "dhcp"; + //if (localDnsAddress.Length > 7) // dhcp:// + //{ + // localDnsAddress = localDnsAddress.Substring(7); + //} + localDnsAddress = null; + } + else if (localDnsAddress.StartsWith("tcp")) + { + localDnsType = "tcp"; + if (localDnsAddress.Length > 6) // tcp:// + { + localDnsAddress = localDnsAddress.Substring(6); + } + } + else if (localDnsAddress.StartsWith("tls")) + { + localDnsType = "tls"; + if (localDnsAddress.Length > 6) // tls:// + { + localDnsAddress = localDnsAddress.Substring(6); + } + } + else if (localDnsAddress.StartsWith("https")) + { + localDnsType = "https"; + if (localDnsAddress.Length > 8) // https:// + { + localDnsAddress = localDnsAddress.Substring(8); + } + } + else if (localDnsAddress.StartsWith("quic")) + { + localDnsType = "quic"; + if (localDnsAddress.Length > 7) // quic:// + { + localDnsAddress = localDnsAddress.Substring(7); + } + } + else + { + localDnsType = "udp"; + } + dns4Sbox.servers.Add(new() { tag = tag, - address = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.SingboxDomainDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress, - detour = Global.DirectTag, - strategy = string.IsNullOrEmpty(dNSItem?.DomainStrategy4Freedom) ? null : dNSItem?.DomainStrategy4Freedom, + type = localDnsType, + server = localDnsAddress }); dns4Sbox.rules.Insert(0, new() { server = tag, - clash_mode = ERuleMode.Direct.ToString() + clash_mode = ERuleMode.Direct.ToString(), + strategy = string.IsNullOrEmpty(dNSItem?.DomainStrategy4Freedom) ? null : dNSItem?.DomainStrategy4Freedom }); dns4Sbox.rules.Insert(0, new() { @@ -1471,27 +1536,14 @@ public class CoreConfigSingboxService clash_mode = ERuleMode.Global.ToString() }); - var lstDomain = singboxConfig.outbounds - .Where(t => t.server.IsNotEmpty() && Utils.IsDomain(t.server)) - .Select(t => t.server) - .Distinct() - .ToList(); - if (lstDomain != null && lstDomain.Count > 0) - { - dns4Sbox.rules.Insert(0, new() - { - server = tag, - domain = lstDomain - }); - } - //Tun2SocksAddress if (_config.TunModeItem.EnableTun && node?.ConfigType == EConfigType.SOCKS && Utils.IsDomain(node?.Sni)) { dns4Sbox.rules.Insert(0, new() { server = tag, - domain = [node?.Sni] + domain = [node?.Sni], + strategy = string.IsNullOrEmpty(dNSItem?.DomainStrategy4Freedom) ? null : dNSItem?.DomainStrategy4Freedom }); } From 9e1e5eb2aa1cbf8f115f985ada504b757874c805 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Tue, 8 Apr 2025 20:05:31 +0800 Subject: [PATCH 05/54] Adds Google cn dns rules --- v2rayN/ServiceLib/Sample/dns_singbox_normal | 7 +++++++ v2rayN/ServiceLib/Sample/tun_singbox_dns | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/v2rayN/ServiceLib/Sample/dns_singbox_normal b/v2rayN/ServiceLib/Sample/dns_singbox_normal index 13aa7ead..19b2ceae 100644 --- a/v2rayN/ServiceLib/Sample/dns_singbox_normal +++ b/v2rayN/ServiceLib/Sample/dns_singbox_normal @@ -15,6 +15,13 @@ } ], "rules": [ + { + "domain_suffix": [ + "googleapis.cn", + "gstatic.com" + ], + "server": "remote" + }, { "rule_set": [ "geosite-cn" diff --git a/v2rayN/ServiceLib/Sample/tun_singbox_dns b/v2rayN/ServiceLib/Sample/tun_singbox_dns index e20c5cd5..31a9af33 100644 --- a/v2rayN/ServiceLib/Sample/tun_singbox_dns +++ b/v2rayN/ServiceLib/Sample/tun_singbox_dns @@ -15,6 +15,13 @@ } ], "rules": [ + { + "domain_suffix": [ + "googleapis.cn", + "gstatic.com" + ], + "server": "remote" + }, { "rule_set": [ "geosite-cn", From 7affcf97b16b9c8c78c58a80c069ea4449c218f0 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Tue, 8 Apr 2025 20:12:03 +0800 Subject: [PATCH 06/54] Improves geoip rule handling in singbox --- .../CoreConfig/CoreConfigSingboxService.cs | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs index 0690f87a..f95318b3 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs @@ -1380,24 +1380,28 @@ public class CoreConfigSingboxService { return false; } - else if (address.StartsWith("geoip:!")) - { - return false; - } else if (address.Equals("geoip:private")) { rule.ip_is_private = true; } else if (address.StartsWith("geoip:")) { - if (rule.geoip is null) - { rule.geoip = new(); } + rule.geoip ??= new(); rule.geoip?.Add(address.Substring(6)); } + else if (address.Equals("geoip:!private")) + { + rule.ip_is_private = false; + } + else if (address.StartsWith("geoip:!")) + { + rule.geoip ??= new(); + rule.geoip?.Add(address.Substring(6)); + rule.invert = true; + } else { - if (rule.ip_cidr is null) - { rule.ip_cidr = new(); } + rule.ip_cidr ??= new(); rule.ip_cidr?.Add(address); } return true; From d77c25aef50ed50e684c9eee55e806f17886f1ea Mon Sep 17 00:00:00 2001 From: DHR60 Date: Thu, 10 Apr 2025 23:04:54 +0800 Subject: [PATCH 07/54] add anytls support --- v2rayN/ServiceLib/Enums/EConfigType.cs | 3 +- v2rayN/ServiceLib/Global.cs | 6 ++- v2rayN/ServiceLib/Handler/ConfigHandler.cs | 31 +++++++++++ v2rayN/ServiceLib/Handler/CoreHandler.cs | 2 +- v2rayN/ServiceLib/Handler/Fmt/AnytlsFmt.cs | 54 +++++++++++++++++++ v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs | 5 ++ v2rayN/ServiceLib/Resx/ResUI.Designer.cs | 9 ++++ v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx | 3 ++ v2rayN/ServiceLib/Resx/ResUI.hu.resx | 3 ++ v2rayN/ServiceLib/Resx/ResUI.resx | 3 ++ v2rayN/ServiceLib/Resx/ResUI.ru.resx | 3 ++ v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx | 3 ++ v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx | 3 ++ .../CoreConfig/CoreConfigSingboxService.cs | 5 ++ .../CoreConfig/CoreConfigV2rayService.cs | 3 +- .../ServiceLib/Services/SpeedtestService.cs | 4 +- .../ViewModels/MainWindowViewModel.cs | 5 ++ .../Views/AddServerWindow.axaml | 20 +++++++ .../Views/AddServerWindow.axaml.cs | 9 ++++ v2rayN/v2rayN.Desktop/Views/MainWindow.axaml | 1 + .../v2rayN.Desktop/Views/MainWindow.axaml.cs | 1 + v2rayN/v2rayN/Views/AddServerWindow.xaml | 29 ++++++++++ v2rayN/v2rayN/Views/AddServerWindow.xaml.cs | 8 +++ v2rayN/v2rayN/Views/MainWindow.xaml | 4 ++ v2rayN/v2rayN/Views/MainWindow.xaml.cs | 1 + 25 files changed, 211 insertions(+), 7 deletions(-) create mode 100644 v2rayN/ServiceLib/Handler/Fmt/AnytlsFmt.cs diff --git a/v2rayN/ServiceLib/Enums/EConfigType.cs b/v2rayN/ServiceLib/Enums/EConfigType.cs index f56d0e0f..6698f962 100644 --- a/v2rayN/ServiceLib/Enums/EConfigType.cs +++ b/v2rayN/ServiceLib/Enums/EConfigType.cs @@ -11,5 +11,6 @@ public enum EConfigType Hysteria2 = 7, TUIC = 8, WireGuard = 9, - HTTP = 10 + HTTP = 10, + Anytls = 11 } diff --git a/v2rayN/ServiceLib/Global.cs b/v2rayN/ServiceLib/Global.cs index 0728b2ea..4888ab6a 100644 --- a/v2rayN/ServiceLib/Global.cs +++ b/v2rayN/ServiceLib/Global.cs @@ -169,7 +169,8 @@ public class Global { EConfigType.Trojan, "trojan://" }, { EConfigType.Hysteria2, "hysteria2://" }, { EConfigType.TUIC, "tuic://" }, - { EConfigType.WireGuard, "wireguard://" } + { EConfigType.WireGuard, "wireguard://" }, + { EConfigType.Anytls, "anytls://" } }; public static readonly Dictionary ProtocolTypes = new() @@ -182,7 +183,8 @@ public class Global { EConfigType.Trojan, "trojan" }, { EConfigType.Hysteria2, "hysteria2" }, { EConfigType.TUIC, "tuic" }, - { EConfigType.WireGuard, "wireguard" } + { EConfigType.WireGuard, "wireguard" }, + { EConfigType.Anytls, "anytls" } }; public static readonly List VmessSecurities = diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs index 0eeadc46..1f8bd079 100644 --- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -262,6 +262,7 @@ public class ConfigHandler EConfigType.Hysteria2 => await AddHysteria2Server(config, item), EConfigType.TUIC => await AddTuicServer(config, item), EConfigType.WireGuard => await AddWireguardServer(config, item), + EConfigType.Anytls => await AddAnytlsServer(config, item), _ => -1, }; return ret; @@ -786,6 +787,35 @@ public class ConfigHandler return 0; } + /// + /// Add or edit a Anytls server + /// Validates and processes Anytls-specific settings + /// + /// Current configuration + /// Anytls profile to add + /// Whether to save to file + /// 0 if successful, -1 if failed + public static async Task AddAnytlsServer(Config config, ProfileItem profileItem, bool toFile = true) + { + profileItem.ConfigType = EConfigType.Anytls; + profileItem.CoreType = ECoreType.sing_box; + + profileItem.Address = profileItem.Address.TrimEx(); + profileItem.Id = profileItem.Id.TrimEx(); + profileItem.Security = profileItem.Security.TrimEx(); + profileItem.Network = string.Empty; + if (profileItem.StreamSecurity.IsNullOrEmpty()) + { + profileItem.StreamSecurity = Global.StreamSecurity; + } + if (profileItem.Id.IsNullOrEmpty()) + { + return -1; + } + await AddServerCommon(config, profileItem, toFile); + return 0; + } + /// /// Sort the server list by the specified column /// Updates the sort order in the profile extension data @@ -1295,6 +1325,7 @@ public class ConfigHandler EConfigType.Hysteria2 => await AddHysteria2Server(config, profileItem, false), EConfigType.TUIC => await AddTuicServer(config, profileItem, false), EConfigType.WireGuard => await AddWireguardServer(config, profileItem, false), + EConfigType.Anytls => await AddAnytlsServer(config, profileItem, false), _ => -1, }; diff --git a/v2rayN/ServiceLib/Handler/CoreHandler.cs b/v2rayN/ServiceLib/Handler/CoreHandler.cs index f0c23c69..f7ad2285 100644 --- a/v2rayN/ServiceLib/Handler/CoreHandler.cs +++ b/v2rayN/ServiceLib/Handler/CoreHandler.cs @@ -101,7 +101,7 @@ public class CoreHandler public async Task LoadCoreConfigSpeedtest(List selecteds) { - var coreType = selecteds.Exists(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC) ? ECoreType.sing_box : ECoreType.Xray; + var coreType = selecteds.Exists(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls) ? ECoreType.sing_box : ECoreType.Xray; var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false)); var configPath = Utils.GetBinConfigPath(fileName); var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType); diff --git a/v2rayN/ServiceLib/Handler/Fmt/AnytlsFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/AnytlsFmt.cs new file mode 100644 index 00000000..bad36b19 --- /dev/null +++ b/v2rayN/ServiceLib/Handler/Fmt/AnytlsFmt.cs @@ -0,0 +1,54 @@ +using static QRCoder.PayloadGenerator; + +namespace ServiceLib.Handler.Fmt; +public class AnytlsFmt : BaseFmt +{ + public static ProfileItem? Resolve(string str, out string msg) + { + msg = ResUI.ConfigurationFormatIncorrect; + + var parsedUrl = Utils.TryUri(str); + if (parsedUrl == null) + { + return null; + } + + ProfileItem item = new() + { + ConfigType = EConfigType.Anytls, + Remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped), + Address = parsedUrl.IdnHost, + Port = parsedUrl.Port, + }; + var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo); + item.Id = rawUserInfo; + + var query = Utils.ParseQueryString(parsedUrl.Query); + item.Sni = query["sni"] ?? Global.None; + item.AllowInsecure = (query["insecure"] ?? "") == "1" ? "true" : "false"; + + return item; + } + + public static string? ToUri(ProfileItem? item) + { + if (item == null) + { + return null; + } + var remark = string.Empty; + if (item.Remarks.IsNotEmpty()) + { + remark = "#" + Utils.UrlEncode(item.Remarks); + } + var pw = item.Id; + var dicQuery = new Dictionary(); + if (item.Sni.IsNotEmpty()) + { + dicQuery.Add("sni", item.Sni); + } + dicQuery.Add("insecure", item.AllowInsecure.ToLower() == "true" ? "1" : "0"); + + return ToUri(EConfigType.Anytls, item.Address, item.Port, pw, dicQuery, remark); + } +} diff --git a/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs b/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs index 3e8ab2ae..814d753d 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs @@ -18,6 +18,7 @@ public class FmtHandler EConfigType.Hysteria2 => Hysteria2Fmt.ToUri(item), EConfigType.TUIC => TuicFmt.ToUri(item), EConfigType.WireGuard => WireguardFmt.ToUri(item), + EConfigType.Anytls => AnytlsFmt.ToUri(item), _ => null, }; @@ -75,6 +76,10 @@ public class FmtHandler { return WireguardFmt.Resolve(str, out msg); } + else if (str.StartsWith(Global.ProtocolShares[EConfigType.Anytls])) + { + return AnytlsFmt.Resolve(str, out msg); + } else { msg = ResUI.NonvmessOrssProtocol; diff --git a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs index 992adb94..ca554860 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs +++ b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs @@ -654,6 +654,15 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Add [Anytls] Configuration 的本地化字符串。 + /// + public static string menuAddAnytlsServer { + get { + return ResourceManager.GetString("menuAddAnytlsServer", resourceCulture); + } + } + /// /// 查找类似 Add a custom configuration Configuration 的本地化字符串。 /// diff --git a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx index eb9ae271..bd2cb887 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx @@ -1401,4 +1401,7 @@ Mldsa65Verify + + Add [Anytls] Configuration + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.hu.resx b/v2rayN/ServiceLib/Resx/ResUI.hu.resx index 0d45540e..7633dd08 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.hu.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.hu.resx @@ -1401,4 +1401,7 @@ Mldsa65Verify + + Add [Anytls] Configuration + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayN/ServiceLib/Resx/ResUI.resx index 03e9b124..7ecfcc98 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.resx @@ -1401,4 +1401,7 @@ Mldsa65Verify + + Add [Anytls] Configuration + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayN/ServiceLib/Resx/ResUI.ru.resx index 75a596aa..37219652 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.ru.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.ru.resx @@ -1401,4 +1401,7 @@ Mldsa65Verify + + Add [Anytls] Configuration + \ 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 8766ca8f..95d69e9b 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx @@ -1398,4 +1398,7 @@ Mldsa65Verify + + 添加 [Anytls] 配置文件 + \ 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 9c7a2a3c..0b23885c 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx @@ -1398,4 +1398,7 @@ Mldsa65Verify + + 新增 [Anytls] 設定檔 + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs index f95318b3..e1eedc2c 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs @@ -740,6 +740,11 @@ public class CoreConfigSingboxService outbound.mtu = node.ShortId.IsNullOrEmpty() ? Global.TunMtus.First() : node.ShortId.ToInt(); break; } + case EConfigType.Anytls: + { + outbound.password = node.Id; + break; + } } await GenOutboundTls(node, outbound); diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs index 2e4f5842..27b1dae6 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs @@ -1350,7 +1350,8 @@ public class CoreConfigV2rayService if (prevNode is not null && prevNode.ConfigType != EConfigType.Custom && prevNode.ConfigType != EConfigType.Hysteria2 - && prevNode.ConfigType != EConfigType.TUIC) + && prevNode.ConfigType != EConfigType.TUIC + && prevNode.ConfigType != EConfigType.Anytls) { var prevOutbound = JsonUtils.Deserialize(txtOutbound); await GenOutbound(prevNode, prevOutbound); diff --git a/v2rayN/ServiceLib/Services/SpeedtestService.cs b/v2rayN/ServiceLib/Services/SpeedtestService.cs index 998cedcc..9c97a217 100644 --- a/v2rayN/ServiceLib/Services/SpeedtestService.cs +++ b/v2rayN/ServiceLib/Services/SpeedtestService.cs @@ -358,8 +358,8 @@ public class SpeedtestService private List> GetTestBatchItem(List lstSelected, int pageSize) { List> lstTest = new(); - var lst1 = lstSelected.Where(t => t.ConfigType is not (EConfigType.Hysteria2 or EConfigType.TUIC)).ToList(); - var lst2 = lstSelected.Where(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC).ToList(); + var lst1 = lstSelected.Where(t => t.ConfigType is not (EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls)).ToList(); + var lst2 = lstSelected.Where(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls).ToList(); for (var num = 0; num < (int)Math.Ceiling(lst1.Count * 1.0 / pageSize); num++) { diff --git a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs index 7bae19be..36e20a87 100644 --- a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs @@ -20,6 +20,7 @@ public class MainWindowViewModel : MyReactiveObject public ReactiveCommand AddHysteria2ServerCmd { get; } public ReactiveCommand AddTuicServerCmd { get; } public ReactiveCommand AddWireguardServerCmd { get; } + public ReactiveCommand AddAnytlsServerCmd { get; } public ReactiveCommand AddCustomServerCmd { get; } public ReactiveCommand AddServerViaClipboardCmd { get; } public ReactiveCommand AddServerViaScanCmd { get; } @@ -111,6 +112,10 @@ public class MainWindowViewModel : MyReactiveObject { await AddServerAsync(true, EConfigType.WireGuard); }); + AddAnytlsServerCmd = ReactiveCommand.CreateFromTask(async () => + { + await AddServerAsync(true, EConfigType.Anytls); + }); AddCustomServerCmd = ReactiveCommand.CreateFromTask(async () => { await AddServerAsync(true, EConfigType.Custom); diff --git a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml index da56d617..56cc82a0 100644 --- a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml +++ b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml @@ -533,6 +533,26 @@ HorizontalAlignment="Left" Watermark="1500" /> + + + + + gridTls.IsVisible = false; break; + + case EConfigType.Anytls: + gridAnytls.IsVisible = true; + cmbCoreType.IsEnabled = false; + break; } cmbStreamSecurity.ItemsSource = lstStreamSecurity; @@ -167,6 +172,10 @@ public partial class AddServerWindow : WindowBase this.Bind(ViewModel, vm => vm.SelectedSource.RequestHost, v => v.txtRequestHost9.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.ShortId, v => v.txtShortId9.Text).DisposeWith(disposables); break; + + case EConfigType.Anytls: + this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId10.Text).DisposeWith(disposables); + break; } this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType.SelectedValue).DisposeWith(disposables); diff --git a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml index bbc78a72..af4ae529 100644 --- a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml +++ b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml @@ -46,6 +46,7 @@ + diff --git a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs index ea95ef0e..2584310b 100644 --- a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs @@ -83,6 +83,7 @@ public partial class MainWindow : WindowBase this.BindCommand(ViewModel, vm => vm.AddHysteria2ServerCmd, v => v.menuAddHysteria2Server).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddTuicServerCmd, v => v.menuAddTuicServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddWireguardServerCmd, v => v.menuAddWireguardServer).DisposeWith(disposables); + this.BindCommand(ViewModel, vm => vm.AddAnytlsServerCmd, v => v.menuAddAnytlsServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddCustomServerCmd, v => v.menuAddCustomServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddServerViaClipboardCmd, v => v.menuAddServerViaClipboard).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddServerViaScanCmd, v => v.menuAddServerViaScan).DisposeWith(disposables); diff --git a/v2rayN/v2rayN/Views/AddServerWindow.xaml b/v2rayN/v2rayN/Views/AddServerWindow.xaml index 9a4b88ad..acea0c28 100644 --- a/v2rayN/v2rayN/Views/AddServerWindow.xaml +++ b/v2rayN/v2rayN/Views/AddServerWindow.xaml @@ -707,6 +707,35 @@ materialDesign:HintAssist.Hint="1500" Style="{StaticResource DefTextBox}" /> + + + + + + + + + + + + + + vm.SelectedSource.RequestHost, v => v.txtRequestHost9.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.ShortId, v => v.txtShortId9.Text).DisposeWith(disposables); break; + + case EConfigType.Anytls: + this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId10.Text).DisposeWith(disposables); + break; } this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType.Text).DisposeWith(disposables); diff --git a/v2rayN/v2rayN/Views/MainWindow.xaml b/v2rayN/v2rayN/Views/MainWindow.xaml index 55384453..0b8c30b3 100644 --- a/v2rayN/v2rayN/Views/MainWindow.xaml +++ b/v2rayN/v2rayN/Views/MainWindow.xaml @@ -108,6 +108,10 @@ x:Name="menuAddTuicServer" Height="{StaticResource MenuItemHeight}" Header="{x:Static resx:ResUI.menuAddTuicServer}" /> + diff --git a/v2rayN/v2rayN/Views/MainWindow.xaml.cs b/v2rayN/v2rayN/Views/MainWindow.xaml.cs index 88af6334..08bdd90b 100644 --- a/v2rayN/v2rayN/Views/MainWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/MainWindow.xaml.cs @@ -80,6 +80,7 @@ public partial class MainWindow this.BindCommand(ViewModel, vm => vm.AddHysteria2ServerCmd, v => v.menuAddHysteria2Server).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddTuicServerCmd, v => v.menuAddTuicServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddWireguardServerCmd, v => v.menuAddWireguardServer).DisposeWith(disposables); + this.BindCommand(ViewModel, vm => vm.AddAnytlsServerCmd, v => v.menuAddAnytlsServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddCustomServerCmd, v => v.menuAddCustomServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddServerViaClipboardCmd, v => v.menuAddServerViaClipboard).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddServerViaScanCmd, v => v.menuAddServerViaScan).DisposeWith(disposables); From 98a5caa47f9a76570aa3400cfb7187ceb02387f1 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Thu, 10 Apr 2025 23:19:56 +0800 Subject: [PATCH 08/54] Simplifies local DNS address handling --- .../CoreConfig/CoreConfigSingboxService.cs | 40 +++++++------------ 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs index e1eedc2c..ac9d927e 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs @@ -1481,46 +1481,36 @@ public class CoreConfigSingboxService localDnsType = "local"; localDnsAddress = null; } - else if (localDnsAddress.StartsWith("dhcp")) + else if (localDnsAddress.StartsWith("dhcp") && localDnsAddress.Length > 7) { localDnsType = "dhcp"; - //if (localDnsAddress.Length > 7) // dhcp:// - //{ - // localDnsAddress = localDnsAddress.Substring(7); - //} + // // dhcp:// + // dhcpDnsInterface = localDnsAddress.Substring(7); localDnsAddress = null; } - else if (localDnsAddress.StartsWith("tcp")) + else if (localDnsAddress.StartsWith("tcp") && localDnsAddress.Length > 6) { localDnsType = "tcp"; - if (localDnsAddress.Length > 6) // tcp:// - { - localDnsAddress = localDnsAddress.Substring(6); - } + // tcp:// + localDnsAddress = localDnsAddress.Substring(6); } - else if (localDnsAddress.StartsWith("tls")) + else if (localDnsAddress.StartsWith("tls") && localDnsAddress.Length > 6) { localDnsType = "tls"; - if (localDnsAddress.Length > 6) // tls:// - { - localDnsAddress = localDnsAddress.Substring(6); - } + // tls:// + localDnsAddress = localDnsAddress.Substring(6); } - else if (localDnsAddress.StartsWith("https")) + else if (localDnsAddress.StartsWith("https") && localDnsAddress.Length > 8) { localDnsType = "https"; - if (localDnsAddress.Length > 8) // https:// - { - localDnsAddress = localDnsAddress.Substring(8); - } + // https:// + localDnsAddress = localDnsAddress.Substring(8); } - else if (localDnsAddress.StartsWith("quic")) + else if (localDnsAddress.StartsWith("quic") && localDnsAddress.Length > 7) { localDnsType = "quic"; - if (localDnsAddress.Length > 7) // quic:// - { - localDnsAddress = localDnsAddress.Substring(7); - } + // quic:// + localDnsAddress = localDnsAddress.Substring(7); } else { From 26b224077919be9d9e1f6e85e85f29d3543eb3cb Mon Sep 17 00:00:00 2001 From: DHR60 Date: Fri, 11 Apr 2025 18:30:45 +0800 Subject: [PATCH 09/54] Enables dhcp interface configuration --- v2rayN/ServiceLib/Models/SingboxConfig.cs | 4 +++- .../Services/CoreConfig/CoreConfigSingboxService.cs | 13 +++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/v2rayN/ServiceLib/Models/SingboxConfig.cs b/v2rayN/ServiceLib/Models/SingboxConfig.cs index 1d3249c9..d5dd3038 100644 --- a/v2rayN/ServiceLib/Models/SingboxConfig.cs +++ b/v2rayN/ServiceLib/Models/SingboxConfig.cs @@ -1,3 +1,5 @@ +using System.Text.Json.Serialization; + namespace ServiceLib.Models; public class SingboxConfig @@ -227,7 +229,7 @@ public class Server4Sbox public string? type { get; set; } public string? server { get; set; } public string? server_resolver { get; set; } - //public string? interface { get; set; } + [JsonPropertyName("interface")] public string? Interface { get; set; } } public class Experimental4Sbox diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs index ac9d927e..78e0014b 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs @@ -1475,7 +1475,7 @@ public class CoreConfigSingboxService var tag = "local_local"; var localDnsAddress = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.SingboxDomainDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress; string? localDnsType = null; - //string? dhcpDnsInterface = null; + string? dhcpDnsInterface = null; if (localDnsAddress == "local") { localDnsType = "local"; @@ -1484,8 +1484,12 @@ public class CoreConfigSingboxService else if (localDnsAddress.StartsWith("dhcp") && localDnsAddress.Length > 7) { localDnsType = "dhcp"; - // // dhcp:// - // dhcpDnsInterface = localDnsAddress.Substring(7); + // dhcp:// + dhcpDnsInterface = localDnsAddress.Substring(7); + if (dhcpDnsInterface == "auto") + { + dhcpDnsInterface = null; + } localDnsAddress = null; } else if (localDnsAddress.StartsWith("tcp") && localDnsAddress.Length > 6) @@ -1521,7 +1525,8 @@ public class CoreConfigSingboxService { tag = tag, type = localDnsType, - server = localDnsAddress + server = localDnsAddress, + Interface = dhcpDnsInterface }); dns4Sbox.rules.Insert(0, new() { From d54433aeb301961539a2a77058e75ffd9ff30c24 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Fri, 11 Apr 2025 19:03:52 +0800 Subject: [PATCH 10/54] Fetches DNS strategy for domain resolution --- .../Services/CoreConfig/CoreConfigSingboxService.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs index 78e0014b..44f9a65d 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs @@ -614,11 +614,11 @@ public class CoreConfigSingboxService if (Utils.IsDomain(node.Address)) { + var item = await AppHandler.Instance.GetDNSItem(ECoreType.sing_box); outbound.domain_resolver = new() { server = "local_local", - // TODO - //strategy = string.IsNullOrEmpty(dNSItem?.DomainStrategy4Freedom) ? null : dNSItem?.DomainStrategy4Freedom + strategy = string.IsNullOrEmpty(item?.DomainStrategy4Freedom) ? null : item?.DomainStrategy4Freedom }; } From 3824879d38f1d334ea20568656e0e267aad60feb Mon Sep 17 00:00:00 2001 From: DHR60 Date: Tue, 8 Apr 2025 21:23:01 +0800 Subject: [PATCH 11/54] support Wireguard endpoint Refactors Singbox config classes for dial fields --- v2rayN/ServiceLib/Models/SingboxConfig.cs | 55 ++-- .../CoreConfig/CoreConfigSingboxService.cs | 236 ++++++++++++++---- 2 files changed, 221 insertions(+), 70 deletions(-) diff --git a/v2rayN/ServiceLib/Models/SingboxConfig.cs b/v2rayN/ServiceLib/Models/SingboxConfig.cs index d5dd3038..6d464b2c 100644 --- a/v2rayN/ServiceLib/Models/SingboxConfig.cs +++ b/v2rayN/ServiceLib/Models/SingboxConfig.cs @@ -8,6 +8,7 @@ public class SingboxConfig public Dns4Sbox? dns { get; set; } public List inbounds { get; set; } public List outbounds { get; set; } + public List? endpoints { get; set; } public Route4Sbox route { get; set; } public Experimental4Sbox? experimental { get; set; } } @@ -96,10 +97,8 @@ public class User4Sbox public string password { get; set; } } -public class Outbound4Sbox +public class Outbound4Sbox : BaseServer4Sbox { - public string type { get; set; } - public string tag { get; set; } public string? server { get; set; } public int? server_port { get; set; } public List? server_ports { get; set; } @@ -114,7 +113,6 @@ public class Outbound4Sbox public int? recv_window_conn { get; set; } public int? recv_window { get; set; } public bool? disable_mtu_discovery { get; set; } - public string? detour { get; set; } public string? method { get; set; } public string? username { get; set; } public string? password { get; set; } @@ -122,26 +120,14 @@ public class Outbound4Sbox public string? version { get; set; } public string? network { get; set; } public string? packet_encoding { get; set; } - public List? local_address { get; set; } - public string? private_key { get; set; } - public string? peer_public_key { get; set; } - public List? reserved { get; set; } - public int? mtu { get; set; } public string? plugin { get; set; } public string? plugin_opts { get; set; } - public Tls4Sbox? tls { get; set; } - public Multiplex4Sbox? multiplex { get; set; } - public Transport4Sbox? transport { get; set; } - public HyObfs4Sbox? obfs { get; set; } public List? outbounds { get; set; } public bool? interrupt_exist_connections { get; set; } - public Rule4Sbox? domain_resolver { get; set; } } -public class Endpoints4Sbox +public class Endpoints4Sbox : BaseServer4Sbox { - public string type { get; set; } - public string tag { get; set; } public bool? system { get; set; } public string? name { get; set; } public int? mtu { get; set; } @@ -219,14 +205,11 @@ public class HyObfs4Sbox public string? password { get; set; } } -public class Server4Sbox +public class Server4Sbox : BaseServer4Sbox { - public string? tag { get; set; } - public string? detour { get; set; } public string? inet4_range { get; set; } public string? inet6_range { get; set; } public string? client_subnet { get; set; } - public string? type { get; set; } public string? server { get; set; } public string? server_resolver { get; set; } [JsonPropertyName("interface")] public string? Interface { get; set; } @@ -277,3 +260,33 @@ public class Ruleset4Sbox public string? download_detour { get; set; } public string? update_interval { get; set; } } + +public abstract class DialFields4Sbox +{ + public string? detour { get; set; } + public string? bind_interface { get; set; } + public string? inet4_bind_address { get; set; } + public string? inet6_bind_address { get; set; } + public int? routing_mark { get; set; } + public bool? reuse_addr { get; set; } + public string? netns { get; set; } + public string? connect_timeout { get; set; } + public bool? tcp_fast_open { get; set; } + public bool? tcp_multi_path { get; set; } + public bool? udp_fragment { get; set; } + public Rule4Sbox? domain_resolver { get; set; } // or string + public string? network_strategy { get; set; } + public List? network_type { get; set; } + public List? fallback_network_type { get; set; } + public string? fallback_delay { get; set; } + public Tls4Sbox? tls { get; set; } + public Multiplex4Sbox? multiplex { get; set; } + public Transport4Sbox? transport { get; set; } + public HyObfs4Sbox? obfs { get; set; } +} + +public abstract class BaseServer4Sbox : DialFields4Sbox +{ + public string type { get; set; } + public string tag { get; set; } +} diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs index 44f9a65d..7ce3e722 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs @@ -1,6 +1,7 @@ using System.Data; using System.Net; using System.Net.NetworkInformation; +using System.Reactive; using DynamicData; using ServiceLib.Models; @@ -55,7 +56,18 @@ public class CoreConfigSingboxService await GenInbounds(singboxConfig); - await GenOutbound(node, singboxConfig.outbounds.First()); + if (node.ConfigType == EConfigType.WireGuard) + { + singboxConfig.outbounds.RemoveAt(0); + var endpoints = new Endpoints4Sbox(); + await GenEndpoint(node, endpoints); + endpoints.tag = Global.ProxyTag; + singboxConfig.endpoints = new() { endpoints }; + } + else + { + await GenOutbound(node, singboxConfig.outbounds.First()); + } await GenMoreOutbounds(node, singboxConfig); @@ -204,16 +216,29 @@ public class CoreConfigSingboxService continue; } - var outbound = JsonUtils.Deserialize(txtOutbound); - await GenOutbound(item, outbound); - outbound.tag = Global.ProxyTag + inbound.listen_port.ToString(); - singboxConfig.outbounds.Add(outbound); + var server = await GenServer(item); + if (server is null) + { + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return ret; + } + var tag = Global.ProxyTag + inbound.listen_port.ToString(); + server.tag = tag; + if (server is Endpoints4Sbox endpoint) + { + singboxConfig.endpoints ??= new(); + singboxConfig.endpoints.Add(endpoint); + } + else if (server is Outbound4Sbox outbound) + { + singboxConfig.outbounds.Add(outbound); + } //rule Rule4Sbox rule = new() { inbound = new List { inbound.tag }, - outbound = outbound.tag + outbound = tag }; singboxConfig.route.rules.Add(rule); } @@ -277,7 +302,18 @@ public class CoreConfigSingboxService } await GenLog(singboxConfig); - await GenOutbound(node, singboxConfig.outbounds.First()); + if (node.ConfigType == EConfigType.WireGuard) + { + singboxConfig.outbounds.RemoveAt(0); + var endpoints = new Endpoints4Sbox(); + await GenEndpoint(node, endpoints); + endpoints.tag = Global.ProxyTag; + singboxConfig.endpoints = new() { endpoints }; + } + else + { + await GenOutbound(node, singboxConfig.outbounds.First()); + } await GenMoreOutbounds(node, singboxConfig); await GenDnsDomains(null, singboxConfig, null); @@ -731,15 +767,6 @@ public class CoreConfigSingboxService outbound.congestion_control = node.HeaderType; break; } - case EConfigType.WireGuard: - { - outbound.private_key = node.Id; - outbound.peer_public_key = node.PublicKey; - outbound.reserved = Utils.String2List(node.Path)?.Select(int.Parse).ToList(); - outbound.local_address = Utils.String2List(node.RequestHost); - outbound.mtu = node.ShortId.IsNullOrEmpty() ? Global.TunMtus.First() : node.ShortId.ToInt(); - break; - } case EConfigType.Anytls: { outbound.password = node.Id; @@ -758,6 +785,76 @@ public class CoreConfigSingboxService return 0; } + private async Task GenEndpoint(ProfileItem node, Endpoints4Sbox endpoint) + { + try + { + endpoint.address = Utils.String2List(node.RequestHost); + // Utils.GetFreePort() 9090 ? + endpoint.listen_port = Utils.GetFreePort(); + endpoint.type = Global.ProtocolTypes[node.ConfigType]; + + if (Utils.IsDomain(node.Address)) + { + var item = await AppHandler.Instance.GetDNSItem(ECoreType.sing_box); + endpoint.domain_resolver = new() + { + server = "local_local", + strategy = string.IsNullOrEmpty(item?.DomainStrategy4Freedom) ? null : item?.DomainStrategy4Freedom + }; + } + + switch (node.ConfigType) + { + case EConfigType.WireGuard: + { + var peer = new Peer4Sbox + { + public_key = node.PublicKey, + reserved = Utils.String2List(node.Path)?.Select(int.Parse).ToList(), + address = node.Address, + port = node.Port, + // TODO default ["0.0.0.0/0", "::/0"] + allowed_ips = new() { "0.0.0.0/0", "::/0" }, + }; + endpoint.private_key = node.Id; + endpoint.mtu = node.ShortId.IsNullOrEmpty() ? Global.TunMtus.First() : node.ShortId.ToInt(); + break; + } + } + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } + return await Task.FromResult(0); + } + + private async Task GenServer(ProfileItem node) + { + try + { + var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound); + if (node.ConfigType == EConfigType.WireGuard) + { + var endpoint = JsonUtils.Deserialize(txtOutbound); + await GenEndpoint(node, endpoint); + return endpoint; + } + else + { + var outbound = JsonUtils.Deserialize(txtOutbound); + await GenOutbound(node, outbound); + return outbound; + } + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } + return await Task.FromResult(null); + } + private async Task GenOutboundMux(ProfileItem node, Outbound4Sbox outbound) { try @@ -924,7 +1021,8 @@ public class CoreConfigSingboxService } //current proxy - var outbound = singboxConfig.outbounds.First(); + BaseServer4Sbox? outbound = singboxConfig.endpoints?.FirstOrDefault(t => t.tag == Global.ProxyTag) == null ? singboxConfig.outbounds.First() : null; + var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound); //Previous proxy @@ -933,17 +1031,32 @@ public class CoreConfigSingboxService if (prevNode is not null && prevNode.ConfigType != EConfigType.Custom) { - var prevOutbound = JsonUtils.Deserialize(txtOutbound); - await GenOutbound(prevNode, prevOutbound); prevOutboundTag = $"prev-{Global.ProxyTag}"; - prevOutbound.tag = prevOutboundTag; - singboxConfig.outbounds.Add(prevOutbound); + var prevServer = await GenServer(prevNode); + prevServer.tag = prevOutboundTag; + if (prevServer is Endpoints4Sbox endpoint) + { + singboxConfig.endpoints ??= new(); + singboxConfig.endpoints.Add(endpoint); + } + else if (prevServer is Outbound4Sbox outboundPrev) + { + singboxConfig.outbounds.Add(outboundPrev); + } } - var nextOutbound = await GenChainOutbounds(subItem, outbound, prevOutboundTag); + var nextServer = await GenChainOutbounds(subItem, outbound, prevOutboundTag); - if (nextOutbound is not null) + if (nextServer is not null) { - singboxConfig.outbounds.Insert(0, nextOutbound); + if (nextServer is Endpoints4Sbox endpoint) + { + singboxConfig.endpoints ??= new(); + singboxConfig.endpoints.Insert(0, endpoint); + } + else if (nextServer is Outbound4Sbox outboundNext) + { + singboxConfig.outbounds.Insert(0, outboundNext); + } } } catch (Exception ex) @@ -966,11 +1079,13 @@ public class CoreConfigSingboxService } var resultOutbounds = new List(); + var resultEndpoints = new List(); // For endpoints var prevOutbounds = new List(); // Separate list for prev outbounds + var prevEndpoints = new List(); // Separate list for prev endpoints var proxyTags = new List(); // For selector and urltest outbounds // Cache for chain proxies to avoid duplicate generation - var nextProxyCache = new Dictionary(); + var nextProxyCache = new Dictionary(); var prevProxyTags = new Dictionary(); // Map from profile name to tag int prevIndex = 0; // Index for prev outbounds @@ -982,19 +1097,18 @@ public class CoreConfigSingboxService // Handle proxy chain string? prevTag = null; - var currentOutbound = JsonUtils.Deserialize(txtOutbound); - var nextOutbound = nextProxyCache.GetValueOrDefault(node.Subid, null); - if (nextOutbound != null) + var currentServer = await GenServer(node); + var nextServer = nextProxyCache.GetValueOrDefault(node.Subid, null); + if (nextServer != null) { - nextOutbound = JsonUtils.DeepCopy(nextOutbound); + nextServer = JsonUtils.DeepCopy(nextServer); } var subItem = await AppHandler.Instance.GetSubItem(node.Subid); // current proxy - await GenOutbound(node, currentOutbound); - currentOutbound.tag = $"{Global.ProxyTag}-{index}"; - proxyTags.Add(currentOutbound.tag); + currentServer.tag = $"{Global.ProxyTag}-{index}"; + proxyTags.Add(currentServer.tag); if (!node.Subid.IsNullOrEmpty()) { @@ -1017,18 +1131,32 @@ public class CoreConfigSingboxService prevProxyTags[node.Subid] = prevTag; } - nextOutbound = await GenChainOutbounds(subItem, currentOutbound, prevTag, nextOutbound); + nextServer = await GenChainOutbounds(subItem, currentServer, prevTag, nextServer); if (!nextProxyCache.ContainsKey(node.Subid)) { - nextProxyCache[node.Subid] = nextOutbound; + nextProxyCache[node.Subid] = nextServer; } } - if (nextOutbound is not null) + if (nextServer is not null) { - resultOutbounds.Add(nextOutbound); + if (nextServer is Endpoints4Sbox nextEndpoint) + { + resultEndpoints.Add(nextEndpoint); + } + else if (nextServer is Outbound4Sbox nextOutbound) + { + resultOutbounds.Add(nextOutbound); + } + } + if (currentServer is Endpoints4Sbox currentEndpoint) + { + resultEndpoints.Add(currentEndpoint); + } + else if (currentServer is Outbound4Sbox currentOutbound) + { + resultOutbounds.Add(currentOutbound); } - resultOutbounds.Add(currentOutbound); } // Add urltest outbound (auto selection based on latency) @@ -1061,6 +1189,9 @@ public class CoreConfigSingboxService resultOutbounds.AddRange(prevOutbounds); resultOutbounds.AddRange(singboxConfig.outbounds); singboxConfig.outbounds = resultOutbounds; + singboxConfig.endpoints ??= new List(); + resultEndpoints.AddRange(singboxConfig.endpoints); + singboxConfig.endpoints = resultEndpoints; } catch (Exception ex) { @@ -1082,7 +1213,7 @@ public class CoreConfigSingboxService /// /// The outbound configuration for the next proxy in the chain, or null if no next proxy exists. /// - private async Task GenChainOutbounds(SubItem subItem, Outbound4Sbox outbound, string? prevOutboundTag, Outbound4Sbox? nextOutbound = null) + private async Task GenChainOutbounds(SubItem subItem, BaseServer4Sbox outbound, string? prevOutboundTag, BaseServer4Sbox? nextOutbound = null) { try { @@ -1098,11 +1229,7 @@ public class CoreConfigSingboxService if (nextNode is not null && nextNode.ConfigType != EConfigType.Custom) { - if (nextOutbound == null) - { - nextOutbound = JsonUtils.Deserialize(txtOutbound); - await GenOutbound(nextNode, nextOutbound); - } + nextOutbound ??= await GenServer(nextNode); nextOutbound.tag = outbound.tag; outbound.tag = $"mid-{outbound.tag}"; @@ -1426,13 +1553,24 @@ public class CoreConfigSingboxService return Global.ProxyTag; } - var txtOutbound = EmbedUtils.GetEmbedText(Global.SingboxSampleOutbound); - var outbound = JsonUtils.Deserialize(txtOutbound); - await GenOutbound(node, outbound); - outbound.tag = Global.ProxyTag + node.IndexId.ToString(); - singboxConfig.outbounds.Add(outbound); + var server = await GenServer(node); + if (server is null) + { + return Global.ProxyTag; + } - return outbound.tag; + server.tag = Global.ProxyTag + node.IndexId.ToString(); + if (server is Endpoints4Sbox endpoint) + { + singboxConfig.endpoints ??= new(); + singboxConfig.endpoints.Add(endpoint); + } + else if (server is Outbound4Sbox outbound) + { + singboxConfig.outbounds.Add(outbound); + } + + return server.tag; } private async Task GenDns(ProfileItem? node, SingboxConfig singboxConfig) From e2faf61a8bc74da054c538dd9a95334d27c6d594 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Fri, 11 Apr 2025 19:33:51 +0800 Subject: [PATCH 12/54] Utils.GetFreePort() default port to be zero --- v2rayN/ServiceLib/Common/Utils.cs | 4 ++-- .../Services/CoreConfig/CoreConfigSingboxService.cs | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/v2rayN/ServiceLib/Common/Utils.cs b/v2rayN/ServiceLib/Common/Utils.cs index 63540ee2..369cc0f4 100644 --- a/v2rayN/ServiceLib/Common/Utils.cs +++ b/v2rayN/ServiceLib/Common/Utils.cs @@ -466,11 +466,11 @@ public class Utils return false; } - public static int GetFreePort(int defaultPort = 9090) + public static int GetFreePort(int defaultPort = 0) { try { - if (!Utils.PortInUse(defaultPort)) + if (!(defaultPort == 0 || Utils.PortInUse(defaultPort))) { return defaultPort; } diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs index 7ce3e722..420cdcfc 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs @@ -790,7 +790,6 @@ public class CoreConfigSingboxService try { endpoint.address = Utils.String2List(node.RequestHost); - // Utils.GetFreePort() 9090 ? endpoint.listen_port = Utils.GetFreePort(); endpoint.type = Global.ProtocolTypes[node.ConfigType]; From 8f9d1951a27c131c9bcf0c0d7f709924d070bcaa Mon Sep 17 00:00:00 2001 From: DHR60 Date: Fri, 11 Apr 2025 19:59:20 +0800 Subject: [PATCH 13/54] Adds Sing-box legacy DNS config support --- v2rayN/ServiceLib/Models/SingboxConfig.cs | 6 ++ v2rayN/ServiceLib/Sample/dns_singbox_normal | 4 +- v2rayN/ServiceLib/Sample/tun_singbox_dns | 4 +- .../CoreConfig/CoreConfigSingboxService.cs | 62 ++++++++++++++++++- 4 files changed, 69 insertions(+), 7 deletions(-) diff --git a/v2rayN/ServiceLib/Models/SingboxConfig.cs b/v2rayN/ServiceLib/Models/SingboxConfig.cs index 6d464b2c..e53cfd8f 100644 --- a/v2rayN/ServiceLib/Models/SingboxConfig.cs +++ b/v2rayN/ServiceLib/Models/SingboxConfig.cs @@ -213,6 +213,12 @@ public class Server4Sbox : BaseServer4Sbox public string? server { get; set; } public string? server_resolver { get; set; } [JsonPropertyName("interface")] public string? Interface { get; set; } + // Deprecated + public string? address { get; set; } + public string? address_resolver { get; set; } + public string? address_strategy { get; set; } + public string? strategy { get; set; } + // Deprecated End } public class Experimental4Sbox diff --git a/v2rayN/ServiceLib/Sample/dns_singbox_normal b/v2rayN/ServiceLib/Sample/dns_singbox_normal index 19b2ceae..69367e86 100644 --- a/v2rayN/ServiceLib/Sample/dns_singbox_normal +++ b/v2rayN/ServiceLib/Sample/dns_singbox_normal @@ -4,14 +4,12 @@ "tag": "remote", "type": "tcp", "server": "8.8.8.8", - "strategy": "prefer_ipv4", "detour": "proxy" }, { "tag": "local", "type": "udp", - "server": "223.5.5.5", - "strategy": "prefer_ipv4" + "server": "223.5.5.5" } ], "rules": [ diff --git a/v2rayN/ServiceLib/Sample/tun_singbox_dns b/v2rayN/ServiceLib/Sample/tun_singbox_dns index 31a9af33..530267b0 100644 --- a/v2rayN/ServiceLib/Sample/tun_singbox_dns +++ b/v2rayN/ServiceLib/Sample/tun_singbox_dns @@ -4,14 +4,12 @@ "tag": "remote", "type": "tcp", "server": "8.8.8.8", - "strategy": "prefer_ipv4", "detour": "proxy" }, { "tag": "local", "type": "udp", - "server": "223.5.5.5", - "strategy": "prefer_ipv4" + "server": "223.5.5.5" } ], "rules": [ diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs index 420cdcfc..ddc07553 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs @@ -1594,7 +1594,14 @@ public class CoreConfigSingboxService } singboxConfig.dns = dns4Sbox; - await GenDnsDomains(node, singboxConfig, item); + if (dns4Sbox.servers != null && dns4Sbox.servers.Count > 0 && dns4Sbox.servers.First().address.IsNullOrEmpty()) + { + await GenDnsDomains(node, singboxConfig, item); + } + else + { + await GenDnsDomainsLegacy(node, singboxConfig, item); + } } catch (Exception ex) { @@ -1692,6 +1699,59 @@ public class CoreConfigSingboxService return await Task.FromResult(0); } + private async Task GenDnsDomainsLegacy(ProfileItem? node, SingboxConfig singboxConfig, DNSItem? dNSItem) + { + var dns4Sbox = singboxConfig.dns ?? new(); + dns4Sbox.servers ??= []; + dns4Sbox.rules ??= []; + + var tag = "local_local"; + dns4Sbox.servers.Add(new() + { + tag = tag, + address = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.SingboxDomainDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress, + detour = Global.DirectTag, + strategy = string.IsNullOrEmpty(dNSItem?.DomainStrategy4Freedom) ? null : dNSItem?.DomainStrategy4Freedom, + }); + 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() + }); + + var lstDomain = singboxConfig.outbounds + .Where(t => t.server.IsNotEmpty() && Utils.IsDomain(t.server)) + .Select(t => t.server) + .Distinct() + .ToList(); + if (lstDomain != null && lstDomain.Count > 0) + { + dns4Sbox.rules.Insert(0, new() + { + server = tag, + domain = lstDomain + }); + } + + //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 async Task GenExperimental(SingboxConfig singboxConfig) { //if (_config.guiItem.enableStatistics) From 70bdb3de1660f0e783f6dbd352a75c8b8f0da8d4 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Fri, 11 Apr 2025 20:10:49 +0800 Subject: [PATCH 14/54] Adds IPv4 preference to DNS configurations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 对应原dns.servers[].strategy = prefer_ipv4 --- v2rayN/ServiceLib/Sample/dns_singbox_normal | 9 ++++++--- v2rayN/ServiceLib/Sample/tun_singbox_dns | 9 ++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/v2rayN/ServiceLib/Sample/dns_singbox_normal b/v2rayN/ServiceLib/Sample/dns_singbox_normal index 69367e86..b32b439c 100644 --- a/v2rayN/ServiceLib/Sample/dns_singbox_normal +++ b/v2rayN/ServiceLib/Sample/dns_singbox_normal @@ -18,14 +18,17 @@ "googleapis.cn", "gstatic.com" ], - "server": "remote" + "server": "remote", + "strategy": "prefer_ipv4" }, { "rule_set": [ "geosite-cn" ], - "server": "local" + "server": "local", + "strategy": "prefer_ipv4" } ], - "final": "remote" + "final": "remote", + "strategy": "prefer_ipv4" } diff --git a/v2rayN/ServiceLib/Sample/tun_singbox_dns b/v2rayN/ServiceLib/Sample/tun_singbox_dns index 530267b0..39bf43ac 100644 --- a/v2rayN/ServiceLib/Sample/tun_singbox_dns +++ b/v2rayN/ServiceLib/Sample/tun_singbox_dns @@ -18,15 +18,18 @@ "googleapis.cn", "gstatic.com" ], - "server": "remote" + "server": "remote", + "strategy": "prefer_ipv4" }, { "rule_set": [ "geosite-cn", "geosite-geolocation-cn" ], - "server": "local" + "server": "local", + "strategy": "prefer_ipv4" } ], - "final": "remote" + "final": "remote", + "strategy": "prefer_ipv4" } From b290a38ef42c1fc0f13677ac787a475f0a4a1ec5 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Fri, 11 Apr 2025 20:55:15 +0800 Subject: [PATCH 15/54] Refactors DNS address parsing --- .../CoreConfig/CoreConfigSingboxService.cs | 67 ++++++++++--------- 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs index ddc07553..20ccb962 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs @@ -1620,45 +1620,50 @@ public class CoreConfigSingboxService var localDnsAddress = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.SingboxDomainDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress; string? localDnsType = null; string? dhcpDnsInterface = null; + var dnsProtocols = new List + { + "dhcp", + "https", + "tcp", + "tls", + "quic", + "h3", + "udp" + }; if (localDnsAddress == "local") { localDnsType = "local"; localDnsAddress = null; } - else if (localDnsAddress.StartsWith("dhcp") && localDnsAddress.Length > 7) + else if (dnsProtocols.Any(protocol => localDnsAddress.StartsWith(protocol))) { - localDnsType = "dhcp"; - // dhcp:// - dhcpDnsInterface = localDnsAddress.Substring(7); - if (dhcpDnsInterface == "auto") + var protocol = dnsProtocols.First(p => localDnsAddress.StartsWith(p)); + localDnsType = protocol; + // +3 for "://" + if (localDnsAddress.Length > protocol.Length + 3) { - dhcpDnsInterface = null; + localDnsAddress = localDnsAddress.Substring(protocol.Length + 3); + if (protocol == "dhcp") + { + dhcpDnsInterface = localDnsAddress; + if (dhcpDnsInterface == "auto") + { + dhcpDnsInterface = null; + } + localDnsAddress = null; + } + else if (protocol is "https" or "h3") + { + if (localDnsAddress.Contains('/')) + { + localDnsAddress = localDnsAddress.Substring(0, localDnsAddress.IndexOf('/')); + } + } + } + else + { + localDnsAddress = null; } - localDnsAddress = null; - } - else if (localDnsAddress.StartsWith("tcp") && localDnsAddress.Length > 6) - { - localDnsType = "tcp"; - // tcp:// - localDnsAddress = localDnsAddress.Substring(6); - } - else if (localDnsAddress.StartsWith("tls") && localDnsAddress.Length > 6) - { - localDnsType = "tls"; - // tls:// - localDnsAddress = localDnsAddress.Substring(6); - } - else if (localDnsAddress.StartsWith("https") && localDnsAddress.Length > 8) - { - localDnsType = "https"; - // https:// - localDnsAddress = localDnsAddress.Substring(8); - } - else if (localDnsAddress.StartsWith("quic") && localDnsAddress.Length > 7) - { - localDnsType = "quic"; - // quic:// - localDnsAddress = localDnsAddress.Substring(7); } else { From 3be0b312d6c171182acfb518c10d267d35f401f8 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Sat, 12 Apr 2025 12:05:47 +0800 Subject: [PATCH 16/54] Fixes config generation --- .../Services/CoreConfig/CoreConfigSingboxService.cs | 1 + .../Services/CoreConfig/CoreConfigV2rayService.cs | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs index 20ccb962..453eda07 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs @@ -818,6 +818,7 @@ public class CoreConfigSingboxService }; endpoint.private_key = node.Id; endpoint.mtu = node.ShortId.IsNullOrEmpty() ? Global.TunMtus.First() : node.ShortId.ToInt(); + endpoint.peers = new() { peer }; break; } } diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs index 27b1dae6..edf85686 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs @@ -1426,7 +1426,8 @@ public class CoreConfigV2rayService if (prevNode is not null && prevNode.ConfigType != EConfigType.Custom && prevNode.ConfigType != EConfigType.Hysteria2 - && prevNode.ConfigType != EConfigType.TUIC) + && prevNode.ConfigType != EConfigType.TUIC + && prevNode.ConfigType != EConfigType.Anytls) { var prevOutbound = JsonUtils.Deserialize(txtOutbound); await GenOutbound(prevNode, prevOutbound); @@ -1495,7 +1496,8 @@ public class CoreConfigV2rayService if (nextNode is not null && nextNode.ConfigType != EConfigType.Custom && nextNode.ConfigType != EConfigType.Hysteria2 - && nextNode.ConfigType != EConfigType.TUIC) + && nextNode.ConfigType != EConfigType.TUIC + && nextNode.ConfigType != EConfigType.Anytls) { if (nextOutbound == null) { From 7f5489c5f7cf1959fb681f646cc1332c349daa13 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Sat, 12 Apr 2025 13:37:51 +0800 Subject: [PATCH 17/54] fix singbox endpoints proxy chain not work --- v2rayN/ServiceLib/Models/SingboxConfig.cs | 1 + .../ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/v2rayN/ServiceLib/Models/SingboxConfig.cs b/v2rayN/ServiceLib/Models/SingboxConfig.cs index e53cfd8f..f9657901 100644 --- a/v2rayN/ServiceLib/Models/SingboxConfig.cs +++ b/v2rayN/ServiceLib/Models/SingboxConfig.cs @@ -39,6 +39,7 @@ public class Route4Sbox public bool? auto_detect_interface { get; set; } public List rules { get; set; } public List? rule_set { get; set; } + public string? final { get; set; } } [Serializable] diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs index 453eda07..d29bfdf4 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs @@ -1248,6 +1248,8 @@ public class CoreConfigSingboxService { try { + singboxConfig.route.final = Global.ProxyTag; + if (_config.TunModeItem.EnableTun) { singboxConfig.route.auto_detect_interface = true; From dca737bdadc25b01a068c95f541356c563d3259e Mon Sep 17 00:00:00 2001 From: DHR60 Date: Mon, 5 May 2025 16:27:41 +0800 Subject: [PATCH 18/54] Fixes wrong field --- v2rayN/ServiceLib/Models/SingboxConfig.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2rayN/ServiceLib/Models/SingboxConfig.cs b/v2rayN/ServiceLib/Models/SingboxConfig.cs index f9657901..7c279b0f 100644 --- a/v2rayN/ServiceLib/Models/SingboxConfig.cs +++ b/v2rayN/ServiceLib/Models/SingboxConfig.cs @@ -212,7 +212,7 @@ public class Server4Sbox : BaseServer4Sbox public string? inet6_range { get; set; } public string? client_subnet { get; set; } public string? server { get; set; } - public string? server_resolver { get; set; } + public new string? domain_resolver { get; set; } [JsonPropertyName("interface")] public string? Interface { get; set; } // Deprecated public string? address { get; set; } From d1c16ecb72ffaf9bde00abb6d7d436713c2b1ef3 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Mon, 5 May 2025 16:41:27 +0800 Subject: [PATCH 19/54] Removes direct clash_mode domain strategy --- .../ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs index d29bfdf4..83a70df0 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs @@ -1683,8 +1683,7 @@ public class CoreConfigSingboxService dns4Sbox.rules.Insert(0, new() { server = tag, - clash_mode = ERuleMode.Direct.ToString(), - strategy = string.IsNullOrEmpty(dNSItem?.DomainStrategy4Freedom) ? null : dNSItem?.DomainStrategy4Freedom + clash_mode = ERuleMode.Direct.ToString() }); dns4Sbox.rules.Insert(0, new() { From 645e4e771158b86d8509976712f585fea245b710 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Mon, 5 May 2025 17:03:22 +0800 Subject: [PATCH 20/54] Improves DNS address parsing in Singbox DNS type, host, port, and path --- v2rayN/ServiceLib/Models/SingboxConfig.cs | 3 + .../CoreConfig/CoreConfigSingboxService.cs | 147 +++++++++++------- 2 files changed, 96 insertions(+), 54 deletions(-) diff --git a/v2rayN/ServiceLib/Models/SingboxConfig.cs b/v2rayN/ServiceLib/Models/SingboxConfig.cs index 7c279b0f..8b087894 100644 --- a/v2rayN/ServiceLib/Models/SingboxConfig.cs +++ b/v2rayN/ServiceLib/Models/SingboxConfig.cs @@ -214,6 +214,9 @@ public class Server4Sbox : BaseServer4Sbox public string? server { get; set; } public new string? domain_resolver { get; set; } [JsonPropertyName("interface")] public string? Interface { get; set; } + public int? server_port { get; set; } + public string? path { get; set; } + public Headers4Sbox? headers { get; set; } // Deprecated public string? address { get; set; } public string? address_resolver { get; set; } diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs index 83a70df0..85e04cbf 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs @@ -1621,65 +1621,19 @@ public class CoreConfigSingboxService var tag = "local_local"; var localDnsAddress = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.SingboxDomainDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress; - string? localDnsType = null; - string? dhcpDnsInterface = null; - var dnsProtocols = new List - { - "dhcp", - "https", - "tcp", - "tls", - "quic", - "h3", - "udp" - }; - if (localDnsAddress == "local") - { - localDnsType = "local"; - localDnsAddress = null; - } - else if (dnsProtocols.Any(protocol => localDnsAddress.StartsWith(protocol))) - { - var protocol = dnsProtocols.First(p => localDnsAddress.StartsWith(p)); - localDnsType = protocol; - // +3 for "://" - if (localDnsAddress.Length > protocol.Length + 3) - { - localDnsAddress = localDnsAddress.Substring(protocol.Length + 3); - if (protocol == "dhcp") - { - dhcpDnsInterface = localDnsAddress; - if (dhcpDnsInterface == "auto") - { - dhcpDnsInterface = null; - } - localDnsAddress = null; - } - else if (protocol is "https" or "h3") - { - if (localDnsAddress.Contains('/')) - { - localDnsAddress = localDnsAddress.Substring(0, localDnsAddress.IndexOf('/')); - } - } - } - else - { - localDnsAddress = null; - } - } - else - { - localDnsType = "udp"; - } + + var (dnsType, dnsHost, dnsPort, dnsPath) = ParseDnsAddress(localDnsAddress); dns4Sbox.servers.Add(new() { tag = tag, - type = localDnsType, - server = localDnsAddress, - Interface = dhcpDnsInterface + type = dnsType, + server = dnsHost, + Interface = dnsType == "dhcp" ? dnsHost : null, + server_port = dnsPort, + path = dnsPath }); + dns4Sbox.rules.Insert(0, new() { server = tag, @@ -1759,6 +1713,91 @@ public class CoreConfigSingboxService return await Task.FromResult(0); } + private (string type, string? host, int? port, string? path) ParseDnsAddress(string address) + { + string type = "udp"; + string? host = null; + int? port = null; + string? path = null; + + if (address is "local" or "localhost") + { + return ("local", null, null, null); + } + + if (address.StartsWith("dhcp://", StringComparison.OrdinalIgnoreCase)) + { + string interface_name = address.Substring(7); + return ("dhcp", interface_name == "auto" ? null : interface_name, null, null); + } + + if (!address.Contains("://")) + { + // udp dns + host = address; + return (type, host, port, path); + } + + try + { + int protocolEndIndex = address.IndexOf("://", StringComparison.Ordinal); + type = address.Substring(0, protocolEndIndex).ToLower(); + + var uri = new Uri(address); + host = uri.Host; + + if (!uri.IsDefaultPort) + { + port = uri.Port; + } + + if ((type == "https" || type == "h3") && !string.IsNullOrEmpty(uri.AbsolutePath) && uri.AbsolutePath != "/") + { + path = uri.AbsolutePath; + } + } + catch (UriFormatException) + { + int protocolEndIndex = address.IndexOf("://", StringComparison.Ordinal); + if (protocolEndIndex > 0) + { + type = address.Substring(0, protocolEndIndex).ToLower(); + string remaining = address.Substring(protocolEndIndex + 3); + + int portIndex = remaining.IndexOf(':'); + int pathIndex = remaining.IndexOf('/'); + + if (portIndex > 0) + { + host = remaining.Substring(0, portIndex); + string portPart = pathIndex > portIndex + ? remaining.Substring(portIndex + 1, pathIndex - portIndex - 1) + : remaining.Substring(portIndex + 1); + + if (int.TryParse(portPart, out int parsedPort)) + { + port = parsedPort; + } + } + else if (pathIndex > 0) + { + host = remaining.Substring(0, pathIndex); + } + else + { + host = remaining; + } + + if (pathIndex > 0 && (type == "https" || type == "h3")) + { + path = remaining.Substring(pathIndex); + } + } + } + + return (type, host, port, path); + } + private async Task GenExperimental(SingboxConfig singboxConfig) { //if (_config.guiItem.enableStatistics) From 228069f499d03a684554c7f2acc101ae5eae443b Mon Sep 17 00:00:00 2001 From: DHR60 Date: Mon, 5 May 2025 18:21:40 +0800 Subject: [PATCH 21/54] Adds properties to Rule4Sbox class --- v2rayN/ServiceLib/Models/SingboxConfig.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/v2rayN/ServiceLib/Models/SingboxConfig.cs b/v2rayN/ServiceLib/Models/SingboxConfig.cs index 8b087894..93393476 100644 --- a/v2rayN/ServiceLib/Models/SingboxConfig.cs +++ b/v2rayN/ServiceLib/Models/SingboxConfig.cs @@ -52,6 +52,7 @@ public class Rule4Sbox public string? mode { get; set; } public bool? ip_is_private { get; set; } public string? client_subnet { get; set; } + public int? rewrite_ttl { get; set; } public bool? invert { get; set; } public string? clash_mode { get; set; } public List? inbound { get; set; } @@ -73,6 +74,24 @@ public class Rule4Sbox public string? action { get; set; } public string? strategy { get; set; } public List? sniffer { get; set; } + public string? rcode { get; set; } + public List? query_type { get; set; } + public List? answer { get; set; } + public List? ns { get; set; } + public List? extra { get; set; } + public string? method { get; set; } + public bool? no_drop { get; set; } + public bool? source_ip_is_private { get; set; } + public bool? ip_accept_any { get; set; } + public int? source_port { get; set; } + public List? source_port_range { get; set; } + public List? network_type { get; set; } + public bool? network_is_expensive { get; set; } + public bool? network_is_constrained { get; set; } + public List? wifi_ssid { get; set; } + public List? wifi_bssid { get; set; } + public bool? rule_set_ip_cidr_match_source { get; set; } + public bool? rule_set_ip_cidr_accept_empty { get; set; } } [Serializable] From f14f39f5a141b3cce3f855a985542ee350d0dbd1 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Wed, 28 May 2025 20:39:20 +0800 Subject: [PATCH 22/54] Removes Wireguard listen port --- v2rayN/ServiceLib/Models/SingboxConfig.cs | 2 +- .../ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/v2rayN/ServiceLib/Models/SingboxConfig.cs b/v2rayN/ServiceLib/Models/SingboxConfig.cs index 93393476..e8166482 100644 --- a/v2rayN/ServiceLib/Models/SingboxConfig.cs +++ b/v2rayN/ServiceLib/Models/SingboxConfig.cs @@ -153,7 +153,7 @@ public class Endpoints4Sbox : BaseServer4Sbox public int? mtu { get; set; } public List address { get; set; } public string private_key { get; set; } - public int listen_port { get; set; } + public int? listen_port { get; set; } public string? udp_timeout { get; set; } public int? workers { get; set; } public List peers { get; set; } diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs index 85e04cbf..445651c2 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs @@ -790,7 +790,6 @@ public class CoreConfigSingboxService try { endpoint.address = Utils.String2List(node.RequestHost); - endpoint.listen_port = Utils.GetFreePort(); endpoint.type = Global.ProtocolTypes[node.ConfigType]; if (Utils.IsDomain(node.Address)) From 8ea39d74753dce59da9217401d9f16d1ec34e7f5 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Tue, 17 Jun 2025 18:52:12 +0800 Subject: [PATCH 23/54] Support sing-box hosts --- v2rayN/ServiceLib/Models/SingboxConfig.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/v2rayN/ServiceLib/Models/SingboxConfig.cs b/v2rayN/ServiceLib/Models/SingboxConfig.cs index e8166482..c63acc4a 100644 --- a/v2rayN/ServiceLib/Models/SingboxConfig.cs +++ b/v2rayN/ServiceLib/Models/SingboxConfig.cs @@ -236,6 +236,8 @@ public class Server4Sbox : BaseServer4Sbox public int? server_port { get; set; } public string? path { get; set; } public Headers4Sbox? headers { get; set; } + // public List? path { get; set; } // hosts + public Dictionary? predefined { get; set; } // Deprecated public string? address { get; set; } public string? address_resolver { get; set; } From d9b0aff8da94c4606f76fe598517ed718c562037 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Tue, 17 Jun 2025 21:58:52 +0800 Subject: [PATCH 24/54] Adds tag resolver supports --- .../CoreConfig/CoreConfigSingboxService.cs | 58 +++++++++++++------ 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs index 445651c2..380880a5 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs @@ -651,9 +651,10 @@ public class CoreConfigSingboxService 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 = "local_local", + server = localDnsAddress.StartsWith("tag://") ? localDnsAddress.Substring(6) : "local_resolver", strategy = string.IsNullOrEmpty(item?.DomainStrategy4Freedom) ? null : item?.DomainStrategy4Freedom }; } @@ -795,9 +796,10 @@ public class CoreConfigSingboxService 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 = "local_local", + server = localDnsAddress.StartsWith("tag://") ? localDnsAddress.Substring(6) : "local_resolver", strategy = string.IsNullOrEmpty(item?.DomainStrategy4Freedom) ? null : item?.DomainStrategy4Freedom }; } @@ -1618,26 +1620,48 @@ public class CoreConfigSingboxService dns4Sbox.servers ??= []; dns4Sbox.rules ??= []; - var tag = "local_local"; + var tag = "local_resolver"; var localDnsAddress = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.SingboxDomainDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress; - var (dnsType, dnsHost, dnsPort, dnsPath) = ParseDnsAddress(localDnsAddress); - - dns4Sbox.servers.Add(new() + if (localDnsAddress.StartsWith("tag://")) { - tag = tag, - type = dnsType, - server = dnsHost, - Interface = dnsType == "dhcp" ? dnsHost : null, - server_port = dnsPort, - path = dnsPath - }); + tag = localDnsAddress.Substring(6); - dns4Sbox.rules.Insert(0, new() + 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 { - server = tag, - clash_mode = ERuleMode.Direct.ToString() - }); + 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", From 9229b491c50332e01e9c714259de9b4f0b7b7d7d Mon Sep 17 00:00:00 2001 From: DHR60 Date: Tue, 1 Jul 2025 21:45:01 +0800 Subject: [PATCH 25/54] Adds sing-box DomainStrategy support --- .../CoreConfig/CoreConfigSingboxService.cs | 38 +++++++++++++------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs index 380880a5..80b89654 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs @@ -1309,22 +1309,24 @@ public class CoreConfigSingboxService clash_mode = ERuleMode.Global.ToString() }); - if (!(_config.Inbound.First().RouteOnly || _config.TunModeItem.EnableTun)) + var domainStrategy = _config.RoutingBasicItem.DomainStrategy4Singbox.IsNullOrEmpty() ? null : _config.RoutingBasicItem.DomainStrategy4Singbox; + var defaultRouting = await ConfigHandler.GetDefaultRouting(_config); + if (defaultRouting.DomainStrategy4Singbox.IsNotEmpty()) { - var domainStrategy = _config.RoutingBasicItem.DomainStrategy4Singbox.IsNullOrEmpty() ? null : _config.RoutingBasicItem.DomainStrategy4Singbox; - var defaultRouting = await ConfigHandler.GetDefaultRouting(_config); - if (defaultRouting.DomainStrategy4Singbox.IsNotEmpty()) - { - domainStrategy = defaultRouting.DomainStrategy4Singbox; - } - singboxConfig.route.rules.Add(new() - { - action = "resolve", - strategy = domainStrategy - }); + domainStrategy = defaultRouting.DomainStrategy4Singbox; + } + var resolveRule = new Rule4Sbox + { + action = "resolve", + strategy = domainStrategy + }; + if (_config.RoutingBasicItem.DomainStrategy == "IPOnDemand") + { + singboxConfig.route.rules.Add(resolveRule); } var routing = await ConfigHandler.GetDefaultRouting(_config); + var ipRules = new List(); if (routing != null) { var rules = JsonUtils.Deserialize>(routing.RuleSet); @@ -1333,9 +1335,21 @@ public class CoreConfigSingboxService if (item.Enabled) { await GenRoutingUserRule(item, singboxConfig); + if (item.Ip != null && item.Ip.Count > 0) + { + ipRules.Add(item); + } } } } + if (_config.RoutingBasicItem.DomainStrategy == "IPIfNonMatch") + { + singboxConfig.route.rules.Add(resolveRule); + foreach (var item in ipRules) + { + await GenRoutingUserRule(item, singboxConfig); + } + } } catch (Exception ex) { From 00505c7c1709f0b4848c1ba2000f581c03b36f45 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Tue, 1 Jul 2025 23:56:45 +0800 Subject: [PATCH 26/54] Deletes Duplicate Rules --- v2rayN/ServiceLib/Sample/tun_singbox_dns | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/v2rayN/ServiceLib/Sample/tun_singbox_dns b/v2rayN/ServiceLib/Sample/tun_singbox_dns index 39bf43ac..3dd55eef 100644 --- a/v2rayN/ServiceLib/Sample/tun_singbox_dns +++ b/v2rayN/ServiceLib/Sample/tun_singbox_dns @@ -23,8 +23,7 @@ }, { "rule_set": [ - "geosite-cn", - "geosite-geolocation-cn" + "geosite-cn" ], "server": "local", "strategy": "prefer_ipv4" @@ -32,4 +31,4 @@ ], "final": "remote", "strategy": "prefer_ipv4" -} +} \ No newline at end of file From 2e283eb00e05ef3cf33aca47607c934cea1b4bbb Mon Sep 17 00:00:00 2001 From: DHR60 Date: Mon, 7 Jul 2025 11:38:06 +0800 Subject: [PATCH 27/54] Adds anytls reality support --- v2rayN/ServiceLib/Handler/Fmt/AnytlsFmt.cs | 9 ++------- v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs | 1 + v2rayN/v2rayN/Views/AddServerWindow.xaml.cs | 1 + 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/v2rayN/ServiceLib/Handler/Fmt/AnytlsFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/AnytlsFmt.cs index bad36b19..3f1d40dc 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/AnytlsFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/AnytlsFmt.cs @@ -24,8 +24,7 @@ public class AnytlsFmt : BaseFmt item.Id = rawUserInfo; var query = Utils.ParseQueryString(parsedUrl.Query); - item.Sni = query["sni"] ?? Global.None; - item.AllowInsecure = (query["insecure"] ?? "") == "1" ? "true" : "false"; + _ = ResolveStdTransport(query, ref item); return item; } @@ -43,11 +42,7 @@ public class AnytlsFmt : BaseFmt } var pw = item.Id; var dicQuery = new Dictionary(); - if (item.Sni.IsNotEmpty()) - { - dicQuery.Add("sni", item.Sni); - } - dicQuery.Add("insecure", item.AllowInsecure.ToLower() == "true" ? "1" : "0"); + _ = GetStdTransport(item, Global.None, ref dicQuery); return ToUri(EConfigType.Anytls, item.Address, item.Port, pw, dicQuery, remark); } diff --git a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs index 15cbf7ec..9d24175f 100644 --- a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs @@ -105,6 +105,7 @@ public partial class AddServerWindow : WindowBase case EConfigType.Anytls: gridAnytls.IsVisible = true; + lstStreamSecurity.Add(Global.StreamSecurityReality); cmbCoreType.IsEnabled = false; break; } diff --git a/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs b/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs index 837d5e9a..4fa32e52 100644 --- a/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs @@ -99,6 +99,7 @@ public partial class AddServerWindow case EConfigType.Anytls: gridAnytls.Visibility = Visibility.Visible; cmbCoreType.IsEnabled = false; + lstStreamSecurity.Add(Global.StreamSecurityReality); break; } cmbStreamSecurity.ItemsSource = lstStreamSecurity; From 1945d9b798ef4256848b10250312d271f7eae439 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Mon, 28 Jul 2025 23:52:36 +0800 Subject: [PATCH 28/54] Fixes --- .../Services/CoreConfig/CoreConfigV2rayService.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs index edf85686..60b4df3e 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs @@ -120,7 +120,7 @@ public class CoreConfigV2rayService { continue; } - if (it.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC) + if (it.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls) { continue; } @@ -640,7 +640,8 @@ public class CoreConfigV2rayService if (node == null || node.ConfigType == EConfigType.Custom || node.ConfigType == EConfigType.Hysteria2 - || node.ConfigType == EConfigType.TUIC) + || node.ConfigType == EConfigType.TUIC + || node.ConfigType == EConfigType.Anytls) { return Global.ProxyTag; } @@ -1222,6 +1223,7 @@ 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); @@ -1233,6 +1235,7 @@ 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); From c5f222bf58385f5622b2f1a84066d5bdbcf47dce Mon Sep 17 00:00:00 2001 From: DHR60 Date: Tue, 29 Jul 2025 07:32:02 +0800 Subject: [PATCH 29/54] Updates sing-box documentation link --- v2rayN/v2rayN.Desktop/Views/RoutingSettingWindow.axaml.cs | 2 +- v2rayN/v2rayN/Views/AddServerWindow.xaml.cs | 1 + v2rayN/v2rayN/Views/RoutingSettingWindow.xaml.cs | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/v2rayN/v2rayN.Desktop/Views/RoutingSettingWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/RoutingSettingWindow.axaml.cs index d9095135..53fba00b 100644 --- a/v2rayN/v2rayN.Desktop/Views/RoutingSettingWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/RoutingSettingWindow.axaml.cs @@ -117,7 +117,7 @@ public partial class RoutingSettingWindow : WindowBase private void linkdomainStrategy4Singbox_Click(object? sender, RoutedEventArgs e) { - ProcUtils.ProcessStart("https://sing-box.sagernet.org/zh/configuration/shared/listen/#domain_strategy"); + ProcUtils.ProcessStart("https://sing-box.sagernet.org/zh/configuration/route/rule_action/#strategy"); } private void btnCancel_Click(object? sender, RoutedEventArgs e) diff --git a/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs b/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs index 4fa32e52..b06002a9 100644 --- a/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs @@ -96,6 +96,7 @@ public partial class AddServerWindow gridTls.Visibility = Visibility.Collapsed; break; + case EConfigType.Anytls: gridAnytls.Visibility = Visibility.Visible; cmbCoreType.IsEnabled = false; diff --git a/v2rayN/v2rayN/Views/RoutingSettingWindow.xaml.cs b/v2rayN/v2rayN/Views/RoutingSettingWindow.xaml.cs index 5781dcba..0c7f1518 100644 --- a/v2rayN/v2rayN/Views/RoutingSettingWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/RoutingSettingWindow.xaml.cs @@ -122,7 +122,7 @@ public partial class RoutingSettingWindow private void linkdomainStrategy4Singbox_Click(object sender, RoutedEventArgs e) { - ProcUtils.ProcessStart("https://sing-box.sagernet.org/zh/configuration/shared/listen/#domain_strategy"); + ProcUtils.ProcessStart("https://sing-box.sagernet.org/zh/configuration/route/rule_action/#strategy"); } private void btnCancel_Click(object sender, System.Windows.RoutedEventArgs e) From 47779e433d3d8663d765a1de1a0d9ec40b70b0c1 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Tue, 29 Jul 2025 07:34:04 +0800 Subject: [PATCH 30/54] Updates translations --- v2rayN/ServiceLib/Resx/ResUI.hu.resx | 2 +- v2rayN/ServiceLib/Resx/ResUI.ru.resx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/v2rayN/ServiceLib/Resx/ResUI.hu.resx b/v2rayN/ServiceLib/Resx/ResUI.hu.resx index 7633dd08..0fc30153 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.hu.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.hu.resx @@ -1402,6 +1402,6 @@ Mldsa65Verify - Add [Anytls] Configuration + [Anytls] konfiguráció hozzáadása \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayN/ServiceLib/Resx/ResUI.ru.resx index 37219652..25f17e0d 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.ru.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.ru.resx @@ -1402,6 +1402,6 @@ Mldsa65Verify - Add [Anytls] Configuration + Добавить сервер [Anytls] \ No newline at end of file From d2169c914ab161387045bb000556866eb1acd911 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Wed, 23 Jul 2025 23:20:54 +0800 Subject: [PATCH 31/54] Add Minimal Core Config --- v2rayN/ServiceLib/Enums/EInboundProtocol.cs | 1 + .../CoreConfig/CoreConfigSingboxService.cs | 86 ++++++++++++++++++ .../CoreConfig/CoreConfigV2rayService.cs | 65 ++++++++++++++ .../Minimal/CoreConfigBrookService.cs | 50 +++++++++++ .../Minimal/CoreConfigHy2Service.cs | 88 +++++++++++++++++++ .../Minimal/CoreConfigJuicityService.cs | 72 +++++++++++++++ .../Minimal/CoreConfigNaiveService.cs | 53 +++++++++++ .../Minimal/CoreConfigShadowquicService.cs | 81 +++++++++++++++++ .../Minimal/CoreConfigTuicService.cs | 75 ++++++++++++++++ 9 files changed, 571 insertions(+) create mode 100644 v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigBrookService.cs create mode 100644 v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigHy2Service.cs create mode 100644 v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigJuicityService.cs create mode 100644 v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigNaiveService.cs create mode 100644 v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigShadowquicService.cs create mode 100644 v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigTuicService.cs diff --git a/v2rayN/ServiceLib/Enums/EInboundProtocol.cs b/v2rayN/ServiceLib/Enums/EInboundProtocol.cs index 768a428b..8c75c65f 100644 --- a/v2rayN/ServiceLib/Enums/EInboundProtocol.cs +++ b/v2rayN/ServiceLib/Enums/EInboundProtocol.cs @@ -9,5 +9,6 @@ public enum EInboundProtocol api, api2, mixed, + split, speedtest = 21 } diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs index 80b89654..14a531d4 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs @@ -513,6 +513,92 @@ public class CoreConfigSingboxService } } + public async Task GeneratePureEndpointConfig(ProfileItem node) + { + var ret = new RetResult(); + try + { + if (node == null + || node.Port <= 0) + { + ret.Msg = ResUI.CheckServerSettings; + return ret; + } + if (node.GetNetwork() is nameof(ETransport.kcp) or nameof(ETransport.xhttp)) + { + ret.Msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}"; + return ret; + } + + ret.Msg = ResUI.InitialConfiguration; + + var result = EmbedUtils.GetEmbedText(Global.SingboxSampleClient); + if (result.IsNullOrEmpty()) + { + ret.Msg = ResUI.FailedGetDefaultConfiguration; + return ret; + } + + var singboxConfig = JsonUtils.Deserialize(result); + if (singboxConfig == null) + { + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return ret; + } + + await GenLog(singboxConfig); + + var inbound = new Inbound4Sbox() + { + type = EInboundProtocol.mixed.ToString(), + tag = EInboundProtocol.socks.ToString(), + listen = Global.Loopback, + listen_port = AppHandler.Instance.GetLocalPort(EInboundProtocol.split) + }; + singboxConfig.inbounds = new() { inbound }; + + if (node.ConfigType == EConfigType.WireGuard) + { + singboxConfig.outbounds.RemoveAt(0); + var endpoints = new Endpoints4Sbox(); + await GenEndpoint(node, endpoints); + endpoints.tag = Global.ProxyTag; + singboxConfig.endpoints = new() { endpoints }; + } + else + { + await GenOutbound(node, singboxConfig.outbounds.First()); + } + + if (singboxConfig.endpoints == null) + { + singboxConfig.outbounds = new() { JsonUtils.DeepCopy(singboxConfig.outbounds.First()) }; + } + else + { + singboxConfig.outbounds.Clear(); + } + + await GenMoreOutbounds(node, singboxConfig); + + ret.Msg = string.Format(ResUI.SuccessfulConfiguration, ""); + ret.Success = true; + var config = JsonNode.Parse(JsonUtils.Serialize(singboxConfig)).AsObject(); + + config.Remove("route"); + + ret.Data = config.ToJsonString(new() { WriteIndented = true }); + + return ret; + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return ret; + } + } + #endregion public gen function #region private gen function diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs index 60b4df3e..c15c05ff 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs @@ -416,6 +416,71 @@ public class CoreConfigV2rayService } } + public async Task GeneratePureEndpointConfig(ProfileItem node) + { + var ret = new RetResult(); + try + { + if (node == null + || node.Port <= 0) + { + ret.Msg = ResUI.CheckServerSettings; + return ret; + } + + if (node.GetNetwork() is nameof(ETransport.quic)) + { + ret.Msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}"; + return ret; + } + + ret.Msg = ResUI.InitialConfiguration; + + var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient); + if (result.IsNullOrEmpty()) + { + ret.Msg = ResUI.FailedGetDefaultConfiguration; + return ret; + } + + var v2rayConfig = JsonUtils.Deserialize(result); + if (v2rayConfig == null) + { + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return ret; + } + + await GenLog(v2rayConfig); + + var inbound = GetInbound(_config.Inbound.First(), EInboundProtocol.split, true); + + v2rayConfig.inbounds = new() { inbound }; + + await GenOutbound(node, v2rayConfig.outbounds.First()); + + v2rayConfig.outbounds = new() { JsonUtils.DeepCopy(v2rayConfig.outbounds.First()) }; + + await GenMoreOutbounds(node, v2rayConfig); + + ret.Msg = string.Format(ResUI.SuccessfulConfiguration, ""); + ret.Success = true; + + var config = JsonNode.Parse(JsonUtils.Serialize(v2rayConfig)).AsObject(); + + config.Remove("routing"); + + ret.Data = config.ToJsonString(new() { WriteIndented = true }); + + return ret; + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return ret; + } + } + #endregion public gen function #region private gen function diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigBrookService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigBrookService.cs new file mode 100644 index 00000000..39e2bc5c --- /dev/null +++ b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigBrookService.cs @@ -0,0 +1,50 @@ +namespace ServiceLib.Services.CoreConfig.Minimal; +public class CoreConfigBrookService +{ + private Config _config; + private static readonly string _tag = "CoreConfigBrookService"; + + public CoreConfigBrookService(Config config) + { + _config = config; + } + + public async Task GeneratePureEndpointConfig(ProfileItem node) + { + var ret = new RetResult(); + try + { + if (node == null + || node.Port <= 0) + { + ret.Msg = ResUI.CheckServerSettings; + return ret; + } + + //if (node.ConfigType != EConfigType.Brook) + //{ + // ret.Msg = ResUI.Incorrectconfiguration + $" - {node.ConfigType}"; + // return ret; + //} + + var processArgs = "client"; + + // inbound + processArgs += " --socks5 " + Global.Loopback + ":" + AppHandler.Instance.GetLocalPort(EInboundProtocol.split).ToString(); + + // outbound + processArgs += " --server " + node.Address + ":" + node.Port; + processArgs += " --password " + node.Id; + + ret.Data = processArgs; + + return await Task.FromResult(ret); + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return await Task.FromResult(ret); + } + } +} diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigHy2Service.cs b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigHy2Service.cs new file mode 100644 index 00000000..25026dba --- /dev/null +++ b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigHy2Service.cs @@ -0,0 +1,88 @@ +using System.Text.Json.Nodes; + +namespace ServiceLib.Services.CoreConfig.Minimal; +public class CoreConfigHy2Service +{ + private Config _config; + private static readonly string _tag = "CoreConfigHy2Service"; + + public CoreConfigHy2Service(Config config) + { + _config = config; + } + + public async Task GeneratePureEndpointConfig(ProfileItem node) + { + var ret = new RetResult(); + try + { + if (node == null + || node.Port <= 0) + { + ret.Msg = ResUI.CheckServerSettings; + return ret; + } + + if (node.ConfigType != EConfigType.Hysteria2) + { + ret.Msg = ResUI.Incorrectconfiguration + $" - {node.ConfigType}"; + return ret; + } + + var configJsonNode = new JsonObject(); + + // inbound + configJsonNode["socks5"] = new JsonObject + { + ["listen"] = Global.Loopback + ":" + AppHandler.Instance.GetLocalPort(EInboundProtocol.split).ToString() + }; + + // outbound + var port = string.Empty; + if (node.Ports.IsNotEmpty()) + { + port = node.Ports.Replace(':', '-'); + if (_config.HysteriaItem.HopInterval > 0) + { + configJsonNode["transport"] = new JsonObject + { + ["udp"] = new JsonObject + { + ["hopInterval"] = $"{_config.HysteriaItem.HopInterval}s" + } + }; + } + } + else + { + port = node.Port.ToString(); + } + configJsonNode["server"] = node.Address + ":" + port; + configJsonNode["auth"] = node.Id; + + var bandwidthObject = new JsonObject(); + if (_config.HysteriaItem.UpMbps > 0) + { + bandwidthObject["up"] = $"{_config.HysteriaItem.UpMbps} mbps"; + } + if (_config.HysteriaItem.DownMbps > 0) + { + bandwidthObject["down"] = $"{_config.HysteriaItem.DownMbps} mbps"; + } + if (bandwidthObject.Count > 0) + { + configJsonNode["bandwidth"] = bandwidthObject; + } + + ret.Data = configJsonNode.ToJsonString(new() { WriteIndented = true }); + + return await Task.FromResult(ret); + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return await Task.FromResult(ret); + } + } +} diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigJuicityService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigJuicityService.cs new file mode 100644 index 00000000..14b6083f --- /dev/null +++ b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigJuicityService.cs @@ -0,0 +1,72 @@ +using System.Text.Json.Nodes; + +namespace ServiceLib.Services.CoreConfig.Minimal; +public class CoreConfigJuicityService +{ + private Config _config; + private static readonly string _tag = "CoreConfigJuicityService"; + + public CoreConfigJuicityService(Config config) + { + _config = config; + } + + public async Task GeneratePureEndpointConfig(ProfileItem node) + { + var ret = new RetResult(); + try + { + if (node == null + || node.Port <= 0) + { + ret.Msg = ResUI.CheckServerSettings; + return ret; + } + + //if (node.ConfigType != EConfigType.Juicity) + //{ + // ret.Msg = ResUI.Incorrectconfiguration + $" - {node.ConfigType}"; + // return ret; + //} + + var configJsonNode = new JsonObject(); + + // log + var logLevel = string.Empty; + switch (_config.CoreBasicItem.Loglevel) + { + case "warning": + logLevel = "warn"; + break; + default: + logLevel = _config.CoreBasicItem.Loglevel; + break; + } + configJsonNode["log_level"] = logLevel; + + // inbound + configJsonNode["listen"] = ":" + AppHandler.Instance.GetLocalPort(EInboundProtocol.split).ToString(); + + // outbound + configJsonNode["server"] = node.Address + ":" + node.Port; + configJsonNode["uuid"] = node.Id; + configJsonNode["password"] = node.Security; + if (node.Sni.IsNotEmpty()) + { + configJsonNode["sni"] = node.Sni; + } + configJsonNode["allow_insecure"] = node.AllowInsecure == "true"; + configJsonNode["congestion_control"] = "bbr"; + + ret.Data = configJsonNode.ToJsonString(new() { WriteIndented = true }); + + return await Task.FromResult(ret); + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return await Task.FromResult(ret); + } + } +} diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigNaiveService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigNaiveService.cs new file mode 100644 index 00000000..8307d5b0 --- /dev/null +++ b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigNaiveService.cs @@ -0,0 +1,53 @@ +using System.Text.Json.Nodes; + +namespace ServiceLib.Services.CoreConfig.Minimal; +public class CoreConfigNaiveService +{ + private Config _config; + private static readonly string _tag = "CoreConfigNaiveService"; + + public CoreConfigNaiveService(Config config) + { + _config = config; + } + + public async Task GeneratePureEndpointConfig(ProfileItem node) + { + var ret = new RetResult(); + try + { + if (node == null + || node.Port <= 0) + { + ret.Msg = ResUI.CheckServerSettings; + return ret; + } + + // TODO: EConfigType.Naive + + //if (node.ConfigType != EConfigType.Naive) + //{ + // ret.Msg = ResUI.Incorrectconfiguration + $" - {node.ConfigType}"; + // return ret; + //} + + var configJsonNode = new JsonObject(); + + // inbound + configJsonNode["listen"] = Global.SocksProtocol + Global.Loopback + ":" + AppHandler.Instance.GetLocalPort(EInboundProtocol.split).ToString(); + + // outbound + configJsonNode["proxy"] = (node.Network == "quic" ? "quic://" : Global.HttpsProtocol) + node.Id + "@" + node.Address + ":" + node.Port; + + ret.Data = configJsonNode.ToJsonString(new() { WriteIndented = true }); + + return await Task.FromResult(ret); + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return await Task.FromResult(ret); + } + } +} diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigShadowquicService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigShadowquicService.cs new file mode 100644 index 00000000..3ca89444 --- /dev/null +++ b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigShadowquicService.cs @@ -0,0 +1,81 @@ +namespace ServiceLib.Services.CoreConfig.Minimal; +public class CoreConfigShadowquicService +{ + private Config _config; + private static readonly string _tag = "CoreConfigShadowquicService"; + + public CoreConfigShadowquicService(Config config) + { + _config = config; + } + + public async Task GeneratePureEndpointConfig(ProfileItem node) + { + var ret = new RetResult(); + try + { + if (node == null + || node.Port <= 0) + { + ret.Msg = ResUI.CheckServerSettings; + return ret; + } + + //if (node.ConfigType != EConfigType.Shadowquic) + //{ + // ret.Msg = ResUI.Incorrectconfiguration + $" - {node.ConfigType}"; + // return ret; + //} + + var configYamlNode = new Dictionary(); + + // log + var logLevel = string.Empty; + switch (_config.CoreBasicItem.Loglevel) + { + case "warning": + logLevel = "warn"; + break; + default: + logLevel = _config.CoreBasicItem.Loglevel; + break; + } + configYamlNode["log-level"] = logLevel; + + // inbound + var inboundNode = new Dictionary + { + ["type"] = "socks5", + ["listen"] = Global.Loopback + ":" + AppHandler.Instance.GetLocalPort(EInboundProtocol.split).ToString() + }; + configYamlNode["inbound"] = inboundNode; + + // outbound + var outboundNode = new Dictionary + { + ["type"] = "shadowquic", + ["addr"] = node.Address + ":" + node.Port, + ["password"] = node.Id, + ["username"] = node.Security, + ["alpn"] = new List { "h3" }, + ["congestion-control"] = "bbr", + ["zero-rtt"] = true + }; + if (node.Sni.IsNotEmpty()) + { + outboundNode["server-name"] = node.Sni; + } + configYamlNode["outbound"] = outboundNode; + + ret.Data = YamlUtils.ToYaml(configYamlNode); + + return await Task.FromResult(ret); + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return await Task.FromResult(ret); + } + } +} diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigTuicService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigTuicService.cs new file mode 100644 index 00000000..3f95bebd --- /dev/null +++ b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigTuicService.cs @@ -0,0 +1,75 @@ +using System.Text.Json.Nodes; + +namespace ServiceLib.Services.CoreConfig.Minimal; +public class CoreConfigTuicService +{ + private Config _config; + private static readonly string _tag = "CoreConfigTuicService"; + + public CoreConfigTuicService(Config config) + { + _config = config; + } + + public async Task GeneratePureEndpointConfig(ProfileItem node) + { + var ret = new RetResult(); + try + { + if (node == null + || node.Port <= 0) + { + ret.Msg = ResUI.CheckServerSettings; + return ret; + } + + if (node.ConfigType != EConfigType.TUIC) + { + ret.Msg = ResUI.Incorrectconfiguration + $" - {node.ConfigType}"; + return ret; + } + + var configJsonNode = new JsonObject(); + + // log + var logLevel = string.Empty; + switch (_config.CoreBasicItem.Loglevel) + { + case "warning": + logLevel = "warn"; + break; + default: + logLevel = _config.CoreBasicItem.Loglevel; + break; + } + configJsonNode["log_level"] = logLevel; + + // inbound + configJsonNode["local"] = new JsonObject + { + ["server"] = Global.Loopback + ":" + AppHandler.Instance.GetLocalPort(EInboundProtocol.split).ToString() + }; + + // outbound + configJsonNode["relay"] = new JsonObject + { + ["server"] = node.Address + ":" + node.Port, + ["uuid"] = node.Id, + ["password"] = node.Security, + ["udp_relay_mode"] = "quic", + ["congestion_control"] = "bbr", + ["alpn"] = new JsonArray { "h3", "spdy/3.1" } + }; + + ret.Data = configJsonNode.ToJsonString(new() { WriteIndented = true }); + + return await Task.FromResult(ret); + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return await Task.FromResult(ret); + } + } +} From edbd8ab54a1432d01bbd12b56d2b45da4e408b9a Mon Sep 17 00:00:00 2001 From: DHR60 Date: Thu, 24 Jul 2025 00:17:26 +0800 Subject: [PATCH 32/54] Add Minimal Config Type --- v2rayN/ServiceLib/Enums/EConfigType.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/v2rayN/ServiceLib/Enums/EConfigType.cs b/v2rayN/ServiceLib/Enums/EConfigType.cs index 6698f962..71878b27 100644 --- a/v2rayN/ServiceLib/Enums/EConfigType.cs +++ b/v2rayN/ServiceLib/Enums/EConfigType.cs @@ -12,5 +12,9 @@ public enum EConfigType TUIC = 8, WireGuard = 9, HTTP = 10, - Anytls = 11 + Anytls = 11, + NaiveProxy = 100, + Juicity = 101, + Brook = 102, + Shadowquic = 103, } From 815a9ce0db78ba23af00e47a29e59d2d2a23d524 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Thu, 24 Jul 2025 10:56:25 +0800 Subject: [PATCH 33/54] Config and GUI --- v2rayN/ServiceLib/Global.cs | 24 ++ v2rayN/ServiceLib/Handler/AppHandler.cs | 11 + v2rayN/ServiceLib/Handler/ConfigHandler.cs | 7 + .../ServiceLib/Handler/CoreConfigHandler.cs | 33 ++ v2rayN/ServiceLib/Models/Config.cs | 1 + v2rayN/ServiceLib/Models/ConfigItems.cs | 8 + .../Minimal/CoreConfigBrookService.cs | 10 +- .../Minimal/CoreConfigJuicityService.cs | 10 +- .../Minimal/CoreConfigShadowquicService.cs | 10 +- .../ViewModels/OptionSettingViewModel.cs | 148 ++++++- v2rayN/v2rayN/Views/OptionSettingWindow.xaml | 398 +++++++++++++----- .../v2rayN/Views/OptionSettingWindow.xaml.cs | 23 + 12 files changed, 561 insertions(+), 122 deletions(-) diff --git a/v2rayN/ServiceLib/Global.cs b/v2rayN/ServiceLib/Global.cs index 4888ab6a..b12e1f50 100644 --- a/v2rayN/ServiceLib/Global.cs +++ b/v2rayN/ServiceLib/Global.cs @@ -278,6 +278,30 @@ public class Global "sing_box" ]; + public static readonly List Hysteria2CoreTypes = + [ + "sing_box", + "Hysteria2" + ]; + + public static readonly List TuicCoreTypes = + [ + "sing_box", + "TUIC" + ]; + + public static readonly List SupportSplitConfigTypes = + [ + EConfigType.VMess, + EConfigType.VLESS, + EConfigType.Shadowsocks, + EConfigType.Trojan, + EConfigType.Hysteria2, + EConfigType.TUIC, + EConfigType.WireGuard, + EConfigType.SOCKS, + ]; + public static readonly List DomainStrategies = [ "AsIs", diff --git a/v2rayN/ServiceLib/Handler/AppHandler.cs b/v2rayN/ServiceLib/Handler/AppHandler.cs index ad9a4029..f3b7e449 100644 --- a/v2rayN/ServiceLib/Handler/AppHandler.cs +++ b/v2rayN/ServiceLib/Handler/AppHandler.cs @@ -235,5 +235,16 @@ public sealed class AppHandler return item?.CoreType ?? ECoreType.Xray; } + public ECoreType GetSplitCoreType(ProfileItem profileItem, EConfigType eConfigType) + { + if (profileItem?.CoreType != null) + { + return (ECoreType)profileItem.CoreType; + } + + var item = _config.SplitCoreItem.SplitCoreTypes?.FirstOrDefault(it => it.ConfigType == eConfigType); + return item?.CoreType ?? ECoreType.Xray; + } + #endregion Core Type } diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs index 1f8bd079..16b5b676 100644 --- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -165,6 +165,13 @@ public class ConfigHandler config.SystemProxyItem.SystemProxyExceptions = Utils.IsWindows() ? Global.SystemProxyExceptionsWindows : Global.SystemProxyExceptionsLinux; } + config.SplitCoreItem ??= new() + { + EnableSplitCore = false, + SplitCoreTypes = new List(), + RouteCoreType = ECoreType.Xray + }; + return config; } diff --git a/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs b/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs index 40c5eacf..0ee2e227 100644 --- a/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs @@ -1,3 +1,5 @@ +using ServiceLib.Services.CoreConfig.Minimal; + namespace ServiceLib.Handler; /// @@ -41,6 +43,37 @@ public class CoreConfigHandler return result; } + public static async Task GeneratePureEndpointConfig(ProfileItem node, string? fileName) + { + var config = AppHandler.Instance.Config; + var result = new RetResult(); + + var coreType = AppHandler.Instance.GetSplitCoreType(node, node.ConfigType); + + result = coreType switch + { + ECoreType.sing_box => await new CoreConfigSingboxService(config).GeneratePureEndpointConfig(node), + ECoreType.Xray => await new CoreConfigV2rayService(config).GeneratePureEndpointConfig(node), + ECoreType.hysteria2 => await new CoreConfigHy2Service(config).GeneratePureEndpointConfig(node), + ECoreType.naiveproxy => await new CoreConfigNaiveService(config).GeneratePureEndpointConfig(node), + ECoreType.tuic => await new CoreConfigTuicService(config).GeneratePureEndpointConfig(node), + ECoreType.juicity => await new CoreConfigJuicityService(config).GeneratePureEndpointConfig(node), + ECoreType.brook => await new CoreConfigBrookService(config).GeneratePureEndpointConfig(node), + ECoreType.shadowquic => await new CoreConfigShadowquicService(config).GeneratePureEndpointConfig(node), + _ => throw new NotImplementedException(), + }; + + if (result.Success != true) + { + return result; + } + if (fileName.IsNotEmpty() && result.Data != null) + { + await File.WriteAllTextAsync(fileName, result.Data.ToString()); + } + return result; + } + private static async Task GenerateClientCustomConfig(ProfileItem node, string? fileName) { var ret = new RetResult(); diff --git a/v2rayN/ServiceLib/Models/Config.cs b/v2rayN/ServiceLib/Models/Config.cs index 15996608..c6536f15 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 SplitCoreItem SplitCoreItem { get; set; } #endregion other entities } diff --git a/v2rayN/ServiceLib/Models/ConfigItems.cs b/v2rayN/ServiceLib/Models/ConfigItems.cs index c6254b9f..a8aa8244 100644 --- a/v2rayN/ServiceLib/Models/ConfigItems.cs +++ b/v2rayN/ServiceLib/Models/ConfigItems.cs @@ -138,6 +138,14 @@ public class CoreTypeItem public ECoreType CoreType { get; set; } } +[Serializable] +public class SplitCoreItem +{ + public bool EnableSplitCore { get; set; } + public List SplitCoreTypes { get; set; } + public ECoreType RouteCoreType { get; set; } +} + [Serializable] public class TunModeItem { diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigBrookService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigBrookService.cs index 39e2bc5c..b3b4cef7 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigBrookService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigBrookService.cs @@ -21,11 +21,11 @@ public class CoreConfigBrookService return ret; } - //if (node.ConfigType != EConfigType.Brook) - //{ - // ret.Msg = ResUI.Incorrectconfiguration + $" - {node.ConfigType}"; - // return ret; - //} + if (node.ConfigType != EConfigType.Brook) + { + ret.Msg = ResUI.Incorrectconfiguration + $" - {node.ConfigType}"; + return ret; + } var processArgs = "client"; diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigJuicityService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigJuicityService.cs index 14b6083f..a08b022b 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigJuicityService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigJuicityService.cs @@ -23,11 +23,11 @@ public class CoreConfigJuicityService return ret; } - //if (node.ConfigType != EConfigType.Juicity) - //{ - // ret.Msg = ResUI.Incorrectconfiguration + $" - {node.ConfigType}"; - // return ret; - //} + if (node.ConfigType != EConfigType.Juicity) + { + ret.Msg = ResUI.Incorrectconfiguration + $" - {node.ConfigType}"; + return ret; + } var configJsonNode = new JsonObject(); diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigShadowquicService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigShadowquicService.cs index 3ca89444..1d224210 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigShadowquicService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigShadowquicService.cs @@ -21,11 +21,11 @@ public class CoreConfigShadowquicService return ret; } - //if (node.ConfigType != EConfigType.Shadowquic) - //{ - // ret.Msg = ResUI.Incorrectconfiguration + $" - {node.ConfigType}"; - // return ret; - //} + if (node.ConfigType != EConfigType.Shadowquic) + { + ret.Msg = ResUI.Incorrectconfiguration + $" - {node.ConfigType}"; + return ret; + } var configYamlNode = new Dictionary(); diff --git a/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs b/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs index 57d8cac7..cf2252d7 100644 --- a/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs @@ -1,6 +1,7 @@ using System.Reactive; using ReactiveUI; using ReactiveUI.Fody.Helpers; +using ServiceLib.Models; namespace ServiceLib.ViewModels; @@ -104,6 +105,21 @@ public class OptionSettingViewModel : MyReactiveObject #endregion CoreType + #region SplitCoreType + + [Reactive] public bool EnableSplitCore { get; set; } + [Reactive] public string SplitCoreType1 { get; set; } + [Reactive] public string SplitCoreType3 { get; set; } + [Reactive] public string SplitCoreType4 { get; set; } + [Reactive] public string SplitCoreType5 { get; set; } + [Reactive] public string SplitCoreType6 { get; set; } + [Reactive] public string SplitCoreType7 { get; set; } + [Reactive] public string SplitCoreType8 { get; set; } + [Reactive] public string SplitCoreType9 { get; set; } + [Reactive] public string RouteSplitCoreType { get; set; } + + #endregion SplitCoreType + public ReactiveCommand SaveCmd { get; } public OptionSettingViewModel(Func>? updateView) @@ -210,14 +226,13 @@ public class OptionSettingViewModel : MyReactiveObject #endregion Tun mode await InitCoreType(); + + await InitSplitCoreItem(); } private async Task InitCoreType() { - if (_config.CoreTypeItem == null) - { - _config.CoreTypeItem = new List(); - } + _config.CoreTypeItem ??= new List(); foreach (EConfigType it in Enum.GetValues(typeof(EConfigType))) { @@ -232,6 +247,7 @@ public class OptionSettingViewModel : MyReactiveObject CoreType = ECoreType.Xray }); } + _config.CoreTypeItem.ForEach(it => { var type = it.CoreType.ToString(); @@ -269,6 +285,87 @@ public class OptionSettingViewModel : MyReactiveObject await Task.CompletedTask; } + private async Task InitSplitCoreItem() + { + _config.SplitCoreItem ??= new() + { + EnableSplitCore = false, + SplitCoreTypes = new List(), + RouteCoreType = ECoreType.Xray + }; + + foreach (EConfigType it in Enum.GetValues(typeof(EConfigType))) + { + if (_config.SplitCoreItem.SplitCoreTypes.FindIndex(t => t.ConfigType == it) >= 0) + { + continue; + } + + if (it is EConfigType.Hysteria2 or EConfigType.TUIC) + { + _config.SplitCoreItem.SplitCoreTypes.Add(new CoreTypeItem() + { + ConfigType = it, + CoreType = ECoreType.sing_box + }); + continue; + } + if (it is EConfigType.Custom) + { + continue; + } + + _config.SplitCoreItem.SplitCoreTypes.Add(new CoreTypeItem() + { + ConfigType = it, + CoreType = ECoreType.Xray + }); + } + + EnableSplitCore = _config.SplitCoreItem.EnableSplitCore; + RouteSplitCoreType = _config.SplitCoreItem.RouteCoreType.ToString(); + + _config.SplitCoreItem.SplitCoreTypes.ForEach(it => + { + var type = it.CoreType.ToString(); + switch ((int)it.ConfigType) + { + case 1: + SplitCoreType1 = type; + break; + + case 3: + SplitCoreType3 = type; + break; + + case 4: + SplitCoreType4 = type; + break; + + case 5: + SplitCoreType5 = type; + break; + + case 6: + SplitCoreType6 = type; + break; + + case 7: + SplitCoreType7 = type; + break; + + case 8: + SplitCoreType8 = type; + break; + + case 9: + SplitCoreType9 = type; + break; + } + }); + await Task.CompletedTask; + } + private async Task SaveSettingAsync() { if (localPort.ToString().IsNullOrEmpty() || !Utils.IsNumeric(localPort.ToString()) @@ -362,6 +459,7 @@ public class OptionSettingViewModel : MyReactiveObject //coreType await SaveCoreType(); + await SaveSplitCoreType(); if (await ConfigHandler.SaveConfig(_config) == 0) { @@ -420,4 +518,46 @@ public class OptionSettingViewModel : MyReactiveObject } await Task.CompletedTask; } + + private async Task SaveSplitCoreType() + { + for (int k = 1; k <= _config.SplitCoreItem.SplitCoreTypes.Count; k++) + { + var item = _config.SplitCoreItem.SplitCoreTypes[k - 1]; + var type = string.Empty; + switch ((int)item.ConfigType) + { + case 1: + type = SplitCoreType1; + break; + case 3: + type = SplitCoreType3; + break; + case 4: + type = SplitCoreType4; + break; + case 5: + type = SplitCoreType5; + break; + case 6: + type = SplitCoreType6; + break; + case 7: + type = SplitCoreType7; + break; + case 8: + type = SplitCoreType8; + break; + case 9: + type = SplitCoreType9; + break; + default: + continue; + } + item.CoreType = (ECoreType)Enum.Parse(typeof(ECoreType), type); + } + _config.SplitCoreItem.RouteCoreType = (ECoreType)Enum.Parse(typeof(ECoreType), RouteSplitCoreType); + _config.SplitCoreItem.EnableSplitCore = EnableSplitCore; + await Task.CompletedTask; + } } diff --git a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml index 890352fc..07d01f53 100644 --- a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml +++ b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml @@ -1110,122 +1110,314 @@ - - - - - - - - - + Margin="{StaticResource Margin8}"> + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + + + Margin="{StaticResource Margin8}"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs index c790e25d..889016bb 100644 --- a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs @@ -43,6 +43,17 @@ public partial class OptionSettingWindow cmbCoreType6.ItemsSource = Global.CoreTypes; cmbCoreType9.ItemsSource = Global.CoreTypes; + cmbCoreSplitRouteType.ItemsSource = Global.CoreTypes; + + cmbCoreSplitType1.ItemsSource = Global.CoreTypes; + cmbCoreSplitType3.ItemsSource = Global.CoreTypes; + cmbCoreSplitType4.ItemsSource = Global.CoreTypes; + cmbCoreSplitType5.ItemsSource = Global.CoreTypes; + cmbCoreSplitType6.ItemsSource = Global.CoreTypes; + cmbCoreSplitType7.ItemsSource = Global.Hysteria2CoreTypes; + cmbCoreSplitType8.ItemsSource = Global.TuicCoreTypes; + cmbCoreSplitType9.ItemsSource = Global.CoreTypes; + cmbMixedConcurrencyCount.ItemsSource = Enumerable.Range(2, 7).ToList(); cmbSpeedTestTimeout.ItemsSource = Enumerable.Range(2, 5).Select(i => i * 5).ToList(); cmbSpeedTestUrl.ItemsSource = Global.SpeedTestUrls; @@ -131,6 +142,18 @@ public partial class OptionSettingWindow this.Bind(ViewModel, vm => vm.CoreType6, v => v.cmbCoreType6.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CoreType9, v => v.cmbCoreType9.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.EnableSplitCore, v => v.togCoreSplit.IsChecked).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.RouteSplitCoreType, v => v.cmbCoreSplitRouteType.Text).DisposeWith(disposables); + + this.Bind(ViewModel, vm => vm.SplitCoreType1, v => v.cmbCoreSplitType1.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SplitCoreType3, v => v.cmbCoreSplitType3.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SplitCoreType4, v => v.cmbCoreSplitType4.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SplitCoreType5, v => v.cmbCoreSplitType5.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SplitCoreType6, v => v.cmbCoreSplitType6.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SplitCoreType7, v => v.cmbCoreSplitType7.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SplitCoreType8, v => v.cmbCoreSplitType8.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SplitCoreType9, v => v.cmbCoreSplitType9.Text).DisposeWith(disposables); + this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables); }); WindowsUtils.SetDarkBorder(this, AppHandler.Instance.Config.UiItem.CurrentTheme); From daf59d01dc98ea6b15a99bcdf9bc7f69c89a5d7e Mon Sep 17 00:00:00 2001 From: DHR60 Date: Thu, 24 Jul 2025 12:48:52 +0800 Subject: [PATCH 34/54] Config Handler --- v2rayN/ServiceLib/Handler/ConfigHandler.cs | 37 ---- v2rayN/ServiceLib/Handler/CoreHandler.cs | 190 ++++++++++++++++----- 2 files changed, 149 insertions(+), 78 deletions(-) diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs index 16b5b676..3d7b51d3 100644 --- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -1197,43 +1197,6 @@ public class ConfigHandler return result; } - /// - /// Get a SOCKS server profile for pre-SOCKS functionality - /// Used when TUN mode is enabled or when a custom config has a pre-SOCKS port - /// - /// Current configuration - /// Server node that might need pre-SOCKS - /// Core type being used - /// A SOCKS profile item or null if not needed - public static async Task GetPreSocksItem(Config config, ProfileItem node, ECoreType coreType) - { - ProfileItem? itemSocks = null; - if (node.ConfigType != EConfigType.Custom && coreType != ECoreType.sing_box && config.TunModeItem.EnableTun) - { - itemSocks = new ProfileItem() - { - CoreType = ECoreType.sing_box, - ConfigType = EConfigType.SOCKS, - Address = Global.Loopback, - Sni = node.Address, //Tun2SocksAddress - Port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks) - }; - } - else if ((node.ConfigType == EConfigType.Custom && node.PreSocksPort > 0)) - { - var preCoreType = config.RunningCoreType = config.TunModeItem.EnableTun ? ECoreType.sing_box : ECoreType.Xray; - itemSocks = new ProfileItem() - { - CoreType = preCoreType, - ConfigType = EConfigType.SOCKS, - Address = Global.Loopback, - Port = node.PreSocksPort.Value, - }; - } - await Task.CompletedTask; - return itemSocks; - } - /// /// Remove servers with invalid test results (timeout) /// Useful for cleaning up subscription lists diff --git a/v2rayN/ServiceLib/Handler/CoreHandler.cs b/v2rayN/ServiceLib/Handler/CoreHandler.cs index f7ad2285..b7a8b4f5 100644 --- a/v2rayN/ServiceLib/Handler/CoreHandler.cs +++ b/v2rayN/ServiceLib/Handler/CoreHandler.cs @@ -1,5 +1,7 @@ using System.Diagnostics; using System.Text; +using ServiceLib.Enums; +using ServiceLib.Models; namespace ServiceLib.Handler; @@ -71,28 +73,24 @@ public class CoreHandler return; } - var fileName = Utils.GetBinConfigPath(Global.CoreConfigFileName); - var result = await CoreConfigHandler.GenerateClientConfig(node, fileName); - if (result.Success != true) + // Create launch context and configure parameters + var context = new CoreLaunchContext(node, _config); + context.AdjustForConfigType(); + context.AdjustForSplitCore(); + + // Start main core + if (!await CoreStart(context)) { - UpdateFunc(true, result.Msg); return; } - UpdateFunc(false, $"{node.GetSummary()}"); - UpdateFunc(false, $"{Utils.GetRuntimeInfo()}"); - UpdateFunc(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"))); - await CoreStop(); - await Task.Delay(100); - - if (Utils.IsWindows() && _config.TunModeItem.EnableTun) + // Start pre-core if needed + if (!await CoreStartPreService(context)) { - await Task.Delay(100); - await WindowsUtils.RemoveTunDevice(); + await CoreStop(); // Clean up main core if pre-core fails + return; } - await CoreStart(node); - await CoreStartPreService(node); if (_process != null) { UpdateFunc(true, $"{node.GetSummary()}"); @@ -181,43 +179,153 @@ public class CoreHandler #region Private - private async Task CoreStart(ProfileItem node) + /// + /// Core launch context that encapsulates all parameters required for launching + /// + private class CoreLaunchContext { - var coreType = _config.RunningCoreType = AppHandler.Instance.GetCoreType(node, node.ConfigType); - var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType); + public ProfileItem Node { get; set; } + public bool SplitCore { get; set; } + public ECoreType CoreType { get; set; } + public ECoreType? PreCoreType { get; set; } + public ECoreType PureEndpointCore { get; set; } + public ECoreType SplitRouteCore { get; set; } + public bool EnableTun { get; set; } + public int PreSocksPort { get; set; } - var displayLog = node.ConfigType != EConfigType.Custom || node.DisplayLog; - var proc = await RunProcess(coreInfo, Global.CoreConfigFileName, displayLog, true); - if (proc is null) + public CoreLaunchContext(ProfileItem node, Config config) { - return; + Node = node; + SplitCore = config.SplitCoreItem.EnableSplitCore; + CoreType = AppHandler.Instance.GetCoreType(node, node.ConfigType); + PureEndpointCore = AppHandler.Instance.GetSplitCoreType(node, node.ConfigType); + SplitRouteCore = config.SplitCoreItem.RouteCoreType; + EnableTun = config.TunModeItem.EnableTun; + PreSocksPort = 0; + PreCoreType = null; } - _process = proc; - } - private async Task CoreStartPreService(ProfileItem node) - { - if (_process != null && !_process.HasExited) + /// + /// Adjust context parameters based on configuration type + /// + public void AdjustForConfigType() { - var coreType = AppHandler.Instance.GetCoreType(node, node.ConfigType); - var itemSocks = await ConfigHandler.GetPreSocksItem(_config, node, coreType); - if (itemSocks != null) + if (Node.ConfigType == EConfigType.Custom) { - var preCoreType = itemSocks.CoreType ?? ECoreType.sing_box; - var fileName = Utils.GetBinConfigPath(Global.CorePreConfigFileName); - var result = await CoreConfigHandler.GenerateClientConfig(itemSocks, fileName); - if (result.Success) + SplitCore = false; + CoreType = Node.CoreType ?? ECoreType.Xray; + if (Node.PreSocksPort > 0) { - var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(preCoreType); - var proc = await RunProcess(coreInfo, Global.CorePreConfigFileName, true, true); - if (proc is null) - { - return; - } - _processPre = proc; + PreCoreType = EnableTun ? ECoreType.sing_box : AppHandler.Instance.GetCoreType(Node, Node.ConfigType); + PreSocksPort = Node.PreSocksPort.Value; + } + else + { + EnableTun = false; + PreCoreType = null; } } } + + /// + /// Adjust split core configuration + /// + public void AdjustForSplitCore() + { + if (SplitCore) + { + PreCoreType = EnableTun ? ECoreType.sing_box : SplitRouteCore; + CoreType = PureEndpointCore; + + if (PreCoreType == CoreType) + { + PreCoreType = null; + SplitCore = false; + } + else + { + PreSocksPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.split); + } + } + } + } + + private async Task CoreStart(CoreLaunchContext context) + { + var fileName = Utils.GetBinConfigPath(Global.CoreConfigFileName); + var result = context.SplitCore + ? await CoreConfigHandler.GeneratePureEndpointConfig(context.Node, fileName) + : await CoreConfigHandler.GenerateClientConfig(context.Node, fileName); + + if (result.Success != true) + { + UpdateFunc(true, result.Msg); + return false; + } + + UpdateFunc(false, $"{context.Node.GetSummary()}"); + UpdateFunc(false, $"{Utils.GetRuntimeInfo()}"); + UpdateFunc(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"))); + + await CoreStop(); + await Task.Delay(100); + + if (Utils.IsWindows() && _config.TunModeItem.EnableTun) + { + await Task.Delay(100); + await WindowsUtils.RemoveTunDevice(); + } + + var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(context.CoreType); + var displayLog = context.Node.ConfigType != EConfigType.Custom || context.Node.DisplayLog; + var proc = await RunProcess(coreInfo, Global.CoreConfigFileName, displayLog, true); + + if (proc is null) + { + UpdateFunc(true, ResUI.FailedToRunCore); + return false; + } + + _process = proc; + _config.RunningCoreType = AppHandler.Instance.GetCoreType(context.Node, context.Node.ConfigType); + return true; + } + + private async Task CoreStartPreService(CoreLaunchContext context) + { + if (context.PreCoreType == null) + { + return true; // No pre-core needed, consider successful + } + + var fileName = Utils.GetBinConfigPath(Global.CorePreConfigFileName); + var itemSocks = new ProfileItem() + { + CoreType = context.PreCoreType, + ConfigType = EConfigType.SOCKS, + Address = Global.Loopback, + Sni = context.EnableTun && Utils.IsDomain(context.Node.Address) ? context.Node.Address : string.Empty, //Tun2SocksAddress + Port = context.PreSocksPort + }; + + var result = await CoreConfigHandler.GenerateClientConfig(itemSocks, fileName); + if (!result.Success) + { + UpdateFunc(true, result.Msg); + return false; + } + + var coreInfo = CoreInfoHandler.Instance.GetCoreInfo((ECoreType)context.PreCoreType); + var proc = await RunProcess(coreInfo, Global.CorePreConfigFileName, true, true); + + if (proc is null || (_process?.HasExited == true)) + { + UpdateFunc(true, ResUI.FailedToRunCore); + return false; + } + + _processPre = proc; + return true; } private void UpdateFunc(bool notify, string msg) From 45a2ff118ea9d80a86d5a5f8b765307cd7d53af2 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Thu, 24 Jul 2025 12:51:17 +0800 Subject: [PATCH 35/54] Fixes --- v2rayN/ServiceLib/Global.cs | 4 +- v2rayN/ServiceLib/Handler/ConfigHandler.cs | 4 +- v2rayN/ServiceLib/Resx/ResUI.Designer.cs | 27 + v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx | 9 + v2rayN/ServiceLib/Resx/ResUI.hu.resx | 9 + v2rayN/ServiceLib/Resx/ResUI.resx | 9 + v2rayN/ServiceLib/Resx/ResUI.ru.resx | 9 + v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx | 9 + v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx | 9 + .../CoreConfig/CoreConfigSingboxService.cs | 3 +- .../CoreConfig/CoreConfigV2rayService.cs | 6 +- .../Minimal/CoreConfigBrookService.cs | 1 + .../Minimal/CoreConfigHy2Service.cs | 3 +- .../Minimal/CoreConfigJuicityService.cs | 5 +- .../Minimal/CoreConfigNaiveService.cs | 3 +- .../Minimal/CoreConfigShadowquicService.cs | 17 +- .../Minimal/CoreConfigTuicService.cs | 17 +- .../Views/AddServerWindow.axaml.cs | 6 +- .../Views/OptionSettingWindow.axaml | 375 ++++++++--- .../Views/OptionSettingWindow.axaml.cs | 23 + v2rayN/v2rayN/Views/AddServerWindow.xaml.cs | 6 +- v2rayN/v2rayN/Views/OptionSettingWindow.xaml | 593 +++++++++--------- 22 files changed, 748 insertions(+), 399 deletions(-) diff --git a/v2rayN/ServiceLib/Global.cs b/v2rayN/ServiceLib/Global.cs index b12e1f50..c2001036 100644 --- a/v2rayN/ServiceLib/Global.cs +++ b/v2rayN/ServiceLib/Global.cs @@ -281,13 +281,13 @@ public class Global public static readonly List Hysteria2CoreTypes = [ "sing_box", - "Hysteria2" + "hysteria2" ]; public static readonly List TuicCoreTypes = [ "sing_box", - "TUIC" + "tuic" ]; public static readonly List SupportSplitConfigTypes = diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs index 3d7b51d3..e4443570 100644 --- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -698,7 +698,7 @@ public class ConfigHandler public static async Task AddHysteria2Server(Config config, ProfileItem profileItem, bool toFile = true) { profileItem.ConfigType = EConfigType.Hysteria2; - profileItem.CoreType = ECoreType.sing_box; + //profileItem.CoreType = ECoreType.sing_box; profileItem.Address = profileItem.Address.TrimEx(); profileItem.Id = profileItem.Id.TrimEx(); @@ -731,7 +731,7 @@ public class ConfigHandler public static async Task AddTuicServer(Config config, ProfileItem profileItem, bool toFile = true) { profileItem.ConfigType = EConfigType.TUIC; - profileItem.CoreType = ECoreType.sing_box; + //profileItem.CoreType = ECoreType.sing_box; profileItem.Address = profileItem.Address.TrimEx(); profileItem.Id = profileItem.Id.TrimEx(); diff --git a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs index ca554860..62ed61c2 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs +++ b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs @@ -3426,6 +3426,33 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Outbound and routing are decoupled. If their Core types differ, two separate cores will be activated. 的本地化字符串。 + /// + public static string TbSettingsSplitCoreDoc1 { + get { + return ResourceManager.GetString("TbSettingsSplitCoreDoc1", resourceCulture); + } + } + + /// + /// 查找类似 Routing Core defaults to sing-box when Tun is enabled. 的本地化字符串。 + /// + public static string TbSettingsSplitCoreDoc2 { + get { + return ResourceManager.GetString("TbSettingsSplitCoreDoc2", resourceCulture); + } + } + + /// + /// 查找类似 Enable separation of outbound and routing cores 的本地化字符串。 + /// + public static string TbSettingsSplitCoreEnable { + get { + return ResourceManager.GetString("TbSettingsSplitCoreEnable", resourceCulture); + } + } + /// /// 查找类似 sing-box ruleset files source (optional) 的本地化字符串。 /// diff --git a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx index bd2cb887..ee776c78 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx @@ -1404,4 +1404,13 @@ Add [Anytls] Configuration + + Enable separation of outbound and routing cores + + + Outbound and routing are decoupled. If their Core types differ, two separate cores will be activated. + + + Routing Core defaults to sing-box when Tun is enabled. + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.hu.resx b/v2rayN/ServiceLib/Resx/ResUI.hu.resx index 0fc30153..6fdfc7dd 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.hu.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.hu.resx @@ -1404,4 +1404,13 @@ [Anytls] konfiguráció hozzáadása + + Enable separation of outbound and routing cores + + + Outbound and routing are decoupled. If their Core types differ, two separate cores will be activated. + + + Routing Core defaults to sing-box when Tun is enabled. + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayN/ServiceLib/Resx/ResUI.resx index 7ecfcc98..bffcea43 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.resx @@ -1404,4 +1404,13 @@ Add [Anytls] Configuration + + Enable separation of outbound and routing cores + + + Outbound and routing are decoupled. If their Core types differ, two separate cores will be activated. + + + Routing Core defaults to sing-box when Tun is enabled. + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayN/ServiceLib/Resx/ResUI.ru.resx index 25f17e0d..42843dc4 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.ru.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.ru.resx @@ -1404,4 +1404,13 @@ Добавить сервер [Anytls] + + Enable separation of outbound and routing cores + + + Outbound and routing are decoupled. If their Core types differ, two separate cores will be activated. + + + Routing Core defaults to sing-box when Tun is enabled. + \ 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..a9ed87cc 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx @@ -1401,4 +1401,13 @@ 添加 [Anytls] 配置文件 + + 启用出站核心与路由核心分离 + + + 出站与路由分离,出站与分流 Core 类型不同时,将启用两个核心 + + + 启用 Tun 时,路由 Core 为 sing-box + \ 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..3e99f83a 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx @@ -1401,4 +1401,13 @@ 新增 [Anytls] 設定檔 + + Enable separation of outbound and routing cores + + + Outbound and routing are decoupled. If their Core types differ, two separate cores will be activated. + + + Routing Core defaults to sing-box when Tun is enabled. + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs index 14a531d4..ea5d787d 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs @@ -2,6 +2,7 @@ using System.Data; using System.Net; using System.Net.NetworkInformation; using System.Reactive; +using System.Text.Json.Nodes; using DynamicData; using ServiceLib.Models; @@ -587,7 +588,7 @@ public class CoreConfigSingboxService config.Remove("route"); - ret.Data = config.ToJsonString(new() { WriteIndented = true }); + ret.Data = JsonUtils.Serialize(config, true); return ret; } diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs index c15c05ff..966e23f5 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs @@ -453,6 +453,10 @@ public class CoreConfigV2rayService await GenLog(v2rayConfig); var inbound = GetInbound(_config.Inbound.First(), EInboundProtocol.split, true); + inbound.sniffing = new Sniffing4Ray + { + enabled = false + }; v2rayConfig.inbounds = new() { inbound }; @@ -469,7 +473,7 @@ public class CoreConfigV2rayService config.Remove("routing"); - ret.Data = config.ToJsonString(new() { WriteIndented = true }); + ret.Data = JsonUtils.Serialize(config, true); return ret; } diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigBrookService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigBrookService.cs index b3b4cef7..6ef1353e 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigBrookService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigBrookService.cs @@ -36,6 +36,7 @@ public class CoreConfigBrookService processArgs += " --server " + node.Address + ":" + node.Port; processArgs += " --password " + node.Id; + ret.Success = true; ret.Data = processArgs; return await Task.FromResult(ret); diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigHy2Service.cs b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigHy2Service.cs index 25026dba..3241aecd 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigHy2Service.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigHy2Service.cs @@ -74,7 +74,8 @@ public class CoreConfigHy2Service configJsonNode["bandwidth"] = bandwidthObject; } - ret.Data = configJsonNode.ToJsonString(new() { WriteIndented = true }); + ret.Success = true; + ret.Data = JsonUtils.Serialize(configJsonNode, true); return await Task.FromResult(ret); } diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigJuicityService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigJuicityService.cs index a08b022b..66fccbbd 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigJuicityService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigJuicityService.cs @@ -56,9 +56,10 @@ public class CoreConfigJuicityService configJsonNode["sni"] = node.Sni; } configJsonNode["allow_insecure"] = node.AllowInsecure == "true"; - configJsonNode["congestion_control"] = "bbr"; + configJsonNode["congestion_control"] = node.HeaderType; - ret.Data = configJsonNode.ToJsonString(new() { WriteIndented = true }); + ret.Success = true; + ret.Data = JsonUtils.Serialize(configJsonNode, true); return await Task.FromResult(ret); } diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigNaiveService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigNaiveService.cs index 8307d5b0..544e6f92 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigNaiveService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigNaiveService.cs @@ -39,7 +39,8 @@ public class CoreConfigNaiveService // outbound configJsonNode["proxy"] = (node.Network == "quic" ? "quic://" : Global.HttpsProtocol) + node.Id + "@" + node.Address + ":" + node.Port; - ret.Data = configJsonNode.ToJsonString(new() { WriteIndented = true }); + ret.Success = true; + ret.Data = JsonUtils.Serialize(configJsonNode, true); return await Task.FromResult(ret); } diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigShadowquicService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigShadowquicService.cs index 1d224210..232b56df 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigShadowquicService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigShadowquicService.cs @@ -1,3 +1,5 @@ +using System.Text.Json.Nodes; + namespace ServiceLib.Services.CoreConfig.Minimal; public class CoreConfigShadowquicService { @@ -51,14 +53,24 @@ public class CoreConfigShadowquicService configYamlNode["inbound"] = inboundNode; // outbound + var alpn = new JsonArray(); + foreach (var item in node.GetAlpn() ?? new List()) + { + alpn.Add(item); + } + if (alpn.Count == 0) + { + alpn.Add("h3"); + } + var outboundNode = new Dictionary { ["type"] = "shadowquic", ["addr"] = node.Address + ":" + node.Port, ["password"] = node.Id, ["username"] = node.Security, - ["alpn"] = new List { "h3" }, - ["congestion-control"] = "bbr", + ["alpn"] = alpn, + ["congestion-control"] = node.HeaderType, ["zero-rtt"] = true }; if (node.Sni.IsNotEmpty()) @@ -67,6 +79,7 @@ public class CoreConfigShadowquicService } configYamlNode["outbound"] = outboundNode; + ret.Success = true; ret.Data = YamlUtils.ToYaml(configYamlNode); return await Task.FromResult(ret); diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigTuicService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigTuicService.cs index 3f95bebd..55dfecb1 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigTuicService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigTuicService.cs @@ -51,17 +51,28 @@ public class CoreConfigTuicService }; // outbound + var alpn = new JsonArray(); + foreach(var item in node.GetAlpn() ?? new List()) + { + alpn.Add(item); + } + if (alpn.Count == 0) + { + alpn.Add("h3"); + } + configJsonNode["relay"] = new JsonObject { ["server"] = node.Address + ":" + node.Port, ["uuid"] = node.Id, ["password"] = node.Security, ["udp_relay_mode"] = "quic", - ["congestion_control"] = "bbr", - ["alpn"] = new JsonArray { "h3", "spdy/3.1" } + ["congestion_control"] = node.HeaderType, + ["alpn"] = alpn }; - ret.Data = configJsonNode.ToJsonString(new() { WriteIndented = true }); + ret.Success = true; + ret.Data = JsonUtils.Serialize(configJsonNode, true); return await Task.FromResult(ret); } diff --git a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs index 9d24175f..3a113722 100644 --- a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs @@ -78,7 +78,8 @@ public partial class AddServerWindow : WindowBase gridHysteria2.IsVisible = true; sepa2.IsVisible = false; gridTransport.IsVisible = false; - cmbCoreType.IsEnabled = false; + //cmbCoreType.IsEnabled = false; + cmbCoreType.ItemsSource = Global.Hysteria2CoreTypes; cmbFingerprint.IsEnabled = false; cmbFingerprint.SelectedValue = string.Empty; break; @@ -87,7 +88,8 @@ public partial class AddServerWindow : WindowBase gridTuic.IsVisible = true; sepa2.IsVisible = false; gridTransport.IsVisible = false; - cmbCoreType.IsEnabled = false; + //cmbCoreType.IsEnabled = false; + cmbCoreType.ItemsSource = Global.TuicCoreTypes; cmbFingerprint.IsEnabled = false; cmbFingerprint.SelectedValue = string.Empty; diff --git a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml index a75ed6fc..e61c16ea 100644 --- a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml +++ b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml @@ -802,101 +802,298 @@ - - - + + + + + + + + + - - + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs index 010488e5..152e629c 100644 --- a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs @@ -41,6 +41,17 @@ public partial class OptionSettingWindow : WindowBase cmbCoreType6.ItemsSource = Global.CoreTypes; cmbCoreType9.ItemsSource = Global.CoreTypes; + cmbCoreSplitRouteType.ItemsSource = Global.CoreTypes; + + cmbCoreSplitType1.ItemsSource = Global.CoreTypes; + cmbCoreSplitType3.ItemsSource = Global.CoreTypes; + cmbCoreSplitType4.ItemsSource = Global.CoreTypes; + cmbCoreSplitType5.ItemsSource = Global.CoreTypes; + cmbCoreSplitType6.ItemsSource = Global.CoreTypes; + cmbCoreSplitType7.ItemsSource = Global.Hysteria2CoreTypes; + cmbCoreSplitType8.ItemsSource = Global.TuicCoreTypes; + cmbCoreSplitType9.ItemsSource = Global.CoreTypes; + cmbMixedConcurrencyCount.ItemsSource = Enumerable.Range(2, 7).ToList(); cmbSpeedTestTimeout.ItemsSource = Enumerable.Range(2, 5).Select(i => i * 5).ToList(); cmbSpeedTestUrl.ItemsSource = Global.SpeedTestUrls; @@ -119,6 +130,18 @@ public partial class OptionSettingWindow : WindowBase this.Bind(ViewModel, vm => vm.CoreType6, v => v.cmbCoreType6.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CoreType9, v => v.cmbCoreType9.SelectedValue).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.EnableSplitCore, v => v.togCoreSplit.IsChecked).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.RouteSplitCoreType, v => v.cmbCoreSplitRouteType.SelectedValue).DisposeWith(disposables); + + this.Bind(ViewModel, vm => vm.SplitCoreType1, v => v.cmbCoreSplitType1.SelectedValue).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SplitCoreType3, v => v.cmbCoreSplitType3.SelectedValue).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SplitCoreType4, v => v.cmbCoreSplitType4.SelectedValue).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SplitCoreType5, v => v.cmbCoreSplitType5.SelectedValue).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SplitCoreType6, v => v.cmbCoreSplitType6.SelectedValue).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SplitCoreType7, v => v.cmbCoreSplitType7.SelectedValue).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SplitCoreType8, v => v.cmbCoreSplitType8.SelectedValue).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SplitCoreType9, v => v.cmbCoreSplitType9.SelectedValue).DisposeWith(disposables); + this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables); }); diff --git a/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs b/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs index b06002a9..664a47bf 100644 --- a/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs @@ -72,7 +72,8 @@ public partial class AddServerWindow gridHysteria2.Visibility = Visibility.Visible; sepa2.Visibility = Visibility.Collapsed; gridTransport.Visibility = Visibility.Collapsed; - cmbCoreType.IsEnabled = false; + //cmbCoreType.IsEnabled = false; + cmbCoreType.ItemsSource = Global.Hysteria2CoreTypes; cmbFingerprint.IsEnabled = false; cmbFingerprint.Text = string.Empty; break; @@ -81,7 +82,8 @@ public partial class AddServerWindow gridTuic.Visibility = Visibility.Visible; sepa2.Visibility = Visibility.Collapsed; gridTransport.Visibility = Visibility.Collapsed; - cmbCoreType.IsEnabled = false; + //cmbCoreType.IsEnabled = false; + cmbCoreType.ItemsSource = Global.TuicCoreTypes; cmbFingerprint.IsEnabled = false; cmbFingerprint.Text = string.Empty; diff --git a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml index 07d01f53..0710d65a 100644 --- a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml +++ b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml @@ -1107,318 +1107,329 @@ - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Margin="{StaticResource Margin8}"> + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + + + Margin="{StaticResource Margin8}"> + + + + + + + + + + + + + + + + + + + - - + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + - + From e28f5ce9c265bd7675e472f09dabbee54044bb5e Mon Sep 17 00:00:00 2001 From: DHR60 Date: Thu, 24 Jul 2025 15:28:51 +0800 Subject: [PATCH 36/54] Enable Tun SplitCore --- v2rayN/ServiceLib/Handler/CoreHandler.cs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/v2rayN/ServiceLib/Handler/CoreHandler.cs b/v2rayN/ServiceLib/Handler/CoreHandler.cs index b7a8b4f5..5e53e47b 100644 --- a/v2rayN/ServiceLib/Handler/CoreHandler.cs +++ b/v2rayN/ServiceLib/Handler/CoreHandler.cs @@ -247,16 +247,30 @@ public class CoreHandler PreSocksPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.split); } } + else if (EnableTun) + { + PreCoreType = ECoreType.sing_box; + + if (PreCoreType != CoreType) // CoreType is xray + { + SplitCore = true; + PreSocksPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.split); + } + else // CoreType is sing_box + { + PreCoreType = null; + } + } } } private async Task CoreStart(CoreLaunchContext context) { var fileName = Utils.GetBinConfigPath(Global.CoreConfigFileName); - var result = context.SplitCore - ? await CoreConfigHandler.GeneratePureEndpointConfig(context.Node, fileName) + var result = context.SplitCore + ? await CoreConfigHandler.GeneratePureEndpointConfig(context.Node, fileName) : await CoreConfigHandler.GenerateClientConfig(context.Node, fileName); - + if (result.Success != true) { UpdateFunc(true, result.Msg); From 8deb897f55773126367beca5f093e4ab5e53fbff Mon Sep 17 00:00:00 2001 From: DHR60 Date: Thu, 24 Jul 2025 15:35:21 +0800 Subject: [PATCH 37/54] Fixes --- .../CoreConfig/Minimal/CoreConfigNaiveService.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigNaiveService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigNaiveService.cs index 544e6f92..f7a29230 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigNaiveService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigNaiveService.cs @@ -23,13 +23,11 @@ public class CoreConfigNaiveService return ret; } - // TODO: EConfigType.Naive - - //if (node.ConfigType != EConfigType.Naive) - //{ - // ret.Msg = ResUI.Incorrectconfiguration + $" - {node.ConfigType}"; - // return ret; - //} + if (node.ConfigType != EConfigType.NaiveProxy) + { + ret.Msg = ResUI.Incorrectconfiguration + $" - {node.ConfigType}"; + return ret; + } var configJsonNode = new JsonObject(); From 6462fa0905ded541859b7661608ae1eb358219c2 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Thu, 24 Jul 2025 20:50:59 +0800 Subject: [PATCH 38/54] Fixes SplitCore == false and Node.CoreType == ECoreType.hysteria2 etc. logic --- v2rayN/ServiceLib/Handler/CoreHandler.cs | 28 +++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/v2rayN/ServiceLib/Handler/CoreHandler.cs b/v2rayN/ServiceLib/Handler/CoreHandler.cs index 5e53e47b..14530e05 100644 --- a/v2rayN/ServiceLib/Handler/CoreHandler.cs +++ b/v2rayN/ServiceLib/Handler/CoreHandler.cs @@ -225,6 +225,24 @@ public class CoreHandler PreCoreType = null; } } + else if (!SplitCore && !EnableTun) + { + switch (Node.CoreType) + { + case ECoreType.hysteria2: + case ECoreType.tuic: + Node.CoreType = ECoreType.sing_box; + CoreType = ECoreType.sing_box; + break; + case ECoreType.v2fly: + case ECoreType.v2fly_v5: + Node.CoreType = ECoreType.Xray; + CoreType = ECoreType.Xray; + break; + default: + break; + } + } } /// @@ -251,15 +269,15 @@ public class CoreHandler { PreCoreType = ECoreType.sing_box; - if (PreCoreType != CoreType) // CoreType is xray + if (PreCoreType == CoreType) // CoreType is sing_box + { + PreCoreType = null; + } + else // CoreType is xray, hysteria2, tuic, etc. { SplitCore = true; PreSocksPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.split); } - else // CoreType is sing_box - { - PreCoreType = null; - } } } } From db708d1e5d1cda40abdcac30191a603bc709ba98 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Thu, 24 Jul 2025 21:26:52 +0800 Subject: [PATCH 39/54] Fixes (Prioritize Node.CoreType over SplitCore configuration) --- v2rayN/ServiceLib/Handler/AppHandler.cs | 63 ++++++++++++++++++++++++ v2rayN/ServiceLib/Handler/CoreHandler.cs | 60 ++-------------------- 2 files changed, 66 insertions(+), 57 deletions(-) diff --git a/v2rayN/ServiceLib/Handler/AppHandler.cs b/v2rayN/ServiceLib/Handler/AppHandler.cs index f3b7e449..1422ad73 100644 --- a/v2rayN/ServiceLib/Handler/AppHandler.cs +++ b/v2rayN/ServiceLib/Handler/AppHandler.cs @@ -1,3 +1,7 @@ +using DynamicData; +using ServiceLib.Enums; +using ServiceLib.Models; + namespace ServiceLib.Handler; public sealed class AppHandler @@ -246,5 +250,64 @@ public sealed class AppHandler return item?.CoreType ?? ECoreType.Xray; } + public (bool, ECoreType, ECoreType?) GetCoreAndPreType(ProfileItem profileItem) + { + var splitCore = _config.SplitCoreItem.EnableSplitCore; + var coreType = GetCoreType(profileItem, profileItem.ConfigType); + ECoreType? preCoreType = null; + + var pureEndpointCore = GetSplitCoreType(profileItem, profileItem.ConfigType); + var splitRouteCore = _config.SplitCoreItem.RouteCoreType; + var enableTun = _config.TunModeItem.EnableTun; + + if (profileItem.ConfigType == EConfigType.Custom) + { + splitCore = false; + coreType = profileItem.CoreType ?? ECoreType.Xray; + if (profileItem.PreSocksPort > 0) + { + preCoreType = enableTun ? ECoreType.sing_box : GetCoreType(profileItem, profileItem.ConfigType); + } + else + { + preCoreType = null; + } + } + else if (!splitCore && profileItem.CoreType is not (ECoreType.Xray or ECoreType.sing_box)) + { + // Force SplitCore for cores that don't support direct routing (like Hysteria2, TUIC, etc.) + splitCore = true; + preCoreType = enableTun ? ECoreType.sing_box : splitRouteCore; + } + else if (splitCore) + { + // User explicitly enabled SplitCore + preCoreType = enableTun ? ECoreType.sing_box : splitRouteCore; + coreType = pureEndpointCore; + + if (preCoreType == coreType) + { + preCoreType = null; + splitCore = false; + } + } + else if (enableTun) // EnableTun is true but SplitCore is false + { + // TUN mode handling for Xray/sing_box cores + preCoreType = ECoreType.sing_box; + + if (preCoreType == coreType) // CoreType is sing_box + { + preCoreType = null; + } + else // CoreType is xray, etc. + { + // Force SplitCore for non-split cores + splitCore = true; + } + } + return (splitCore, coreType, preCoreType); + } + #endregion Core Type } diff --git a/v2rayN/ServiceLib/Handler/CoreHandler.cs b/v2rayN/ServiceLib/Handler/CoreHandler.cs index 14530e05..5b045238 100644 --- a/v2rayN/ServiceLib/Handler/CoreHandler.cs +++ b/v2rayN/ServiceLib/Handler/CoreHandler.cs @@ -76,7 +76,6 @@ public class CoreHandler // Create launch context and configure parameters var context = new CoreLaunchContext(node, _config); context.AdjustForConfigType(); - context.AdjustForSplitCore(); // Start main core if (!await CoreStart(context)) @@ -210,74 +209,21 @@ public class CoreHandler /// public void AdjustForConfigType() { + (SplitCore, CoreType, PreCoreType) = AppHandler.Instance.GetCoreAndPreType(Node); if (Node.ConfigType == EConfigType.Custom) { - SplitCore = false; - CoreType = Node.CoreType ?? ECoreType.Xray; if (Node.PreSocksPort > 0) { - PreCoreType = EnableTun ? ECoreType.sing_box : AppHandler.Instance.GetCoreType(Node, Node.ConfigType); PreSocksPort = Node.PreSocksPort.Value; } else { EnableTun = false; - PreCoreType = null; } } - else if (!SplitCore && !EnableTun) + else if (PreCoreType != null) { - switch (Node.CoreType) - { - case ECoreType.hysteria2: - case ECoreType.tuic: - Node.CoreType = ECoreType.sing_box; - CoreType = ECoreType.sing_box; - break; - case ECoreType.v2fly: - case ECoreType.v2fly_v5: - Node.CoreType = ECoreType.Xray; - CoreType = ECoreType.Xray; - break; - default: - break; - } - } - } - - /// - /// Adjust split core configuration - /// - public void AdjustForSplitCore() - { - if (SplitCore) - { - PreCoreType = EnableTun ? ECoreType.sing_box : SplitRouteCore; - CoreType = PureEndpointCore; - - if (PreCoreType == CoreType) - { - PreCoreType = null; - SplitCore = false; - } - else - { - PreSocksPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.split); - } - } - else if (EnableTun) - { - PreCoreType = ECoreType.sing_box; - - if (PreCoreType == CoreType) // CoreType is sing_box - { - PreCoreType = null; - } - else // CoreType is xray, hysteria2, tuic, etc. - { - SplitCore = true; - PreSocksPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.split); - } + PreSocksPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.split); } } } From 94afd9969a58fe8443ec9dcdcea56f369bf8bbf2 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Thu, 24 Jul 2025 22:09:16 +0800 Subject: [PATCH 40/54] ShowClashUI --- v2rayN/ServiceLib/Handler/CoreHandler.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/v2rayN/ServiceLib/Handler/CoreHandler.cs b/v2rayN/ServiceLib/Handler/CoreHandler.cs index 5b045238..201b9ba6 100644 --- a/v2rayN/ServiceLib/Handler/CoreHandler.cs +++ b/v2rayN/ServiceLib/Handler/CoreHandler.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using System.Text; using ServiceLib.Enums; using ServiceLib.Models; +using static SQLite.SQLite3; namespace ServiceLib.Handler; @@ -265,7 +266,8 @@ public class CoreHandler } _process = proc; - _config.RunningCoreType = AppHandler.Instance.GetCoreType(context.Node, context.Node.ConfigType); + var (_, coreType, preCoreType) = AppHandler.Instance.GetCoreAndPreType(context.Node); + _config.RunningCoreType = (ECoreType)(preCoreType != null ? preCoreType : coreType); return true; } From 2b468577631ffdfff5988de3eefd567b97e5eb4c Mon Sep 17 00:00:00 2001 From: DHR60 Date: Mon, 28 Jul 2025 21:19:27 +0800 Subject: [PATCH 41/54] Fixes --- v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs | 6 ++---- v2rayN/v2rayN/Views/AddServerWindow.xaml.cs | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs index 3a113722..39d6e68a 100644 --- a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs @@ -78,8 +78,7 @@ public partial class AddServerWindow : WindowBase gridHysteria2.IsVisible = true; sepa2.IsVisible = false; gridTransport.IsVisible = false; - //cmbCoreType.IsEnabled = false; - cmbCoreType.ItemsSource = Global.Hysteria2CoreTypes; + cmbCoreType.ItemsSource = Global.Hysteria2CoreTypes.AppendEmpty(); cmbFingerprint.IsEnabled = false; cmbFingerprint.SelectedValue = string.Empty; break; @@ -88,8 +87,7 @@ public partial class AddServerWindow : WindowBase gridTuic.IsVisible = true; sepa2.IsVisible = false; gridTransport.IsVisible = false; - //cmbCoreType.IsEnabled = false; - cmbCoreType.ItemsSource = Global.TuicCoreTypes; + cmbCoreType.ItemsSource = Global.TuicCoreTypes.AppendEmpty(); cmbFingerprint.IsEnabled = false; cmbFingerprint.SelectedValue = string.Empty; diff --git a/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs b/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs index 664a47bf..0e90ce0c 100644 --- a/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs @@ -72,8 +72,7 @@ public partial class AddServerWindow gridHysteria2.Visibility = Visibility.Visible; sepa2.Visibility = Visibility.Collapsed; gridTransport.Visibility = Visibility.Collapsed; - //cmbCoreType.IsEnabled = false; - cmbCoreType.ItemsSource = Global.Hysteria2CoreTypes; + cmbCoreType.ItemsSource = Global.Hysteria2CoreTypes.AppendEmpty(); cmbFingerprint.IsEnabled = false; cmbFingerprint.Text = string.Empty; break; @@ -82,8 +81,7 @@ public partial class AddServerWindow gridTuic.Visibility = Visibility.Visible; sepa2.Visibility = Visibility.Collapsed; gridTransport.Visibility = Visibility.Collapsed; - //cmbCoreType.IsEnabled = false; - cmbCoreType.ItemsSource = Global.TuicCoreTypes; + cmbCoreType.ItemsSource = Global.TuicCoreTypes.AppendEmpty(); cmbFingerprint.IsEnabled = false; cmbFingerprint.Text = string.Empty; From 3e322b4d68a7c497d7d6b063eec5ca7c45584cc8 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Mon, 28 Jul 2025 22:40:50 +0800 Subject: [PATCH 42/54] Try add GUI support --- v2rayN/ServiceLib/Global.cs | 29 ++- v2rayN/ServiceLib/Handler/ConfigHandler.cs | 146 ++++++++++++- v2rayN/ServiceLib/Resx/ResUI.Designer.cs | 45 ++++ v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx | 15 ++ v2rayN/ServiceLib/Resx/ResUI.hu.resx | 15 ++ v2rayN/ServiceLib/Resx/ResUI.resx | 15 ++ v2rayN/ServiceLib/Resx/ResUI.ru.resx | 15 ++ v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx | 15 ++ v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx | 15 ++ .../ViewModels/MainWindowViewModel.cs | 20 ++ .../Views/AddServerWindow.axaml.cs | 2 +- v2rayN/v2rayN/Views/AddServerWindow.xaml | 193 ++++++++++++++++++ v2rayN/v2rayN/Views/AddServerWindow.xaml.cs | 66 +++++- v2rayN/v2rayN/Views/MainWindow.xaml | 17 ++ v2rayN/v2rayN/Views/MainWindow.xaml.cs | 4 + 15 files changed, 607 insertions(+), 5 deletions(-) diff --git a/v2rayN/ServiceLib/Global.cs b/v2rayN/ServiceLib/Global.cs index c2001036..e1daa015 100644 --- a/v2rayN/ServiceLib/Global.cs +++ b/v2rayN/ServiceLib/Global.cs @@ -290,6 +290,26 @@ public class Global "tuic" ]; + public static readonly List NaiveProxyCoreTypes = + [ + "naiveproxy" + ]; + + public static readonly List JuicityProxyCoreTypes = + [ + "juicity" + ]; + + public static readonly List BrookCoreTypes = + [ + "brook" + ]; + + public static readonly List ShadowquicCoreTypes = + [ + "shadowquic" + ]; + public static readonly List SupportSplitConfigTypes = [ EConfigType.VMess, @@ -488,13 +508,20 @@ public class Global "" ]; - public static readonly List TuicCongestionControls = + public static readonly List CongestionControls = [ "cubic", "new_reno", "bbr" ]; + public static readonly List NaiveProxyProtocols = + [ + "https", + "http", + "quic" + ]; + public static readonly List allowSelectType = [ "selector", diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs index e4443570..446f0813 100644 --- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -270,6 +270,10 @@ public class ConfigHandler EConfigType.TUIC => await AddTuicServer(config, item), EConfigType.WireGuard => await AddWireguardServer(config, item), EConfigType.Anytls => await AddAnytlsServer(config, item), + EConfigType.NaiveProxy => await AddNaiveServer(config, item), + EConfigType.Juicity => await AddJuicityServer(config, item), + EConfigType.Brook => await AddBrookServer(config, item), + EConfigType.Shadowquic => await AddShadowquicServer(config, item), _ => -1, }; return ret; @@ -738,9 +742,9 @@ public class ConfigHandler profileItem.Security = profileItem.Security.TrimEx(); profileItem.Network = string.Empty; - if (!Global.TuicCongestionControls.Contains(profileItem.HeaderType)) + if (!Global.CongestionControls.Contains(profileItem.HeaderType)) { - profileItem.HeaderType = Global.TuicCongestionControls.FirstOrDefault()!; + profileItem.HeaderType = Global.CongestionControls.FirstOrDefault()!; } if (profileItem.StreamSecurity.IsNullOrEmpty()) @@ -823,6 +827,140 @@ public class ConfigHandler return 0; } + /// + /// Add or edit a Naive server + /// Validates and processes Naive-specific settings + /// + /// Current configuration + /// Naive profile to add + /// Whether to save to file + /// 0 if successful, -1 if failed + public static async Task AddNaiveServer(Config config, ProfileItem profileItem, bool toFile = true) + { + profileItem.ConfigType = EConfigType.NaiveProxy; + profileItem.CoreType = ECoreType.naiveproxy; + + profileItem.Address = profileItem.Address.TrimEx(); + profileItem.Id = profileItem.Id.TrimEx(); + profileItem.Network = string.Empty; + if (profileItem.StreamSecurity.IsNullOrEmpty()) + { + profileItem.StreamSecurity = Global.StreamSecurity; + } + if (profileItem.Id.IsNullOrEmpty()) + { + return -1; + } + await AddServerCommon(config, profileItem, toFile); + return 0; + } + + /// + /// Add or edit a Juicity server + /// Validates and processes Juicity-specific settings + /// + /// Current configuration + /// Juicity profile to add + /// Whether to save to file + /// 0 if successful, -1 if failed + public static async Task AddJuicityServer(Config config, ProfileItem profileItem, bool toFile = true) + { + profileItem.ConfigType = EConfigType.Juicity; + profileItem.CoreType = ECoreType.juicity; + + profileItem.Address = profileItem.Address.TrimEx(); + profileItem.Id = profileItem.Id.TrimEx(); + profileItem.Security = profileItem.Security.TrimEx(); + profileItem.Network = string.Empty; + + if (!Global.CongestionControls.Contains(profileItem.HeaderType)) + { + profileItem.HeaderType = Global.CongestionControls.FirstOrDefault()!; + } + + if (profileItem.StreamSecurity.IsNullOrEmpty()) + { + profileItem.StreamSecurity = Global.StreamSecurity; + } + if (profileItem.Alpn.IsNullOrEmpty()) + { + profileItem.Alpn = "h3"; + } + if (profileItem.Id.IsNullOrEmpty()) + { + return -1; + } + + await AddServerCommon(config, profileItem, toFile); + + return 0; + } + + /// + /// Add or edit a Brook server + /// Validates and processes Brook-specific settings + /// + /// Current configuration + /// Brook profile to add + /// Whether to save to file + /// 0 if successful, -1 if failed + public static async Task AddBrookServer(Config config, ProfileItem profileItem, bool toFile = true) + { + profileItem.ConfigType = EConfigType.Brook; + profileItem.CoreType = ECoreType.brook; + + profileItem.Address = profileItem.Address.TrimEx(); + profileItem.Id = profileItem.Id.TrimEx(); + profileItem.Network = string.Empty; + if (profileItem.Id.IsNullOrEmpty()) + { + return -1; + } + await AddServerCommon(config, profileItem, toFile); + return 0; + } + + /// + /// Add or edit a Shadowquic server + /// Validates and processes Shadowquic-specific settings + /// + /// Current configuration + /// Shadowquic profile to add + /// Whether to save to file + /// 0 if successful, -1 if failed + public static async Task AddShadowquicServer(Config config, ProfileItem profileItem, bool toFile = true) + { + profileItem.ConfigType = EConfigType.Shadowquic; + profileItem.CoreType = ECoreType.shadowquic; + + profileItem.Address = profileItem.Address.TrimEx(); + profileItem.Id = profileItem.Id.TrimEx(); + profileItem.Security = profileItem.Security.TrimEx(); + profileItem.Network = string.Empty; + + if (!Global.CongestionControls.Contains(profileItem.HeaderType)) + { + profileItem.HeaderType = Global.CongestionControls.FirstOrDefault()!; + } + + if (profileItem.StreamSecurity.IsNullOrEmpty()) + { + profileItem.StreamSecurity = Global.StreamSecurity; + } + if (profileItem.Alpn.IsNullOrEmpty()) + { + profileItem.Alpn = "h3"; + } + if (profileItem.Id.IsNullOrEmpty()) + { + return -1; + } + + await AddServerCommon(config, profileItem, toFile); + + return 0; + } + /// /// Sort the server list by the specified column /// Updates the sort order in the profile extension data @@ -1296,6 +1434,10 @@ public class ConfigHandler EConfigType.TUIC => await AddTuicServer(config, profileItem, false), EConfigType.WireGuard => await AddWireguardServer(config, profileItem, false), EConfigType.Anytls => await AddAnytlsServer(config, profileItem, false), + EConfigType.NaiveProxy => await AddNaiveServer(config, profileItem, false), + EConfigType.Juicity => await AddJuicityServer(config, profileItem, false), + EConfigType.Brook => await AddBrookServer(config, profileItem, false), + EConfigType.Shadowquic => await AddShadowquicServer(config, profileItem, false), _ => -1, }; diff --git a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs index 62ed61c2..b998e8b4 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs +++ b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs @@ -663,6 +663,15 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Add [Brook] Configuration 的本地化字符串。 + /// + public static string menuAddBrookServer { + get { + return ResourceManager.GetString("menuAddBrookServer", resourceCulture); + } + } + /// /// 查找类似 Add a custom configuration Configuration 的本地化字符串。 /// @@ -690,6 +699,24 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Add [Juicity] Configuration 的本地化字符串。 + /// + public static string menuAddJuicityServer { + get { + return ResourceManager.GetString("menuAddJuicityServer", resourceCulture); + } + } + + /// + /// 查找类似 Add [Naive] Configuration 的本地化字符串。 + /// + public static string menuAddNaiveServer { + get { + return ResourceManager.GetString("menuAddNaiveServer", resourceCulture); + } + } + /// /// 查找类似 Import Share Links from clipboard (Ctrl+V) 的本地化字符串。 /// @@ -717,6 +744,15 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Add [Shadowquic] Configuration 的本地化字符串。 + /// + public static string menuAddShadowquicServer { + get { + return ResourceManager.GetString("menuAddShadowquicServer", resourceCulture); + } + } + /// /// 查找类似 Add [Shadowsocks] Configuration 的本地化字符串。 /// @@ -2472,6 +2508,15 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Proxy Protocol 的本地化字符串。 + /// + public static string TbHeaderType100 { + get { + return ResourceManager.GetString("TbHeaderType100", resourceCulture); + } + } + /// /// 查找类似 Congestion control 的本地化字符串。 /// diff --git a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx index ee776c78..aee7a852 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx @@ -1413,4 +1413,19 @@ Routing Core defaults to sing-box when Tun is enabled. + + افزودن سرور [Brook] + + + افزودن سرور [Juicity] + + + افزودن سرور [Naive] + + + افزودن سرور [Shadowquic] + + + Proxy Protocol + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.hu.resx b/v2rayN/ServiceLib/Resx/ResUI.hu.resx index 6fdfc7dd..167f3b88 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.hu.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.hu.resx @@ -1413,4 +1413,19 @@ Routing Core defaults to sing-box when Tun is enabled. + + [Brook] szerver hozzáadása + + + [Juicity] szerver hozzáadása + + + [Naive] szerver hozzáadása + + + [Shadowquic] szerver hozzáadása + + + Proxy Protocol + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayN/ServiceLib/Resx/ResUI.resx index bffcea43..eaf705b6 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.resx @@ -1413,4 +1413,19 @@ Routing Core defaults to sing-box when Tun is enabled. + + Add [Brook] Configuration + + + Add [Juicity] Configuration + + + Add [Naive] Configuration + + + Add [Shadowquic] Configuration + + + Proxy Protocol + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayN/ServiceLib/Resx/ResUI.ru.resx index 42843dc4..bb855dfe 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.ru.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.ru.resx @@ -1413,4 +1413,19 @@ Routing Core defaults to sing-box when Tun is enabled. + + Добавить сервер [Brook] + + + Добавить сервер [Juicity] + + + Добавить сервер [Naive] + + + Добавить сервер [Shadowquic] + + + Proxy Protocol + \ 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 a9ed87cc..fa4f1e8b 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx @@ -1410,4 +1410,19 @@ 启用 Tun 时,路由 Core 为 sing-box + + 添加 [Brook] 配置文件 + + + 添加 [Juicity] 配置文件 + + + 添加 [Naive] 配置文件 + + + 添加 [Shadowquic] 配置文件 + + + 代理协议 + \ 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 3e99f83a..ebea5465 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx @@ -1410,4 +1410,19 @@ Routing Core defaults to sing-box when Tun is enabled. + + 新增 [Brook] 設定檔 + + + 新增 [Juicity] 設定檔 + + + 新增 [Naive] 設定檔 + + + 新增 [Shadowquic] 設定檔 + + + Proxy Protocol + \ No newline at end of file diff --git a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs index 36e20a87..0dc991cb 100644 --- a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs @@ -25,6 +25,10 @@ public class MainWindowViewModel : MyReactiveObject public ReactiveCommand AddServerViaClipboardCmd { get; } public ReactiveCommand AddServerViaScanCmd { get; } public ReactiveCommand AddServerViaImageCmd { get; } + public ReactiveCommand AddBrookServerCmd { get; } + public ReactiveCommand AddJuicityServerCmd { get; } + public ReactiveCommand AddNaiveServerCmd { get; } + public ReactiveCommand AddShadowquicServerCmd { get; } //Subscription public ReactiveCommand SubSettingCmd { get; } @@ -116,6 +120,22 @@ public class MainWindowViewModel : MyReactiveObject { await AddServerAsync(true, EConfigType.Anytls); }); + AddBrookServerCmd = ReactiveCommand.CreateFromTask(async () => + { + await AddServerAsync(true, EConfigType.Brook); + }); + AddJuicityServerCmd = ReactiveCommand.CreateFromTask(async () => + { + await AddServerAsync(true, EConfigType.Juicity); + }); + AddNaiveServerCmd = ReactiveCommand.CreateFromTask(async () => + { + await AddServerAsync(true, EConfigType.NaiveProxy); + }); + AddShadowquicServerCmd = ReactiveCommand.CreateFromTask(async () => + { + await AddServerAsync(true, EConfigType.Shadowquic); + }); AddCustomServerCmd = ReactiveCommand.CreateFromTask(async () => { await AddServerAsync(true, EConfigType.Custom); diff --git a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs index 39d6e68a..fae84814 100644 --- a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs @@ -91,7 +91,7 @@ public partial class AddServerWindow : WindowBase cmbFingerprint.IsEnabled = false; cmbFingerprint.SelectedValue = string.Empty; - cmbHeaderType8.ItemsSource = Global.TuicCongestionControls; + cmbHeaderType8.ItemsSource = Global.CongestionControls; break; case EConfigType.WireGuard: diff --git a/v2rayN/v2rayN/Views/AddServerWindow.xaml b/v2rayN/v2rayN/Views/AddServerWindow.xaml index acea0c28..2f5c73c0 100644 --- a/v2rayN/v2rayN/Views/AddServerWindow.xaml +++ b/v2rayN/v2rayN/Views/AddServerWindow.xaml @@ -736,6 +736,199 @@ Margin="{StaticResource Margin4}" Style="{StaticResource DefTextBox}" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + vm.SelectedSource.Id, v => v.txtId10.Text).DisposeWith(disposables); break; + + case EConfigType.NaiveProxy: + this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId100.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType100.Text).DisposeWith(disposables); + break; + + case EConfigType.Juicity: + this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId101.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity101.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType101.Text).DisposeWith(disposables); + break; + + case EConfigType.Brook: + this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId102.Text).DisposeWith(disposables); + break; + + case EConfigType.Shadowquic: + this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId103.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity103.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType103.Text).DisposeWith(disposables); + break; } this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType.Text).DisposeWith(disposables); diff --git a/v2rayN/v2rayN/Views/MainWindow.xaml b/v2rayN/v2rayN/Views/MainWindow.xaml index 0b8c30b3..59b6e6ed 100644 --- a/v2rayN/v2rayN/Views/MainWindow.xaml +++ b/v2rayN/v2rayN/Views/MainWindow.xaml @@ -112,6 +112,23 @@ x:Name="menuAddAnytlsServer" Height="{StaticResource MenuItemHeight}" Header="{x:Static resx:ResUI.menuAddAnytlsServer}" /> + + + + + diff --git a/v2rayN/v2rayN/Views/MainWindow.xaml.cs b/v2rayN/v2rayN/Views/MainWindow.xaml.cs index 08bdd90b..b4d41965 100644 --- a/v2rayN/v2rayN/Views/MainWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/MainWindow.xaml.cs @@ -81,6 +81,10 @@ public partial class MainWindow this.BindCommand(ViewModel, vm => vm.AddTuicServerCmd, v => v.menuAddTuicServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddWireguardServerCmd, v => v.menuAddWireguardServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddAnytlsServerCmd, v => v.menuAddAnytlsServer).DisposeWith(disposables); + this.BindCommand(ViewModel, vm => vm.AddBrookServerCmd, v => v.menuAddBrookServer).DisposeWith(disposables); + this.BindCommand(ViewModel, vm => vm.AddJuicityServerCmd, v => v.menuAddJuicityServer).DisposeWith(disposables); + this.BindCommand(ViewModel, vm => vm.AddNaiveServerCmd, v => v.menuAddNaiveServer).DisposeWith(disposables); + this.BindCommand(ViewModel, vm => vm.AddShadowquicServerCmd, v => v.menuAddShadowquicServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddCustomServerCmd, v => v.menuAddCustomServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddServerViaClipboardCmd, v => v.menuAddServerViaClipboard).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddServerViaScanCmd, v => v.menuAddServerViaScan).DisposeWith(disposables); From 372e832651966772d28b4511478d679fccc2da75 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Mon, 28 Jul 2025 22:49:46 +0800 Subject: [PATCH 43/54] avalonia --- .../Views/AddServerWindow.axaml | 145 ++++++++++++++++++ .../Views/AddServerWindow.axaml.cs | 64 ++++++++ v2rayN/v2rayN.Desktop/Views/MainWindow.axaml | 5 + .../v2rayN.Desktop/Views/MainWindow.axaml.cs | 4 + 4 files changed, 218 insertions(+) diff --git a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml index 56cc82a0..84f00a4d 100644 --- a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml +++ b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml @@ -553,6 +553,151 @@ Width="400" Margin="{StaticResource Margin4}" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + lstStreamSecurity.Add(Global.StreamSecurityReality); cmbCoreType.IsEnabled = false; break; + + case EConfigType.NaiveProxy: + gridNaive.IsVisible = true; + sepa2.IsVisible = false; + gridTransport.IsVisible = false; + cmbAlpn.IsEnabled = false; + cmbFingerprint.IsEnabled = false; + cmbFingerprint.SelectedValue = string.Empty; + cmbCoreType.IsEnabled = false; + + cmbHeaderType100.ItemsSource = Global.NaiveProxyProtocols; + break; + + case EConfigType.Juicity: + gridJuicity.IsVisible = true; + sepa2.IsVisible = false; + gridTransport.IsVisible = false; + cmbAlpn.IsEnabled = false; + cmbFingerprint.IsEnabled = false; + cmbFingerprint.SelectedValue = string.Empty; + cmbCoreType.IsEnabled = false; + + cmbHeaderType101.ItemsSource = Global.CongestionControls; + break; + + case EConfigType.Brook: + gridBrook.IsVisible = true; + sepa2.IsVisible = false; + gridTransport.IsVisible = false; + gridTls.IsVisible = false; + cmbCoreType.IsEnabled = false; + break; + + case EConfigType.Shadowquic: + gridShadowquic.IsVisible = true; + sepa2.IsVisible = false; + gridTransport.IsVisible = false; + cmbFingerprint.IsEnabled = false; + cmbFingerprint.SelectedValue = string.Empty; + cmbCoreType.IsEnabled = false; + + cmbHeaderType103.ItemsSource = Global.CongestionControls; + break; } cmbStreamSecurity.ItemsSource = lstStreamSecurity; @@ -177,6 +220,27 @@ public partial class AddServerWindow : WindowBase case EConfigType.Anytls: this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId10.Text).DisposeWith(disposables); break; + + case EConfigType.NaiveProxy: + this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId100.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType100.SelectedValue).DisposeWith(disposables); + break; + + case EConfigType.Juicity: + this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId101.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity101.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType101.SelectedValue).DisposeWith(disposables); + break; + + case EConfigType.Brook: + this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId102.Text).DisposeWith(disposables); + break; + + case EConfigType.Shadowquic: + this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId103.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity103.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType103.SelectedValue).DisposeWith(disposables); + break; } this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType.SelectedValue).DisposeWith(disposables); diff --git a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml index af4ae529..9054ad69 100644 --- a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml +++ b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml @@ -47,6 +47,11 @@ + + + + + diff --git a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs index 2584310b..67bb67f3 100644 --- a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs @@ -84,6 +84,10 @@ public partial class MainWindow : WindowBase this.BindCommand(ViewModel, vm => vm.AddTuicServerCmd, v => v.menuAddTuicServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddWireguardServerCmd, v => v.menuAddWireguardServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddAnytlsServerCmd, v => v.menuAddAnytlsServer).DisposeWith(disposables); + this.BindCommand(ViewModel, vm => vm.AddBrookServerCmd, v => v.menuAddBrookServer).DisposeWith(disposables); + this.BindCommand(ViewModel, vm => vm.AddJuicityServerCmd, v => v.menuAddJuicityServer).DisposeWith(disposables); + this.BindCommand(ViewModel, vm => vm.AddNaiveServerCmd, v => v.menuAddNaiveServer).DisposeWith(disposables); + this.BindCommand(ViewModel, vm => vm.AddShadowquicServerCmd, v => v.menuAddShadowquicServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddCustomServerCmd, v => v.menuAddCustomServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddServerViaClipboardCmd, v => v.menuAddServerViaClipboard).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.AddServerViaScanCmd, v => v.menuAddServerViaScan).DisposeWith(disposables); From 8ec94fe812b21cea903c96853a14de92705f6cf3 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Mon, 28 Jul 2025 22:53:05 +0800 Subject: [PATCH 44/54] Fixes NaiveProxy --- v2rayN/ServiceLib/Handler/ConfigHandler.cs | 4 ++++ .../Services/CoreConfig/Minimal/CoreConfigNaiveService.cs | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs index 446f0813..95ad5486 100644 --- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -843,6 +843,10 @@ public class ConfigHandler profileItem.Address = profileItem.Address.TrimEx(); profileItem.Id = profileItem.Id.TrimEx(); profileItem.Network = string.Empty; + if (!Global.NaiveProxyProtocols.Contains(profileItem.HeaderType)) + { + profileItem.HeaderType = Global.NaiveProxyProtocols.FirstOrDefault()!; + } if (profileItem.StreamSecurity.IsNullOrEmpty()) { profileItem.StreamSecurity = Global.StreamSecurity; diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigNaiveService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigNaiveService.cs index f7a29230..0e1230cc 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigNaiveService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigNaiveService.cs @@ -35,7 +35,7 @@ public class CoreConfigNaiveService configJsonNode["listen"] = Global.SocksProtocol + Global.Loopback + ":" + AppHandler.Instance.GetLocalPort(EInboundProtocol.split).ToString(); // outbound - configJsonNode["proxy"] = (node.Network == "quic" ? "quic://" : Global.HttpsProtocol) + node.Id + "@" + node.Address + ":" + node.Port; + configJsonNode["proxy"] = (node.HeaderType == "quic" ? "quic://" : Global.HttpsProtocol) + node.Id + "@" + node.Address + ":" + node.Port; ret.Success = true; ret.Data = JsonUtils.Serialize(configJsonNode, true); From 3298286f01c8404a89aac585c5242889b9ac3537 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Mon, 28 Jul 2025 23:28:20 +0800 Subject: [PATCH 45/54] Add Naive etc. uri share URI sharing is not compatible with other clients. Compatibility was not part of the original design and it is intended solely for internal sharing and copying within the application. --- v2rayN/ServiceLib/Global.cs | 12 +++- v2rayN/ServiceLib/Handler/Fmt/BrookFmt.cs | 49 +++++++++++++++ v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs | 20 ++++++ v2rayN/ServiceLib/Handler/Fmt/JuicityFmt.cs | 63 +++++++++++++++++++ v2rayN/ServiceLib/Handler/Fmt/NaiveFmt.cs | 52 +++++++++++++++ .../ServiceLib/Handler/Fmt/ShadowquicFmt.cs | 63 +++++++++++++++++++ 6 files changed, 257 insertions(+), 2 deletions(-) create mode 100644 v2rayN/ServiceLib/Handler/Fmt/BrookFmt.cs create mode 100644 v2rayN/ServiceLib/Handler/Fmt/JuicityFmt.cs create mode 100644 v2rayN/ServiceLib/Handler/Fmt/NaiveFmt.cs create mode 100644 v2rayN/ServiceLib/Handler/Fmt/ShadowquicFmt.cs diff --git a/v2rayN/ServiceLib/Global.cs b/v2rayN/ServiceLib/Global.cs index e1daa015..e7a2cd27 100644 --- a/v2rayN/ServiceLib/Global.cs +++ b/v2rayN/ServiceLib/Global.cs @@ -170,7 +170,11 @@ public class Global { EConfigType.Hysteria2, "hysteria2://" }, { EConfigType.TUIC, "tuic://" }, { EConfigType.WireGuard, "wireguard://" }, - { EConfigType.Anytls, "anytls://" } + { EConfigType.Anytls, "anytls://" }, + { EConfigType.NaiveProxy, "naive://" }, + { EConfigType.Juicity, "juicity://" }, + { EConfigType.Brook, "brook://" }, + { EConfigType.Shadowquic, "shadowquic://" } }; public static readonly Dictionary ProtocolTypes = new() @@ -184,7 +188,11 @@ public class Global { EConfigType.Hysteria2, "hysteria2" }, { EConfigType.TUIC, "tuic" }, { EConfigType.WireGuard, "wireguard" }, - { EConfigType.Anytls, "anytls" } + { EConfigType.Anytls, "anytls" }, + { EConfigType.NaiveProxy, "naiveproxy" }, + { EConfigType.Juicity, "juicity" }, + { EConfigType.Brook, "brook" }, + { EConfigType.Shadowquic, "shadowquic" } }; public static readonly List VmessSecurities = diff --git a/v2rayN/ServiceLib/Handler/Fmt/BrookFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/BrookFmt.cs new file mode 100644 index 00000000..4bde23b5 --- /dev/null +++ b/v2rayN/ServiceLib/Handler/Fmt/BrookFmt.cs @@ -0,0 +1,49 @@ +using static QRCoder.PayloadGenerator; + +namespace ServiceLib.Handler.Fmt; +public class BrookFmt : BaseFmt +{ + public static ProfileItem? Resolve(string str, out string msg) + { + msg = ResUI.ConfigurationFormatIncorrect; + + var parsedUrl = Utils.TryUri(str); + if (parsedUrl == null) + { + return null; + } + + ProfileItem item = new() + { + ConfigType = EConfigType.Brook, + Remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped), + Address = parsedUrl.IdnHost, + Port = parsedUrl.Port, + }; + var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo); + item.Id = rawUserInfo; + + var query = Utils.ParseQueryString(parsedUrl.Query); + _ = ResolveStdTransport(query, ref item); + + return item; + } + + public static string? ToUri(ProfileItem? item) + { + if (item == null) + { + return null; + } + var remark = string.Empty; + if (item.Remarks.IsNotEmpty()) + { + remark = "#" + Utils.UrlEncode(item.Remarks); + } + var pw = item.Id; + var dicQuery = new Dictionary(); + _ = GetStdTransport(item, Global.None, ref dicQuery); + + return ToUri(EConfigType.Brook, item.Address, item.Port, pw, dicQuery, remark); + } +} diff --git a/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs b/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs index 814d753d..41e112ff 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs @@ -19,6 +19,10 @@ public class FmtHandler EConfigType.TUIC => TuicFmt.ToUri(item), EConfigType.WireGuard => WireguardFmt.ToUri(item), EConfigType.Anytls => AnytlsFmt.ToUri(item), + EConfigType.NaiveProxy => NaiveFmt.ToUri(item), + EConfigType.Juicity => JuicityFmt.ToUri(item), + EConfigType.Brook => BrookFmt.ToUri(item), + EConfigType.Shadowquic => ShadowquicFmt.ToUri(item), _ => null, }; @@ -80,6 +84,22 @@ public class FmtHandler { return AnytlsFmt.Resolve(str, out msg); } + else if (str.StartsWith(Global.ProtocolShares[EConfigType.NaiveProxy])) + { + return NaiveFmt.Resolve(str, out msg); + } + else if (str.StartsWith(Global.ProtocolShares[EConfigType.Juicity])) + { + return JuicityFmt.Resolve(str, out msg); + } + else if (str.StartsWith(Global.ProtocolShares[EConfigType.Brook])) + { + return BrookFmt.Resolve(str, out msg); + } + else if (str.StartsWith(Global.ProtocolShares[EConfigType.Shadowquic])) + { + return ShadowquicFmt.Resolve(str, out msg); + } else { msg = ResUI.NonvmessOrssProtocol; diff --git a/v2rayN/ServiceLib/Handler/Fmt/JuicityFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/JuicityFmt.cs new file mode 100644 index 00000000..d6cda203 --- /dev/null +++ b/v2rayN/ServiceLib/Handler/Fmt/JuicityFmt.cs @@ -0,0 +1,63 @@ +namespace ServiceLib.Handler.Fmt; + +public class JuicityFmt : BaseFmt +{ + public static ProfileItem? Resolve(string str, out string msg) + { + msg = ResUI.ConfigurationFormatIncorrect; + + ProfileItem item = new() + { + ConfigType = EConfigType.Juicity + }; + + var url = Utils.TryUri(str); + if (url == null) + { + return null; + } + + item.Address = url.IdnHost; + item.Port = url.Port; + item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped); + var rawUserInfo = Utils.UrlDecode(url.UserInfo); + var userInfoParts = rawUserInfo.Split(new[] { ':' }, 2); + if (userInfoParts.Length == 2) + { + item.Id = userInfoParts.First(); + item.Security = userInfoParts.Last(); + } + + var query = Utils.ParseQueryString(url.Query); + ResolveStdTransport(query, ref item); + item.HeaderType = query["congestion_control"] ?? ""; + + return item; + } + + public static string? ToUri(ProfileItem? item) + { + if (item == null) + { + return null; + } + + var remark = string.Empty; + if (item.Remarks.IsNotEmpty()) + { + remark = "#" + Utils.UrlEncode(item.Remarks); + } + var dicQuery = new Dictionary(); + if (item.Sni.IsNotEmpty()) + { + dicQuery.Add("sni", item.Sni); + } + if (item.Alpn.IsNotEmpty()) + { + dicQuery.Add("alpn", Utils.UrlEncode(item.Alpn)); + } + dicQuery.Add("congestion_control", item.HeaderType); + + return ToUri(EConfigType.Juicity, item.Address, item.Port, $"{item.Id}:{item.Security}", dicQuery, remark); + } +} diff --git a/v2rayN/ServiceLib/Handler/Fmt/NaiveFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/NaiveFmt.cs new file mode 100644 index 00000000..95c5583d --- /dev/null +++ b/v2rayN/ServiceLib/Handler/Fmt/NaiveFmt.cs @@ -0,0 +1,52 @@ +using static QRCoder.PayloadGenerator; + +namespace ServiceLib.Handler.Fmt; +public class NaiveFmt : BaseFmt +{ + public static ProfileItem? Resolve(string str, out string msg) + { + msg = ResUI.ConfigurationFormatIncorrect; + + var parsedUrl = Utils.TryUri(str); + if (parsedUrl == null) + { + return null; + } + + ProfileItem item = new() + { + ConfigType = EConfigType.NaiveProxy, + Remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped), + Address = parsedUrl.IdnHost, + Port = parsedUrl.Port, + }; + var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo); + item.Id = rawUserInfo; + + var query = Utils.ParseQueryString(parsedUrl.Query); + _ = ResolveStdTransport(query, ref item); + + item.HeaderType = query["protocol"] ?? ""; + + return item; + } + + public static string? ToUri(ProfileItem? item) + { + if (item == null) + { + return null; + } + var remark = string.Empty; + if (item.Remarks.IsNotEmpty()) + { + remark = "#" + Utils.UrlEncode(item.Remarks); + } + var pw = item.Id; + var dicQuery = new Dictionary(); + _ = GetStdTransport(item, Global.None, ref dicQuery); + dicQuery.Add("protocol", item.HeaderType); + + return ToUri(EConfigType.NaiveProxy, item.Address, item.Port, pw, dicQuery, remark); + } +} diff --git a/v2rayN/ServiceLib/Handler/Fmt/ShadowquicFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/ShadowquicFmt.cs new file mode 100644 index 00000000..468923da --- /dev/null +++ b/v2rayN/ServiceLib/Handler/Fmt/ShadowquicFmt.cs @@ -0,0 +1,63 @@ +namespace ServiceLib.Handler.Fmt; + +public class ShadowquicFmt : BaseFmt +{ + public static ProfileItem? Resolve(string str, out string msg) + { + msg = ResUI.ConfigurationFormatIncorrect; + + ProfileItem item = new() + { + ConfigType = EConfigType.Shadowquic + }; + + var url = Utils.TryUri(str); + if (url == null) + { + return null; + } + + item.Address = url.IdnHost; + item.Port = url.Port; + item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped); + var rawUserInfo = Utils.UrlDecode(url.UserInfo); + var userInfoParts = rawUserInfo.Split(new[] { ':' }, 2); + if (userInfoParts.Length == 2) + { + item.Id = userInfoParts.First(); + item.Security = userInfoParts.Last(); + } + + var query = Utils.ParseQueryString(url.Query); + ResolveStdTransport(query, ref item); + item.HeaderType = query["congestion_control"] ?? ""; + + return item; + } + + public static string? ToUri(ProfileItem? item) + { + if (item == null) + { + return null; + } + + var remark = string.Empty; + if (item.Remarks.IsNotEmpty()) + { + remark = "#" + Utils.UrlEncode(item.Remarks); + } + var dicQuery = new Dictionary(); + if (item.Sni.IsNotEmpty()) + { + dicQuery.Add("sni", item.Sni); + } + if (item.Alpn.IsNotEmpty()) + { + dicQuery.Add("alpn", Utils.UrlEncode(item.Alpn)); + } + dicQuery.Add("congestion_control", item.HeaderType); + + return ToUri(EConfigType.Shadowquic, item.Address, item.Port, $"{item.Id}:{item.Security}", dicQuery, remark); + } +} From f6b69264197fab7f648d099c0fc45d8858604dfa Mon Sep 17 00:00:00 2001 From: DHR60 Date: Mon, 28 Jul 2025 23:48:01 +0800 Subject: [PATCH 46/54] Adds Xray and Singbox config type support --- v2rayN/ServiceLib/Global.cs | 25 ++++++++++++ .../ServiceLib/Handler/CoreConfigHandler.cs | 8 +++- .../CoreConfig/CoreConfigSingboxService.cs | 13 ++++--- .../CoreConfig/CoreConfigV2rayService.cs | 39 +++++-------------- .../ServiceLib/Services/SpeedtestService.cs | 13 ++++--- 5 files changed, 55 insertions(+), 43 deletions(-) diff --git a/v2rayN/ServiceLib/Global.cs b/v2rayN/ServiceLib/Global.cs index e7a2cd27..f7371cf2 100644 --- a/v2rayN/ServiceLib/Global.cs +++ b/v2rayN/ServiceLib/Global.cs @@ -330,6 +330,31 @@ public class Global EConfigType.SOCKS, ]; + public static readonly HashSet XraySupportConfigType = + [ + EConfigType.VMess, + EConfigType.VLESS, + EConfigType.Shadowsocks, + EConfigType.Trojan, + EConfigType.WireGuard, + EConfigType.SOCKS, + EConfigType.HTTP, + ]; + + public static readonly HashSet SingboxSupportConfigType = + [ + EConfigType.VMess, + EConfigType.VLESS, + EConfigType.Shadowsocks, + EConfigType.Trojan, + EConfigType.Hysteria2, + EConfigType.TUIC, + EConfigType.Anytls, + EConfigType.WireGuard, + EConfigType.SOCKS, + EConfigType.HTTP, + ]; + public static readonly List DomainStrategies = [ "AsIs", diff --git a/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs b/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs index 0ee2e227..f2d38bfc 100644 --- a/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs @@ -27,10 +27,16 @@ public class CoreConfigHandler { result = await new CoreConfigSingboxService(config).GenerateClientConfigContent(node); } - else + else if (AppHandler.Instance.GetCoreType(node, node.ConfigType) == ECoreType.Xray) { result = await new CoreConfigV2rayService(config).GenerateClientConfigContent(node); } + else + { + result.Msg = ResUI.OperationFailed; + result.Success = false; + return result; + } if (result.Success != true) { return result; diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs index ea5d787d..5ff9c35d 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs @@ -1,4 +1,5 @@ using System.Data; +using System.Linq; using System.Net; using System.Net.NetworkInformation; using System.Reactive; @@ -142,7 +143,7 @@ public class CoreConfigSingboxService foreach (var it in selecteds) { - if (it.ConfigType == EConfigType.Custom) + if (!Global.SingboxSupportConfigType.Contains(it.ConfigType)) { continue; } @@ -378,7 +379,7 @@ public class CoreConfigSingboxService var proxyProfiles = new List(); foreach (var it in selecteds) { - if (it.ConfigType == EConfigType.Custom) + if (!Global.SingboxSupportConfigType.Contains(it.ConfigType)) { continue; } @@ -1117,7 +1118,7 @@ public class CoreConfigSingboxService var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile); string? prevOutboundTag = null; if (prevNode is not null - && prevNode.ConfigType != EConfigType.Custom) + && Global.SingboxSupportConfigType.Contains(prevNode.ConfigType)) { prevOutboundTag = $"prev-{Global.ProxyTag}"; var prevServer = await GenServer(prevNode); @@ -1208,7 +1209,7 @@ public class CoreConfigSingboxService { var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile); if (prevNode is not null - && prevNode.ConfigType != EConfigType.Custom) + && Global.SingboxSupportConfigType.Contains(prevNode.ConfigType)) { var prevOutbound = JsonUtils.Deserialize(txtOutbound); await GenOutbound(prevNode, prevOutbound); @@ -1315,7 +1316,7 @@ public class CoreConfigSingboxService // Next proxy var nextNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.NextProfile); if (nextNode is not null - && nextNode.ConfigType != EConfigType.Custom) + && Global.SingboxSupportConfigType.Contains(nextNode.ConfigType)) { nextOutbound ??= await GenServer(nextNode); nextOutbound.tag = outbound.tag; @@ -1652,7 +1653,7 @@ public class CoreConfigSingboxService var node = await AppHandler.Instance.GetProfileItemViaRemarks(outboundTag); if (node == null - || node.ConfigType == EConfigType.Custom) + || !Global.SingboxSupportConfigType.Contains(node.ConfigType)) { return Global.ProxyTag; } diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs index 966e23f5..0920cb95 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs @@ -1,3 +1,4 @@ +using System.Linq; using System.Net; using System.Net.NetworkInformation; using System.Text.Json.Nodes; @@ -116,11 +117,7 @@ public class CoreConfigV2rayService var proxyProfiles = new List(); foreach (var it in selecteds) { - if (it.ConfigType == EConfigType.Custom) - { - continue; - } - if (it.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls) + if (!Global.XraySupportConfigType.Contains(it.ConfigType)) { continue; } @@ -255,7 +252,7 @@ public class CoreConfigV2rayService foreach (var it in selecteds) { - if (it.ConfigType == EConfigType.Custom) + if (!Global.SingboxSupportConfigType.Contains(it.ConfigType)) { continue; } @@ -707,10 +704,7 @@ public class CoreConfigV2rayService var node = await AppHandler.Instance.GetProfileItemViaRemarks(outboundTag); if (node == null - || node.ConfigType == EConfigType.Custom - || node.ConfigType == EConfigType.Hysteria2 - || node.ConfigType == EConfigType.TUIC - || node.ConfigType == EConfigType.Anytls) + || !Global.SingboxSupportConfigType.Contains(node.ConfigType)) { return Global.ProxyTag; } @@ -1289,10 +1283,7 @@ public class CoreConfigV2rayService // Previous proxy var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile); if (prevNode is not null - && prevNode.ConfigType != EConfigType.Custom - && prevNode.ConfigType != EConfigType.Hysteria2 - && prevNode.ConfigType != EConfigType.TUIC - && prevNode.ConfigType != EConfigType.Anytls + && Global.SingboxSupportConfigType.Contains(prevNode.ConfigType) && Utils.IsDomain(prevNode.Address)) { domainList.Add(prevNode.Address); @@ -1301,10 +1292,7 @@ public class CoreConfigV2rayService // Next proxy var nextNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.NextProfile); if (nextNode is not null - && nextNode.ConfigType != EConfigType.Custom - && nextNode.ConfigType != EConfigType.Hysteria2 - && nextNode.ConfigType != EConfigType.TUIC - && nextNode.ConfigType != EConfigType.Anytls + && Global.SingboxSupportConfigType.Contains(nextNode.ConfigType) && Utils.IsDomain(nextNode.Address)) { domainList.Add(nextNode.Address); @@ -1420,10 +1408,7 @@ public class CoreConfigV2rayService var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile); string? prevOutboundTag = null; if (prevNode is not null - && prevNode.ConfigType != EConfigType.Custom - && prevNode.ConfigType != EConfigType.Hysteria2 - && prevNode.ConfigType != EConfigType.TUIC - && prevNode.ConfigType != EConfigType.Anytls) + && Global.XraySupportConfigType.Contains(prevNode.ConfigType)) { var prevOutbound = JsonUtils.Deserialize(txtOutbound); await GenOutbound(prevNode, prevOutbound); @@ -1496,10 +1481,7 @@ public class CoreConfigV2rayService { var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile); if (prevNode is not null - && prevNode.ConfigType != EConfigType.Custom - && prevNode.ConfigType != EConfigType.Hysteria2 - && prevNode.ConfigType != EConfigType.TUIC - && prevNode.ConfigType != EConfigType.Anytls) + && !Global.XraySupportConfigType.Contains(prevNode.ConfigType)) { var prevOutbound = JsonUtils.Deserialize(txtOutbound); await GenOutbound(prevNode, prevOutbound); @@ -1566,10 +1548,7 @@ public class CoreConfigV2rayService // Next proxy var nextNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.NextProfile); if (nextNode is not null - && nextNode.ConfigType != EConfigType.Custom - && nextNode.ConfigType != EConfigType.Hysteria2 - && nextNode.ConfigType != EConfigType.TUIC - && nextNode.ConfigType != EConfigType.Anytls) + && !Global.XraySupportConfigType.Contains(nextNode.ConfigType)) { if (nextOutbound == null) { diff --git a/v2rayN/ServiceLib/Services/SpeedtestService.cs b/v2rayN/ServiceLib/Services/SpeedtestService.cs index 9c97a217..458cc334 100644 --- a/v2rayN/ServiceLib/Services/SpeedtestService.cs +++ b/v2rayN/ServiceLib/Services/SpeedtestService.cs @@ -1,5 +1,6 @@ using System.Collections.Concurrent; using System.Diagnostics; +using System.Linq; using System.Net; using System.Net.Sockets; @@ -70,7 +71,7 @@ public class SpeedtestService var lstSelected = new List(); foreach (var it in selecteds) { - if (it.ConfigType == EConfigType.Custom) + if (!(Global.XraySupportConfigType.Contains(it.ConfigType) || Global.SingboxSupportConfigType.Contains(it.ConfigType))) { continue; } @@ -122,7 +123,7 @@ public class SpeedtestService List tasks = []; foreach (var it in selecteds) { - if (it.ConfigType == EConfigType.Custom) + if (!(Global.XraySupportConfigType.Contains(it.ConfigType) || Global.SingboxSupportConfigType.Contains(it.ConfigType))) { continue; } @@ -207,7 +208,7 @@ public class SpeedtestService { continue; } - if (it.ConfigType == EConfigType.Custom) + if (!(Global.XraySupportConfigType.Contains(it.ConfigType) || Global.SingboxSupportConfigType.Contains(it.ConfigType))) { continue; } @@ -244,7 +245,7 @@ public class SpeedtestService UpdateFunc(it.IndexId, "", ResUI.SpeedtestingSkip); continue; } - if (it.ConfigType == EConfigType.Custom) + if (!(Global.XraySupportConfigType.Contains(it.ConfigType) || Global.SingboxSupportConfigType.Contains(it.ConfigType))) { continue; } @@ -358,8 +359,8 @@ public class SpeedtestService private List> GetTestBatchItem(List lstSelected, int pageSize) { List> lstTest = new(); - var lst1 = lstSelected.Where(t => t.ConfigType is not (EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls)).ToList(); - var lst2 = lstSelected.Where(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls).ToList(); + var lst1 = lstSelected.Where(t => Global.XraySupportConfigType.Contains(t.ConfigType)).ToList(); + var lst2 = lstSelected.Where(t => Global.SingboxSupportConfigType.Contains(t.ConfigType) && !Global.XraySupportConfigType.Contains(t.ConfigType)).ToList(); for (var num = 0; num < (int)Math.Ceiling(lst1.Count * 1.0 / pageSize); num++) { From 8ed5c575fc6f82249c7d537cb44a2a0941336b67 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Tue, 29 Jul 2025 09:13:15 +0800 Subject: [PATCH 47/54] Fixes hy2 --- .../Minimal/CoreConfigHy2Service.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigHy2Service.cs b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigHy2Service.cs index 3241aecd..819a3e55 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigHy2Service.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigHy2Service.cs @@ -60,6 +60,27 @@ public class CoreConfigHy2Service configJsonNode["server"] = node.Address + ":" + port; configJsonNode["auth"] = node.Id; + if (node.Sni.IsNotEmpty()) + { + configJsonNode["tls"] = new JsonObject + { + ["sni"] = node.Sni, + ["insecure"] = node.AllowInsecure.ToLower() == "true" + }; + } + + if (node.Path.IsNotEmpty()) + { + configJsonNode["obfs"] = new JsonObject + { + ["type"] = "salamander ", + ["salamander"] = new JsonObject + { + ["password"] = node.Path + } + }; + } + var bandwidthObject = new JsonObject(); if (_config.HysteriaItem.UpMbps > 0) { From 35f9e4bca05a794d887857bf81b0153fc290c159 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Tue, 29 Jul 2025 09:24:32 +0800 Subject: [PATCH 48/54] Fixes --- v2rayN/ServiceLib/Handler/AppHandler.cs | 2 +- v2rayN/ServiceLib/Handler/CoreConfigHandler.cs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/v2rayN/ServiceLib/Handler/AppHandler.cs b/v2rayN/ServiceLib/Handler/AppHandler.cs index 1422ad73..699162a5 100644 --- a/v2rayN/ServiceLib/Handler/AppHandler.cs +++ b/v2rayN/ServiceLib/Handler/AppHandler.cs @@ -256,7 +256,7 @@ public sealed class AppHandler var coreType = GetCoreType(profileItem, profileItem.ConfigType); ECoreType? preCoreType = null; - var pureEndpointCore = GetSplitCoreType(profileItem, profileItem.ConfigType); + var pureEndpointCore = profileItem.CoreType ?? GetSplitCoreType(profileItem, profileItem.ConfigType); var splitRouteCore = _config.SplitCoreItem.RouteCoreType; var enableTun = _config.TunModeItem.EnableTun; diff --git a/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs b/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs index f2d38bfc..41005efe 100644 --- a/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs @@ -23,11 +23,11 @@ public class CoreConfigHandler _ => await GenerateClientCustomConfig(node, fileName) }; } - else if (AppHandler.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box) + else if (AppHandler.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box || (Global.SingboxSupportConfigType.Contains(node.ConfigType) && !Global.XraySupportConfigType.Contains(node.ConfigType))) { result = await new CoreConfigSingboxService(config).GenerateClientConfigContent(node); } - else if (AppHandler.Instance.GetCoreType(node, node.ConfigType) == ECoreType.Xray) + else if (Global.XraySupportConfigType.Contains(node.ConfigType)) { result = await new CoreConfigV2rayService(config).GenerateClientConfigContent(node); } @@ -159,7 +159,7 @@ public class CoreConfigHandler { result = await new CoreConfigSingboxService(config).GenerateClientSpeedtestConfig(node, port); } - else + else if (Global.XraySupportConfigType.Contains(node.ConfigType)) { result = await new CoreConfigV2rayService(config).GenerateClientSpeedtestConfig(node, port); } @@ -179,7 +179,7 @@ public class CoreConfigHandler { result = await new CoreConfigSingboxService(config).GenerateClientMultipleLoadConfig(selecteds); } - else + else if (coreType == ECoreType.Xray) { result = await new CoreConfigV2rayService(config).GenerateClientMultipleLoadConfig(selecteds, multipleLoad); } From 147e837bbb7fdcb20c3594738daa7f6b0581e120 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Wed, 30 Jul 2025 09:08:59 +0800 Subject: [PATCH 49/54] Enhances core configuration handling. --- v2rayN/ServiceLib/Common/Utils.cs | 21 +++++++++++++++++-- v2rayN/ServiceLib/Global.cs | 4 ++-- v2rayN/ServiceLib/Handler/CoreAdminHandler.cs | 2 +- v2rayN/ServiceLib/Handler/CoreHandler.cs | 20 +++++++++--------- 4 files changed, 32 insertions(+), 15 deletions(-) diff --git a/v2rayN/ServiceLib/Common/Utils.cs b/v2rayN/ServiceLib/Common/Utils.cs index 369cc0f4..fd0002c5 100644 --- a/v2rayN/ServiceLib/Common/Utils.cs +++ b/v2rayN/ServiceLib/Common/Utils.cs @@ -813,7 +813,7 @@ public class Utils } } - public static string GetBinConfigPath(string filename = "") + public static string GetBinConfigPath(string filename = "", ECoreType coreType = ECoreType.v2rayN) { var tempPath = Path.Combine(StartupPath(), "binConfigs"); if (!Directory.Exists(tempPath)) @@ -827,10 +827,27 @@ public class Utils } else { - return Path.Combine(tempPath, filename); + return Path.Combine(tempPath, GetBinConfigFileName(filename, coreType)); } } + public static string GetBinConfigFileName(string filename, ECoreType coreType = ECoreType.v2rayN) + { + var fileSuffix = coreType switch + { + ECoreType.sing_box => ".json", + ECoreType.Xray => ".json", + ECoreType.hysteria2 => ".json", + ECoreType.naiveproxy => ".json", + ECoreType.tuic => ".json", + ECoreType.juicity => ".json", + ECoreType.brook => ".cac", + ECoreType.shadowquic => ".yaml", + _ => string.Empty + }; + return filename.EndsWith(fileSuffix) ? filename : $"{filename}{fileSuffix}"; + } + #endregion TempPath #region Platform diff --git a/v2rayN/ServiceLib/Global.cs b/v2rayN/ServiceLib/Global.cs index f7371cf2..773f0bc9 100644 --- a/v2rayN/ServiceLib/Global.cs +++ b/v2rayN/ServiceLib/Global.cs @@ -12,8 +12,8 @@ public class Global public const string PromotionUrl = @"aHR0cHM6Ly85LjIzNDQ1Ni54eXovYWJjLmh0bWw="; public const string ConfigFileName = "guiNConfig.json"; - public const string CoreConfigFileName = "config.json"; - public const string CorePreConfigFileName = "configPre.json"; + public const string CoreConfigFileName = "config"; + public const string CorePreConfigFileName = "configPre"; public const string CoreSpeedtestConfigFileName = "configTest{0}.json"; public const string CoreMultipleLoadConfigFileName = "configMultipleLoad.json"; public const string ClashMixinConfigFileName = "Mixin.yaml"; diff --git a/v2rayN/ServiceLib/Handler/CoreAdminHandler.cs b/v2rayN/ServiceLib/Handler/CoreAdminHandler.cs index ec99b26b..9ce5a411 100644 --- a/v2rayN/ServiceLib/Handler/CoreAdminHandler.cs +++ b/v2rayN/ServiceLib/Handler/CoreAdminHandler.cs @@ -35,7 +35,7 @@ public class CoreAdminHandler { StringBuilder sb = new(); sb.AppendLine("#!/bin/bash"); - var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetBinConfigPath(configPath).AppendQuotes())}"; + var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetBinConfigPath(configPath, coreInfo.CoreType).AppendQuotes())}"; sb.AppendLine($"sudo -S {cmdLine}"); var shFilePath = await FileManager.CreateLinuxShellFile("run_as_sudo.sh", sb.ToString(), true); diff --git a/v2rayN/ServiceLib/Handler/CoreHandler.cs b/v2rayN/ServiceLib/Handler/CoreHandler.cs index 201b9ba6..3ab29804 100644 --- a/v2rayN/ServiceLib/Handler/CoreHandler.cs +++ b/v2rayN/ServiceLib/Handler/CoreHandler.cs @@ -101,7 +101,7 @@ public class CoreHandler { var coreType = selecteds.Exists(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls) ? ECoreType.sing_box : ECoreType.Xray; var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false)); - var configPath = Utils.GetBinConfigPath(fileName); + var configPath = Utils.GetBinConfigPath(fileName, coreType); var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType); UpdateFunc(false, result.Msg); if (result.Success != true) @@ -130,15 +130,15 @@ public class CoreHandler return -1; } + var coreType = AppHandler.Instance.GetCoreType(node, node.ConfigType); var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false)); - var configPath = Utils.GetBinConfigPath(fileName); + var configPath = Utils.GetBinConfigPath(fileName, coreType); var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, node, testItem, configPath); if (result.Success != true) { return -1; } - var coreType = AppHandler.Instance.GetCoreType(node, node.ConfigType); var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType); var proc = await RunProcess(coreInfo, fileName, true, false); if (proc is null) @@ -231,7 +231,8 @@ public class CoreHandler private async Task CoreStart(CoreLaunchContext context) { - var fileName = Utils.GetBinConfigPath(Global.CoreConfigFileName); + var coreType = context.SplitCore ? context.PureEndpointCore : context.CoreType; + var fileName = Utils.GetBinConfigPath(Global.CoreConfigFileName, coreType); var result = context.SplitCore ? await CoreConfigHandler.GeneratePureEndpointConfig(context.Node, fileName) : await CoreConfigHandler.GenerateClientConfig(context.Node, fileName); @@ -257,7 +258,7 @@ public class CoreHandler var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(context.CoreType); var displayLog = context.Node.ConfigType != EConfigType.Custom || context.Node.DisplayLog; - var proc = await RunProcess(coreInfo, Global.CoreConfigFileName, displayLog, true); + var proc = await RunProcess(coreInfo, Utils.GetBinConfigFileName(Global.CoreConfigFileName, coreType), displayLog, true); if (proc is null) { @@ -266,8 +267,7 @@ public class CoreHandler } _process = proc; - var (_, coreType, preCoreType) = AppHandler.Instance.GetCoreAndPreType(context.Node); - _config.RunningCoreType = (ECoreType)(preCoreType != null ? preCoreType : coreType); + _config.RunningCoreType = (ECoreType)(context.PreCoreType != null ? context.PreCoreType : coreType); return true; } @@ -278,7 +278,7 @@ public class CoreHandler return true; // No pre-core needed, consider successful } - var fileName = Utils.GetBinConfigPath(Global.CorePreConfigFileName); + var fileName = Utils.GetBinConfigPath(Global.CorePreConfigFileName, (ECoreType)context.PreCoreType); var itemSocks = new ProfileItem() { CoreType = context.PreCoreType, @@ -296,7 +296,7 @@ public class CoreHandler } var coreInfo = CoreInfoHandler.Instance.GetCoreInfo((ECoreType)context.PreCoreType); - var proc = await RunProcess(coreInfo, Global.CorePreConfigFileName, true, true); + var proc = await RunProcess(coreInfo, Utils.GetBinConfigFileName(Global.CorePreConfigFileName, (ECoreType)context.PreCoreType), true, true); if (proc is null || (_process?.HasExited == true)) { @@ -355,7 +355,7 @@ public class CoreHandler StartInfo = new() { FileName = fileName, - Arguments = string.Format(coreInfo.Arguments, coreInfo.AbsolutePath ? Utils.GetBinConfigPath(configPath).AppendQuotes() : configPath), + Arguments = string.Format(coreInfo.Arguments, coreInfo.AbsolutePath ? Utils.GetBinConfigPath(configPath, coreInfo.CoreType).AppendQuotes() : configPath), WorkingDirectory = Utils.GetBinConfigPath(), UseShellExecute = false, RedirectStandardOutput = displayLog, From 15c3eea92e70e6897890985ad76b255f937fc88f Mon Sep 17 00:00:00 2001 From: DHR60 Date: Wed, 30 Jul 2025 09:20:18 +0800 Subject: [PATCH 50/54] Fixes Shadowquic Config Generate --- .../Services/CoreConfig/Minimal/CoreConfigShadowquicService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigShadowquicService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigShadowquicService.cs index 232b56df..c16c99f3 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigShadowquicService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigShadowquicService.cs @@ -53,7 +53,7 @@ public class CoreConfigShadowquicService configYamlNode["inbound"] = inboundNode; // outbound - var alpn = new JsonArray(); + var alpn = new List(); foreach (var item in node.GetAlpn() ?? new List()) { alpn.Add(item); From bee0521e47f6722c7cde6d5b620d5f375b558a61 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Thu, 31 Jul 2025 10:46:01 +0800 Subject: [PATCH 51/54] Refactor --- .../ServiceLib/Handler/CoreConfigHandler.cs | 228 ++++++++++-------- v2rayN/ServiceLib/Handler/CoreHandler.cs | 65 +---- v2rayN/ServiceLib/Models/CoreLaunchContext.cs | 53 ++++ .../CoreConfig/CoreConfigServiceBase.cs | 83 +++++++ .../CoreConfig/CoreConfigSingboxService.cs | 24 +- .../CoreConfig/CoreConfigV2rayService.cs | 37 ++- .../Minimal/CoreConfigBrookService.cs | 14 +- .../{ => Minimal}/CoreConfigClashService.cs | 20 +- .../Minimal/CoreConfigHy2Service.cs | 22 +- .../Minimal/CoreConfigJuicityService.cs | 14 +- .../Minimal/CoreConfigNaiveService.cs | 14 +- .../Minimal/CoreConfigShadowquicService.cs | 14 +- .../Minimal/CoreConfigTuicService.cs | 14 +- .../ViewModels/ProfilesViewModel.cs | 6 +- 14 files changed, 338 insertions(+), 270 deletions(-) create mode 100644 v2rayN/ServiceLib/Models/CoreLaunchContext.cs create mode 100644 v2rayN/ServiceLib/Services/CoreConfig/CoreConfigServiceBase.cs rename v2rayN/ServiceLib/Services/CoreConfig/{ => Minimal}/CoreConfigClashService.cs (93%) diff --git a/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs b/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs index 41005efe..780e6f2b 100644 --- a/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs @@ -9,33 +9,26 @@ public class CoreConfigHandler { private static readonly string _tag = "CoreConfigHandler"; - public static async Task GenerateClientConfig(ProfileItem node, string? fileName) + public static async Task GenerateClientConfig(CoreLaunchContext context, string? fileName) { - var config = AppHandler.Instance.Config; var result = new RetResult(); - if (node.ConfigType == EConfigType.Custom) + if (context.ConfigType == EConfigType.Custom) { - result = node.CoreType switch - { - ECoreType.mihomo => await new CoreConfigClashService(config).GenerateClientCustomConfig(node, fileName), - ECoreType.sing_box => await new CoreConfigSingboxService(config).GenerateClientCustomConfig(node, fileName), - _ => await GenerateClientCustomConfig(node, fileName) - }; - } - else if (AppHandler.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box || (Global.SingboxSupportConfigType.Contains(node.ConfigType) && !Global.XraySupportConfigType.Contains(node.ConfigType))) - { - result = await new CoreConfigSingboxService(config).GenerateClientConfigContent(node); - } - else if (Global.XraySupportConfigType.Contains(node.ConfigType)) - { - result = await new CoreConfigV2rayService(config).GenerateClientConfigContent(node); + result = await GetCoreConfigServiceForCustom(context.CoreType).GenerateClientCustomConfig(context.Node, fileName); } else { - result.Msg = ResUI.OperationFailed; - result.Success = false; - return result; + try + { + result = await GetCoreConfigServiceForClientConfig(context.CoreType).GenerateClientConfigContent(context.Node); + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + result.Msg = ResUI.FailedGenDefaultConfiguration; + return result; + } } if (result.Success != true) { @@ -49,96 +42,44 @@ public class CoreConfigHandler return result; } - public static async Task GeneratePureEndpointConfig(ProfileItem node, string? fileName) + public static async Task GeneratePassthroughConfig(CoreLaunchContext context, string? fileName) { - var config = AppHandler.Instance.Config; var result = new RetResult(); - var coreType = AppHandler.Instance.GetSplitCoreType(node, node.ConfigType); - - result = coreType switch - { - ECoreType.sing_box => await new CoreConfigSingboxService(config).GeneratePureEndpointConfig(node), - ECoreType.Xray => await new CoreConfigV2rayService(config).GeneratePureEndpointConfig(node), - ECoreType.hysteria2 => await new CoreConfigHy2Service(config).GeneratePureEndpointConfig(node), - ECoreType.naiveproxy => await new CoreConfigNaiveService(config).GeneratePureEndpointConfig(node), - ECoreType.tuic => await new CoreConfigTuicService(config).GeneratePureEndpointConfig(node), - ECoreType.juicity => await new CoreConfigJuicityService(config).GeneratePureEndpointConfig(node), - ECoreType.brook => await new CoreConfigBrookService(config).GeneratePureEndpointConfig(node), - ECoreType.shadowquic => await new CoreConfigShadowquicService(config).GeneratePureEndpointConfig(node), - _ => throw new NotImplementedException(), - }; - - if (result.Success != true) - { - return result; - } - if (fileName.IsNotEmpty() && result.Data != null) - { - await File.WriteAllTextAsync(fileName, result.Data.ToString()); - } - return result; - } - - private static async Task GenerateClientCustomConfig(ProfileItem node, string? fileName) - { - var ret = new RetResult(); try { - if (node == null || fileName is null) - { - ret.Msg = ResUI.CheckServerSettings; - return ret; - } - - if (File.Exists(fileName)) - { - File.SetAttributes(fileName, FileAttributes.Normal); //If the file has a read-only attribute, direct deletion will fail - File.Delete(fileName); - } - - string addressFileName = node.Address; - if (!File.Exists(addressFileName)) - { - addressFileName = Utils.GetConfigPath(addressFileName); - } - if (!File.Exists(addressFileName)) - { - ret.Msg = ResUI.FailedGenDefaultConfiguration; - return ret; - } - File.Copy(addressFileName, fileName); - File.SetAttributes(fileName, FileAttributes.Normal); //Copy will keep the attributes of addressFileName, so we need to add write permissions to fileName just in case of addressFileName is a read-only file. - - //check again - if (!File.Exists(fileName)) - { - ret.Msg = ResUI.FailedGenDefaultConfiguration; - return ret; - } - - ret.Msg = string.Format(ResUI.SuccessfulConfiguration, ""); - ret.Success = true; - return await Task.FromResult(ret); + result = await GetCoreConfigServiceForPassthrough(context.CoreType).GeneratePassthroughConfig(context.Node); } catch (Exception ex) { Logging.SaveLog(_tag, ex); - ret.Msg = ResUI.FailedGenDefaultConfiguration; - return ret; + result.Msg = ResUI.FailedGenDefaultConfiguration; + return result; } + + if (result.Success != true) + { + return result; + } + if (fileName.IsNotEmpty() && result.Data != null) + { + await File.WriteAllTextAsync(fileName, result.Data.ToString()); + } + return result; } public static async Task GenerateClientSpeedtestConfig(Config config, string fileName, List selecteds, ECoreType coreType) { var result = new RetResult(); - if (coreType == ECoreType.sing_box) + try { - result = await new CoreConfigSingboxService(config).GenerateClientSpeedtestConfig(selecteds); + result = await GetCoreConfigServiceForMultipleSpeedtest(coreType).GenerateClientSpeedtestConfig(selecteds); } - else if (coreType == ECoreType.Xray) + catch (Exception ex) { - result = await new CoreConfigV2rayService(config).GenerateClientSpeedtestConfig(selecteds); + Logging.SaveLog(_tag, ex); + result.Msg = ResUI.FailedGenDefaultConfiguration; + return result; } if (result.Success != true) { @@ -148,21 +89,24 @@ public class CoreConfigHandler return result; } - public static async Task GenerateClientSpeedtestConfig(Config config, ProfileItem node, ServerTestItem testItem, string fileName) + public static async Task GenerateClientSpeedtestConfig(Config config, CoreLaunchContext context, ServerTestItem testItem, string fileName) { var result = new RetResult(); var initPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.speedtest); var port = Utils.GetFreePort(initPort + testItem.QueueNum); testItem.Port = port; - if (AppHandler.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box) + try { - result = await new CoreConfigSingboxService(config).GenerateClientSpeedtestConfig(node, port); + result = await GetCoreConfigServiceForSpeedtest(context.CoreType).GenerateClientSpeedtestConfig(context.Node, port); } - else if (Global.XraySupportConfigType.Contains(node.ConfigType)) + catch (Exception ex) { - result = await new CoreConfigV2rayService(config).GenerateClientSpeedtestConfig(node, port); + Logging.SaveLog(_tag, ex); + result.Msg = ResUI.FailedGenDefaultConfiguration; + return result; } + if (result.Success != true) { return result; @@ -191,4 +135,94 @@ public class CoreConfigHandler await File.WriteAllTextAsync(fileName, result.Data.ToString()); return result; } + + private static CoreConfigServiceMinimalBase GetCoreConfigServiceForPassthrough(ECoreType coreType) + { + switch (coreType) + { + case ECoreType.sing_box: + return new CoreConfigSingboxService(AppHandler.Instance.Config); + case ECoreType.Xray: + return new CoreConfigV2rayService(AppHandler.Instance.Config); + case ECoreType.hysteria2: + return new CoreConfigHy2Service(AppHandler.Instance.Config); + case ECoreType.naiveproxy: + return new CoreConfigNaiveService(AppHandler.Instance.Config); + case ECoreType.tuic: + return new CoreConfigTuicService(AppHandler.Instance.Config); + case ECoreType.juicity: + return new CoreConfigJuicityService(AppHandler.Instance.Config); + case ECoreType.brook: + return new CoreConfigBrookService(AppHandler.Instance.Config); + case ECoreType.shadowquic: + return new CoreConfigShadowquicService(AppHandler.Instance.Config); + default: + throw new NotImplementedException($"Core type {coreType} is not implemented for passthrough configuration."); + } + } + + private static CoreConfigServiceMinimalBase GetCoreConfigServiceForSpeedtest(ECoreType coreType) + { + switch (coreType) + { + case ECoreType.sing_box: + return new CoreConfigSingboxService(AppHandler.Instance.Config); + case ECoreType.Xray: + return new CoreConfigV2rayService(AppHandler.Instance.Config); + case ECoreType.hysteria2: + return new CoreConfigHy2Service(AppHandler.Instance.Config); + case ECoreType.naiveproxy: + return new CoreConfigNaiveService(AppHandler.Instance.Config); + case ECoreType.tuic: + return new CoreConfigTuicService(AppHandler.Instance.Config); + case ECoreType.juicity: + return new CoreConfigJuicityService(AppHandler.Instance.Config); + case ECoreType.brook: + return new CoreConfigBrookService(AppHandler.Instance.Config); + case ECoreType.shadowquic: + return new CoreConfigShadowquicService(AppHandler.Instance.Config); + default: + throw new NotImplementedException($"Core type {coreType} is not implemented for passthrough configuration."); + } + } + + private static CoreConfigServiceBase GetCoreConfigServiceForMultipleSpeedtest(ECoreType coreType) + { + switch (coreType) + { + case ECoreType.sing_box: + return new CoreConfigSingboxService(AppHandler.Instance.Config); + case ECoreType.Xray: + return new CoreConfigV2rayService(AppHandler.Instance.Config); + default: + throw new NotImplementedException($"Core type {coreType} is not implemented for passthrough configuration."); + } + } + + private static CoreConfigServiceMinimalBase GetCoreConfigServiceForCustom(ECoreType coreType) + { + switch (coreType) + { + case ECoreType.mihomo: + return new CoreConfigClashService(AppHandler.Instance.Config); + case ECoreType.sing_box: + return new CoreConfigSingboxService(AppHandler.Instance.Config); + default: + // CoreConfigServiceMinimalBase + return new CoreConfigV2rayService(AppHandler.Instance.Config); + } + } + + private static CoreConfigServiceBase GetCoreConfigServiceForClientConfig(ECoreType coreType) + { + switch (coreType) + { + case ECoreType.sing_box: + return new CoreConfigSingboxService(AppHandler.Instance.Config); + case ECoreType.Xray: + return new CoreConfigV2rayService(AppHandler.Instance.Config); + default: + throw new NotImplementedException($"Core type {coreType} is not implemented for client configuration."); + } + } } diff --git a/v2rayN/ServiceLib/Handler/CoreHandler.cs b/v2rayN/ServiceLib/Handler/CoreHandler.cs index 3ab29804..01838cc1 100644 --- a/v2rayN/ServiceLib/Handler/CoreHandler.cs +++ b/v2rayN/ServiceLib/Handler/CoreHandler.cs @@ -130,10 +130,12 @@ public class CoreHandler return -1; } - var coreType = AppHandler.Instance.GetCoreType(node, node.ConfigType); + var context = new CoreLaunchContext(node, _config); + context.AdjustForConfigType(); + var coreType = context.CoreType; var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false)); var configPath = Utils.GetBinConfigPath(fileName, coreType); - var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, node, testItem, configPath); + var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, context, testItem, configPath); if (result.Success != true) { return -1; @@ -179,63 +181,13 @@ public class CoreHandler #region Private - /// - /// Core launch context that encapsulates all parameters required for launching - /// - private class CoreLaunchContext - { - public ProfileItem Node { get; set; } - public bool SplitCore { get; set; } - public ECoreType CoreType { get; set; } - public ECoreType? PreCoreType { get; set; } - public ECoreType PureEndpointCore { get; set; } - public ECoreType SplitRouteCore { get; set; } - public bool EnableTun { get; set; } - public int PreSocksPort { get; set; } - - public CoreLaunchContext(ProfileItem node, Config config) - { - Node = node; - SplitCore = config.SplitCoreItem.EnableSplitCore; - CoreType = AppHandler.Instance.GetCoreType(node, node.ConfigType); - PureEndpointCore = AppHandler.Instance.GetSplitCoreType(node, node.ConfigType); - SplitRouteCore = config.SplitCoreItem.RouteCoreType; - EnableTun = config.TunModeItem.EnableTun; - PreSocksPort = 0; - PreCoreType = null; - } - - /// - /// Adjust context parameters based on configuration type - /// - public void AdjustForConfigType() - { - (SplitCore, CoreType, PreCoreType) = AppHandler.Instance.GetCoreAndPreType(Node); - if (Node.ConfigType == EConfigType.Custom) - { - if (Node.PreSocksPort > 0) - { - PreSocksPort = Node.PreSocksPort.Value; - } - else - { - EnableTun = false; - } - } - else if (PreCoreType != null) - { - PreSocksPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.split); - } - } - } - private async Task CoreStart(CoreLaunchContext context) { var coreType = context.SplitCore ? context.PureEndpointCore : context.CoreType; var fileName = Utils.GetBinConfigPath(Global.CoreConfigFileName, coreType); var result = context.SplitCore - ? await CoreConfigHandler.GeneratePureEndpointConfig(context.Node, fileName) - : await CoreConfigHandler.GenerateClientConfig(context.Node, fileName); + ? await CoreConfigHandler.GeneratePassthroughConfig(context, fileName) + : await CoreConfigHandler.GenerateClientConfig(context, fileName); if (result.Success != true) { @@ -287,8 +239,9 @@ public class CoreHandler Sni = context.EnableTun && Utils.IsDomain(context.Node.Address) ? context.Node.Address : string.Empty, //Tun2SocksAddress Port = context.PreSocksPort }; - - var result = await CoreConfigHandler.GenerateClientConfig(itemSocks, fileName); + var itemSocksLaunch = new CoreLaunchContext(itemSocks, _config); + + var result = await CoreConfigHandler.GenerateClientConfig(itemSocksLaunch, fileName); if (!result.Success) { UpdateFunc(true, result.Msg); diff --git a/v2rayN/ServiceLib/Models/CoreLaunchContext.cs b/v2rayN/ServiceLib/Models/CoreLaunchContext.cs new file mode 100644 index 00000000..cf327dac --- /dev/null +++ b/v2rayN/ServiceLib/Models/CoreLaunchContext.cs @@ -0,0 +1,53 @@ +namespace ServiceLib.Models; + +/// +/// Core launch context that encapsulates all parameters required for launching +/// +public class CoreLaunchContext +{ + public ProfileItem Node { get; set; } + public bool SplitCore { get; set; } + public ECoreType CoreType { get; set; } + public ECoreType? PreCoreType { get; set; } + public ECoreType PureEndpointCore { get; set; } + public ECoreType SplitRouteCore { get; set; } + public bool EnableTun { get; set; } + public int PreSocksPort { get; set; } + public EConfigType ConfigType { get; set; } + + public CoreLaunchContext(ProfileItem node, Config config) + { + Node = node; + SplitCore = config.SplitCoreItem.EnableSplitCore; + CoreType = AppHandler.Instance.GetCoreType(node, node.ConfigType); + PureEndpointCore = AppHandler.Instance.GetSplitCoreType(node, node.ConfigType); + SplitRouteCore = config.SplitCoreItem.RouteCoreType; + EnableTun = config.TunModeItem.EnableTun; + PreSocksPort = 0; + PreCoreType = null; + ConfigType = node.ConfigType; + } + + /// + /// Adjust context parameters based on configuration type + /// + public void AdjustForConfigType() + { + (SplitCore, CoreType, PreCoreType) = AppHandler.Instance.GetCoreAndPreType(Node); + if (Node.ConfigType == EConfigType.Custom) + { + if (Node.PreSocksPort > 0) + { + PreSocksPort = Node.PreSocksPort.Value; + } + else + { + EnableTun = false; + } + } + else if (PreCoreType != null) + { + PreSocksPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.split); + } + } +} diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigServiceBase.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigServiceBase.cs new file mode 100644 index 00000000..9015fbe6 --- /dev/null +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigServiceBase.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using YamlDotNet.Core.Tokens; + +namespace ServiceLib.Services.CoreConfig; + +public abstract class CoreConfigServiceMinimalBase(Config config) +{ + public virtual string _tag => GetType().Name; + protected Config _config = config; + + public virtual Task GeneratePassthroughConfig(ProfileItem node) + { + return GeneratePassthroughConfig(node, AppHandler.Instance.GetLocalPort(EInboundProtocol.split)); + } + public virtual Task GenerateClientSpeedtestConfig(ProfileItem node, int port) + { + return GeneratePassthroughConfig(node, port); + } + protected abstract Task GeneratePassthroughConfig(ProfileItem node, int port); + public virtual async Task GenerateClientCustomConfig(ProfileItem node, string? fileName) + { + var ret = new RetResult(); + try + { + if (node == null || fileName is null) + { + ret.Msg = ResUI.CheckServerSettings; + return ret; + } + + if (File.Exists(fileName)) + { + File.SetAttributes(fileName, FileAttributes.Normal); //If the file has a read-only attribute, direct deletion will fail + File.Delete(fileName); + } + + string addressFileName = node.Address; + if (!File.Exists(addressFileName)) + { + addressFileName = Utils.GetConfigPath(addressFileName); + } + if (!File.Exists(addressFileName)) + { + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return ret; + } + File.Copy(addressFileName, fileName); + File.SetAttributes(fileName, FileAttributes.Normal); //Copy will keep the attributes of addressFileName, so we need to add write permissions to fileName just in case of addressFileName is a read-only file. + + //check again + if (!File.Exists(fileName)) + { + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return ret; + } + + ret.Msg = string.Format(ResUI.SuccessfulConfiguration, ""); + ret.Success = true; + return await Task.FromResult(ret); + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return ret; + } + } +} + +public abstract class CoreConfigServiceBase(Config config) : CoreConfigServiceMinimalBase(config) +{ + public abstract Task GenerateClientConfigContent(ProfileItem node); + public abstract Task GenerateClientMultipleLoadConfig(List selecteds, EMultipleLoad multipleLoad); + public virtual Task GenerateClientMultipleLoadConfig(List selecteds) + { + return GenerateClientMultipleLoadConfig(selecteds, EMultipleLoad.LeastPing); + } + public abstract Task GenerateClientSpeedtestConfig(List selecteds); +} diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs index 5ff9c35d..51678637 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigSingboxService.cs @@ -9,19 +9,11 @@ using ServiceLib.Models; namespace ServiceLib.Services.CoreConfig; -public class CoreConfigSingboxService +public class CoreConfigSingboxService(Config config) : CoreConfigServiceBase(config) { - private Config _config; - private static readonly string _tag = "CoreConfigSingboxService"; - - public CoreConfigSingboxService(Config config) - { - _config = config; - } - #region public gen function - public async Task GenerateClientConfigContent(ProfileItem node) + public override async Task GenerateClientConfigContent(ProfileItem node) { var ret = new RetResult(); try @@ -94,7 +86,7 @@ public class CoreConfigSingboxService } } - public async Task GenerateClientSpeedtestConfig(List selecteds) + public override async Task GenerateClientSpeedtestConfig(List selecteds) { var ret = new RetResult(); try @@ -271,7 +263,7 @@ public class CoreConfigSingboxService } } - public async Task GenerateClientSpeedtestConfig(ProfileItem node, int port) + public override async Task GenerateClientSpeedtestConfig(ProfileItem node, int port) { var ret = new RetResult(); try @@ -342,7 +334,7 @@ public class CoreConfigSingboxService } } - public async Task GenerateClientMultipleLoadConfig(List selecteds) + public override async Task GenerateClientMultipleLoadConfig(List selecteds, EMultipleLoad multipleLoad) { var ret = new RetResult(); try @@ -434,7 +426,7 @@ public class CoreConfigSingboxService } } - public async Task GenerateClientCustomConfig(ProfileItem node, string? fileName) + public override async Task GenerateClientCustomConfig(ProfileItem node, string? fileName) { var ret = new RetResult(); if (node == null || fileName is null) @@ -515,7 +507,7 @@ public class CoreConfigSingboxService } } - public async Task GeneratePureEndpointConfig(ProfileItem node) + protected override async Task GeneratePassthroughConfig(ProfileItem node, int port) { var ret = new RetResult(); try @@ -555,7 +547,7 @@ public class CoreConfigSingboxService type = EInboundProtocol.mixed.ToString(), tag = EInboundProtocol.socks.ToString(), listen = Global.Loopback, - listen_port = AppHandler.Instance.GetLocalPort(EInboundProtocol.split) + listen_port = port }; singboxConfig.inbounds = new() { inbound }; diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs index 0920cb95..7f219032 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigV2rayService.cs @@ -5,19 +5,11 @@ using System.Text.Json.Nodes; namespace ServiceLib.Services.CoreConfig; -public class CoreConfigV2rayService +public class CoreConfigV2rayService(Config config) : CoreConfigServiceBase(config) { - private Config _config; - private static readonly string _tag = "CoreConfigV2rayService"; - - public CoreConfigV2rayService(Config config) - { - _config = config; - } - #region public gen function - public async Task GenerateClientConfigContent(ProfileItem node) + public override async Task GenerateClientConfigContent(ProfileItem node) { var ret = new RetResult(); try @@ -78,7 +70,7 @@ public class CoreConfigV2rayService } } - public async Task GenerateClientMultipleLoadConfig(List selecteds, EMultipleLoad multipleLoad) + public override async Task GenerateClientMultipleLoadConfig(List selecteds, EMultipleLoad multipleLoad) { var ret = new RetResult(); @@ -203,7 +195,7 @@ public class CoreConfigV2rayService } } - public async Task GenerateClientSpeedtestConfig(List selecteds) + public override async Task GenerateClientSpeedtestConfig(List selecteds) { var ret = new RetResult(); try @@ -355,7 +347,7 @@ public class CoreConfigV2rayService } } - public async Task GenerateClientSpeedtestConfig(ProfileItem node, int port) + public override async Task GenerateClientSpeedtestConfig(ProfileItem node, int port) { var ret = new RetResult(); try @@ -413,7 +405,7 @@ public class CoreConfigV2rayService } } - public async Task GeneratePureEndpointConfig(ProfileItem node) + protected override async Task GeneratePassthroughConfig(ProfileItem node, int port) { var ret = new RetResult(); try @@ -449,13 +441,18 @@ public class CoreConfigV2rayService await GenLog(v2rayConfig); - var inbound = GetInbound(_config.Inbound.First(), EInboundProtocol.split, true); - inbound.sniffing = new Sniffing4Ray + v2rayConfig.inbounds = new() { new() { - enabled = false - }; - - v2rayConfig.inbounds = new() { inbound }; + tag = EInboundProtocol.socks.ToString(), + listen = Global.Loopback, + port = port, + protocol = EInboundProtocol.mixed.ToString(), + settings = new Inboundsettings4Ray() + { + udp = true, + auth = "noauth" + }, + } }; await GenOutbound(node, v2rayConfig.outbounds.First()); diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigBrookService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigBrookService.cs index 6ef1353e..b5f34dbe 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigBrookService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigBrookService.cs @@ -1,15 +1,7 @@ namespace ServiceLib.Services.CoreConfig.Minimal; -public class CoreConfigBrookService +public class CoreConfigBrookService(Config config) : CoreConfigServiceMinimalBase(config) { - private Config _config; - private static readonly string _tag = "CoreConfigBrookService"; - - public CoreConfigBrookService(Config config) - { - _config = config; - } - - public async Task GeneratePureEndpointConfig(ProfileItem node) + protected override async Task GeneratePassthroughConfig(ProfileItem node, int port) { var ret = new RetResult(); try @@ -30,7 +22,7 @@ public class CoreConfigBrookService var processArgs = "client"; // inbound - processArgs += " --socks5 " + Global.Loopback + ":" + AppHandler.Instance.GetLocalPort(EInboundProtocol.split).ToString(); + processArgs += " --socks5 " + Global.Loopback + ":" + port.ToString(); // outbound processArgs += " --server " + node.Address + ":" + node.Port; diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigClashService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigClashService.cs similarity index 93% rename from v2rayN/ServiceLib/Services/CoreConfig/CoreConfigClashService.cs rename to v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigClashService.cs index 481ae92d..313dabf0 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigClashService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigClashService.cs @@ -1,19 +1,21 @@ -namespace ServiceLib.Services.CoreConfig; +namespace ServiceLib.Services.CoreConfig.Minimal; /// /// Core configuration file processing class /// -public class CoreConfigClashService +public class CoreConfigClashService(Config config) : CoreConfigServiceMinimalBase(config) { - private Config _config; - private static readonly string _tag = "CoreConfigClashService"; - - public CoreConfigClashService(Config config) + protected override async Task GeneratePassthroughConfig(ProfileItem node, int port) { - _config = config; + var ret = new RetResult + { + Success = false, + Msg = ResUI.OperationFailed + }; + return await Task.FromResult(ret); } - - public async Task GenerateClientCustomConfig(ProfileItem node, string? fileName) + + public override async Task GenerateClientCustomConfig(ProfileItem node, string? fileName) { var ret = new RetResult(); if (node == null || fileName is null) diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigHy2Service.cs b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigHy2Service.cs index 819a3e55..fe269a5b 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigHy2Service.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigHy2Service.cs @@ -1,17 +1,9 @@ using System.Text.Json.Nodes; namespace ServiceLib.Services.CoreConfig.Minimal; -public class CoreConfigHy2Service +public class CoreConfigHy2Service(Config config) : CoreConfigServiceMinimalBase(config) { - private Config _config; - private static readonly string _tag = "CoreConfigHy2Service"; - - public CoreConfigHy2Service(Config config) - { - _config = config; - } - - public async Task GeneratePureEndpointConfig(ProfileItem node) + protected override async Task GeneratePassthroughConfig(ProfileItem node, int port) { var ret = new RetResult(); try @@ -34,14 +26,14 @@ public class CoreConfigHy2Service // inbound configJsonNode["socks5"] = new JsonObject { - ["listen"] = Global.Loopback + ":" + AppHandler.Instance.GetLocalPort(EInboundProtocol.split).ToString() + ["listen"] = Global.Loopback + ":" + port.ToString() }; // outbound - var port = string.Empty; + var outboundPort = string.Empty; if (node.Ports.IsNotEmpty()) { - port = node.Ports.Replace(':', '-'); + outboundPort = node.Ports.Replace(':', '-'); if (_config.HysteriaItem.HopInterval > 0) { configJsonNode["transport"] = new JsonObject @@ -55,9 +47,9 @@ public class CoreConfigHy2Service } else { - port = node.Port.ToString(); + outboundPort = node.Port.ToString(); } - configJsonNode["server"] = node.Address + ":" + port; + configJsonNode["server"] = node.Address + ":" + outboundPort; configJsonNode["auth"] = node.Id; if (node.Sni.IsNotEmpty()) diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigJuicityService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigJuicityService.cs index 66fccbbd..0d73147d 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigJuicityService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigJuicityService.cs @@ -1,17 +1,9 @@ using System.Text.Json.Nodes; namespace ServiceLib.Services.CoreConfig.Minimal; -public class CoreConfigJuicityService +public class CoreConfigJuicityService(Config config) : CoreConfigServiceMinimalBase(config) { - private Config _config; - private static readonly string _tag = "CoreConfigJuicityService"; - - public CoreConfigJuicityService(Config config) - { - _config = config; - } - - public async Task GeneratePureEndpointConfig(ProfileItem node) + protected override async Task GeneratePassthroughConfig(ProfileItem node, int port) { var ret = new RetResult(); try @@ -45,7 +37,7 @@ public class CoreConfigJuicityService configJsonNode["log_level"] = logLevel; // inbound - configJsonNode["listen"] = ":" + AppHandler.Instance.GetLocalPort(EInboundProtocol.split).ToString(); + configJsonNode["listen"] = ":" + port.ToString(); // outbound configJsonNode["server"] = node.Address + ":" + node.Port; diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigNaiveService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigNaiveService.cs index 0e1230cc..b86e053a 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigNaiveService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigNaiveService.cs @@ -1,17 +1,9 @@ using System.Text.Json.Nodes; namespace ServiceLib.Services.CoreConfig.Minimal; -public class CoreConfigNaiveService +public class CoreConfigNaiveService(Config config) : CoreConfigServiceMinimalBase(config) { - private Config _config; - private static readonly string _tag = "CoreConfigNaiveService"; - - public CoreConfigNaiveService(Config config) - { - _config = config; - } - - public async Task GeneratePureEndpointConfig(ProfileItem node) + protected override async Task GeneratePassthroughConfig(ProfileItem node, int port) { var ret = new RetResult(); try @@ -32,7 +24,7 @@ public class CoreConfigNaiveService var configJsonNode = new JsonObject(); // inbound - configJsonNode["listen"] = Global.SocksProtocol + Global.Loopback + ":" + AppHandler.Instance.GetLocalPort(EInboundProtocol.split).ToString(); + configJsonNode["listen"] = Global.SocksProtocol + Global.Loopback + ":" + port.ToString(); // outbound configJsonNode["proxy"] = (node.HeaderType == "quic" ? "quic://" : Global.HttpsProtocol) + node.Id + "@" + node.Address + ":" + node.Port; diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigShadowquicService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigShadowquicService.cs index c16c99f3..86ec9c53 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigShadowquicService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigShadowquicService.cs @@ -1,17 +1,9 @@ using System.Text.Json.Nodes; namespace ServiceLib.Services.CoreConfig.Minimal; -public class CoreConfigShadowquicService +public class CoreConfigShadowquicService(Config config) : CoreConfigServiceMinimalBase(config) { - private Config _config; - private static readonly string _tag = "CoreConfigShadowquicService"; - - public CoreConfigShadowquicService(Config config) - { - _config = config; - } - - public async Task GeneratePureEndpointConfig(ProfileItem node) + protected override async Task GeneratePassthroughConfig(ProfileItem node, int port) { var ret = new RetResult(); try @@ -48,7 +40,7 @@ public class CoreConfigShadowquicService var inboundNode = new Dictionary { ["type"] = "socks5", - ["listen"] = Global.Loopback + ":" + AppHandler.Instance.GetLocalPort(EInboundProtocol.split).ToString() + ["listen"] = Global.Loopback + ":" + port.ToString() }; configYamlNode["inbound"] = inboundNode; diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigTuicService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigTuicService.cs index 55dfecb1..0edc612c 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigTuicService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigTuicService.cs @@ -1,17 +1,9 @@ using System.Text.Json.Nodes; namespace ServiceLib.Services.CoreConfig.Minimal; -public class CoreConfigTuicService +public class CoreConfigTuicService(Config config) : CoreConfigServiceMinimalBase(config) { - private Config _config; - private static readonly string _tag = "CoreConfigTuicService"; - - public CoreConfigTuicService(Config config) - { - _config = config; - } - - public async Task GeneratePureEndpointConfig(ProfileItem node) + protected override async Task GeneratePassthroughConfig(ProfileItem node, int port) { var ret = new RetResult(); try @@ -47,7 +39,7 @@ public class CoreConfigTuicService // inbound configJsonNode["local"] = new JsonObject { - ["server"] = Global.Loopback + ":" + AppHandler.Instance.GetLocalPort(EInboundProtocol.split).ToString() + ["server"] = Global.Loopback + ":" + port.ToString() }; // outbound diff --git a/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs b/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs index f312b890..3c25b073 100644 --- a/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs @@ -770,7 +770,8 @@ public class ProfilesViewModel : MyReactiveObject } if (blClipboard) { - var result = await CoreConfigHandler.GenerateClientConfig(item, null); + var coreLaunchContext = new CoreLaunchContext(item, _config); + var result = await CoreConfigHandler.GenerateClientConfig(coreLaunchContext, null); if (result.Success != true) { NoticeHandler.Instance.Enqueue(result.Msg); @@ -793,7 +794,8 @@ public class ProfilesViewModel : MyReactiveObject { return; } - var result = await CoreConfigHandler.GenerateClientConfig(item, fileName); + var coreLaunchContext = new CoreLaunchContext(item, _config); + var result = await CoreConfigHandler.GenerateClientConfig(coreLaunchContext, fileName); if (result.Success != true) { NoticeHandler.Instance.Enqueue(result.Msg); From 2910f4838e8b26ba21721fc6b851f8546d5a4329 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Thu, 31 Jul 2025 10:54:01 +0800 Subject: [PATCH 52/54] Fix missing hysteria2 arguments --- v2rayN/ServiceLib/Handler/CoreInfoHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v2rayN/ServiceLib/Handler/CoreInfoHandler.cs b/v2rayN/ServiceLib/Handler/CoreInfoHandler.cs index 6b7e1df2..befd80fa 100644 --- a/v2rayN/ServiceLib/Handler/CoreInfoHandler.cs +++ b/v2rayN/ServiceLib/Handler/CoreInfoHandler.cs @@ -130,7 +130,7 @@ public sealed class CoreInfoHandler { CoreType = ECoreType.hysteria, CoreExes = ["hysteria"], - Arguments = "", + Arguments = "-c {0}", Url = GetCoreUrl(ECoreType.hysteria), }, @@ -180,7 +180,7 @@ public sealed class CoreInfoHandler { CoreType = ECoreType.hysteria2, CoreExes = ["hysteria-windows-amd64", "hysteria-linux-amd64", "hysteria"], - Arguments = "", + Arguments = "-c {0}", Url = GetCoreUrl(ECoreType.hysteria2), }, From 50dcae9a0483725ff9eac35230a796c5785c85f0 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Fri, 1 Aug 2025 11:29:29 +0800 Subject: [PATCH 53/54] Fix hy2 custom config --- .../ServiceLib/Handler/CoreConfigHandler.cs | 2 + .../Minimal/CoreConfigHy2Service.cs | 69 +++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs b/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs index 780e6f2b..ae68ce80 100644 --- a/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs @@ -207,6 +207,8 @@ public class CoreConfigHandler return new CoreConfigClashService(AppHandler.Instance.Config); case ECoreType.sing_box: return new CoreConfigSingboxService(AppHandler.Instance.Config); + case ECoreType.hysteria2: + return new CoreConfigHy2Service(AppHandler.Instance.Config); default: // CoreConfigServiceMinimalBase return new CoreConfigV2rayService(AppHandler.Instance.Config); diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigHy2Service.cs b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigHy2Service.cs index fe269a5b..9f899969 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigHy2Service.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Minimal/CoreConfigHy2Service.cs @@ -99,4 +99,73 @@ public class CoreConfigHy2Service(Config config) : CoreConfigServiceMinimalBase( return await Task.FromResult(ret); } } + + public override async Task GenerateClientCustomConfig(ProfileItem node, string? fileName) + { + var ret = new RetResult(); + try + { + if (node == null || fileName is null) + { + ret.Msg = ResUI.CheckServerSettings; + return ret; + } + + if (File.Exists(fileName)) + { + File.SetAttributes(fileName, FileAttributes.Normal); //If the file has a read-only attribute, direct deletion will fail + File.Delete(fileName); + } + + string addressFileName = node.Address; + if (!File.Exists(addressFileName)) + { + addressFileName = Utils.GetConfigPath(addressFileName); + } + if (!File.Exists(addressFileName)) + { + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return ret; + } + // Try deserializing the file to check if it is a valid JSON or YAML file + var fileContent = File.ReadAllText(addressFileName); + var jsonContent = JsonUtils.Deserialize(fileContent); + if (jsonContent != null) + { + File.Copy(addressFileName, fileName); + } + else + { + // If it's YAML, convert to JSON and write it + var yamlContent = YamlUtils.FromYaml>(fileContent); + if (yamlContent != null) + { + File.WriteAllText(fileName, JsonUtils.Serialize(yamlContent, true)); + } + else + { + ret.Msg = ResUI.FailedReadConfiguration + "2"; + return ret; + } + } + File.SetAttributes(fileName, FileAttributes.Normal); //Copy will keep the attributes of addressFileName, so we need to add write permissions to fileName just in case of addressFileName is a read-only file. + + //check again + if (!File.Exists(fileName)) + { + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return ret; + } + + ret.Msg = string.Format(ResUI.SuccessfulConfiguration, ""); + ret.Success = true; + return await Task.FromResult(ret); + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + ret.Msg = ResUI.FailedGenDefaultConfiguration; + return ret; + } + } } From 739a5aeaa272329d2c973d216bfab8cda611c193 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Fri, 1 Aug 2025 11:30:07 +0800 Subject: [PATCH 54/54] Fix custom config core type not working --- v2rayN/ServiceLib/Handler/AppHandler.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/v2rayN/ServiceLib/Handler/AppHandler.cs b/v2rayN/ServiceLib/Handler/AppHandler.cs index 699162a5..f8495a79 100644 --- a/v2rayN/ServiceLib/Handler/AppHandler.cs +++ b/v2rayN/ServiceLib/Handler/AppHandler.cs @@ -235,6 +235,11 @@ public sealed class AppHandler return (ECoreType)profileItem.CoreType; } + return GetCoreType(eConfigType); + } + + public ECoreType GetCoreType(EConfigType eConfigType) + { var item = _config.CoreTypeItem?.FirstOrDefault(it => it.ConfigType == eConfigType); return item?.CoreType ?? ECoreType.Xray; } @@ -246,6 +251,11 @@ public sealed class AppHandler return (ECoreType)profileItem.CoreType; } + return GetSplitCoreType(eConfigType); + } + + public ECoreType GetSplitCoreType(EConfigType eConfigType) + { var item = _config.SplitCoreItem.SplitCoreTypes?.FirstOrDefault(it => it.ConfigType == eConfigType); return item?.CoreType ?? ECoreType.Xray; } @@ -266,7 +276,7 @@ public sealed class AppHandler coreType = profileItem.CoreType ?? ECoreType.Xray; if (profileItem.PreSocksPort > 0) { - preCoreType = enableTun ? ECoreType.sing_box : GetCoreType(profileItem, profileItem.ConfigType); + preCoreType = enableTun ? ECoreType.sing_box : GetCoreType(profileItem.ConfigType); } else {